♻️ 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:
Rumio
2026-07-14 16:48:33 +08:00
committed by GitHub
co-authored by webjoin111 pre-commit-ci[bot]
parent 922d092650
commit 52f7dbdedf
66 changed files with 2131 additions and 2353 deletions
@@ -9,25 +9,26 @@ from zhenxun.services.ai.capabilities import (
CombinedCapability,
)
from zhenxun.services.ai.context.memory.builder import MemoryBuilder
from zhenxun.services.ai.context.memory.engine import MemoryReader, MemoryWriter
from zhenxun.services.ai.context.memory.engine import SessionMemoryContext
from zhenxun.services.ai.context.memory.models import MemoryConfig
from zhenxun.services.ai.context.memory.types import SessionMetadata
from zhenxun.services.ai.core.messages import LLMMessage, TextPart
from zhenxun.services.ai.core.options import GenerationConfig
from zhenxun.services.ai.core.options import BaseOutputDefinition, GenerationConfig
from zhenxun.services.ai.core.protocols.tool import ToolExecutable
from zhenxun.services.ai.core.templates import PromptTemplate
from zhenxun.services.ai.flow.agent.capabilities import (
OutputValidationCapability,
TaskTrackingCapability,
)
from zhenxun.services.ai.flow.agent.models import Persona
from zhenxun.services.ai.run import RunContext
from zhenxun.services.ai.run import AgentTask, RunContext
from zhenxun.services.ai.run.di import DependencyInjector
from zhenxun.services.ai.tools.engine.registry import (
ToolCollection,
tool_provider_manager,
)
from zhenxun.services.ai.tools.models import ResolvedToolPayload
from zhenxun.services.ai.utils.scope import ScopeSelector
from zhenxun.services.ai.utils.runtime import ContextUtils
from zhenxun.utils.pydantic_compat import model_copy
@@ -36,7 +37,8 @@ class AgentProfileResolver:
@staticmethod
def resolve_memory(
agent_memory_config: MemoryConfig, override_memory: Any | None
agent_memory_config: MemoryConfig,
override_memory: bool | MemoryConfig | MemoryBuilder | None,
) -> MemoryConfig:
"""
解析并合并 Memory 记忆域的配置。
@@ -86,11 +88,11 @@ class CapabilityBuilder:
async def build_for_run(
agent_name: str,
namespace: str,
output_type: Any | None,
output_type: type[Any] | BaseOutputDefinition | None,
raw_schema: dict | None,
agent_guardrails: list,
task_guardrails: list,
task_obj: Any | None,
task_obj: AgentTask | None,
agent_capabilities: list,
profile_capabilities: list | None,
context: RunContext,
@@ -160,11 +162,11 @@ class ContextBuilder:
@staticmethod
async def build_prompts(
instruction: str | PromptTemplate,
system_prompts: list[Any],
system_prompts: list[Callable],
run_context: RunContext,
run_scoped_cap: CombinedCapability,
persona: Persona | None = None,
) -> tuple[str, list[Any]]:
) -> tuple[str, list[LLMMessage]]:
"""
解析、合并并渲染 Agent 的系统提示词和上下文记忆。
包含对依赖参数的动态注入和 Jinja 模板渲染。
@@ -291,9 +293,9 @@ class ToolBuilder:
@staticmethod
async def resolve_tools(
tool_definitions: list[Any],
toolset_funcs: list[Any],
system_tools: list[Any],
tool_definitions: list[ToolExecutable | Callable | dict[str, Any] | str],
toolset_funcs: list[Callable],
system_tools: list[ToolExecutable | Callable | dict[str, Any] | str],
namespace: str,
run_context: RunContext,
run_scoped_cap: CombinedCapability,
@@ -359,7 +361,7 @@ class ToolBuilder:
@staticmethod
async def prepare_effective_tools(
effective_tools: list[Any],
effective_tools: list[ToolExecutable],
context: RunContext,
tool_filters: list[Callable],
run_scoped_cap: CombinedCapability,
@@ -407,11 +409,15 @@ class ToolBuilder:
final_defs_map = {d.name.lower(): d for d in current_tool_defs if d}
final_effective_tools_list = []
seen_names = set()
for t_exec in effective_tools:
t_name = getattr(t_exec, "name", "unknown")
if t_name.lower() in final_defs_map:
t_name_lower = t_name.lower()
if t_name_lower in final_defs_map and t_name_lower not in seen_names:
seen_names.add(t_name_lower)
cloned_tool = copy.copy(t_exec)
cloned_tool._dynamic_def = final_defs_map[t_name.lower()]
setattr(cloned_tool, "_dynamic_def", final_defs_map[t_name_lower])
final_effective_tools_list.append(cloned_tool)
return ToolCollection(final_effective_tools_list)
@@ -425,7 +431,7 @@ class SessionBuilder:
namespace: str,
agent_name: str,
effective_memory: MemoryConfig,
) -> tuple[Any, Any, Any]:
) -> tuple[SessionMetadata, SessionMemoryContext]:
"""
根据当前用户、群组和平台标识,动态隔离前缀,计算并构建会话与记忆存储的读写门面。
@@ -436,78 +442,21 @@ class SessionBuilder:
effective_memory: 运行时最终生效的 MemoryConfig 配置对象。
返回:
tuple[Any, Any, Any]: 包含 (SessionMetadata 会话元数据, MemoryReader 记忆读取器, MemoryWriter 记忆写入器) 的元组。
tuple[SessionMetadata, SessionMemoryContext]: 包含 (会话元数据, 会话记忆门面上下文) 的元组。
""" # noqa: E501
bot_id = None
bot_inst = context.get_bot()
if bot_inst and hasattr(bot_inst, "self_id"):
bot_id = str(bot_inst.self_id)
selector = ScopeSelector(
user_id=context.get_user_id(),
group_id=context.get_group_id(),
platform=context.get_platform(),
bot_id=bot_id,
namespace=namespace,
agent_name=agent_name,
)
all_scopes = {"/"}
scope_name_mapping = {}
target_builder = None
if effective_memory.short_term and effective_memory.short_term.isolation:
sel = effective_memory.short_term.isolation.resolve(
deps=context.deps,
prefix="",
default_namespace=namespace,
default_agent=agent_name,
)
all_scopes.add(sel.scope_prefix)
target_builder = effective_memory.short_term.isolation
for config_part in [effective_memory.slots, effective_memory.long_term]:
if config_part and hasattr(config_part, "scopes") and config_part.scopes:
for name, builder in config_part.scopes.items():
sel = builder.resolve(
deps=context.deps,
prefix="",
default_namespace=namespace,
default_agent=agent_name,
)
all_scopes.add(sel.scope_prefix)
scope_name_mapping[sel.scope_prefix] = name
parts = selector.get_scope_parts()
for i in range(len(parts)):
all_scopes.add("/" + "/".join(parts[: i + 1]))
accessible_scopes = sorted(all_scopes, key=lambda x: len(x.split("/")))
short_term_builder = (
effective_memory.short_term.isolation
if effective_memory.short_term
else effective_memory.base_isolation
)
short_term_selector = short_term_builder.resolve(
deps=context.deps,
prefix="",
default_namespace=namespace,
default_agent=agent_name,
session_metadata = ContextUtils.build_session_meta(
context=context, target_builder=target_builder, custom_namespace=namespace
)
context.session.session_meta = session_metadata
session_metadata = SessionMetadata(
session_id=short_term_selector.scope_prefix,
selector=selector,
scope_prefix=selector.scope_prefix,
accessible_scopes=accessible_scopes,
scope_name_mapping=scope_name_mapping,
)
reader = MemoryReader(
session_meta=session_metadata, memory_config=effective_memory
)
writer = MemoryWriter(
memory_context = SessionMemoryContext(
session_meta=session_metadata,
memory_config=effective_memory,
context=context,
)
return session_metadata, reader, writer
return session_metadata, memory_context
@@ -1,7 +1,8 @@
from abc import ABC, abstractmethod
import asyncio
from collections.abc import Awaitable, Callable
import json
from typing import Any
from typing import Any, Literal, cast
from zhenxun.services.ai.capabilities import CombinedCapability
from zhenxun.services.ai.core.engine.context_renderer import ContextConverter
@@ -27,8 +28,9 @@ from zhenxun.services.ai.core.messages import (
ToolReturnPart,
VideoPart,
)
from zhenxun.services.ai.core.models import LLMContext
from zhenxun.services.ai.core.models import CancellationToken, LLMContext, ToolChoice
from zhenxun.services.ai.core.options import GenerationConfig
from zhenxun.services.ai.core.protocols.tool import ToolExecutable
from zhenxun.services.ai.core.stream_events import (
LLMEndEvent,
LLMStartEvent,
@@ -37,7 +39,8 @@ from zhenxun.services.ai.core.stream_events import (
from zhenxun.services.ai.flow.agent.models import AgentRunResources, AgentState
from zhenxun.services.ai.llm.engine.router import LLMOrchestrator
from zhenxun.services.ai.run import AgentRunResult, RunContext
from zhenxun.services.ai.run.session import session_manager
from zhenxun.services.ai.run.models import OutputDataT
from zhenxun.services.ai.run.session import SessionInfo, session_manager
from zhenxun.services.ai.tools.engine.executor import ToolExecutor
from zhenxun.services.ai.tools.models import ToolResult
from zhenxun.services.ai.utils.logger import log_agent as logger
@@ -57,7 +60,7 @@ class BaseAgentExecutor(ABC):
async def run(
self, state: AgentState, resources: AgentRunResources
) -> AgentRunResult[Any]:
) -> AgentRunResult[OutputDataT]:
"""
核心模板方法 (Template Method)。
组织整个大模型推导与工具调用的生命周期循环。如无必要,请勿重写此方法。
@@ -156,7 +159,7 @@ class BaseAgentExecutor(ABC):
@abstractmethod
async def on_fallback(
self, state: AgentState, resources: AgentRunResources
) -> AgentRunResult[Any]:
) -> AgentRunResult[OutputDataT]:
"""生命周期: 当大模型思考循环达到 max_cycles 时触发,执行兜底策略。"""
pass
@@ -181,7 +184,7 @@ class StandardAgentExecutor(BaseAgentExecutor):
return result.is_retryable
def _check_follow_up(
self, state: AgentState, resources: AgentRunResources, session_info: Any
self, state: AgentState, resources: AgentRunResources, session_info: SessionInfo
) -> bool:
"""检查追加队列,排空并合并数据到上下文,返回是否发现新消息"""
follow_ups = session_info.follow_up_queue.drain()
@@ -201,8 +204,8 @@ class StandardAgentExecutor(BaseAgentExecutor):
state: AgentState,
resources: AgentRunResources,
messages: list[AgentMessage],
tools: list[Any] | None,
tool_choice: Any = None,
tools: list[ToolExecutable | dict[str, Any]] | None,
tool_choice: str | dict[str, Any] | ToolChoice | None = None,
) -> ChatResponse:
"""执行 LLM 请求,处理基础指标遥测统计,并将新对话上下文追加到状态流"""
run_context = resources.run_context
@@ -274,10 +277,10 @@ class StandardAgentExecutor(BaseAgentExecutor):
messages: list[LLMMessage],
config: GenerationConfig,
run_context: RunContext,
tools: list[Any] | None = None,
tool_choice: Any = None,
tools: list[ToolExecutable | dict[str, Any]] | None = None,
tool_choice: str | dict[str, Any] | ToolChoice | None = None,
extra: dict[str, Any] | None = None,
cancellation_token: Any = None,
cancellation_token: CancellationToken | None = None,
) -> ChatResponse:
request = ChatRequest(
messages=messages,
@@ -307,9 +310,24 @@ class StandardAgentExecutor(BaseAgentExecutor):
async def on_start(self, state: AgentState, resources: AgentRunResources) -> None:
resources.run_context.run.messages = state.messages
async def _execute_phase(
self,
phase_func: Callable[[AgentState, AgentRunResources], Awaitable[None]],
state: AgentState,
resources: AgentRunResources,
session_info: SessionInfo,
) -> Literal["NEXT", "RESET_CYCLE", "FINISH"]:
"""统一处理阶段执行与状态机完成/追加检查的高阶函数"""
await phase_func(state, resources)
if state.is_finished:
if self._check_follow_up(state, resources, session_info):
return "RESET_CYCLE"
return "FINISH"
return "NEXT"
async def run(
self, state: AgentState, resources: AgentRunResources
) -> AgentRunResult[Any]:
) -> AgentRunResult[OutputDataT]:
"""覆盖基类的模板方法,实现灵活的 while 控制流和 FOLLOW_UP 合并"""
await self.on_start(state, resources)
session_info = await session_manager.get_or_create(
@@ -325,29 +343,35 @@ class StandardAgentExecutor(BaseAgentExecutor):
await self.build_llm_request(state, resources)
await self.execute_llm(state, resources)
await self.handle_llm_response(state, resources)
if state.is_finished:
if self._check_follow_up(state, resources, session_info):
cycle_count = 0
continue
status = await self._execute_phase(
self.handle_llm_response, state, resources, session_info
)
if status == "RESET_CYCLE":
cycle_count = 0
continue
if status == "FINISH":
assert state.final_result is not None
return state.final_result
await self.filter_tool_calls(state, resources)
if state.is_finished:
if self._check_follow_up(state, resources, session_info):
cycle_count = 0
continue
status = await self._execute_phase(
self.filter_tool_calls, state, resources, session_info
)
if status == "RESET_CYCLE":
cycle_count = 0
continue
if status == "FINISH":
assert state.final_result is not None
return state.final_result
await self.execute_tools(state, resources)
await self.handle_tool_results(state, resources)
if state.is_finished:
if self._check_follow_up(state, resources, session_info):
cycle_count = 0
continue
status = await self._execute_phase(
self.handle_tool_results, state, resources, session_info
)
if status == "RESET_CYCLE":
cycle_count = 0
continue
if status == "FINISH":
assert state.final_result is not None
return state.final_result
@@ -544,7 +568,7 @@ class StandardAgentExecutor(BaseAgentExecutor):
def _assemble_tool_message(
self,
original_call: ToolCallPart,
res_or_exc: Any,
res_or_exc: BaseException | tuple[ToolCallPart, ToolResult],
tool_res: ToolResult | None,
state: AgentState,
) -> LLMMessage:
@@ -631,7 +655,7 @@ class StandardAgentExecutor(BaseAgentExecutor):
async def on_fallback(
self, state: AgentState, resources: AgentRunResources
) -> AgentRunResult[Any]:
) -> AgentRunResult[OutputDataT]:
run_context = resources.run_context
if not resources.config.enable_fallback_summary:
@@ -666,10 +690,13 @@ class StandardAgentExecutor(BaseAgentExecutor):
tool_choice="none",
)
return model_construct(
AgentRunResult,
output=fallback_response.text,
messages=state.messages,
structured_data=None,
usage=state.usage,
return cast(
AgentRunResult[OutputDataT],
model_construct(
AgentRunResult,
output=fallback_response.text,
messages=state.messages,
structured_data=None,
usage=state.usage,
),
)