首页
/ claw-code Mock Parity Harness:用 Anthropic 兼容 Mock 服务对 Rust CLI 做确定性端到端验证

claw-code Mock Parity Harness:用 Anthropic 兼容 Mock 服务对 Rust CLI 做确定性端到端验证

2026-09-04 16:34:33作者:齐添朝

claw-code 仓库内置了一套 Mock Parity Harness(模拟一致性测试装置):一个确定性的 Anthropic 兼容 mock 服务,加上一组在干净环境中运行 Rust claw 二进制的可复现测试脚本。本文基于 rust/MOCK_PARITY_HARNESS.md 展开,逐条覆盖文档中的产物清单、12 个脚本化场景与运行方式,并结合 mock-anthropic-service 源码harness 测试实现 说明场景如何被识别、SSE 流如何被构造、干净环境如何被隔离,帮助你既能在本地跑通整套验证,也能理解每个断言背后的请求级证据。

一、这套装置解决什么问题,产物在哪

在 Agent CLI 这类产品中,模型输出是不可控变量:真实 API 的回复文本、工具调用参数、token 用量都会随时间和模型版本漂移,导致端到端测试无法稳定断言。claw-code 的做法是把"模型"也变成一个可脚本化的组件:mock 服务按预设剧本应答(文本流、工具调用、usage 数据),CLI 侧的行为(工具执行、权限拦截、权限提示、自动压缩、成本统计)就可以被精确断言。

文档列出的三个产物及其在仓库中的位置:

产物 路径 作用
Mock /v1/messages 服务 rust/crates/mock-anthropic-service/ 实现确定性的 Anthropic Messages API 应答
端到端干净环境 harness rust/crates/rusty-claude-cli/tests/mock_parity_harness.rs 在隔离工作区中运行 claw 二进制并断言行为
便捷运行脚本 rust/scripts/run_mock_parity_harness.sh 一行命令触发整个 harness

二、Mock 服务的内部机制:场景探测、剧本应答与请求捕获

场景如何被探测

mock 服务并不需要额外的路由参数。它从 CLI 发来的 /v1/messages 请求体中"读剧本":用户消息文本里会带一个 PARITY_SCENARIO: 前缀的 token。常量定义在 lib.rs 第 13 行SCENARIO_PREFIX),而 detect_scenariolib.rs 第 244-L254 行)从最后一条消息的文本块倒序扫描,提取前缀并解析为 12 种 Scenario 之一。harness 侧正是在构造 prompt 时拼上这个前缀的(format!("{SCENARIO_PREFIX}{}", case.name),见 mock_parity_harness.rs 第 339 行)。

状态感知:同一个场景会应答两轮

多数工具类场景需要"先要工具调用、拿到工具结果后再给最终文本"。mock 通过 latest_tool_result 判断当前请求里是否已带 tool_result 块:

  • 第一轮(无工具结果):返回 stop_reason: "tool_use" 的消息,携带固定 ID 和固定参数的工具调用块。例如 read_file_roundtrip 会要求读取 fixture.txtgrep_chunk_assembly 会要求 grep_search pattern=parity output_mode=count
  • 第二轮(有工具结果):从 tool_result 的 JSON 里提取关键字段(extract_read_contentfile.contentextract_num_matchesnumMatchesextract_bash_stdoutstdout 等,均位于 lib.rs 第 1067-L1123 行),拼进最终助语文本,如 read_file roundtrip complete: alpha parity line

SSE 流与"JSON 分块"边界情况

当请求带 stream: true 时,服务按标准 Anthropic SSE 事件序列应答:message_startcontent_block_start → 若干 content_block_deltacontent_block_stopmessage_deltamessage_stop(见 streaming_text_sse)。一个值得注意的细节:grep_chunk_assemblymulti_tool_turn_roundtrip 场景下,工具的 input_json_delta.partial_json故意拆成 3 个碎片下发(如 "{\"pattern\":\"par""ity\",\"path\":\"fixture.txt\""",\"output_mode\":\"count\"}",见 lib.rs 第 355-L359 行)。这是在专门验证 CLI 端对"工具输入 JSON 需要跨 chunk 拼装"这一边界条件的处理能力——这正是该场景名 "chunk assembly" 的含义。

请求捕获与默认响应参数

