mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-10-10 06:10:01 +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
@@ -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,
|
||||
),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user