章节导航 第 9 章 / 共 21 章
Task/Agent/Tool 协议与 Agent 范式对比
三层协议设计、ToolExecutor 机制、MCP 集成、以及 16 种 Agent 的架构范式深度对比
三层协议概览
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 的统一入口。
核心职责
- Schema 生成:从 Pydantic InputModel 自动生成 OpenAI function schema
- 名称解析:处理 schema name vs canonical name 的映射(因为 override 可以改名)
- 参数验证:用 Pydantic
model_validate校验 LLM 传过来的参数 - 执行路由:根据
ToolType决定本地执行还是远程执行 - 状态管理:执行后 apply
context_updates到共享状态 - 并行执行:支持多个工具并行调用
Token 效率视角
Braintrust 对 Agent 会话的 token 分布分析揭示了一个被忽视的事实:工具响应占总 token 的 67.6%,远超系统 prompt 的 3.4% 和用户输入的 ~5%。这意味着工具输出的上下文工程比 prompt 工程更重要。
对 ToolExecutor 的设计启示:
-
截断策略:当前 Nexus 的
codeact已支持truncate将输出截断到可控长度,但缺少更精细的 token 预算控制。理想的策略是按 token 预算而非字符长度截断,并在截断时保留结构化摘要(如”前 50 行 + 错误信息”)。 -
渐进式工具暴露: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 正文,“已实现”应限定为”标题层面可见”。
- 工具输出摘要:对于长输出的工具(如
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:
- 从 tool_registry 获取 import_path
- 序列化参数为 dict
- 通过
runtime.call_tool(import_path, params_dict, context)发送到沙箱 - 沙箱的 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={'path': 'The file path to edit'},
)
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/listhandler——这正是对上述变更的配套出口。
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 不再维护会话映射,无状态更易水平扩展 |
| 路由 | 需解析 body | Mcp-Method/Mcp-Name 必选头 | 更高效的路由,无需反序列化即可分发 |
| 能力发现 | 初始化时交换 | server/discover 方法 | 运行时按需发现,与 Ch07 的远程发现对接更自然 |
关键适配点:
-
连接模式变更:
load_mcp_tools()中的 initialize 握手需替换为 per-request_meta传递。这实际上是简化——少了握手步骤,MCP Tool 的加载更快。 -
缓存策略:
tools/list响应可通过ttlMs缓存,减少重复获取。无状态模型下缓存更安全——不依赖会话状态一致性。 -
显式句柄模式:MCP Tool 需要跨调用状态时,不再依赖会话,而是通过 tool 返回显式句柄(如
browser_id)让模型在后续调用中传递。这与 Nexus 的ToolResponse.context_updates模式高度一致——context_updates天然就是跨调用的状态传递机制。 -
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_callerscontrols how the tool is presented to Claude and is validated againsttool_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 directtool_usefor any tool it defines. Do not rely onallowed_callersas 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+ timeoutInstalledAgentOutput: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_code | codex | gemini_cli | openclaw | |
|---|---|---|---|---|
| 语言 | TypeScript | TypeScript | TypeScript | Python |
| 安装方式 | npm | npm | npm | pip + git clone |
| LLM Proxy | Anthropic → OpenAI | OpenAI → OpenAI | Gemini → OpenAI | OpenAI → OpenAI |
| runtime_setup 大小 | 12KB | 5.5KB | 8.6KB | 48KB |
| 特殊点 | 需要 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.py17KB)——只发送变化的 DOM,节省 token - DOM 压缩(
dom_compression.py16KB)——去除不必要的属性和节点 - 站点感知过滤(
site_aware_filter.py11KB)——根据网站类型调整保留策略 - 任务感知过滤(
task_aware_filter.py24KB)——根据任务目标过滤无关 DOM 元素
循环:
1. observation = runtime.browser_get_observation(include_screenshot=True)
2. compressed_dom = compress(filter(observation.dom_tree))
3. completion = llm.chat([..., {"观察到的 DOM": compressed_dom}])
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.py12KB)——对话过长时自动摘要 - 终端输出处理(
terminal_output.py5KB)——处理 ANSI 转义、过滤噪声 - 完整的解析体系:
parsing.py(12KB)+xml_parsing.py(15KB)
search_agent / mm_search_agent:
- 搜索 → 阅读 → 推理 的循环
- mm_search_agent 额外处理图片理解
science_agent:
- 带 generator(数据生成)+ agent(推理循环)
- 支持科学实验的特定工作流
范式总结
| 范式 | Agent | 工具调用方式 | 灵活性 | 可控性 | 适合场景 |
|---|---|---|---|---|---|
| Tool-Calling | main/base/demo | 结构化 JSON + ToolExecutor | 中 | 高 | 通用 |
| Code-as-Action | codeact | 自由 Python 代码 | 高 | 中(仅靠容器隔离;allowed_callers 非强制) | 代码修复 |
| Installed | claude_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 的雏形。
范式融合:“双面暴露”模式
四种范式不必是单选。一个趋势是将同一能力以两种方式暴露:
- 为交互式场景暴露 typed tool surface(Tool-calling 范式)——用户可见每步操作,可审计可干预
- 为批量/无人值守场景暴露 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_agent | DOM 处理做了大量优化 |
| 终端操作 benchmark | terminus2 | XML 解析更适合终端场景 |
| 搜索类任务 | search_agent | 搜索-阅读-推理循环 |
| RL 训练环境 | main_agent + 自定义 tools | 最适合做 reward shaping |
| 只想跑单轮 LLM | single_turn | 最简单,无工具 |