mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-10-07 21:00:21 +08:00
♻️ refactor(core): 重构 AI 编排框架与记忆及 RAG 子系统 (#2149)
* ♻️ refactor(core): 重构 AI 编排框架与记忆及 RAG 子系统 - 【重构】重构 `BaseRunnable` 并引入统一的 `RunIntent` 意图载体,规范 Agent、Team 和 Workflow 的执行流 - 【解耦】将中期记忆槽和长期向量记忆从 `MemoryConfig` 中解耦,转为独立的能力组件与工具箱进行管理 - 【记忆】移除 `MemoryReader` 和 `MemoryWriter`,统一封装为 `SessionMemoryContext` 会话记忆门面 - 【RAG】重构检索器与存储后端接口,统一采用 `QueryRequest` 进行多维度联合检索,并引入 `InMemoryScorer` 提升打分性能 - 【事件】优化 `EventBus` 异步事件分发机制,引入队列机制确保事件按序处理,避免并发竞态问题 - 【依赖注入】移除 `memory` 注入项,优化 `DependencyInjector` 的签名解析缓存以提升性能 * 🚨 auto fix by pre-commit hooks --------- Co-authored-by: webjoin111 <455457521@qq.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
webjoin111
pre-commit-ci[bot]
parent
922d092650
commit
52f7dbdedf
@@ -1,7 +1,8 @@
|
||||
from collections import defaultdict
|
||||
from collections.abc import Callable
|
||||
from typing import Any, cast
|
||||
|
||||
from zhenxun.services.ai.context.rag.backends import Embedder, StorageBackend
|
||||
from zhenxun.services.ai.context.rag.backends import StorageBackend
|
||||
from zhenxun.services.ai.utils.logger import log_memory as logger
|
||||
from zhenxun.services.ai.utils.scope import BaseScopeBuilder
|
||||
from zhenxun.utils.utils import infer_plugin_namespace
|
||||
@@ -9,11 +10,10 @@ from zhenxun.utils.utils import infer_plugin_namespace
|
||||
from .models import MemoryConfig
|
||||
from .storage.backends import (
|
||||
InMemoryChatContext,
|
||||
MemoryScope,
|
||||
)
|
||||
from .storage.interfaces import (
|
||||
BaseChatContext,
|
||||
BaseSlotContext,
|
||||
IClearableBackend,
|
||||
)
|
||||
|
||||
|
||||
@@ -33,41 +33,46 @@ class MemoryCleaner(BaseScopeBuilder["MemoryCleaner"]):
|
||||
self._config = cfg.build() if hasattr(cfg, "build") else cfg
|
||||
return self
|
||||
|
||||
async def clear_target(self, target_name: str):
|
||||
"""底层派发器:定向清理指定注册名称的泛型扩展后端数据"""
|
||||
ns_dict = self.manager._backends.get(target_name, {})
|
||||
for backend in ns_dict.values():
|
||||
if isinstance(backend, IClearableBackend):
|
||||
await backend.clear_by_query(self._selector)
|
||||
else:
|
||||
logger.warning(
|
||||
f"后端 {backend.__class__.__name__}"
|
||||
"未实现 IClearableBackend 协议,已跳过清理。"
|
||||
)
|
||||
|
||||
async def clear_short_term(self):
|
||||
"""一键清理目标范围下的短期对话历史记忆"""
|
||||
if self._config and self._config.short_term.backend:
|
||||
if isinstance(self._config, MemoryConfig) and isinstance(
|
||||
self._config.short_term.backend, IClearableBackend
|
||||
):
|
||||
await self._config.short_term.backend.clear_by_query(self._selector)
|
||||
else:
|
||||
for backend in self.manager._chat_backends.values():
|
||||
await backend.clear_by_query(self._selector)
|
||||
await self.clear_target("chat")
|
||||
|
||||
async def clear_slots(self):
|
||||
"""一键清理目标范围下的中期记忆槽 (Memory Slots)"""
|
||||
if self._config and self._config.slots.backend:
|
||||
await self._config.slots.backend.clear_by_query(self._selector)
|
||||
else:
|
||||
for backend in self.manager._slot_backends.values():
|
||||
await backend.clear_by_query(self._selector)
|
||||
"""一键清理目标范围下的记忆槽数据"""
|
||||
await self.clear_target("slots")
|
||||
|
||||
async def clear_long_term(self):
|
||||
"""一键清理目标范围下的长期向量记忆 (RAG Vector Database)"""
|
||||
if self._config and self._config.long_term.backend:
|
||||
from zhenxun.services.ai.context.rag.backends import StorageBackend
|
||||
|
||||
storage = cast(StorageBackend, self._config.long_term.backend)
|
||||
await storage.clear_by_query(self._selector)
|
||||
else:
|
||||
for factory in self.manager._storage_factories.values():
|
||||
storage = factory()
|
||||
if hasattr(storage, "clear_by_query"):
|
||||
await storage.clear_by_query(self._selector)
|
||||
else:
|
||||
await storage.delete(scope_prefix=self._selector.scope_prefix)
|
||||
for factory in self.manager._storage_factories.values():
|
||||
storage = factory()
|
||||
if isinstance(storage, IClearableBackend):
|
||||
await storage.clear_by_query(self._selector)
|
||||
|
||||
async def clear_all(self):
|
||||
"""一键清理指定范围下的所有生命周期记忆(对话、槽位、RAG)"""
|
||||
await self.clear_short_term()
|
||||
await self.clear_slots()
|
||||
"""一键清理指定范围下的所有生命周期记忆(对话、记忆槽、RAG、及其他泛型扩展后端)"""
|
||||
if isinstance(self._config, MemoryConfig) and isinstance(
|
||||
self._config.short_term.backend, IClearableBackend
|
||||
):
|
||||
await self._config.short_term.backend.clear_by_query(self._selector)
|
||||
for target_name in self.manager._backends:
|
||||
await self.clear_target(target_name)
|
||||
await self.clear_long_term()
|
||||
logger.info(
|
||||
f"🧹 成功清理作用域 '{self._selector.scope_prefix}'下的所有记忆痕迹!"
|
||||
@@ -81,10 +86,8 @@ class GlobalMemoryManager:
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._chat_backends: dict[str, BaseChatContext] = {
|
||||
"global": InMemoryChatContext()
|
||||
}
|
||||
self._slot_backends: dict[str, BaseSlotContext] = {}
|
||||
self._backends: dict[str, dict[str, Any]] = defaultdict(dict)
|
||||
self.register_backend("chat", InMemoryChatContext(), "global")
|
||||
|
||||
from zhenxun.services.ai.context.rag.backends import DictStorageBackend
|
||||
|
||||
@@ -92,19 +95,23 @@ class GlobalMemoryManager:
|
||||
"global": lambda: DictStorageBackend()
|
||||
}
|
||||
|
||||
def register_backend(
|
||||
self, backend_type: str, backend: Any, scope: str | None = None
|
||||
) -> None:
|
||||
"""泛型注册:注册任意类型的存储后端"""
|
||||
ns = scope if scope is not None else infer_plugin_namespace()
|
||||
self._backends[backend_type][ns] = backend
|
||||
|
||||
def get_backend(self, backend_type: str, namespace: str = "global") -> Any | None:
|
||||
"""泛型获取:获取任意类型的存储后端"""
|
||||
backends = self._backends.get(backend_type, {})
|
||||
return backends.get(namespace) or backends.get("global")
|
||||
|
||||
def register_chat_backend(
|
||||
self, backend: BaseChatContext, scope: str | None = None
|
||||
) -> None:
|
||||
"""注册特定命名空间的短期记忆存储后端。"""
|
||||
ns = scope if scope is not None else infer_plugin_namespace()
|
||||
self._chat_backends[ns] = backend
|
||||
|
||||
def register_slot_backend(
|
||||
self, backend: BaseSlotContext, scope: str | None = None
|
||||
) -> None:
|
||||
"""注册特定命名空间的中期记忆槽存储后端。"""
|
||||
ns = scope if scope is not None else infer_plugin_namespace()
|
||||
self._slot_backends[ns] = backend
|
||||
self.register_backend("chat", backend, scope)
|
||||
|
||||
def register_storage_factory(
|
||||
self, factory: Callable[[], StorageBackend], scope: str | None = None
|
||||
@@ -117,20 +124,6 @@ class GlobalMemoryManager:
|
||||
"""获取声明式记忆清理构建器,供第三方开发者极速清理指定记忆"""
|
||||
return MemoryCleaner(self)
|
||||
|
||||
def get_embedder(self, embedder_val: "Embedder | str | None") -> Embedder | None:
|
||||
"""获取向量化引擎实例。如果传入的是字符串,则视为 API 模型名称。"""
|
||||
if not embedder_val:
|
||||
return None
|
||||
|
||||
if isinstance(embedder_val, str):
|
||||
from zhenxun.services.ai.context.rag.backends.embedders import (
|
||||
DefaultEmbedder,
|
||||
)
|
||||
|
||||
return DefaultEmbedder(model_name=embedder_val)
|
||||
|
||||
return embedder_val
|
||||
|
||||
def get_chat_context(
|
||||
self, config: MemoryConfig | None, namespace: str = "global"
|
||||
) -> BaseChatContext | None:
|
||||
@@ -142,69 +135,7 @@ class GlobalMemoryManager:
|
||||
if backend_cfg is not None:
|
||||
return cast(BaseChatContext, backend_cfg)
|
||||
|
||||
return self._chat_backends.get(namespace) or self._chat_backends["global"]
|
||||
|
||||
def get_slot_context(
|
||||
self, config: MemoryConfig | None, namespace: str = "global"
|
||||
) -> BaseSlotContext | None:
|
||||
"""根据配置分配对应的槽位记忆实例"""
|
||||
if not config or not config.slots.enable:
|
||||
return None
|
||||
|
||||
backend_cfg = config.slots.backend
|
||||
if backend_cfg is not None:
|
||||
return cast(BaseSlotContext, backend_cfg)
|
||||
|
||||
return self._slot_backends.get(namespace) or self._slot_backends["global"]
|
||||
|
||||
def get_long_term_memory(
|
||||
self, config: MemoryConfig | None, namespace: str = "global"
|
||||
) -> MemoryScope | None:
|
||||
"""根据声明式配置动态组装长期向量记忆实例"""
|
||||
if not config or not config.long_term.enable:
|
||||
return None
|
||||
|
||||
if config.long_term.engine is not None:
|
||||
return MemoryScope(
|
||||
rag_client=config.long_term.engine,
|
||||
)
|
||||
|
||||
storage_instance = None
|
||||
backend_cfg = config.long_term.backend
|
||||
if backend_cfg is not None:
|
||||
storage_instance = cast(StorageBackend, backend_cfg)
|
||||
else:
|
||||
factory = (
|
||||
self._storage_factories.get(namespace)
|
||||
or self._storage_factories["global"]
|
||||
)
|
||||
storage_instance = factory()
|
||||
|
||||
embedder = self.get_embedder(config.long_term.embedder)
|
||||
|
||||
from zhenxun.services.ai.context.rag.builder import RAGBuilder
|
||||
|
||||
builder = RAGBuilder(storage_instance).with_scope("/")
|
||||
if embedder:
|
||||
builder.with_embedder(embedder)
|
||||
|
||||
from .models import MemoryScoringConfig
|
||||
|
||||
scoring_cfg = MemoryScoringConfig()
|
||||
|
||||
builder.enable_lifecycle_scoring(
|
||||
half_life_days=scoring_cfg.recency_half_life_days,
|
||||
decay_weight=scoring_cfg.recency_weight,
|
||||
semantic_weight=scoring_cfg.semantic_weight,
|
||||
importance_weight=scoring_cfg.importance_weight,
|
||||
reinforcement_weight=scoring_cfg.reinforcement_weight,
|
||||
)
|
||||
|
||||
client = builder.build()
|
||||
|
||||
return MemoryScope(
|
||||
rag_client=client,
|
||||
)
|
||||
return self.get_backend("chat", namespace)
|
||||
|
||||
|
||||
memory_manager = GlobalMemoryManager()
|
||||
|
||||
Reference in New Issue
Block a user