Tool Runtime 模块对比分析
2026/4/27大约 4 分钟
Tool Runtime 模块对比分析
1. 模块边界
Tool Runtime 负责三件事:把工具定义暴露给模型、在工具被调用时完成参数校验与执行、把结果重新映射回对话状态。这里不单独讨论权限策略本身,而关注运行时装配和执行链路。
2. Claude Code:高耦合的工具编排层
2.1 关键源码路径
claude-code-rev/src/tools.ts:内置工具总表,按 feature flag、运行模式、环境能力决定哪些工具进入当前集合。claude-code-rev/src/Tool.ts:统一的工具协议与ToolUseContext,把权限、AppState、MCP、agent、UI 状态等全部注入工具执行上下文。claude-code-rev/src/services/tools/toolOrchestration.ts:根据isConcurrencySafe()将工具调用分成并发批和串行批。claude-code-rev/src/services/tools/toolExecution.ts:单次工具调用执行入口,负责权限、hook、遥测、错误分类、MCP 结果处理。claude-code-rev/src/services/tools/StreamingToolExecutor.ts:支持流式进度回传。claude-code-rev/src/services/mcp/client.ts、src/utils/toolSearch.ts:把 MCP 工具与 deferred tool search 接到同一运行时。
2.2 运行方式
Claude Code 的工具运行时不是简单的“注册表 + execute”:
tools.ts根据环境和 flag 决定候选工具集合。query.ts收到模型的tool_use后交给runTools()。toolOrchestration.ts先做批次划分。只读、可并发的工具会被批量执行;写操作或无法证明安全并发的工具会串行执行。toolExecution.ts在真正执行前串起权限判定、pre/post hook、进度事件、OTel/analytics、MCP 特殊处理、结果存储。- 执行结果再回写成
tool_result或错误消息,由 query loop 决定是否继续采样。
2.3 模块职责特点
- 工具定义层很重。
ToolUseContext里除了消息、abort、权限,还包含 AppState、MCP 连接、UI、会话活动、文件历史等状态。 - 运行时对“并发是否安全”非常在意,这也是
toolOrchestration.ts独立存在的原因。 - Tool Runtime 与权限、hook、MCP 深度耦合,实际是“工具编排层”而不只是“工具调用层”。
3. Opencode:以注册表为中心的工具层
3.1 关键源码路径
opencode/packages/opencode/src/tool/registry.ts:统一收集内置工具、目录工具、插件工具,并在运行前生成最终工具集。opencode/packages/opencode/src/tool/tool.ts:定义工具协议、参数校验、截断包装与执行上下文。opencode/packages/opencode/src/tool/schema.ts:工具 ID 类型定义。opencode/packages/opencode/src/tool/*.ts:各工具独立实现,如bash.ts、read.ts、edit.ts、write.ts、skill.ts、apply_patch.ts。opencode/packages/opencode/src/session/processor.ts:消费tool-call / tool-result / tool-error事件。
3.2 运行方式
ToolRegistry启动时收集三类工具:内置工具、配置目录里的{tool,tools}/*.ts、插件暴露的tool定义。tools(model, agent)会按模型能力和 flag 过滤最终可见工具。例如 GPT 系列优先用apply_patch,其他模型走edit/write。- 每个工具通过
Tool.define()自动获得 Zod 参数校验和输出截断包装。 session/llm.ts把这些工具转换成 AI SDK 可消费的 definitions。- 流程执行时由
SessionProcessor按流事件记录 tool part 的pending / running / completed / error状态。
3.3 模块职责特点
- 工具注册与工具执行协议更清晰,
Tool.Context只保留 session、message、abort、ask、metadata 等必要字段。 - 插件和目录工具直接接到
ToolRegistry,扩展点天然比 Claude Code 更扁平。 - 运行时没有单独的“并发编排器”模块,更多依赖模型流与 AI SDK 的工具执行时序。
4. 核心差异
| 维度 | Claude Code | Opencode |
|---|---|---|
| 工具来源 | 内置工具为主,外加 MCP、plugin、skill 等多路注入 | 内置工具 + 目录工具 + 插件工具统一挂到 ToolRegistry |
| 上下文对象 | ToolUseContext 很重,带 UI/AppState/MCP/agent 状态 | Tool.Context 更薄,只保留执行必需字段 |
| 并发策略 | toolOrchestration.ts 显式按安全性分批 | 没有独立并发编排器,偏简单执行链 |
| 权限/Hook 接入 | 与执行入口深度耦合 | 由工具自身 ctx.ask() 或 session/permission 模块配合 |
| MCP 集成位置 | Tool Runtime 内部深度融合 | MCP 独立成模块,再由 session/tool 注入 |
5. 设计取舍
5.1 Claude Code 的取舍
- 追求的是“复杂工具生态下仍能统一治理”。
- 因此工具执行入口要承担更多事情:权限裁决、hook、分析埋点、MCP 特殊输出、并发调度。
- 好处是所有工具都走同一条治理链;代价是工具层本身变厚。
5.2 Opencode 的取舍
- 追求的是“工具实现和扩展尽量轻量”。
- 把参数校验、输出截断、注册发现做成公共骨架,把插件工具和本地工具看成同一种对象。
- 好处是扩展成本低;代价是高级治理能力更多依赖外围模块,而不是运行时中心统一调度。
6. 结论
Claude Code 的 Tool Runtime 更像带审计和调度能力的“工具总线”;Opencode 更像一个轻量工具容器,强调统一协议和低门槛扩展。前者强在复杂环境下的统一治理,后者强在模块清晰和扩展平坦。