1. 我最先在哪个真实场景里撞上这个问题
事情发生在为一个多步骤 Agent 工作流编写单元测试的时候。Agent 需要依次调用 MCP 工具、读取 context 并做条件跳转。常规的 pytest mock 把每个工具调用都替换成固定返回值,但测试总是通过,上线后却频繁超时。拖了两天,才发现问题出在 mock 没有模拟真实工具的延迟和错误响应——而 harness 正是用来解决“测试环境与生产环境差异”的工具。
Harnesses(测试 harness)本质上是一个轻量级的执行框架,它接管 Agent 的输入输出,让你可以在隔离环境中注入 mock 数据、模拟错误、记录行为。但当时我踩的第一个坑是:把 harness 当成普通 mock 用,只替换返回值,完全忽略了超时、重试和异常流。
2. 做完之后真正改变我判断方式的几个结论
在成功用 harness 稳定住测试之后,我得到了三个关键推论:
- Harness 不是万能胶。它擅长模拟外部依赖,但无法替代码逻辑本身背锅。如果你的 Agent 决策逻辑有 bug,harness 只会让 bug 更快暴露——这是好事,但很多团队误以为 harness 能让脆弱代码通过测试。
- Harness 的价值在于“可控的非确定性”。Agent 的行为往往依赖 LLM 的随机输出,harness 可以固定 prompt 和工具输出,使得每次测试行为可重复。这比单纯 mock LLM 端点更彻底,因为它也捕获了 context 的组装过程。
- 学习曲线比你想象得陡。Harnesses 的配置项很多(timeout、retry、error injection、logging),很容易在配置阶段引入新 bug。我最初花了三天才让第一个 harness 运行通过,而写实际测试只用了一天。

3. 当时踩过的坑和后来怎么修正
最大的坑是过度 harness。当时我想把所有外部调用都塞进 harness,包括一个只返回常量版本的静态函数。结果 harness 对象变得臃肿,每次测试都加载一堆不必要 mock。修正方法很简单:只在 harness 中管理真正外部的不稳定依赖(如 LLM 调用、第三方 API、文件系统),纯计算逻辑用普通单元测试覆盖。
第二个坑是忽略 harness 的生命周期。Harnesses 通常有 setup 和 teardown 阶段,我最初没有在测试之间清理 context,导致多个测试共享同一个 mock 状态,下游测试被上游的副作用污染。后来在每个测试前后显式调用 reset() 才解决。
第三个坑是错误模拟不全面。我只模拟了正常返回和超时,但漏掉了部分错误(如认证失败、速率限制)。结果上线后 Agent 遇到了 429 错误,而 harness 测试从未覆盖到这种情况。教训是:用 harness 模拟错误时,至少要覆盖 timeout、HTTP 4xx/5xx 和网络中断这三种常见异常。

4. 如果你也在做类似工作,最值得先照抄的一步
最直接的起步动作:用 harness 为你 Agent 中最关键的一个外部调用(比如唯一一个 LLM API)编写一个“模板 harness”。具体操作是:
- 选择当前项目中最核心的不稳定依赖(通常是 LLM 或数据库)。
- 写一个 harness 类,包含 setup(初始化 mock 服务器)、execute(调用 Agent 并捕获输出)、teardown(清理 mock)。
- 在 harness 中固定该调用的响应(例如返回一个固定 JSON),并记录实际被调用的 prompt 和参数。
- 跑一次测试,确认输出符合预期。
这一步能让你半小时内拿到第一个可复用的 harness 测试,并立刻发现 prompt 是否被正确拼接、参数是否传对。我的经验是,90% 的 prompt 错误跑一次 harness 就能暴露。
5. 什么时候应该继续投入,什么时候该换路线
继续投入 harness 的场景:
- 你的 Agent 有 3 个以上的外部依赖。
- 你正在开发新功能,需要确保新代码不破坏已有行为。
- 团队不断增加新 agent 或新工具。
该换路线的信号:
- 你花了超过 2 天仍在调试 harness 配置本身,而不是在写业务测试。
- 你的外部依赖经常变化,导致 harness 维护工作超过测试收益。
- 项目还处于原型阶段,API 和流程每天都在变。
这些情况下,先用更轻量的 mock 顶住,等稳定性提高后再引入 harness。
写在最后
Harnesses 不是银弹,但用对了地方能极大提升 Agent 工程的测试信心。关键在于明确它的适用边界:控制非确定性、模拟真实失败、隔离外部依赖。希望这 5 个教训能让你少走一些弯路。

暂无评论,快来发表你的见解吧