工具注册表与执行流水线

模型吐出 tool-call 之后,真正干活的是 ctx.tools.execute()。校验、权限、超时、格式化如果散落在每个工具里,行为会对不齐,漏检也难查。dsh 要求工具先注册进表,每次调用走同一条固定流水线;工具作者写 execute,管治与观测交给 pipeline。

工具执行流水线

注册表与面向模型的投影

ctx.tools.register(defineTool({...})) 即让工具可见。注册表在作用域层持有完整 ToolDefinition,请求时投影为模型侧 ToolSchema。模型只看到 namedescriptionparametersoutputexecutefinalizeContenttimeoutMs、UI 呈现回调不会泄漏到协议。

1
2
3
4
5
6
7
8
9
10
11
ctx.tools.register(defineTool({
name: 'read_file',
description: '读取磁盘上的文件。',
parameters: {
path: { type: 'string', required: true, description: '绝对文件路径' },
},
output: { schema: { type: 'string' } },
async execute(args, exec) {
return readFile(args.path, { encoding: 'utf8', signal: exec.signal })
},
}))

arguments 进流水线前先按 schema 校验。字段名错了会变成结构化错误结果,轮次继续;未知工具是 UNKNOWN_TOOL。工具失败不等于会话结束,模型可以改参数再试。

流水线阶段

固定顺序:

tools/pre-execute → 单调守卫 → tools/executetools/post-executefinalizeContenttools/result

pre-execute 是可扩展门禁:允许、拒绝或询问,瀑布里多个插件可插手。单调守卫(guard)是底线:一旦拒绝,后续监听器不能改回允许。tools/execute 是环绕分发包装层,适合超时、指标;也是唯一可替换必需 signal 的环节,注册表在进主体前会重新融合调用方 signal。post-execute 可检查或替换结果、附加上下文。finalizeContent 由工具定义持有,把结果收成模型可见内容。tools/result 仅观测:公布前冻结 execution 与 outcome,观测到的即最终事实。

packages/core/tools/src/invariant.ts 用阶段追踪强制顺序:同一 execution 只能走一次 pre-executeexecute 必须跟在 pre 之后。顺序错了直接 fail,顺序固定写进运行时不变式。

并行、取消与 PTC

同 Step 多工具时,parallel-safe 可并发(默认最多 10),exclusive 构成排序屏障。调度仍在 Agent Loop 的 tool-calls.ts,注册表负责单次调用的管治语义。

取消协作式:主体前取消 → ABORTED_BEFORE_DISPATCH;主体后取消 → ABORTED(成功结果被替换)。工具必须观测 exec.signal;不观测则取消落不到主体上。

PTC mode(Programmatic Tool Calling)下,模型主要写代码,经 run_code 调生成 SDK,中间结果默认不进对话,只有 console.log / return 才进入。模式可选 native(各工具 schema)、ptc(仅 run_code + SDK)、both。PTC 下直呼其他工具名会解析为 UNKNOWN_TOOL,通告面与可调用面一致。

工具包入口在 packages/core/tools/schema.ts 提供 defineTooljson-schema.ts 做强制子集校验,ptc.ts 桥接 SDK 与 run_code。流水线把「干什么」留给定义,把「怎么管」收成一条可观测的传送带。

Author

simonisacoder

Posted on

2026-09-16

Licensed under

Comments