Files
zhenxun_bot/zhenxun/services/ai/flow/team/router.py
T
52f7dbdedf ♻️ 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>
2026-07-14 16:48:33 +08:00

237 lines
8.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from abc import ABC, abstractmethod
from collections.abc import Callable, Mapping, Sequence
import re
from zhenxun.services.ai.core.messages import AgentMessage
from zhenxun.services.ai.core.templates import PromptTemplate
from zhenxun.services.ai.flow.agent.agent import Agent, ToolSource
from zhenxun.services.ai.flow.agent.models import AgentConfig
from zhenxun.services.ai.flow.core.base import BaseRunnable
from zhenxun.services.ai.run import RunContext, RunIntent
from zhenxun.services.ai.run.di import DependencyInjector
from zhenxun.services.ai.utils.logger import log_team as logger
from .capabilities import TeamRoutingCapability
from .models import RouteDecision, TeamRuntimeConfig, Transition
class BaseRouter(ABC):
"""团队多智能体路由器基类"""
@abstractmethod
async def route(
self,
context: RunContext,
history: Sequence[AgentMessage],
intent: RunIntent,
) -> RouteDecision | None:
"""核心路由方法"""
pass
class FunctionRouter(BaseRouter):
"""基于纯函数的极速路由器"""
def __init__(
self, selector_func: Callable[..., str | bool | None], target: str | None = None
):
"""
初始化基于函数的极速路由器。
参数:
selector_func: 用于进行路由判断的选择函数,返回布尔值或字符串目标名。
target: 当选择函数返回 True 时,默认路由到的目标成员名称。
"""
self.selector_func = selector_func
self.target = target
async def route(
self,
context: RunContext,
history: Sequence[AgentMessage],
intent: RunIntent,
) -> RouteDecision | None:
call_kwargs = {
"intent": intent,
"prompt": intent.original_input,
"context": context,
"history": history,
}
if intent.task_obj:
call_kwargs["agent_task"] = intent.task_obj
call_kwargs["task"] = intent.task_obj
selected_target = await DependencyInjector.invoke(
self.selector_func, call_kwargs, context
)
if isinstance(selected_target, bool):
if selected_target and self.target:
logger.debug(f"命中函数极速路由 -> {self.target}")
return RouteDecision(target_name=self.target, reason="")
elif selected_target is not None and isinstance(selected_target, str):
logger.debug(f"命中函数动态路由 -> {selected_target}")
return RouteDecision(target_name=selected_target, reason="")
return None
class RegexRouter(BaseRouter):
"""基于正则表达式的极速路由器"""
def __init__(self, pattern: str, target: str):
"""
初始化基于正则表达式的极速路由器。
参数:
pattern: 正则表达式匹配规则。
target: 当正则表达式成功匹配用户输入时路由到的目标成员名称。
"""
self.pattern = re.compile(pattern)
self.target = target
async def route(
self,
context: RunContext,
history: Sequence[AgentMessage],
intent: RunIntent,
) -> RouteDecision | None:
text_to_match = intent.text or context.run.user_input or ""
if self.pattern.search(text_to_match):
logger.debug(f"命中正则极速路由 -> {self.target}")
return RouteDecision(target_name=self.target, reason="")
return None
class ChainRouter(BaseRouter):
"""责任链路由器:按顺序执行,直到其中一个命中"""
def __init__(self, routers: list[BaseRouter]):
"""
初始化责任链路由器。
参数:
routers: 路由器实例列表,按顺序链式匹配,遇到首个命中的路由器即返回。
"""
self.routers = routers
async def route(
self,
context: RunContext,
history: Sequence[AgentMessage],
intent: RunIntent,
) -> RouteDecision | None:
for router in self.routers:
decision = await router.route(context, history, intent)
if decision is not None:
return decision
return None
class LLMRouter(BaseRouter):
"""基于大模型的意图路由器"""
def __init__(
self,
team_name: str,
members: list[BaseRunnable],
leader_model: str | None = None,
leader_tools: list[ToolSource] | None = None,
state_flow: Mapping[str, Sequence[Transition | str]] | Callable | None = None,
runtime_config: TeamRuntimeConfig | None = None,
custom_prompt: str | None = None,
allowed_transitions: list[Transition] | None = None,
max_handoffs: int = 3,
):
"""
初始化基于大模型的意图路由器。
参数:
team_name: 当前团队的名称标识。
members: 团队的成员列表,包含 Agent, Team 或 Workflow。
leader_model: 用于进行意图决策的路由器大模型名称,若为空则默认继承全局配置。
leader_tools: 挂载给意图决策路由器的额外可用工具列表。
state_flow: 状态流转规则字典或动态流转函数,定义智能体成员之间的转接路径。
runtime_config: 团队级别的运行时全局配置。
custom_prompt: 自定义的系统提示词模板,用以覆盖默认的路由系统指令。
allowed_transitions: 允许的状态移交规则与前置条件列表。
max_handoffs: 同一会话中允许连续移交的最大次数。
"""
self.team_name = team_name
self.members = members
self.leader_model = leader_model
self.leader_tools = leader_tools or []
self.state_flow = state_flow
self.runtime_config = runtime_config
self.custom_prompt = custom_prompt
self.allowed_transitions = allowed_transitions
self.max_handoffs = max_handoffs
async def route(
self,
context: RunContext,
history: Sequence[AgentMessage],
intent: RunIntent,
) -> RouteDecision | None:
default_system_prompt = """## 角色与目标
你是一个高级任务路由器 (所在团队: {{ team_name }})。
请根据用户的输入意图,立刻调用相应的移交工具 (transfer_to_...)
将对话物理转移给合适的专员处理。
你必须且只能选择移交,不能自己作答。"""
if self.allowed_transitions:
transitions_desc = "\n## 可用的移交目标及条件:\n"
for t in self.allowed_transitions:
desc = getattr(t, "description", "") or "无特定条件"
transitions_desc += (
f"- 移交至 [{getattr(t, 'target', 'unknown')}]:{desc}\n"
)
default_system_prompt += transitions_desc
template = self.custom_prompt or default_system_prompt
route_prompt = PromptTemplate(template).render(team_name=self.team_name)
routing_cap = TeamRoutingCapability(
team_name=self.team_name,
members=self.members,
state_flow=self.state_flow,
max_handoffs=self.max_handoffs,
)
leader_config = AgentConfig(
stateless=self.runtime_config.stateless if self.runtime_config else True,
enable_hitl=getattr(self.runtime_config, "leader_enable_hitl", False),
)
target_model = self.leader_model
router_agent = Agent(
name=f"{self.team_name}_Router",
instruction=route_prompt,
model=target_model,
tools=self.leader_tools,
config=leader_config,
)
sub_context = context.clone_for_member(router_agent.name)
sub_context.capabilities = list(sub_context.capabilities)
sub_context.capabilities.append(routing_cap)
logger.debug("🤖 [LLMRouter] 启动 LLM 思考路由决策...")
res = await router_agent.run(
prompt=intent.original_input,
context=sub_context,
config=AgentConfig(message_history=history),
)
if res.handoff:
logger.debug(f"🤖 [LLMRouter] 决策完毕: 移交给 -> {res.handoff.target}")
return RouteDecision(
target_name=res.handoff.target,
reason=res.handoff.reason,
context_data=res.handoff.context_data,
)
logger.warning("🤖 [LLMRouter] LLM 没有调用移交工具,放弃路由。")
return None