工具注册表与执行流水线
模型吐出 tool-call 之后,真正干活的是 ctx.tools.execute()。校验、权限、超时、格式化如果散落在每个工具里,行为会对不齐,漏检也难查。dsh 要求工具先注册进表,每次调用走同一条固定流水线;工具作者写 execute,管治与观测交给 pipeline。
注册表与面向模型的投影
ctx.tools.register(defineTool({...})) 即让工具可见。注册表在作用域层持有完整 ToolDefinition,请求时投影为模型侧 ToolSchema。模型只看到 name、description、parameters;output、execute、finalizeContent、timeoutMs、UI 呈现回调不会泄漏到协议。
1 | ctx.tools.register(defineTool({ |
arguments 进流水线前先按 schema 校验。字段名错了会变成结构化错误结果,轮次继续;未知工具是 UNKNOWN_TOOL。工具失败不等于会话结束,模型可以改参数再试。
流水线阶段
固定顺序:
tools/pre-execute → 单调守卫 → tools/execute → tools/post-execute → finalizeContent → tools/result
pre-execute 是可扩展门禁:允许、拒绝或询问,瀑布里多个插件可插手。单调守卫(guard)是底线:一旦拒绝,后续监听器不能改回允许。tools/execute 是环绕分发包装层,适合超时、指标;也是唯一可替换必需 signal 的环节,注册表在进主体前会重新融合调用方 signal。post-execute 可检查或替换结果、附加上下文。finalizeContent 由工具定义持有,把结果收成模型可见内容。tools/result 仅观测:公布前冻结 execution 与 outcome,观测到的即最终事实。
packages/core/tools/src/invariant.ts 用阶段追踪强制顺序:同一 execution 只能走一次 pre-execute,execute 必须跟在 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 提供 defineTool,json-schema.ts 做强制子集校验,ptc.ts 桥接 SDK 与 run_code。流水线把「干什么」留给定义,把「怎么管」收成一条可观测的传送带。