dsh-tool-cassette
  
> 给 Agent 的工具调用装一台录像机。 > > 第一次让 HTTP、MCP、数据库和本地程序真跑;以后拔掉网线,也能把同一段戏完整演完。
当外部 API 在 CI 里闹脾气、测试数据突然变脸、第三方服务又一次返回 429,按下 Replay:被选工具的真实正文不再执行,录好的规范结果会精确回到 DeepSeek Harness 的工具链中。
> 非官方社区插件。 本项目由社区成员独立开发和维护,与 DeepSeek 官方无隶属关系,也未获得官方审核或背书。
当前版本为 0.1.0,兼容边界固定在 DeepSeek Harness 0.1.0-rc.8。
先看它表演

演示过程很朴素:
1. 启动一个纯本地 HTTP 天气工具; 2. Record 一次,工具正文与网络请求各发生 1 次; 3. 关闭 HTTP 服务; 4. Replay 同一条调用,工具正文与网络请求都变成 0; 5. 返回结果保持一致,cassette 中的记录全部消费。
整个演示不调用模型,也不产生付费 API 请求。服务器已经下班,测试仍然准时上班。
| 阶段 | 工具正文 | 该工具产生的网络请求 | 结果 |
|---|---|---|---|
| Record | 1 | 1 | 武汉天气结果 |
| Replay | 0 | 0 | 同一份武汉天气结果 |
它解决什么问题
Agent 测试经常同时依赖两类不稳定因素:模型输出和外部工具。
官方 dsh-llm-replay 已经能回放模型流;HTTP、MCP、数据库、Python、Go、Java 等工具仍可能真的访问网络、修改数据或依赖一台刚好没开机的服务。dsh-tool-cassette 接管工具正文边界,把一次真实执行变成可验证、可离线复用的测试制品。
Record
Agent -> tools/execute -> 真实 HTTP / MCP / DB / 本地程序
-> DSH 规范 value/error -> cassette
Replay
Agent -> tools/execute -> cassette 精确命中 -> 当前 schema / renderer / post policy
真实工具正文调用 = 0它适合这些场景:
- 给 Agent 工作流做离线回归测试;
- 在 CI 中复现一次昂贵或偶发的工具响应;
- 验证插件升级后,当前 schema、renderer 和后置策略仍能处理旧结果;
- 调试 HTTP、MCP、数据库或多语言 sidecar 工具,又不想反复触碰真实服务;
- 与
dsh-llm-replay组合,搭建模型流与工具流都可回放的无密钥测试。
快速开始
前置条件:已安装 pnpm。本插件固定兼容 DeepSeek Harness 0.1.0-rc.8;该版本当前位于 npm next 标签。安装插件与运行 profile 时请持续使用同一 rc.8 CLI。
pnpm dlx --package @deepseek-ai/dsh@0.1.0-rc.8 dsh plugin --profile headless add dsh-tool-cassette安装包会通过 cordis.patch.yml 注入一个默认禁用的 tool-cassette 条目。在 profile 的 cordis.patch.yml 中覆盖它,明确填写模式、文件和叶子工具范围:
- id: tool-cassette
name: dsh-tool-cassette
disabled: false
config:
mode: record
file: .dsh-cassettes/weather.tool-cassette.jsonl
include:
- weather_lookup
- mcp_*_read跑完一次真实调用后,把 mode 改成 replay,其余轨迹保持一致:
config:
mode: replay
file: .dsh-cassettes/weather.tool-cassette.jsonl
include:
- weather_lookup
- mcp_*_read此时再调用 weather_lookup,cassette 会递出录制结果,真实工具正文继续休息。
从源码构建并安装本地包:
pnpm install
pnpm run demo
pnpm pack
pnpm dlx --package @deepseek-ai/dsh@0.1.0-rc.8 dsh plugin --profile headless add .\dsh-tool-cassette-0.1.0.tgz相对路径基于 DSH 进程工作目录解析。include 必须非空,支持精确名称和 * 通配符。空范围、空路径和重复模式都会在插件启动时直接失败。
三项配置,拒绝猜心
interface Config {
mode: 'record' | 'replay'
file: string
include: string[]
}| 字段 | 含义 |
|---|---|
mode | record 执行真实工具并写制品;replay 精确命中并跳过正文 |
file | 正式 cassette 文件;录制期间使用同路径加 .partial 后缀 |
include | 显式选择叶子工具;* 匹配任意长度字符 |
V1 只录显式选中的工具。未选工具按照原有流程执行,录像机不会抢戏。
它怎样认出“同一场戏”
每次被选调用都要交出四项身份证明:
1. 工具在调用树中的结构路径; 2. 工具名称; 3. 递归排序对象键后的无损 JSON 参数; 4. 按调用开始顺序分配的 ordinal。
对象键从 { "city": "武汉", "unit": "c" } 换成 { "unit": "c", "city": "武汉" } 仍能命中。数组顺序、参数值、调用顺序或结构路径发生变化时,回放器会立刻返回 CASSETTE_MISMATCH。
并发调用按“谁先开拍”分配 ordinal,结果可以倒序完成。A 先开始、B 后开始、B 先结束、A 后结束,最终仍会各回各家。
台词对不上,立刻喊卡
第一次轨迹偏差会让回放器进入 poisoned 状态。后续选中工具持续失败,真实正文保持不执行,避免一半读录像、一半碰生产服务的混合场面。
每个被选调用都必须经过 cassette 的 tools/execute 监听器,并在最终 tools/result 中完成核对:
- 参数、路径、顺序或工具名不一致:
CASSETTE_MISMATCH; - 录制存在未完成调用:保留
.partial,拒绝发布正式文件; - 回放存在额外调用或未消费记录:进程退出码为
1; - 更高优先级包装器短路、缺少结果或绕过轨迹:Record 失效,Replay poison;
- 被选调用在 DSH 的
pre-execute或 guard 阶段被拒绝:按未进入 cassette 的轨迹失败关闭。
tools/result 是 DSH 的只读观察事件。若另一个更高优先级插件同时短路 tools/execute 与 tools/post-execute,当前调用可能已经拿到那个插件的结果;cassette 会在最终观察点阻止录制发布或 poison 回放,并把进程退出码设为非零。
错误诊断只显示工具、路径、ordinal 和参数指纹。原始参数与结果不会跟着错误消息到处旅行。
回放时,旧结果还要参加今天的考试
录制阶段先缓存 tools/post-execute 的输入边界,再用 rc.8 的最终 tools/result 快照核对调用:
- 一般结果保存后置策略处理前的规范
value/error与additionalContexts; - 后置阶段触发取消时,保存 DSH 的最终取消结果;
- Replay 命中后,保存的成功
value会重新经过当前输出 schema、renderer、presentation meta 和后置策略。
因此回放时仍需注册同名工具,并保持输出契约兼容。录像带负责保存演员的表演,今天的安检规则仍由今天的 DSH 执行。
制品协议:每一帧都要接得上
cassette 使用版本化 NDJSON。每帧包含连续 seq、前一帧哈希和本帧 SHA-256,最后一帧为 complete。
Record 的发布过程:
1. 独占创建 <file>.partial; 2. 串行追加 header、call/start、call/result; 3. 每帧同步到磁盘; 4. 全部调用完成后写入 complete; 5. 关闭文件,以 create-only 方式原子发布正式文件,再删除 partial。
正式文件或 partial 已存在时,录制器拒绝启动。截断、帧重复、哈希篡改、协议版本错误和缺少尾帧都会在 Replay 激活前被拦下。
哈希链可以发现传输损坏、截断和普通篡改。它不包含数字签名;面对能够重写全部帧与哈希的攻击者,应由制品库补充签名、WORM 或不可变存储。
CLI:先验带,再放带
dsh-tool-cassette verify .dsh-cassettes/weather.tool-cassette.jsonl
dsh-tool-cassette inspect .dsh-cassettes/weather.tool-cassette.jsonlverify完整验证协议、帧配对、连续 ordinal、哈希链和完成尾帧,并用退出码表达结果;inspect只显示协议版本、工具数、调用数、完整性和消费说明;- 校验失败只输出结构性原因,不回显原始行、工具正文或绝对路径。
回放消费状态只存在于当前进程。所有记录消费完毕时卸载成功;poison、额外调用或未消费记录会让 headless/CI 进程退出码变为 1,同时由 Cordis 记录关闭错误。
安全说明:录像带可能拍到桌上的密码
V1 为了精确回放,会原样保存规范化后的参数、成功 value、失败信息、渲染内容和附加上下文。请把 cassette 当作密钥文件或测试数据库快照处理:
- 默认
.gitignore已排除 cassette 与 partial; - 只在隔离的本地或 CI 工作目录录制;
- 分享前人工检查全部内容;
- 录制结束后关闭不再需要的真实凭据;
- 参数指纹没有盐,低熵参数仍可能被猜测。
V1 不提供自动脱敏、加密、签名或远端制品库。
V1 的舞台边界
| 已支持 | 当前拒绝或留待后续版本 |
|---|---|
| 单 Agent、单场景 | 多 Agent 与并发 subagent |
| 显式选择的叶子工具 | 同时选择复合工具及其子工具 |
成功、结构化失败、additionalContexts | concludesTurn: true |
| 并发开始顺序与倒序完成 | 模糊匹配、参数忽略、自动更新 fixture |
| 调用前取消不消费记录 | 延迟、hang、流式输出、取消时序仿真 |
| 当前 schema、renderer 与 post policy 重跑 | UI、云端制品库、benchmark DSL、模型 judge |
回放调用命中 cassette 后会消费对应记录;随后发生的 post 阶段取消不会回滚消费位置。
LLM 流回放由官方 dsh-llm-replay 负责。本插件承诺的是被选工具正文边界的确定性回放。
它和邻居们怎样分工
| 项目 | 负责的边界 |
|---|---|
| dsh-llm-replay | 回放模型流 |
| dsh-subagent-cassette | 回放 one-shot subagent provider |
| dsh-tool-idempotency | 在线调用去重与并发 join |
dsh-tool-cassette | 保存工具规范 value/error,离线跳过真实工具正文 |
它是一盘可验证的测试录像带,也是一块明确的工具执行边界。它不会充当缓存、模型回放器或生产幂等层。
工程设计看点
这个小插件里藏着几件适合认真聊一聊的事:
- 确定性身份:使用结构路径、规范参数与 admission ordinal,避开易变的 call ID;
- 并发配对:以开始顺序建立身份,以 handle 配对结果,完成顺序不参与猜测;
- 失败关闭:首次 mismatch 后 poison,阻止真实工具偷偷补跑;
- 完整性协议:连续序号、SHA-256 哈希链、完成尾帧和严格解析共同发现损坏;
- 原子发布:
.partial、逐帧同步、create-only 正式文件与原子改名保护历史证据; - 策略再验证:回放规范 value,继续执行当前 schema、renderer 与 post policy;
- 真实运行时测试:ToolRuntime 回放时正文调用严格为
0,同时覆盖错误、上下文、并发、取消和生命周期。
这些约束共同回答一个问题:怎样让 Agent 测试可重复,同时保留对轨迹漂移的敏感度。
开发与验收
pnpm install --frozen-lockfile
pnpm run lint
pnpm run typecheck
pnpm run test:coverage
pnpm run build
pnpm run demo
pnpm run test:tarball测试只使用本地假工具与本地 HTTP 服务,模型调用和付费 API 调用均为零。CI 矩阵覆盖 Ubuntu、Windows、Node 22.19 与 Node 24。
常见问题
<details> <summary>它和普通 mock 有什么区别?</summary>
mock 通常由开发者手写期望结果;cassette 先执行一次真实工具,再保存 DSH 规范化后的 value/error。Replay 使用精确轨迹匹配,并重新经过当前输出契约与策略。
</details>
<details> <summary>为什么 Replay 还要注册同名工具?</summary>
当前工具定义提供 schema、renderer 和 presentation meta。回放结果要重新通过这些检查,旧录像才能参加今天的考试。
</details>
<details> <summary>可以把 cassette 提交进 Git 吗?</summary>
协议允许,安全默认建议保留在本地或受控 CI 制品库。cassette 可能包含参数、业务数据、错误详情和附加上下文,仓库默认已将它加入 .gitignore。
</details>
<details> <summary>整个 Agent 都会完全离线吗?</summary>
插件只拦截 include 选中的工具。模型流可搭配官方 dsh-llm-replay;未选工具仍按原流程执行。
</details>
许可证
[MIT](LICENSE)