先问一个有点扎心的问题:同样是接了 Claude 或者 GPT-4,为什么 ChatGPT 给你的感觉是 “复制粘贴助手”,而 Cursor、Claude Code 这类工具却像一个能自己改文件、跑命令、然后回来跟你汇报的队友?
差距不在模型本身。两家背后可能跑着同一套底座,但后者在外面多包了一层东西:指令、工具、上下文、子智能体、权限边界。
这层东西在业内有个专门的名字,叫智能体 Harness(智能体外壳/智能体编排层)。模型是发动机,Harness 才是那台能上路的车。

这篇文章想聊清楚一件事:在测试自动化这个场景里,Harness 到底该怎么搭。尤其是当你用 Playwright 这种动辄一次输出几千行日志的框架时,把所有东西一股脑塞给大模型,结果就是速度慢、成本贵、还越来越看不懂自己在干什么。
我们会拆开看四件事:Harness 本体、代码索引与记忆、分层模型路由,以及让一切不跑偏的 AI 治理。
为什么测试自动化,特别容易把大模型用废
做过端到端测试的同学都懂那个酸爽:一个用例挂了,终端里能滚出几百行 Playwright 输出,页面对象、步骤定义、测试数据、几套环境配置全搅在一起。
你把这坨日志直接粘贴给大模型,它能给你 “总结” 出一大段看似合理、其实南辕北辙的解释。
更麻烦的是成本。根据 Anthropic 官方 2025 年底公布的定价,Claude Opus 4.5 输入是 5 美元/百万 token、输出 25 美元/百万 token;同代的 Haiku 4.5 是 1 美元/5 美元。
也就是说,你让 Opus 去读一遍 Playwright 的 stdout,再让它 “理解” 一下,光这一轮往返就是真金白银。
npm 上 @playwright/test 的周下载量已经破亿,团队里每天跑的用例成千上万,把日志全喂给最贵的模型,账是不划算的。
所以,一个成熟的 Harness 在测试场景里干的事,本质上是把大模型当 “规划器”,而不是 “日志搬运工”:
• 把代码库先索引好,让模型用结构化查询问 “哪个方法调用了我”,而不是逐文件读一遍;
• 跑测试、生成文件这种脏活,扔给更小、更便宜的子智能体,主会话只收一张精简的 JSON 摘要;
• 开跑之前,先查验凭证、环境配置和参考用例。如果是配置问题就别硬进自愈流程;
• 出问题时,按固定路径走:诊断 → 修复补丁 → 审核 → 只重跑受影响的用例 → 给人看得懂的报告。
这套思路跟你用 Cursor、Claude Code 还是自己拿 SDK 手搓,都没关系。模式是同一个。
到底什么是智能体 Harness
我自己更喜欢把 Harness 类比成一个 “给模型用的操作系统”。模型本身只会做一件事:根据上下文预测下一个 Token。
Harness 负责把它变成能干活的智能体:给它记忆、给它工具、给它一个沙盒、给它一套什么时候该停的规则。
一个最小可用的 Harness 至少要兜住三件事:
1. 指令:系统提示、CLAUDE.md 这类规则文件、技能脚本、子智能体定义;
2. 工具:文件读/写、终端、搜索、MCP 服务器、浏览器自动化;
3. 循环:模型提方案 → Harness 执行该方案 → 结果返回给模型 → 再来一轮,直到任务完成。
但只是这三件,离 “能上生产” 还差得远。真正拉开差距的是下面这几件事:
• 上下文管理:模型能看见哪些文件、哪些工具输出,什么时候把旧内容裁剪掉;
• 任务编排:把规划、执行、审核拆分给不同的子智能体,别让一个会话全包;
• 安全边界:沙盒、权限、钩子,哪些事必须由人确认;
• 终止条件:知道什么时候算干完了,而不是在那儿空转。
一个能落地的 Harness,长什么样
下面这套结构,是 Anthropic 生态里(也就是 Claude Code+.claude/ 目录)非常典型的搭法。
你换到 OpenAI Agents SDK、LangGraph 或者自己的 SDK,骨架是一样的。
1. 控制面板:CLAUDE.md+settings.json
主会话固定用 Opus 来做规划、判断和调用 MCP 工具;跑测试这种重复体力活,派给 Haiku 子智能体在独立上下文里完成。
路由表和硬规则都写在 CLAUDE.md 里,比如下面这种:
markdown
CLAUDE.md 节选:分层模型路由
## Tiered model routing (Opus parent + Haiku runner)
This project pins the main Claude Code session to **Opus**
(.claude/settings.json → model: claude-opus-4-7) and delegates
**test execution only** to a Haiku subagent defined in
.claude/agents/test-runner.md.
1. When the user says "run / execute / verify / re-run" a
scenario id / tag / feature file → invoke the `test-runner`
subagent. Do NOT run `npx playwright test` directly in the
main session.
...
4. If failure_category is auth / config / network / other
→ do NOT self-heal. Surface the JSON report and stop.注意第 4 条:当失败类别是鉴权、配置、网络或者 “其他” 的时候,不能进行自愈,直接把 JSON 报告甩出来就停止。
这就是治理思维,不是所有失败都该让 AI 自己动手解决。
2. 技能:把多步流程写成状态机
技能(skill)在这里不是啥玄学概念,就是一堆写好的 Markdown 脚本,规定 Opus 主会话一步一步该干嘛。
它们就是 Harness 的状态机:执行顺序是什么、停止条件是什么、每一步由谁处理。
比如 /selfheal 这个技能,专门处理失败用例的自动修复:
markdown
/selfheal 技能流程
0. Preflight. 先调用 /preflight 跑凭证和环境检查
0.5 Fixture check. 校验测试数据
1. 通过 test-runner 子智能体跑失败的场景
2. 通过 MCP Playwright 浏览器快照看页面到底长啥样
...
5. 调用 heal-edit-reviewer 子智能体审查补丁
7. 用 get_impact_radius_tool 找出所有受影响的场景
8. 在 env1 和 env2 并行重跑再比如/codegen,把 CSV 里写好的验收标准翻译成 BDD 的 .feature 和 Playwright 代码:
markdown
/codegen 技能流程(规划器/渲染器分离)
0. Preflight.
2. 用 semantic_search_nodes_tool 在知识图谱里找可复用的步骤
6. 把 BuildSpec 交给 codegen-writer 子智能体渲染文件
7. 让 feature-reviewer 审查 .feature
8. 让 heal-edit-reviewer 审查生成的 .ts
10. 抽一个场景用 test-runner 冒烟跑一下关键在第 2 步和第 6 步:规划器只负责 “想”,渲染器只负责 “写”。写代码的 Haiku 看不到 MCP、不做判断、不跟用户对话,它就是个 “按图施工” 的工人。
3. 子智能体:“瘦身高工”,只返回 JSON 数据
每个放在 .claude/agents/*.md 下的子智能体,都是一个被剥夺了大部分能力的 “专业工人”,拥有精简的工具集、固定的输出模式和严格的禁止规则。
以 test-runner 为例:
yaml
.claude/agents/test-runner.md 的 frontmatter
---
name: test-runner
description: Run Playwright tests. Does NOT self-heal,
edit code, or modify any files.
model: claude-haiku-4-5
tools: Bash, Read, Glob
---看到没有!它没有写文件的权限,没有搜索,没有 MCP。它被强制返回一种严格的 JSON 数据,大概 200 字节:
json
test-runner 返回给主会话的契约
{
"status": "pass" | "fail",
"exit_code": 0,
"scenario_id": "login_003",
"failure_category": "locator-timeout" |
"assertion" | "auth" | "config" |
"network" | "other",
"stdout_tail": "<最后约 30 行 stdout/stderr>"
}这就是治理,主会话里再也不会出现几 MB 的 Playwright 日志。Opus 看到的是结论和分类,要调试的话,再自己决定要不要去翻阅那 30 行尾巴。
类似的子智能体通常还有:codegen-writer(写 BDD/TS)、heal-edit-reviewer(审查补丁)、feature-reviewer(审查 .feature)、test-data-fixer(处理测试数据)。
每人一套工具白名单、一套输出 schema、一份 “禁止事项”。
4. MCP 与钩子:Harness 的外设和后台程序
MCP 服务器把 Harness 的 “触角” 伸出去:Playwright 浏览器 MCP 可以让模型实时查看页面、定位元素;代码审查图 MCP 提供结构化+语义化的代码索引,不用打开整个文件。
钩子则在智能体循环之外静默运行,比如每次写完文件,自动触发一次知识图谱的增量更新。这些事不占 Opus 的 token,但治理和基础设施该跑的一步都没少。
记忆和索引:别再让模型读全量代码
“记忆索引” 听着玄,其实是两套产品,解决的是两种完全不同的问题。
1. 代码知识图谱:给代码库装一张 “地图”
一套典型实现是基于 AST 的代码审查知识图谱插件。它在每次编辑、写入之后,通过 PostToolUse 钩子做一次增量更新(通常 2 秒左右),把代码库解析成一棵节点树。
每个节点存储如下信息:
• 限定名称、文件路径、行号范围;
• 参数、返回类型;
• 内容的 SHA-256 哈希,用来检测是否变更了。
在这张图之上再加一层语义:所有代码节点(比如 600+ 个)用 all-MiniLM-L6-v2 这一类小模型 embed 成 384 维向量。
于是,你可以问:CSV 里写的 “验证用户名输入框是否存在”。
它可能对应 Username.verifyUsernameFieldVisible(),哪怕两边命名风格完全不一样,也能语义命中。
CLAUDE.md 里要立一条铁律:先查知识图谱,再读文件。真要修改某个方法的插入位置,再用 offset/limit 做一次窄范围读取,不要 cat 整个文件。
2. 会话记忆:把规则留下来
另一套记忆放在 .claude/memory/ 目录里,由 memory-template 初始化。它解决的是 “这个团队的约定”。
比如 “我们登录态走 Redis 不走 Cookie”“修复失败用例最多重试 2 次”“数据替换这种事必须由人确认”。这些约定,如果每次都要重新跟模型讲一遍,成本会很高。
会话记忆每次启动都跟 CLAUDE.md 一起加载。
也就是知识图谱告诉你代码库有什么,会话记忆告诉你这个团队怎么干活。
3. 实战里怎么用
把上面那两套产品串起来看,三个典型场景的节奏是这样的:
• 跑 /codegen 时:Opus 读取 CSV、拆出场景和步骤;对每一步,先调用 semantic_search_nodes_tool 找出现有可复用的步骤或页面对象方法;图告诉它 “别重复造轮子”;然后 Haiku 渲染器按 BuildSpec 写文件;审核员先 lint、再冒烟测试。
• 跑 /selfheal 时:test-runner 返回一份紧凑的 JSON;Opus 用 callers_of(brokenMethod) 反查受影响范围,只去读那些真正会被牵连的步骤;再调用 MCP 浏览器查看真实页面;修复完之后 get_impact_radius_tool 告诉它 “只需要重跑这 4 个场景”,而不是整个套件;最后在两套环境上并行回归。
• Code Review 时:review-delta 命令只看自上次提交以来的变更,目标控制在 800 Token 上下文以内;review-pr 才会把完整 PR、受影响范围、测试覆盖缺口一次性给出来。
分层模型路由:让 Opus 当 “大脑”,Haiku 当 “手脚”
这是 Harness 能省钱的关键。我们按 Anthropic 2025 年 11 月的公开定价算笔账:
• Opus 4.5:输入 5 美元/百万 token,输出 25 美元/百万 token;
• Haiku 4.5:输入 1 美元/百万 token,输出 5 美元/百万 token。
也就是说,每百万输出 token,Opus 的价格是 Haiku 的 5 倍。
测试执行、文件生成、单行修复这种事,其实不太需要 Opus 那种 “深思熟虑” 的推理能力,它们要的是 “按章程干活”。把这些事扔给 Haiku,主会话即使系统 Token 总量稍微涨一点,整体成本仍是下降的。
更关键的是上下文洁净度:Opus 主会话里如果全是 Playwright 的 stdout、生成的 .ts 文件、中间产物,它做判断的质量也会下降。分层路由不只是省钱,是保护规划器的认知带宽。
AI 治理:让行为可预测,而不是神奇
“治理” 这个词容易被理解成 “少用 AI”。但在 Harness 语境下,治理其实是三件事:行为可预测、输出可审计、坏模式有硬性刹车。
1. 预检关卡:快速失败,别硬修复
测试启动之前,先把这些配置项查验一遍:
• 凭证是否还有效(Token 是否过期);
• 环境配置文件是否完整、指向对的环境;
• 参考用例(smoke suite)能不能跑通。
因为设置错了、数据漂移了,跟 “定位器失效” 完全是两回事。把这两类失败混在同一个自愈循环里,AI 就会去修改本不该修改的代码。
是配置问题就直接报错、给人看,不要让 AI 猜。
2. 关注点分离:不明确就报错,别瞎猜
每个子智能体都有明确职责。规范没说清楚的时候,它的反应是返回一个错误 JSON,而不是 "我觉得应该这样"。
这一条对维护成本影响巨大,你回头看日志,能一眼知道哪一步没对齐契约。
3. 上下文预算九条军规
这些规则直接写在 CLAUDE.md 里,等于把上下文治理制度化:
1)图优先查询,禁止盲读文件;
2)窄范围读取(offset/limit);
3)MCP 快照也拿窄的;
4)批量 I/O 全甩给子智能体;
5)子智能体返回值要 “瘦”,不要把 stdout 原样透传;
6)能复用就别重写,动手前先语义搜一遍;
7)按影响范围重跑,不要每次都跑整套套件;
8)不要把原始工件(原始日志、生成代码)回显给用户;
9)源代码里禁止出现 cat/head/tail 这种操作。
4. 钩子:把治理自动化
很多约束靠 “模型自觉” 是靠不住的。在 .claude/settings.json 里挂钩子:写文件后自动更新知识图谱、跑 lint、跑敏感信息扫描。
这些钩子在智能体循环外面跑,不花 Opus 的 Token,但该拦的事一样拦得住。
5. 输出合同:JSON 才是优选项
每个子智能体都返回严格的 JSON。父智能体负责把 JSON 渲染成表格或者输出人话给人看。这既是用户体验,也是上下文治理。
你不会想在主会话里收到一段 800 字的 “我刚刚尝试做了以下事情……” 这种散文。
端到端:一次失败用例是怎么被兜住的
把上面这些拼起来,你会得到这样一次真实失败的处理过程:
1. 开发在 main 上提了一个登录页改动;
2. CI 里的智能体跑了一组 smoke 用例,login_003 挂了;
3. test-runner 返回 JSON:failure_category = locator-timeout,附了 30 行 stdout 尾巴;
4. Opus 主会话没去看完整日志,而是用 callers_of(loginButton) 在知识图谱里反查:哪几个场景依赖这个定位器;
5. 通过 Playwright MCP 看了一眼实时页面:选择器从 #login-btn 变成了button[data-testid=submit];
6. heal-edit-reviewer 生成补丁,限制修改只在页面对象这一层;
7. 影响范围工具算出 “受牵连的还有 login_001、login_002、login_005”;
8. Haiku 子智能体在两套环境并行重跑这 4 个用例,而不是全量 2000 个;
9. 成功后,Opus 给人一份简短报告:改了哪一行、为什么、影响了哪几条用例、有没有附带跑过 smoke。
整个过程里,最贵的 Opus 只做了判断和审核;真正跑命令、读文件、写补丁的是 Haiku;知识图谱让它不需要扫描整个仓库;钩子在背后做了图谱更新和 lint。这就是 “可预测” 和 “神奇” 的区别。
收尾:四条可以直接抄走的原则
不管你最后用的是 Claude Code、Cursor、OpenAI Agents SDK 还是自己搭的 LangGraph,这四条原则都建议写进团队规范:
1.决策和执行分开。一个会话负责规划、审核、拍板;专门的工人负责跑测试、写文件、审补丁。每个工人职责单一、返回结构化结果。
2.先验证,再动手。凭证、配置、参考用例在开跑之前就该查验完。配置类和数据类失败不要混进自愈循环,快速失败、清晰汇报。
3.先建索引,再读代码。一张结构化加语义化的图,能把 “整个仓库读一遍” 变成 “精确问几个问题”。成本降了,分类速度反而快了。
4.把每一条退出路径都管控住。权限规则、审核关卡、有限次数的修复尝试、双环境并行回归、数据替换的人工确认。这些不是繁文缛节,是 demo 工作流和生产工作流之间真正的分水岭。
模型会一直变强,但 Harness 的差别会一直存在。同一个 Opus,被合理的外壳包起来,可以无人值守跑一晚上测试、第二天早上给你一份干净的报告;被直接丢在聊天框里,它就是个帮你读日志的昂贵实习生。
从哪里开始?我的建议是先做两件事:给 test-runner 定一个严格的 JSON 返回契约,再加一道预检关卡。这两步成本最低,能立刻让你看到成本和稳定性的差别。
代码索引、分层模型路由、审核子智能体这些,随着 AI 生成代码量变大,再逐步叠上去就行。