第 9 章 第二部分:架构原理

Task/Agent/Tool 协议与 Agent 范式对比

三层协议设计、ToolExecutor 机制、MCP 集成、以及 16 种 Agent 的架构范式深度对比

Nexus Agent Tool Task 设计模式 范式对比

三层协议概览

Nexus 的三层协议(Task → Agent → Tool)是松耦合的——每一层只通过注册名引用上下层,不直接 import。这种设计使得任意组合成为可能。

Task 协议

class TaskRunInput(BaseModel): ...
class TaskRunOutput(BaseModel): ...

# Mixin:需要 Agent 时加
class WithAgentRunner(BaseModel):
    run_agent: Callable[..., Awaitable[AgentRunOutput]]

# Mixin:有评分时加
class WithScore(BaseModel):
    score: float

@task_registry.register('my_task')
async def my_task(input: MyInput) -> MyOutput: ...

Task 的 run_agent 不是 Task 自己构造的——是 Runner 注入的工厂函数。这意味着 Task 不知道也不关心用的是哪个 Agent。

Agent 协议

class AgentRunInput(BaseModel): ...
class AgentRunOutput(BaseModel): ...

# Mixin
class WithUserPrompt(BaseModel):
    user_prompt: str | list[ChatCompletionContentPartParam]

class WithWorkingDir(BaseModel):
    working_dir: str

class WithMainTrajectory(BaseModel):
    main_trajectory: list[dict]

@agent_registry.register('my_agent')
async def my_agent(input: MyInput) -> MyOutput: ...

Agent 通过 require_context() 获取 LLM Client 和 Runtime,不通过参数传递。

Tool 协议

class ToolContext(BaseModel):
    run_id: str | None
    tool_configs: ImmutableDict   # 只读配置
    tool_state: ImmutableDict     # 只读共享状态
    trace_id: str | None

class ToolResponse(BaseModel):
    output: list[ChatCompletionContentPartParam | dict]
    meta: dict = {}
    context_updates: dict = {}   # 写回共享状态

@tool_registry.register('my_tool', meta={
    'description': '...', 'tool_type': 'local',
})
async def my_tool(params: MyInput, context: ToolContext) -> ToolResponse: ...

Tool 的 ToolContext 是不可变的(ImmutableDict)。如果 Tool 需要修改共享状态,必须通过 ToolResponse.context_updates 返回变更,由 ToolExecutor 统一 apply。这防止了并行工具执行时的竞态条件。

ToolExecutor 深度解析

ToolExecutor(nexus/core/tool/executor.py,323 行)是 Agent 调用 Tool 的统一入口。

核心职责

  1. Schema 生成:从 Pydantic InputModel 自动生成 OpenAI function schema
  2. 名称解析:处理 schema name vs canonical name 的映射(因为 override 可以改名)
  3. 参数验证:用 Pydantic model_validate 校验 LLM 传过来的参数
  4. 执行路由:根据 ToolType 决定本地执行还是远程执行
  5. 状态管理:执行后 apply context_updates 到共享状态
  6. 并行执行:支持多个工具并行调用

Token 效率视角

Braintrust 对 Agent 会话的 token 分布分析揭示了一个被忽视的事实:工具响应占总 token 的 67.6%,远超系统 prompt 的 3.4% 和用户输入的 ~5%。这意味着工具输出的上下文工程比 prompt 工程更重要。

对 ToolExecutor 的设计启示:

  1. 截断策略:当前 Nexus 的 codeact 已支持 truncate 将输出截断到可控长度,但缺少更精细的 token 预算控制。理想的策略是按 token 预算而非字符长度截断,并在截断时保留结构化摘要(如”前 50 行 + 错误信息”)。

  2. 渐进式工具暴露:Claude Skills 的 ~100 token 元数据启动 vs <5K 完整加载模式可作为 ToolExecutor Schema Override 的参考。将 schema 摘要(名字 + 一句话描述)作为默认暴露,详细 schema 按需加载——对 56 个工具可节省约 20K token 的上下文占用。

