0-4. 学习目标与一句话
Tool 调用系统是 OpenCode 的行动层:模型提出 tool call,runtime 完成参数校验、权限检查、执行、截断、附件补 ID、状态回写。来源:packages/opencode/src/tool/tool.ts:16-45、packages/opencode/src/session/tools.ts:24-116。
Java 类比
Tool.Def 像 Strategy;ToolRegistry 像工具注册表;SessionTools.resolve 像 adapter;ctx.ask 像安全拦截器;插件工具像 SPI。
5. 最小源码路径
tool/tool.ts:16-45:上下文、结果、定义。tool/tool.ts:79-130:参数校验和截断。tool/registry.ts:203-275:自定义/插件/内置工具。tool/registry.ts:322-367:按模型筛选。session/tools.ts:24-116:包装成 AI SDK tool。session/tools.ts:118-205:MCP tool 接入。
6. 用户输入到 agent 行动的整体链路
runLoop
-> SessionTools.resolve
-> ToolRegistry.tools
-> ProviderTransform.schema
-> AI SDK tool({ execute })
-> model tool-call
-> Tool.Context with ask/metadata
-> item.execute
-> processor.completeToolCall7. 核心源码逐段讲解
Tool.Context
export type Context = {
sessionID: SessionID
messageID: MessageID
agent: string
abort: AbortSignal
metadata(input: { title?: string; metadata?: M }): Effect.Effect<void>
ask(input: Omit<Permission.Request, "id" | "sessionID" | "tool">): Effect.Effect<void>
}路径:packages/opencode/src/tool/tool.ts:16-26。
Tool.Def
export interface Def<Parameters extends Schema.Decoder<unknown>, M extends Metadata = Metadata> {
id: string
description: string
parameters: Parameters
execute(args: Schema.Schema.Type<Parameters>, ctx: Context): Effect.Effect<ExecuteResult<M>>
}路径:packages/opencode/src/tool/tool.ts:35-45。
wrap 校验和截断
const decoded = yield* decode(args)
const result = yield* execute(decoded as Schema.Schema.Type<Parameters>, ctx)
const truncated = yield* truncate.output(result.output, {}, agent)
return { ...result, output: truncated.content, metadata: { ...result.metadata, truncated: truncated.truncated } }路径:packages/opencode/src/tool/tool.ts:79-130。
内置工具列表
shell/read/glob/grep/edit/write/task/fetch/todo/search/skill/patch/lsp/plan 在 registry 中初始化。来源:packages/opencode/src/tool/registry.ts:229-275。
包装为 AI SDK tool
tools[item.id] = tool({
description: item.description,
inputSchema: jsonSchema(schema),
execute(args, options) {
const ctx = context(args, options)
const result = yield* item.execute(args, ctx)
return { ...result, attachments: result.attachments?.map(addIds) }
},
})路径:packages/opencode/src/session/tools.ts:75-116。
8. 关键 TypeScript 语法复习
- 泛型接口:
Def<Parameters extends Schema.Decoder<unknown>> - 条件类型:
InferParameters<T> Omit:工具 ask 不让调用方传 session/tool id。Record<string, AITool>:工具表。- dynamic import:加载自定义工具文件。
9-10. 架构思想与协作
- Strategy + Registry + Adapter。
- plugin hook 在定义阶段和执行阶段都可介入。
- ProviderTransform 负责按模型调整 schema。
- Session/Processor 提供 messageID、metadata 和 completeToolCall。
11. mini agent 对应代码
function resolveTools(registry, ctxBase) {
const tools = {}
for (const item of registry) {
tools[item.name] = {
description: item.description,
inputSchema: item.schema,
async execute(args, options) {
const ctx = makeToolContext(ctxBase, options)
await ctx.ask({ permission: item.name, patterns: ["*"] })
return item.execute(args, ctx)
},
}
}
return tools
}12. 费曼复述区
- ToolRegistry 和 SessionTools.resolve 的职责差异是什么?
- 为什么工具参数必须运行时 decode?
- 为什么工具上下文要有
ask和metadata?
13-15. 练习与自测
- 找到
Tool.Context并列出字段。 - 解释 patch/edit/write 的模型过滤规则。
- 实现一个
echotool。 - 追踪
SessionTools.resolve到ToolRegistry.tools再到Tool.init(read)。 - 回答:错误参数、超长输出、外部目录分别由哪一层处理?