每个请求都会被记录进 CapturedRequest(method、path、headers、scenario、stream、raw_body,lib.rs 第 16-L24 行),供测试事后做请求级审计。非流式文本响应的默认 usage 为 input_tokens: 10 / output_tokens: 6;而 auto_compact_triggeredtoken_cost_reporting 场景会返回被放大的 usage(50000/200 与 1000/500),用来验证 CLI 的压缩阈值触发与成本上报路径。所有响应的 model 字段固定为 claude-sonnet-4-6DEFAULT_MODEL)。

三、干净环境:harness 的隔离设计

harness 的核心价值在于"clean-environment"。每个场景都会:

  1. 在临时目录创建独立 workspace(claw-mock-parity-{场景名}-{pid}-{毫秒}-{序号}),并预留 config-homehome 两个子目录;
  2. 按场景准备 fixture(如写入 fixture.txt、构造外部插件目录);
  3. 以完全清空的环境变量启动 claw 二进制。

环境清空的细节在 run_case(mock_parity_harness.rs 第 310-L366 行)env_clear() 之后仅注入 6 个变量——ANTHROPIC_API_KEY=test-parity-key(mock 服务不校验密钥,任意非空值即可)、ANTHROPIC_BASE_URL(指向本进程内启动的 mock)、CLAW_CONFIG_HOMEHOMENO_COLOR=1PATH=/usr/bin:/bin。CLI 启动参数为 --model sonnet --permission-mode <场景模式> --output-format=json,可选 --allowedTools--resume。权限提示类场景还会通过管道写入 stdin(y\n / n\n)来模拟人工批准或拒绝。

每个场景结束后 workspace 立即被删除,保证场景之间零串扰;测试末尾还会对整个 mock 服务捕获到的请求序列做全量断言(见下节)。

四、12 个脚本化场景逐项解析

文档列出的 12 个场景与 mock_parity_scenarios.json 清单一一对应。下表汇总了每个场景在 harness 中的权限模式、工具白名单、stdin 注入及核心断言(均取自 mock_parity_harness.rs):

场景 权限模式 验证点
streaming_text read-only 纯文本流式回复;iterations=1,无工具调用;最终文本精确匹配 "Mock streaming says hello from the parity harness."
read_file_roundtrip read-only read_file 执行并回传内容,最终文本含 alpha parity line,tool result 含 fixture 绝对路径
grep_chunk_assembly read-only 跨 chunk 拼装的 grep_search 输入可正确解析,输出 2 occurrences(fixture 中 2 行含 parity),is_error=false
write_file_allowed workspace-write generated/output.txt 成功,文件实际落盘且内容为 created by mock service\n
write_file_denied read-only write_file 被拒,tool result 含 requires workspace-write permissionis_error=true,且 generated/denied.txt 不存在
multi_tool_turn_roundtrip read-only 单轮内并行发起 read_file + grep_search 两个工具调用,两条结果均回传,最终文本同时包含两处证据
bash_stdout_roundtrip danger-full-access bash 执行 printf 'alpha from bash',stdout JSON 字段被断言
bash_permission_prompt_approved workspace-write 触发 "Permission approval required / Approve this tool call? [y/N]:" 提示,stdin 注入 y,命令实际执行
bash_permission_prompt_denied workspace-write 同样触发提示,stdin 注入 n,tool result 含 denied by user approval promptis_error=true
plugin_tool_roundtrip workspace-write external-plugins/parity-plugin 加载外部插件工具 plugin_echo.claude-plugin/plugin.json 声明 + shell 脚本实现),经运行时工具注册表执行,回显 JSON 中含插件 ID parity-plugin@external
auto_compact_triggered read-only JSON 输出必须含 auto_compaction 键(低于阈值时可为 null),且 usage.input_tokens >= 50000(来自 mock 放大的 usage)
token_cost_reporting read-only usage 的 input/output tokens 均非零,estimated_cost$ 开头的字符串

插件场景的 fixture 构造很能说明加载路径:prepare_plugin_fixture 会创建 external-plugins/parity-plugin/.claude-plugin/plugin.json 与可执行的 tools/echo-json.sh,并在 config-home/settings.json 中写入 enabledPlugins: {"parity-plugin@external": true}plugins.externalDirectoriesmock_parity_harness.rs 第 413-L478 行)。

请求级总量断言:21 次 /v1/messages

测试结束时并非只信各场景的局部断言,而是对 mock 捕获的全量请求做序列校验(mock_parity_harness.rs 第 185-L232 行):

  • 12 个场景共产生 21 次 /v1/messages 请求(9 个单轮场景各 1 次,8 个两轮回调场景各 2 次……精确展开为:1 次请求的 4 个场景 + 2 次请求的 8 个场景 = 4 + 16 = 20,加上 multi_tool_turn_roundtrip 等按实际剧本合计 21);
  • 21 次请求全部带 stream: true,证明 CLI 始终走流式通道;
  • 请求的 scenario 序列被逐一对齐到一个硬编码的 21 元素数组,任何一次请求顺序或剧本漂移都会让测试失败。

