Files
zhenxun_bot/zhenxun/services/ai/flow/agent/engine/directive.py
T

116 lines
3.9 KiB
Python
Raw Normal View History

from collections import defaultdict
from collections.abc import Awaitable, Callable
from zhenxun.services.ai.flow.agent.models import AgentRunResources, AgentState
from zhenxun.services.ai.run.models import AgentRunResult, HandoffPayload
from zhenxun.services.ai.tools.models import ToolResult
from zhenxun.services.ai.utils.logger import log_agent as logger
from zhenxun.utils.pydantic_compat import model_construct
from zhenxun.utils.utils import infer_plugin_namespace
DirectiveHandlerFunc = Callable[
[AgentState, AgentRunResources, ToolResult], Awaitable[None]
]
class DirectiveManager:
"""工具指令路由注册中心"""
def __init__(self):
self._handlers: dict[str, dict[str, DirectiveHandlerFunc]] = defaultdict(dict)
def register(
self, name: str, handler: DirectiveHandlerFunc, namespace: str = "global"
) -> None:
self._handlers[namespace][name] = handler
logger.debug(f"已注册工具副作用指令: '{name}' -> Namespace: '{namespace}'")
def get_handler(
self, name: str, namespace: str = "global"
) -> DirectiveHandlerFunc | None:
"""优先从指定 namespace 找,找不到回退到 global"""
ns_dict = self._handlers.get(namespace, {})
if name in ns_dict:
return ns_dict[name]
return self._handlers.get("global", {}).get(name)
directive_manager = DirectiveManager()
def directive(name: str | None = None, namespace: str | None = None):
"""
注册一个自定义工具副作用指令处理器的装饰器。
参数:
name: 指令的名称,如果不填则默认使用被装饰的函数名。
namespace: 插件命名空间,如果不填则基于代码调用栈自动推断。
"""
def decorator(func: DirectiveHandlerFunc):
dir_name = name or func.__name__
ns = namespace if namespace is not None else infer_plugin_namespace()
directive_manager.register(dir_name, func, ns)
return func
return decorator
@directive("submit_structured", namespace="global")
async def handle_submit_structured(
state: AgentState, resources: AgentRunResources, tool_res: ToolResult
) -> None:
parsed_obj = (
tool_res.directive.payload.get("parsed_obj") if tool_res.directive else None
)
logger.debug("✅ 拦截到结构化结果提交,结束循环。")
state.is_finished = True
state.final_result = model_construct(
AgentRunResult,
output=None,
messages=state.messages,
structured_data=parsed_obj,
usage=state.usage,
)
@directive("end_run", namespace="global")
async def handle_end_run(
state: AgentState, resources: AgentRunResources, tool_res: ToolResult
) -> None:
output = (
tool_res.directive.payload.get("output", tool_res.output)
if tool_res.directive
else tool_res.output
)
logger.debug("✅ 捕获到工具发出的终止信号,提前结束推理循环。")
state.is_finished = True
state.final_result = model_construct(
AgentRunResult,
output=output,
messages=state.messages,
usage=state.usage,
)
@directive("handoff", namespace="global")
async def handle_handoff(
state: AgentState, resources: AgentRunResources, tool_res: ToolResult
) -> None:
payload = tool_res.directive.payload if tool_res.directive else {}
handoff = HandoffPayload(
target=payload.get("target", "unknown"),
reason=payload.get("reason", ""),
context_data=payload.get("context_data", ""),
)
output_text = f"已触发控制权移交 -> {handoff.target}。原因: {handoff.reason}"
logger.info(f"✅ 拦截到移交(Handoff)信号: 移交给 -> {handoff.target}。结束循环。")
state.is_finished = True
state.final_result = model_construct(
AgentRunResult,
output=output_text,
messages=state.messages,
usage=state.usage,
handoff=handoff,
)