"""按照执行策略把 action 路由到脚本、MCP 或 fake runner。""" from __future__ import annotations from .constants import ALLOWED_ACTIONS, HOME_ACTIONS, NODE_ACTIONS from .models import AgentState, BackendName, ExecutionStrategy, ActionResult def build_action_backends(strategy: ExecutionStrategy) -> dict[str, BackendName]: """根据执行策略生成每个 action 对应的后端类型。""" if strategy == "fake": return {action: "fake" for action in ALLOWED_ACTIONS} if strategy == "script_only": return {action: "script" for action in ALLOWED_ACTIONS} if strategy == "hybrid_node_mcp": routes: dict[str, BackendName] = {action: "script" for action in HOME_ACTIONS} routes.update({action: "mcp" for action in NODE_ACTIONS}) return routes raise ValueError(f"未知执行策略: {strategy}") class ActionRouter: """统一的 action 调度器,屏蔽脚本、MCP 和 fake 后端差异。""" def __init__(self, *, script_runner, mcp_runner=None, fake_runner=None) -> None: """保存各类 runner,运行时按 state 中的路由表选择后端。""" self.script_runner = script_runner self.mcp_runner = mcp_runner self.fake_runner = fake_runner def run_action(self, state: AgentState, action: str, **kwargs) -> ActionResult: """执行一个 action,并返回统一的 ActionResult。""" backend = state.action_backends.get(action) if not backend: raise ValueError(f"action 未配置路由: {action}") if backend == "script": return self.script_runner.run( action, params=state.params, script_entry=state.script_entry, config_path=state.config_path, trace_file_path=state.trace_file_path, **kwargs, ) if backend == "mcp": if self.mcp_runner is None: raise RuntimeError(f"action 需要 MCP runner: {action}") mcp_kwargs = dict(kwargs) hash_code = mcp_kwargs.pop("hash_code", None) or state.hash_code node_url = mcp_kwargs.pop("node_url", None) or state.node_url return self.mcp_runner.run( action, params=state.params, hash_code=hash_code, node_url=node_url, **mcp_kwargs, ) if self.fake_runner is None: raise RuntimeError(f"action 需要 fake runner: {action}") return self.fake_runner.run(action, params=state.params, **kwargs)