DeepSeek Harness Tools:从函数调用到可审计工具流水线
JotaroRD

工具执行流水线

模块定位

packages/core/tools 是工具注册和执行中心。它解决的不只是“模型调用函数”,而是工具 schema、权限、审批、超时、并发、输出校验、上下文附加、UI 展示、Code Mode SDK 这些问题。

源码入口:

  • packages/core/tools/src/index.ts
  • packages/core/tools/src/schema.ts
  • packages/core/tools/src/code-mode.ts
  • packages/core/tools/src/presentation.ts
  • docs/tool-execution-pipeline.md
  • docs/tool-catalog.md

ToolDefinition 包含什么

一个工具定义通常包含:

  • name
  • description
  • parameters
  • output.schema
  • output.render
  • execute(args, exec)
  • 可选 timeoutMs
  • 可选 isConcurrencySafe(args)
  • 可选 presentCall()
  • 可选 presentResult()
  • 可选 finalizeContent()

重点是 canonical output。工具 body 返回 JSON value,registry 校验它符合 output schema,再通过 render 生成模型可见 content。这样程序消费值和模型读取文本被分开。

执行流水线

一次工具执行顺序:

1
2
3
4
5
6
7
8
9
10
flowchart LR
A["arguments snapshot"] --> B["validate args"]
B --> C["tools/pre-execute"]
C --> D["guards"]
D --> E["tools/execute wrappers"]
E --> F["tool body"]
F --> G["tools/post-execute"]
G --> H["finalizeContent"]
H --> I["tools/result"]
I --> J["session tool/result"]

几个边界要分清:

  • tools/pre-execute 可以 allow、ask、deny,但不能改 args。
  • guard 是单调拒绝。
  • tools/execute 是 around-dispatch wrapper,适合 timeout、metrics、retry。
  • tools/post-execute 可以 block、替换 content、替换 value、附加 contexts。
  • finalizeContent 由工具定义拥有,保证最后的内容不变量。
  • tools/result 是 live observation;tool/result 是 durable session event。

参数 schema 与输出 schema

defineTool() 提供 typed schema DSL。它支持 string、number、integer、boolean、null、array、object、oneOf、enum、const 等 JSON schema 子集。

输入错误不会直接 throw 到 loop,而会走正常 error-result path,变成 INVALID_ARGS。输出则由 registry 强制校验,工具不能返回不符合声明的值。

并发模型

工具可以声明 isConcurrencySafe(args)。只有返回精确 true 的调用才是 parallel。其他情况都是 exclusive,包括:

  • 未声明 classifier。
  • 参数无效。
  • classifier throw。
  • 返回非 true。

Loop 会让连续 parallel 调用进入 bounded rolling pool,exclusive 调用形成 barrier。这样读文件、搜索等可以并发,而写文件、shell、状态变更默认保守。

Tool-owned UI Presentation

工具可以返回 render intent,而不是让前端按工具名硬编码。

典型 card:

  • generic
  • terminal
  • diff
  • search
  • read
  • web

UI 在 live streaming 和 replay 时都可以调用 presenter,所以 presenter 必须是纯函数:只依赖 args 和 durable result,不依赖当前进程状态。

Code Mode

Code Mode 是这个工具系统最有启发的部分。它不把所有 end tools 都直接暴露给模型,而是暴露一个 run_code transport,再生成当前可见工具集合的 SDK。

模型写代码:

1
2
const files = await tools.grep({ pattern: "SessionEventMap", path: "packages" })
return files

每个 binding 调用仍然回到完整工具流水线。区别是中间值只在代码执行里流动,不进入对话历史;最终只有 console.log 和 return 的结果回到模型。

这适合:

  • 多文件读取后聚合。
  • 搜索结果过滤。
  • 多工具中间计算。
  • 避免把大量中间结果塞爆上下文。

常见坑

第一,timeoutMs 是声明元数据,真正 enforce 需要 timeout policy wrapper。

第二,tools/pre-execute 不能改 arguments,避免日志和实际执行不同步。

第三,content replacement 不是保密边界。如果程序消费者不能拿到值,要替换 canonical value 或 block。

第四,Code Mode 的中间 canonical values 不进 session log,不能用于 replay 重建。

继续读源码

  • packages/core/tools/README.md
  • docs/tool-catalog.md
  • docs/tool-execution-pipeline.md
  • packages/code-runtime/README.md