Harness Engineering 实战(一):为 fdk-acc 添加单元测试

系列文章目录


文章目录


前言

Harness Engineering 近期很火,似乎所有 code agent 相关的公司都在提。虽然 Anthropic、OpenAI 等公司都写了不少关于 Harness Engine 的文章,但想要落地在自己的业务上,似乎缺有些不知从哪下手,有点牛头不对马嘴,有点不知所措。

作为一个混迹互联网多年的程序员,在 ai 浪潮狠狠的拍打在我身上的时候,我在思考下一步能做什么,而 harness engineering 无疑是一个值得探索的领域。因此我在这方便投入和尝试很多,有了一些一些实战的经验,想要和大家一起分享分享,一起讨论讨论。

整体代码已经公开在 Harness-Engineering-for-fdk-aac

Harness 简介

当一个词被所有人用来指代一切时,它最终将不再意味着任何东西。

Harness 原意 "马具、束缚、控制装置",也就是套在马匹上的,让你更好的驾驭马匹的工具。

在 ai agent 领域,意思大致是:用来"编排、驱动、约束、测试、执行"AI agent 的底层运行引擎。因此 Harness engineering 也被翻译为「驾驭工程」。

以我们常用的 code agent 来举例:Agent = LLM + Harness,也就说除了 LLM 大模型以外的所有东西都可以成为 Harness,一个聪明的 code agent 往往包括:

  • 管理上下文
  • 调用工具(tool use)
  • 规划任务(planning)
  • 执行动作(actions)
  • 处理失败重试
  • 记录轨迹(trace)
  • 评估结果
  • 加安全约束和权限控制

网上关于 harness 的讨论很多,但这句话我觉得高度概况了什么是 harness:如果你不是模型,那你就是 harness

我理解的 Harness

不同任务有着不同的 Harness

当你在看关于 Harness 的文章时,可能会发现不同的文章表述的 harness 并不是一致的,这是因为他们的任务目标是不一样的:

我的第一个理解:不同的任务有着不同的 Harness。因为不同任务需要的工具、执行动作、评估结果的方式都是不同的。不存在一套 harness 就能通吃所有任务,而是为不同的任务搭建不同的 harness。

因此在做 harness engineering 的时候要时常问自己,我可以提供哪些工具,我要提供怎么样的评估标准,执行步骤是怎么样的。

Harness 有层级关系

如果你是 agent 开发工程师,你直接使用 LLM SDK + API 进行开发,你需要提供的 Harness 有很多,就如之前提到的管理上下文、调用工具、规划任务等等。

但如果你是软件开发人员,使用 code agent 实现需求,你需要提供的 Harness 就不再是这些了,你需要提供需求文档、验收标准、项目背景、编码约束、开发流程、额外的工具等。这些 Harness 在 agent harness 之上。

两层 Harness 相互独立,又相互之间可能会有影响,例如:

  1. 在编码约束下,agent 调用的工具可能被约束,
  2. 为了满足你验收标准,agent 对于任务的评估结果也需要跟着变化
  3. 由于 agent 无法阅读 pdf,你需要提供额外的pdf转文字的能力,以便 agent 调用

Harness Engineering 就是蒸馏自己

为了让 agent 自主地完成任务,你提供的脚本,你给的 skill,你的经验,你的判断,你的审美,你的喜好,你对这个世界的感知,你的大脑,你的一切,变成一个包裹在 Harness Engineering 外壳下的说明文档。它就在那,召之即来挥之即去。

当然,这和写技术博客、开源代码本质上没有什么区别,只是以前你仍然需要去理解这些内容,内化为自己的东西后才能去执行,但 ai 时代这些都不需要了,让它按这个文档去做就行了,它甚至还能自我进化。

写到这我似乎有些悲观,Harness Engineering 似乎对双方都不友好。做 Harness Engineering 的人将自己隐形的知识白纸黑字的写出来后,可能就被踹到一边了,可能不再被需要;而执行者的角色变成 ai,你不再需要去理解这些知识,对你的成长也没有任何帮助。也许你可能会说,不不不,执行的过程中如果有问题,我还是得介入的,得去纠正 ai,这时候就需要去理解这些内容了。但就以 ai 当前的进化速度来说,很快也就不需要外部的纠正了。


实战

好,理论部分结束,进入实战环节。

fdk-aac

我们的任务是为 fdk-acc 添加单元测试,fdk-aac 提供 AAC 音频文件的编码和解码功能。选择它是因为:

  1. 我对音视频比较熟悉,选择它比较有信心
  2. 它使用 c/c++ 和 cmake 构建,技术栈我了解
  3. 它缺乏单元测试,为它添加单测是一个长任务

拆解流程

先想象一下,如果你自己来添加单元测试,需要做什么事情。

首先是准备工作,包括:

  1. 为 fdk-aac 引入测试框架,例如 google test
  2. 提供编译、运行单测的脚本
  3. 提供获取代码覆盖率的脚本

