02.2 工具契约与注册
关注源码
src/Tool.tssrc/tools.tssrc/utils/toolPool.ts
Tool 不是简单函数
在这套系统里,一个 Tool 同时定义:
- 输入 schema 与 API schema
- prompt 文案
- 调用逻辑
call() - 是否只读、是否 destructive、是否可并发
- 权限检查与输入校验
- tool result 如何映射回 transcript
- tool use / tool result / progress 的 UI 渲染
- transcript search 文本
- auto-classifier 输入
这让工具成为横跨三层的统一对象:
- 模型可见能力
- 运行时可执行能力
- UI 可展示能力
ToolUseContext 是运行时最重的上下文对象之一
ToolUseContext 里包含:
- 当前工具池、命令池、主模型、thinking config
getAppState()/setAppState()- read file cache
- notifications / prompt request / permission 相关回调
- response length、stream mode、OS notification
- 当前
messages - 内容替换状态、查询链路跟踪、agentId/agentType
可以把它理解为“工具运行时的环境对象”,不是单纯的调用参数。
buildTool() 体现了默认安全策略
Tool.ts 里的 buildTool() 会给工具填充一组默认实现:
isEnabled -> trueisConcurrencySafe -> falseisReadOnly -> falseisDestructive -> falsecheckPermissions -> allowtoAutoClassifierInput -> ''
尤其重要的是:
- 并发默认关闭
- 读写默认按写处理
这说明工具系统是保守设计,而不是默认放开。
工具池装配流程
1. getAllBaseTools()
这里定义“当前构建理论上有哪些工具”,并受:
- feature gate
USER_TYPE- simple mode / REPL mode
- worktree / LSP / todo v2 等能力开关
影响。
2. getTools(permissionContext)
把理论工具池裁剪成“当前会话内建可见工具池”,同时处理:
- deny rule 过滤
- simple mode
- REPL 隐藏 primitive tools
- coordinator mode 特殊组合
3. assembleToolPool(permissionContext, mcpTools)
把内建工具与 MCP 工具合并,并保持:
- deny rules 同样作用于 MCP
- built-in 作为前缀
- 名称排序稳定
uniqBy(name)去重
这里专门强调 prompt-cache 稳定排序,说明工具顺序本身就是缓存契约的一部分。
4. mergeAndFilterTools(initialTools, assembled, mode)
交互式 REPL 还会进一步把初始 tools 与组装好的 tools 合并,并应用 coordinator mode 的最终过滤。
工具延迟加载
ToolSearchTool 与 deferred tools 让工具系统支持“大工具池但不把完整 schema 一次塞给模型”。这背后有两个目的:
- 控制 prompt 体积
- 保持缓存稳定
因此工具注册并不只是“罗列功能”,而是 prompt 工程的一部分。
设计上最重要的三个点
1. 工具对象横跨模型、运行时、UI 三个世界
这让系统不会出现“模型看到一套能力、运行时执行另一套、UI 渲染第三套”的分裂。
2. 工具池组装高度依赖上下文
权限模式、MCP 连接、运行模式、feature gate 都会改变“模型最终能看到什么”。
3. prompt-cache 稳定性是工具层一等约束
工具的排序、过滤、是否 deferred,不只是实现细节,而是缓存命中策略。