这条不再是设想,对照产品已经在做了。 qwen-code 的 0.24 线连续合入两项正对着这条的改动:align Code Mode text with lazy tool discovery(#12990)与 share a per-turn token budget from a +Nk directive(#11917),另有 budget-based ACP child admission(#11911)。两点含义值得写进设计:

  • “按需加载”与”显式预算”是配套出现的。 只做 lazy discovery 而不给预算,省下来的 token 会被后续轮次吃掉——省是在某一轮省,用是在整条轨迹用。
  • 本章”省约 20K token”是静态估算(一次性算出的上下文占用),而对照产品用的是每轮可声明的动态预算(+Nk 指令)。后者对长程 Agent 更可控,因为它把约束放到了每一轮而不是整条轨迹。 因此对 Nexus 的提法应从”schema 摘要 + 按需加载”升级为”按需加载 + 每轮 token 预算”。

⚠️ 上述三条均取自 release notes 的条目标题;lazy tool discovery 只在 nightly 的 fix 标题里反复出现,其具体语义(工具 schema 是否真的延迟进入上下文)本轮未读 PR #12990 正文,“已实现”应限定为”标题层面可见”。

  1. 工具输出摘要:对于长输出的工具(如 read_file 返回大文件内容),ToolExecutor 可在返回前自动生成摘要,将完整输出存入 context_updates 供后续按需检索。

LOCAL vs RUNTIME 执行路径

async def _call_resolved_tool(self, tool_name, params):
    tool_func = self._tools[tool_name]
    meta = get_tool_meta(tool_func)
    parsed_params = meta.input_model.model_validate(params)
    tool_context = self._build_tool_context()

    if meta.tool_type == ToolType.LOCAL:
        return await self._call_local(tool_func, parsed_params, tool_context)
    return await self._call_runtime(tool_func, parsed_params, tool_context)

LOCAL:直接在 Host 进程调用函数。

RUNTIME:

  1. 从 tool_registry 获取 import_path
  2. 序列化参数为 dict
  3. 通过 runtime.call_tool(import_path, params_dict, context) 发送到沙箱
  4. 沙箱的 Runtime Server 反序列化参数,导入函数,执行,返回结果

Tool Override 机制

Agent 可以在运行时修改 Tool 的 schema(名字、描述、参数描述):

tool_executor.set_tool_override('str_replace_editor',
    name='edit_file',
    description='Edit a file by replacing text',
    params=&#123;'path': 'The file path to edit'&#125;,
)

Override 有两层:Context 级(来自 tool_configs.meta_overrides)和 Instance 级(来自 set_tool_override),Instance 优先。

⚠️ 不要把客户端侧的 override 与服务端侧的清单改写混为一谈。 MCP Python SDK 2.3.0(2026-10-02) 起(#3630),Mcp-Param-* 的校验改为按名查注册时的 schema,MCPServer 不再为每个 tools/call 跑一次 tools/list,因此过滤或重写 tools/list 的中间件不再影响校验——校验用的是注册时的 schema,而不是清单被改写后的样子。

Nexus 的 override 是客户端侧行为,不依赖服务端清单被改写,所以这条变更不影响 override 的生效。但若将来把 override 实现成”改写 tools/list 的响应”,就会正面踩到这条——那样的实现在 2.3.0 之后会静默失效。同一批还新增了 Server(get_tool_input_schema=...),让底层 server 能为某个工具直接提供 header 校验所需的 schema,而不必跑 tools/list handler——这正是对上述变更的配套出口。

MCP Tool 集成

ToolExecutor 同时管理静态注册的 Tool 和动态加载的 MCP Tool:

def _all_canonical_tool_names(self):
    names = list(self._tools.keys())           # 静态 Tool
    manager = self._get_mcp_manager()
    if manager:
        names.extend(manager.tools.keys())     # MCP Tool
    return names

MCP Tool 的 schema 直接来自 MCP Server 的 tools/list 响应,不走 Pydantic model。

MCP Tool 集成的安全考量:IDE 侧已经出现针对 MCP 配置的真实攻击。CVE-2026-30615 是 Windsurf 1.9544.26 的 prompt injection 漏洞(CVSS v3.1 8.0 / HIGH,2026-04-15 披露):当 Windsurf 处理攻击者可控的 HTML 内容时,注入的指令会篡改本地 MCP 配置并自动注册恶意 MCP STDIO 服务器,进而在受害者系统上执行任意命令,无需进一步用户交互。对 Nexus 而言,MCP Tool 的安全风险比静态注册 Tool 更高:(1) schema 来自外部响应,不经 Pydantic 验证;(2) MCP Server 本身可能是恶意的。建议 MCP Tool 默认标记为 RUNTIME 类型(在沙箱内执行),避免在 Host 侧直接执行不可信代码。

⚠️ 更正说明(2026-09-15):本节此前把 CVE-2026-30615 描述为「MCP STDIO 接口的设计缺陷」并称「影响 20 万+ 服务器」。经回源 NVD 官方记录,这两处均不成立——该 CVE 是特定 IDE 产品(Windsurf)的 prompt injection 漏洞,NVD 记载中并无「20 万+ 服务器」的统计。此处保留原始表述供读者对照。需要强调的是,攻击面的位置变了,结论反而更强:既然配置文件本身可被篡改,那么”不让 MCP Tool 在 Host 侧执行”就从”防范恶意 Server”升级为”防范被投毒的配置”。

还有第三层:即便配置没被投毒、沙箱也照常工作,工具自身被授予的范围仍然可能是攻击面。 上面两层分别是”传输层”(已更正)与”配置层”(本轮的结论),SalesBleed(Zenity Labs,2026-09-24 披露,未分配 CVE)给出的是授权层的实证,且它前两层都防不住。

攻击链:攻击者在公开的 Web-to-Lead 表单里提交含隐藏指令的线索 → 之后员工正常地问一句”帮我看看最新的线索” → Agent 读到该记录并按其中的指令,用它自己的 Query Records 子代理去读 Accounts 表 → 把数据编进攻击者域名的子域、以 HTML <img src> 输出 → 前端自动取图,DNS 查询本身就完成了外泄,全程零点击。第二条路径借 Slack 的链接展开(抓取发生在 Slack 基础设施上,受害方的 DNS 与代理日志看不到)。第三条是冒用 Agent 身份在内部 Slack 线程发钓鱼消息(无需确认、且不记录触发者)。

Zenity 报告原话是:「Our payload asked for company names and deal sizes, but the injection could have asked for anything the subagent’s Query Records tool can reach」——没有任何人提升权限,Agent 用的正是它本来就被授予的访问权限。防护失败在解析层:URL 脱敏层只认一组固定顶级域名(.fun 不在其中);且脱敏层与渲染层对”URL 在哪结束”判断不一致,花括号与方括号可用,形如 https://random_string.oast.fun/{email} 的字符串能整条穿过。

落到本章的三条推论:① 沙箱化解决的是”在哪执行”,不解决”能访问到什么”;② 因此”MCP Tool 默认标 RUNTIME”是必要但不充分的,应再加一条——每个 MCP Tool 的工具范围需要显式声明并被审计;③ 这条同时给范式总结表里 Installed Agent 的”可控性 最低(黑盒)“评级补了一条实证理由:黑盒不只是看不见推理过程,还包括看不见被代理方内部把工具授权到了什么范围。

时间线(注意 SalesBleed 是 Zenity 的两篇报告,日期分属两处):第一篇《0-Click Data Exfiltration on Agentforce》给出 2026-06-01 报给 Salesforce、06-02 确认、08-18 修复完成、08-19 验证 Trusted URLs 绕过;第二篇《Hijacking Agentforce in Slack for Anonymous Phishing》给出 08-20 确认 Reply to a Slack Thread 已带调用者归属、08-25 Salesforce 称确认步骤仍在改、09-10 称预计 09-21 完成、09-21 全部修复确认并测试完毕。两篇均于 2026-09-24 公开披露。Zenity 称完整攻击链已不成立。

MCP 2026-07-28 无状态迁移对 Nexus 的影响

MCP 协议正在经历最大规模的修订(2026-07-28 发布最终规范)。六个 SEP 协同实现无状态核心,对 Nexus 的 MCP 集成有直接影响:

变更项旧协议新协议对 Nexus 的影响
连接建立initialize/initialized 握手移除,通过 per-request _meta 传递load_mcp_tools() 需重构,去掉握手流程
会话管理Mcp-Session-Id 协议级会话移除,任何请求可落在任何服务器实例MCP Manager 不再维护会话映射,无状态更易水平扩展
路由需解析 bodyMcp-Method/Mcp-Name 必选头更高效的路由,无需反序列化即可分发
能力发现初始化时交换server/discover 方法运行时按需发现,与 Ch07 的远程发现对接更自然

关键适配点:

  1. 连接模式变更:load_mcp_tools() 中的 initialize 握手需替换为 per-request _meta 传递。这实际上是简化——少了握手步骤,MCP Tool 的加载更快。

  2. 缓存策略:tools/list 响应可通过 ttlMs 缓存,减少重复获取。无状态模型下缓存更安全——不依赖会话状态一致性。

  3. 显式句柄模式:MCP Tool 需要跨调用状态时,不再依赖会话,而是通过 tool 返回显式句柄(如 browser_id)让模型在后续调用中传递。这与 Nexus 的 ToolResponse.context_updates 模式高度一致——context_updates 天然就是跨调用的状态传递机制。

  4. Tasks Extension:MCP 长时任务可能需要与 Nexus 的 Task 协议对接。当前 Nexus Task 是短时同步调用,MCP Tasks 的异步长时任务模式(进度回调、取消信号)需要新的集成设计。

12 个月宽限期内(2026-07-28 至 2027-07-28),Nexus 需同时支持新旧两种 MCP 协议。

16 种 Agent 的架构范式对比

这是本章的核心内容。Nexus 的 16 种 Agent 不是同一个模板的变体——它们代表了当前 Agent 设计的四种根本不同的范式。

范式一:Tool-Calling 循环

代表:main_agent、demo_agent、toolcall_agent

这是最经典的 ReAct 范式:

while not done:
    completion = llm.chat(messages, tools=tool_schemas)
    if completion.tool_calls:
        for call in completion.tool_calls:
            result = tool_executor.call(call.name, call.args)
            messages.append(tool_result_msg)
    else:
        done = True  # LLM 不再调用工具,任务完成

main_agent 的独特之处:

  • 工具列表是参数化的(tools: list[str]),而不是硬编码
  • 支持并行工具调用(parallel_tool_calls: true)
  • 支持 post_iteration_hook——每轮工具执行后的回调(可注入自动截图等)
  • 支持 Schema Override——运行时修改工具的名字和描述
  • 完整的退出状态枚举(7 种退出原因)
  • 集成 Compaction——超长对话自动压缩
  • 每个阶段都有独立 Span(inference / observation / compaction)

toolcall_agent(base 框架)的独特之处:

  • 基于 nexus/extensions/agents/base/ 的抽象基类
  • 更结构化的 ReAct 循环(react.py)
  • 带 FinalAnswerTool——Agent 显式调用 final_answer 工具结束任务
  • 内置评估机制(evaluation.py)
  • 更丰富的 prompt 模板系统(prompt.py 9.95KB)

范式二:Code-as-Action

代表:codeact

CodeAct 范式不使用预定义的 Tool Schema,而是让 LLM 生成 Python 代码作为 action:

while not done:
    completion = llm.chat(messages)
    code = extract_code_from_response(completion)
    if code:
        result = runtime.exec(code)  # 直接在沙箱里执行
        messages.append(execution_result)
    else:
        done = True

codeact 的独特之处(agent.py 24KB + tools.py 39KB):

  • 自带一套完整的 Python API 暴露给 LLM(文件操作、终端控制、浏览器操作等)
  • 不走 ToolExecutor——直接用 runtime.invoke() 执行代码
  • 支持 IPython 和 raw shell 两种执行模式
  • 代码执行结果会被 truncate 到可控长度
  • 支持 working_dir 设置(Agent 在仓库目录下操作)

vs Tool-Calling 的关键差异:

  • Tool-Calling:LLM 输出结构化的 JSON 参数,框架路由执行
  • Code-as-Action:LLM 输出自由代码,沙箱直接执行

Code-as-Action 的灵活性更高(LLM 可以写任意逻辑),但也更难控制(可能产生不安全的代码)。

Anthropic Programmatic Tool Calling 的验证:2026 年 1 月,Anthropic 正式发布了 Programmatic Tool Calling(code_execution_20260120)——Claude 在沙箱容器内写 Python 代码调用工具,工具执行暂停返回 tool_use block,结果返回后继续执行,只有最终输出进入上下文窗口。官方给出的 token 节省是分口径的:75 工具的项目管理 agent 基准上 input tokens 减少约 38% 且准确率不变;生产 API 流量中 tools 数组含 10–49 个工具定义的请求典型节省 20%–40%。allowedCallers: ["code_execution_20260120"] 白名单机制提供了”可控的 Code-as-Action”——但它是引导,不是强制,见下。

⚠️ allowed_callers 不是安全边界(2026-09-15 补):Anthropic 官方文档原文——“allowed_callers controls how the tool is presented to Claude and is validated against tool_choice, but it is not a hard API-level block on direct invocation. Claude is strongly guided to respect it, but your client should still be prepared to handle a direct tool_use for any tool it defines. Do not rely on allowed_callers as a security boundary.”换言之,白名单决定的是工具如何呈现给模型,客户端必须准备好处理任何工具的直接调用。把 allowed_callers 当权限控制来用,正是官方警告的用法。

一个必须知道的反向边界:官方在同一页给了不适用 PTC 的场景——τ²-bench(航空/零售/电信,每轮一到两次顺序工具调用)上,启用 PTC 后分数不变而成本高出约 8%,官方结论是”顺序单调用工作流不受益”。也就是说,PTC 的收益来自”省掉多工具之间的推理往返”,工具调用本来就是串行的场景反而会因代码执行的额外开销而变贵。选型时应先问工作流形状,再谈 token 节省。

截至 2026-09,官方 Supported models 共 12 个:claude-opus-5、claude-fable-5-1、claude-mythos-5-1、claude-fable-5、claude-mythos-5、claude-opus-4-8/4-7/4-6/4-5-20251101、claude-sonnet-5、claude-sonnet-4-6/4-5-20250929(较 2026-07 记录的 9 个新增 fable-5-1、mythos-5-1、opus-5)。另支持 REPL 状态持久化(同一容器内多次 code execution 共享 Python 进程状态)。BrowseComp + DeepSearchQA 基准:+11% 性能(平均),input tokens −24%。Sonnet 5 的支持意味着 Code-as-Action 范式已从顶级模型专属能力扩展到中端模型。

🔄 这个枚举又变了,但本轮无法一手复核——请按下述方式理解上面这句。 上面”共 12 个”是 2026-09 的快照,而本章自己的修订史已经证明这类枚举腐烂得极快:2026-07-21 记 9 个 → 09-15 改 12 个 → 本轮(2026-10-03)研究员报告已到 14 个,两个月内动了三次。凡带”截至某月 + 一个计数”的写法,过了那个日期即失效,这里正是本章自己在别处反复强调过的那条规则的例子。

⚠️ 本轮未直接改写的原因:2026-10-03 尝试回源 docs.claude.com / platform.claude.com 的 PTC 官方页时,该站全部路径均 302 到 claude.com/app-unavailable-in-region(区域不可用);docs.anthropic.com 同样跳转。只有静态的 llms.txt(81,883 字节,确认该页 URL 仍在索引中)可访问。换句话说,本轮拿不到一手页面,所以不把新数字写进正文。

研究员报告的变更(待核实,勿直接引用):Supported models 增至 14 个,新增 Opus 5.5 与 Sonnet 5.5(官方页现列 Fable 5 / 5.1、Mythos 5 / 5.1、Opus 4.5 / 4.6 / 4.7 / 4.8 / 5 / 5.5、Sonnet 4.5 / 4.6 / 5 / 5.5);并新增 Supported platforms 维度——Claude API、Claude Platform on AWS、Microsoft Foundry(后者需 Hosted on Anthropic 部署)。若核实为真,这里值得补一句方法论:新增的 Supported platforms 是本章此前完全没有的维度,而对 Nexus 这类要跨云对接的框架,“模型支持”与”平台支持”是两个独立的门槛,只查前者会漏掉后者。

若 Sonnet 5.5 确在列,上面那句”已扩展到中端模型”的论断会更强——最新一代中端模型也在支持范围内。但请注意这只是论断的强弱变化,不是方向变化。

能力是分两级的:Claude Haiku 4.5 接受 code_execution_20260120 及更高工具版本,却不支持 programmatic tool calling。即”能跑代码执行容器”和”能在容器内编排工具调用”是两级能力,选型时只看前者会误判。

除 code_execution_20260120(REPL 持久化)外,Anthropic 另有 code_execution_20260521 变体。官方 2026-09 文档声明两者在 allowed_callers 中均被接受且可互换(interchangeable):用任一版本的请求都能满足列出任一 caller 的工具,而响应块始终把 caller 标为 code_execution_20260120,与请求声明的版本无关。因此更准确的定位是”同一能力的两个别名”,而不是”快速迭代中的不稳定快照”。allowed_callers 的完整取值为 ["direct"](默认,引导直接调用)、["code_execution_20260120"](引导只在代码执行中调用)、["direct", "code_execution_20260120"](两者皆可);官方 Tip 建议每个工具只选其一,给模型的指引更清晰。

这意味着 Code-as-Action 范式已经从学术论文(CodeAct)升级为 Anthropic 官方工业能力。范式总结表中 Code-as-Action 的”可控性”可从”低”调整为”中——且这个『中』完全来自容器隔离,不来自 allowed_callers”。沙箱容器隔离执行环境提供了可控性的实质来源;allowed_callers 只提供引导,不能作为权限边界。

范式三:Installed Agent(外壳代理)

代表:claude_code、codex、gemini_cli、openclaw

这些 Agent 不自己实现推理循环——它们在沙箱里安装并运行第三方 Agent CLI,只代理 LLM 请求。

1. runtime.invoke(installer.install)          # 安装 Agent CLI
2. runtime.invoke(runtime_setup.setup_proxy)  # 启动 LLM Proxy
3. runtime.invoke(agent.run_agent_cli)        # 运行 CLI,CLI 的 LLM 请求被代理
4. 收集输出

共同的架构(installed_common.py):

  • InstalledAgentInput:继承 WithUserPrompt + WithWorkingDir + timeout
  • InstalledAgentOutput:success + output + stderr + tmux_session_id
  • 每个 Installed Agent 有三个模块:
    • installer.py:安装逻辑(npm install / pip install)
    • runtime_setup.py:LLM Proxy 配置 + 环境变量设置
    • agent.py:运行 CLI 并采集输出

四个 Installed Agent 的差异:

claude_codecodexgemini_cliopenclaw
语言TypeScriptTypeScriptTypeScriptPython
安装方式npmnpmnpmpip + git clone
LLM ProxyAnthropic → OpenAIOpenAI → OpenAIGemini → OpenAIOpenAI → OpenAI
runtime_setup 大小12KB5.5KB8.6KB48KB
特殊点需要 Anthropic API 格式转换相对简单需要 Gemini API 格式转换最复杂——自带沙箱内沙箱

openclaw 为什么 runtime_setup 有 48KB:它需要在 Nexus 的沙箱内再搭建一套 OpenClaw 的运行环境——包括配置文件生成、工具服务启动、MCP Server 配置等。这是”套娃”最深的一层。

去模型化的 Installed Agent:qwen-code(npm 包 @qwen-code/qwen-code,v0.24.7,2026-09-29 发布,npm registry 实测 2026-10-03 ;Apache 2.0)提出了一个不同的 Installed Agent 模式——框架本身不绑定模型。同一 Agent CLI 通过配置切换底层模型(OpenAI/Anthropic/Gemini/Qwen/本地 Ollama)。这对 Nexus 的 Installed Agent 适配策略有直接启示:未来可能不需要为每个模型供应商维护独立的 claude_code/codex/gemini_cli 外壳,而只需对接一个多协议 Agent 框架。Nexus 的 installed_common.py 中的 LLM Proxy 逻辑(Anthropic → OpenAI 格式转换)在去模型化 Agent 中完全不需要——框架层的多协议抽象已经解决了这个问题。

🔄 版本锚点刷新(2026-10-03):0.24 线(2026-09-16 的 0.24.0 到 2026-09-29 的 0.24.7)的重心不在包格式,而在运行时形态:feat(managed-agent): admit workspace-bound sessions without execution(#12709)、feat(cli): Run Managed Runtime tools in the Session's Workspace directory(#12732)、feat(acp-bridge): route sessions across legacy and managed engines(#12698)——合起来是”同一 CLI 同时跑 legacy 与 managed 两套引擎,且 managed 侧可在不执行的情况下接纳绑定工作区的会话”。另有 add bwrap kernel sandbox backend for Linux(#11614)与 cap web_search calls per session(#11852),以及 fix(permissions): honor approved cross-directory tool calls(#12987)。

上面这段的核心论断(“框架本身不绑定模型……未来可能不需要为每个模型供应商维护独立外壳”)不受这次版本漂移影响,仍然成立。release notes 中未见 Agent Plugins v1 包格式的变更条目,故本轮未改动对 v1 包格式的能力描述。

⚠️ Ch06 那一处(原 0.23.3)本轮已同步改为 0.24.7——两处相差一个版本的情况上一轮已出现过一次,本轮是两处同时过期(相差约 18 天)。

范式四:专用交互循环

代表:browser_gym_agent、terminus2、search_agent、science_agent

这些 Agent 有自定义的交互循环,不走标准的 Tool-Calling 也不走 Code-as-Action。

browser_gym_agent(37KB,最大的单文件 Agent):

  • 维护浏览器状态(AXTree / HTML / Screenshot)
  • 每轮先获取浏览器观察(DOM),然后让 LLM 决定操作
  • 增量 DOM 更新(delta_observation.py 17KB)——只发送变化的 DOM,节省 token
  • DOM 压缩(dom_compression.py 16KB)——去除不必要的属性和节点
  • 站点感知过滤(site_aware_filter.py 11KB)——根据网站类型调整保留策略
  • 任务感知过滤(task_aware_filter.py 24KB)——根据任务目标过滤无关 DOM 元素
循环:
1. observation = runtime.browser_get_observation(include_screenshot=True)
2. compressed_dom = compress(filter(observation.dom_tree))
3. completion = llm.chat([..., &#123;"观察到的 DOM": compressed_dom&#125;])
4. action = parse_action(completion)  # 如 click(bid="42"), fill(bid="17", value="hello")
5. runtime.browser_click(session_id, bid="42")

terminus2(agent.py 20KB + 解析模块 40KB+):

  • XML 解析范式——LLM 输出 XML 格式的动作
  • 自带 summarization 机制(summarization.py 12KB)——对话过长时自动摘要
  • 终端输出处理(terminal_output.py 5KB)——处理 ANSI 转义、过滤噪声
  • 完整的解析体系:parsing.py(12KB)+ xml_parsing.py(15KB)

search_agent / mm_search_agent:

  • 搜索 → 阅读 → 推理 的循环
  • mm_search_agent 额外处理图片理解

science_agent:

  • 带 generator(数据生成)+ agent(推理循环)
  • 支持科学实验的特定工作流

范式总结

范式Agent工具调用方式灵活性可控性适合场景
Tool-Callingmain/base/demo结构化 JSON + ToolExecutor中高通用
Code-as-Actioncodeact自由 Python 代码高中(仅靠容器隔离;allowed_callers 非强制)代码修复
Installedclaude_code/codex/gemini/openclaw代理第三方 CLI最高(用别人的实现)最低(黑盒)对标评测
专用循环browser_gym/terminus2/search/science领域特定协议领域内高领域内高浏览器/终端/搜索

范式总结表补充量化数据:

范式token 效率实证基准
Tool-Calling中(每步推理 + 上下文)SWE-bench Pro / WebArena
Code-as-Action高(75 工具基准 input tokens −38% 且准确率不变;生产 10–49 工具请求典型节省 20%–40%;顺序单调用工作流反而贵约 8%,Anthropic 官方数据)DPAB-α (Dria Pythonic Agent Benchmark) / BrowseComp / DeepSearchQA
Installed取决于被代理 Agent各 Agent 原始基准
专用循环领域内可优化领域专用基准

Nexus 当前的 16 种 Agent 主要覆盖上述四种范式,但 Agent 设计领域还有两个值得关注的范式方向:

范式核心思路效率特征与现有范式的关系
ReWOO (Reasoning WithOut Observation)先规划所有工具调用,再执行——将推理与观察解耦省 50-70% latency(减少推理-观察交替轮次)可视为 Tool-Calling 的”批处理”变体,但与 Reflection 逻辑冲突(Reflection 需要观察后反思)
Reflection自我反思→修正循环HumanEval 80→91%(品质优先)ReAct + Reflection = Devin/Claude Code 的设计逻辑——每步推理后检查是否需要修正

ReWOO 的”先规划后执行”模式适合工具调用链可预知的场景(如 ETL 流水线),但不适合需要根据中间结果动态调整的场景。Reflection 的”反思-修正”循环几乎可以叠加在任何范式之上——Nexus 的 post_iteration_hook 已经是 Reflection 的雏形。

范式融合:“双面暴露”模式

四种范式不必是单选。一个趋势是将同一能力以两种方式暴露:

  1. 为交互式场景暴露 typed tool surface(Tool-calling 范式)——用户可见每步操作,可审计可干预
  2. 为批量/无人值守场景暴露 HTTP API + 代码执行(Code-as-Action 范式)——Agent 写编排脚本一次执行,减少推理往返

Anthropic 的 allowedCallers 就是融合的技术实现:同一工具可以被交互式 Tool-calling 调用,也可以被 code_execution 环境中的代码调用。Nexus 的 main_agent 也可以同时支持两种模式——有用户交互时走 Tool-calling,后台运行时走 Code-as-Action。

allowedCallers 的另一个新维度是 allowed_callers: ["direct"]——web_search_20260209 / web_fetch_20260209 新增的调用方限制,允许工具声明仅由特定调用方(如直接用户指令)触发。这把工具暴露控制从”是否暴露”演进为”暴露给谁调用”:同一工具可以同时声明”可被 code_execution 调用”和”仅直接用户指令触发”,构成更细粒度的权限模型。

选择指南

场景推荐 Agent理由
通用任务(有明确工具集)main_agent工具列表参数化,最灵活
SWE-bench 代码修复swe_agent 或 codeact专为代码场景优化
对标 Claude Code 的效果claude_code(Installed)原汁原味运行
WebArena / 网页操作browser_gym_agentDOM 处理做了大量优化
终端操作 benchmarkterminus2XML 解析更适合终端场景
搜索类任务search_agent搜索-阅读-推理循环
RL 训练环境main_agent + 自定义 tools最适合做 reward shaping
只想跑单轮 LLMsingle_turn最简单,无工具