准备工作完成后,接下去是添加单测的循环流程:

  1. 运行单测,获取代码覆盖率情况
  2. 选择一个覆盖率低的文件,为其添加单测
  3. 重新运行单测,查看提升情况,如果满足要求,则到步骤2,如果不满足则继续添加
  4. 所有文件单测覆盖率都满足要求,任务完成

现在接着想象如果有了 ai agent,这个循环流程你会怎样和它进行合作。

  1. 运行单测,获取代码覆盖率情况.
    • 你:帮我运行当前项目单测,并获取所有文件的代码覆盖率情况
    • ai:没问题,让我看看项目,它是用 cmake 构建的
    • ai:让我用 cmake 编译一把, 编译成功了
    • ai: 让我尝试去获取代码覆盖率的情况,(...一顿摸索中),成功了,我拿到了代码覆盖率情况
  2. 选择一个覆盖率低的文件,为其添加单测
    • 你:我看 xx.cpp 的 branch cov 很低,你来添加单测,提升代码覆盖率
    • ai:没问题,让我先看看哪些行代码没有覆盖到,(...一顿摸索中),这些代码都没有覆盖,让我添加单测覆盖它们。
    • ai: (...吭哧吭哧写代码中),写好了
    • 你:我检查下你写的单测,写的什么鬼,单测要满足 1/2/3/4 (很多很多...)要求,重改
    • ai: 没问题,让我改。(等...)改好了
    • 你:这里 zzzz 还有问题,应该这么写(指挥中...)
    • ai:好的,我继续改。(等...)改好了
  3. 重新运行单测,查看提升情况,如果满足要求,则到步骤2,如果不满足则继续添加
    • 你:不错不错。重新运行单测,查看 xx.cpp 覆盖率提升情况
    • ai:运行好,现在 xx.cpp 覆盖率是 80%
    • 你:给我提升到至少 90%
    • ai: 好,我继续写。(等...)写好了,让我重新运行(等...)现在单测覆盖率到 95% 了
    • 你:好,git 提交下。继续为下一个 yy.cpp 文件。
    • ai:好
    • (循环...)
  4. 所有文件单测覆盖率都满足要求,任务完成
    • 你:终于,150个文件单测覆盖率都满足要求了。任务结束吧。
    • ai:终于结束了。

那么,接着思考,如果需要把你的角色抽离,那么你需要提供哪些工具和约束?这些就是 harness

Harness Engineering for fdk-aac

固定流程的脚本

虽然 agent 很聪明,对于编译、运行单测这种简单的任务,它简单阅读项目后就能得到正确的执行不走,但是提供固定流程的脚本,可以减少 agent 的心智,减少对于上下文的占用,让它把精力花费在重要的任务上,而不是这种一个脚本就能搞定的事情上。因此我们提供几个脚本方便 agent 使用:

  1. run-unit-tests.sh ,编译和运行单测
  2. llvm-cov-unit-tests.sh ,获取整个项目的代码覆盖率情况,
  3. llvm-cov-file.sh ,查询某个文件未覆盖的代码情况
  4. llvm-cov-sqlite.sh,建立整个项目代码覆盖率的 sqlite 数据库文件

使用 sqlite 来记录文件状态

要处理的文件约有 100+个,我们使用 sqlite 数据库文件来记录每个文件的当前状态,重要字段包括:

  1. status。done, skip 或者 continue。done 表示满足要求,skip 表示被跳过,continue 表示仍有提升空间。其中 done 和 skip 是终止状态。
  2. description。说明当前文件状态的原因。
  3. branch_cov。文件的代码覆盖率

为什么使用 sqlite ?原因有:

  1. 简单,一行代码可以查询 agent 需要的所有信息
  2. 我曾尝试过使用 md 文件来记录,但是效果不好,agent 有时候会忘记更新。可能是因为修改 md 时,需要先 read,再做 write,每次 read 会影响 agent 的 context window。长时间运行下此类无用消息会让 agent 变成弱智。

什么是好的单测?这是你的品味

ai 写单测经常糊弄你,为了提升单测覆盖率它可能会写出一些令人匪夷所思的代码。那么如何让它写出好的单测呢?

理想情况下,如果有这么一个神奇的函数:输入单测,输出一个分数,比如 0-100。那事情会简单很多,我们可以让 ai 至少要写出 90 分的单测,并且把分数也作为文件状态的一部分,要求 >= 90 时,才能记录为 done。

但很遗憾,没有这样的函数。单测的好与坏,这是个人的品味,可能每个人都有自己的标准。因此为了让 ai 写出满足你要求的单测,你首先得先蒸馏自己,将自己关于单测的品味,从模糊的、不可描述的感性知识,写成一个具有指导性的文档。

这里我蒸馏了关于单测的文章 测试驱动开发(TDD)实践与技巧,形成一个 skill 文档。

引入 review 机制

虽然提供了写出好单测的指导文件,但 agent 仍然可能跑偏,特别是在长时间运行后,agent 可能只记得最重要的那个事情:写单测,提升代码覆盖率,其他的内容它已经模糊了或者忘记了。