源码中有一段注释解释了历史演变:自 be561bf 引入 count_tokens 预检后,每个回合会在 /v1/messages 之前多发一次 POST /v1/messages/count_tokens,因此现在的断言先过滤出 /v1/messages 子集再比对(mock_parity_harness.rs 第 186-L201 行)。另外,harness 还会把每个场景的 name / category / description / parity_refs / iterations / request_count / tool_uses / tool_error_count / final_message 汇总为报告,当环境变量 MOCK_PARITY_REPORT_PATH 存在时写入指定 JSON 文件(maybe_write_report,第 802-L817 行)——这正是 parity diff 脚本的数据来源。

五、运行方式:harness、parity diff 与清单对齐

一键运行 harness

文档给出的标准命令:

cd rust/
./scripts/run_mock_parity_harness.sh

run_mock_parity_harness.sh 的实现只有三行有效逻辑:切到 rust/ 目录后执行

cargo test -p rusty-claude-cli --test mock_parity_harness -- --nocapture

行为清单 / parity diff

cd rust/
python3 scripts/run_mock_parity_diff.py

run_mock_parity_diff.py 的工作分三步:

  1. 引用完整性检查:读取 mock_parity_scenarios.json,确认每个场景的每条 parity_refs 字符串都能在仓库根目录的 PARITY.md 中找到,缺失则直接报错退出(退出码 1);
  2. 运行 harness:以临时目录中的 report.json 作为 MOCK_PARITY_REPORT_PATH 触发测试,拿到结构化报告;
  3. 输出检查单:逐场景打印 [PASS|MAPPED|MISSING] 状态、描述、parity 引用、迭代/请求/工具调用/错误数与最终文本,最后附一张 "PARITY coverage map"(每个 PARITY.md 条目被哪些场景覆盖)与 harness 汇总统计。

--no-run 参数可跳过实际执行,只做清单与 PARITY.md 的对齐检查:

python3 scripts/run_mock_parity_diff.py --no-run

三方一致性约束

文档特别强调:mock_parity_scenarios.json 必须与 mock_parity_harness.rsPARITY.md 保持对齐。这个约束不是靠约定,而是靠断言强制执行——测试启动时先加载 manifest,再断言"硬编码场景数组名序列 == manifest 名序列"(assert_eq!(case_names, manifest_names, "manifest and harness cases must stay aligned")mock_parity_harness.rs 第 153-L161 行)。也就是说,新增一个场景如果只改清单不改 harness,cargo test 会直接红掉。

六、手动启动 Mock 服务器

除了被 harness 内嵌使用(测试进程内 MockAnthropicService::spawn() 起在 127.0.0.1:0 随机端口),mock 服务也提供独立二进制,便于手工调试:

cd rust/
cargo run -p mock-anthropic-service -- --bind 127.0.0.1:0

服务器启动后打印一行 MOCK_ANTHROPIC_BASE_URL=...(见 main.rs 第 30 行)。把 ANTHROPIC_BASE_URL 指向该 URL、ANTHROPIC_API_KEY 设为任意非空值,即可用真实 claw CLI 或任何 Anthropic 兼容客户端对接这个 mock。参数解析支持 --bind HOST:PORT--bind=HOST:PORT 两种写法,--help/-h 打印用法(main.rs 第 7-L27 行);注意端口为 0 时由 OS 分配随机端口,具体值以打印的 MOCK_ANTHROPIC_BASE_URL 为准。服务器会持续等待请求直到 Ctrl-C 后干净退出。

七、小结

Mock Parity Harness 把"模型行为"降级为可复现的输入剧本,把"CLI 行为"升格为可断言的输出契约:SSE 事件序列、工具 JSON 分块拼装、权限三档(read-only / workspace-write / danger-full-access)与提示批准、外部插件加载、自动压缩的 usage 门槛、成本字符串格式,全部落在 21 次被逐条捕获与排序的 HTTP 请求上。配合 mock_parity_scenarios.json ↔ harness ↔ PARITY.md 的三方一致性断言和 run_mock_parity_diff.py 的覆盖度报告,它构成了一套可重复、可审计、不依赖外部网络的行为一致性验证体系,是理解 claw-code Rust 实现如何对标上游行为的最佳入口。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
980
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384