这时候我们可以引入另外一个 agent,可以叫它 evaluator, 来做代码 review,它根据检查项,追个评估单测的质量,并给出 review 建议。

这种类似 GAN 的想法,很多 Harness Engineering 中都有使用到。

git hook

引入 review 之后,接下来要解决的问题是:如何稳定地触发它?

一种简单的方式,是直接在指令里明确告诉 agent,例如要求它每次提交代码后都进入 review 阶段,并根据 review 的建议继续修改。这种方式本质上是一种软约束,是否严格执行,最终仍然取决于 agent 本身,因此稳定性有限。

另一种更可靠的方式,是借助 git hook 机制来把 review 流程固化下来。在实践中,我加了一个 post hook 脚本:每产生 5 个 commit,就自动触发一次 agent review。这样一来,主链路上的 agent 在执行 git commit 之后,就会定期收到 review 反馈,再由它自行分析这些建议,判断是否需要进一步修改。

git hook 是一个非常好用的工具。除了触发 review 之外,我们还可以在 hook 中注入各种提醒信息,帮助 agent 始终对齐当前任务,避免执行过程逐渐偏航。比如,可以把当前任务进度写进去,也可以把当前指标与目标指标之间的差距写进去。总之,git hook 非常适合作为 Harness Engineering 的一个关键抓手:它既能增强流程的确定性,也能持续为 agent 提供上下文约束。

任务编排

最后我们提供一段 prompt 来编排我们的任务。将所有流程串起来,每次只要告诉 agent 按这个 prompt 来执行即可。这部分大家直接看 add_unit_tests_v2.md 吧,重点是写清楚目标、工作流程、可用工具、退出条件等等。

确保无限循环

理想情况下 agent 应该处理完所有文件后才能退出,但实际上 agent 可能会在中途停止,我在使用 codex 执行任务的时候,它有时候会在处理完 3~5 个文件后停下来给一个阶段性的总结报告,这种情况 claude code 就不会有,cc 能比较好的长时间执行这个任务。

为了让 agent 能够长时间的处理任务,我想到 ralph loop,这个任务天然适用于 ralph loop,因此我提供了另外一个脚本,用来执行 ralph loop,简单来说就是下面这个逻辑:

bash 复制代码
while true:
	bash run_codex_once_with_prompt.sh prompt.md
	
	查询 sqlite 数据库 file_coverage:
    done_count = 状态属于 ('done', 'skip') 的文件数
    total_count = 文件总数

  如果 done_count == total_count:
    打印完成
    break   # 退出循环

完整脚本参考 https://github.com/jiemojiemo/Harness-Engineering-for-fdk-aac/blob/jiemo/main/script/run_codex_once_with_prompt.sh

小结

以上是我们当前提供的 Harness 能力。

我们之所以做 Harness Engineering,本质上是为了给 Agent 提供一个更低认知负担、更强感知能力、更连续执行、且可检验结果的运行环境。其核心作用包括:

  • 降低心智负担:减少上下文冗余和污染,让 Agent 聚焦关键任务。
  • 提升可观测性:让 Agent 看见任务当前进行到哪里、还缺什么、周边信息是什么。
  • 保证可追踪性:让 Agent 能继承历史过程,在中断后也可以继续完成任务。
  • 提供可验证性:让 Agent 知道"做得对不对"、以及"离目标还有多远"。

那么接下去就是让 agent 去干活了,在实际过程中也在不断地打磨这套 Harness,稳定后基本能长时间运行了,唯一能打断它就是你的 token 够不够的问题了。

经过一段时间的运行,fdk-aac 整体 branch 覆盖率从 0 -> 37.70%,添加了约 1400+ 单元测试。

参考

相关推荐
笨笨狗吞噬者3 小时前
Opus 4.7 使用体验
前端·ai编程
tianyuanwo4 小时前
OS/DevOps程序员切入Harness Engineering的入门与进阶指南
运维·devops·harness
程序员老刘5 小时前
跨平台开发地图:四月风暴前夕,你该怎么选?| 2026年4月
flutter·ai编程·客户端
仰望誓言5 小时前
GLM Coding Plan速率限制429问题
ai编程
凌奕6 小时前
给 Claude Code 装上"长期记忆":claude-mem 从安装到实战
ai编程·claude
麦哲思科技任甲林6 小时前
AI编程的三大痛点及其工作模式
人工智能·ai编程·工作模式·自以为是·忘性大
gyx_这个杀手不太冷静6 小时前
大人工智能时代下前端界面全新开发模式的思考(六)
前端·架构·ai编程
带刺的坐椅9 小时前
国产 AI 编程工具不完整调研分析报告
ai编程·claudecode·opencode·soloncode
财经资讯数据_灵砚智能9 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(夜间-次晨)2026年4月19日
人工智能·python·信息可视化·语言模型·自然语言处理·知识图谱·ai编程