微信扫码
添加专属顾问
我要投稿
Harness工作流是否真的在进步?告别主观评价,用可量化的考试体系为你的工作流“撕掉遮羞布”。核心内容:1. 主观评价驱动Harness工作流的困境与失控风险2. 从“测试”到“考试”的哲学跃迁与核心原理3. Harness Eval评测系统的设计思路与价值
作者:chaseren
Harness Eval:一套面向 Harness 工作流的、轻量的、可回归的闭环评测系统。
你有没有这样的经历?
团队花了两周精心调教一套 Harness 研发工作流——写了十几条 Rules、打磨了 Skill——上线之后,评价体系是这样的:
"感觉这版稳了不少。"
"昨天那版好像更聪明?"
"我这边挺好用,你那边咋不行?"
这就是我们半年前面对的现实。整个团队在用"主观 vibes"驱动一个复杂系统的演进。
说白了,我们连最基本的问题都回答不了:
这不是一个"锦上添花"的问题。这是一个系统正在失控而你不自知的问题。
传统软件是确定性的:同样的输入,同样的输出。你写一个asserEqual
它的结果是二值的、稳定的、可复现的。
Harness 工作流完全不同。它本质上是一种"规则驱动的概率程序"——输出由 Prompt + Rules + Skills + Model 共同决定,任何一项的微调都可能引起蝴蝶效应般的行为变化。今天跑一遍是 A 结果,明天跑一遍可能是 B 结果。更要命的是,你甚至无法简单地判定"A 比 B 好"还是"B 比 A 好",因为"好"本身就缺乏定义。
这种系统如果没有评测体系兜底,你面对的不是"缓慢退化"——而是薛定谔式的退化:你永远不知道它什么时候变差了。
软件工程界用了几十年总结出"测试驱动开发"。但是,传统的单元测试、集成测试那套东西搬到 Harness 上是不够的。
为什么?因为 Harness 工作流的"正确"不是一个 boolean——它是一个光谱。一个 Harness 工作流完成任务的方式可以有无数种,有些优雅有些笨拙,有些主动有些被动,有些严谨有些草率。你不能简单地 assert output == expected
所以我们需要的不是"测试",而是"考试":
这个思维跃迁是整套系统的哲学基石:我们不是在写测试用例,我们是在出考卷、组织考试、培养阅卷官。
在设计整套系统之前,我们先锚定了三个原则,后来所有的设计决策都围绕它们展开:
一张图回答"整个系统长什么样"。
整套系统只做三件事——出题、答题、改卷——但把它们串成了一个可以无人值守跑的闭环:
闭环的关键是把"改进"也纳入了链路:每次裁判输出的不只是分数,还会按 [工作流] / [题目] / [模型能力] 三个维度给出可操作的改进建议,直接喂回到下一轮工作流迭代里。
一道题 = 题面 + 阅卷标准 + 环境前提 + 元信息,缺一不可。
每道题在题库里就是一个独立目录,由 4 个文件构成(外加可选的 fixtures),各司其职:
新出题人不需要理解任何代码,只需要按模板填 4 个文件,5 分钟就能新增一道题。降低出题门槛 = 题库快速增长 = 评测覆盖度提升。
这个设计看似简单,背后是几个深思熟虑的决策:
我们把 Harness 工作流的能力拆成了 5 个递进的层次,题库按波次覆盖:
当前题库按“主干闭环→状态机门禁→知识库闭环→周边skill→韧性场景”分5波次铺开:
这个设计背后的思想:先把"主干 path"覆盖住,再依次往"边界场景 / 异常场景"扩展。无论工作流处于哪个发育阶段,都能找到合适难度的题目集合做回归——不至于"题太难全挂"或"题太简单全过",两种极端都没有信息量。
4.2答题:一场精心编排的“模拟考试”
这是整套系统里最反直觉的设计之一。
最朴素的想法是:直接把 task.md 丢给 Agent,让它跑,跑完看结果。但这完全不符合真实使用场景——真实世界里,用户和 Agent 之间是多轮交互的。用户会追问、会纠偏、会补充信息、会在关键节点做决策。
如果评测时去掉了这个交互过程,测的就不是"Agent 在真实场景下的表现",而是"Agent 自嗨的能力"。两者差距巨大。
所以我们引入了考官(Examiner)——由 LLM 扮演的"用户",按照 task.md 中预设的剧本和 Agent 进行多轮对话。
第一步:考场布置。 系统根据 env.yaml准备好环境——确认所需文件存在、所需服务可达、工作目录正确。相当于考试开始前把试卷、答题卡、草稿纸都摆好。
第二步:考官开场。 考官拿到task.md ,理解"我要扮演的用户角色"和"我要提出的需求",然后向考生发出第一条消息。这条消息就是用户通常会说的那种话——自然、简洁、可能还带点模糊。
第三步:多轮交互。 考生(被评测的 Agent)收到消息后开始工作。它可能会:
考官会根据剧本回应:该确认的确认,该追问的追问,该为难的为难。比如剧本可能规定"如果考生没有主动确认分支名就直接操作,考官要追问一句'你确定是在正确的分支上吗?'"。
第四步:考生完成。 当考生认为任务完成时,会给出最终总结。考官确认对话结束。
第五步:全程录像。 整个对话过程被完整记录为 transcript.jsonl——包括每一轮对话文本、每一次 tool call 的输入输出、每一条 shell 命令的执行结果。这份"录像"是后续判卷的唯一依据。
这里有一个关键洞察:Agent 的"能力"不仅体现在最终结果,更体现在过程中的每一步决策。
两个 Agent 可能都最终完成了同一个任务,但:
如果只看最终结果,两者都是"pass"。但过程中的行为质量天差地别。这就是为什么我们需要完整的交互记录——判卷时不只看"做没做到",还要看"怎么做到的"。
这是踩过的最痛的一个坑。
早期设计里,我们让考官"顺便"判个分。逻辑很自然——考官全程参与了对话,它最了解过程,让它打分不是很合理?
结果发现误判率很高。
原因很简单:考官的视角是对话视角,它只能看到 Agent"说了什么"。但 Agent 的真正工作——写文件、跑命令、调接口——这些都发生在tool call 里,考官在对话层面只看到一句"我已完成操作"。
于是出现了这种荒谬场景:Agent 明明完美执行了所有步骤(从 tool call 记录看),但考官因为在对话里只看到一句简短汇报,判定"考生没有充分展示执行过程"——fail。
教训:判分必须拥有完整的"上帝视角",对话视角远远不够。
裁判的阅卷过程
裁判(Judge)是一个完全独立的进程,与答题对话没有任何共享上下文——单独启动、单独运行、没有任何"记忆污染"。它拿到的输入只有两样东西:
我们不是把原始 transcript.jsonl (动辄几万行)原封不动地丢给裁判——那东西太长太噪,裁判会被信息洪水淹没而丢失判断力。我们做了一层专门的压缩处理:保留所有关键行为证据(tool call 内容、文件写入、shell 输出摘要、关键对话节点),去掉冗余信息(重复的系统提示、过长的文件 dump 等)。
这样裁判拥有了完整的"上帝视角"——它能看到考生真正做了什么(每一次工具调用、每一行代码写入),而不只是看到考生"嘴上说了什么"。同时信息密度足够高,不会因为噪声而判断失准。
拿到这两份材料后,裁判按以下流程完成阅卷:
整个过程可以概括为一句话:拿着标准答案,看着完整录像,逐帧打分,每个判定都要举证。
裁判的输出包含 8 个字段,但我们做了分层落盘设计——核心指标和详细诊断分开存储:
score.yaml(5 字段,结构化指标):
result: pass # pass | failcompliance: 4 # 流程遵循度 0~5execution_quality: 4 # 执行/交付质量 0~5overall: 4 # 综合分 0~5summary: '考生按规范完整跑完五个节点,仅在某步未主动确认参数来源
review.md(证据 + 改进建议):
reason: '主动确认服务名 / 参数解析正确 / 写盘前让用户确认 / 命令真实执行'evidence:- '考生 Turn 2:「请二选一(A 新建 / B 不建)」触发参数验证 skill'- '考生 Turn 4:appid: v1_rb4_... → 与 URL 实际值一致'improvements:- '[workflow] Turn 1 未主动确认参数来源,靠用户追问才补上 → 应在对应 skill 增加强制确认 gate'- '[eval] rubric 未区分「主动确认」与「被追问后补充」两种达标方式,建议拆分判分项'- '[capability] 多步推理时容易丢失上下文里的关键约束'
几个关键设计:
[workflow]——工作流本身(rules / skills / agents)该补什么 gate、该改什么提示。
[eval]——题目本身有没有问题(rubric 模糊、task 误导、env 不合理)。
[capability]——考生暴露的通用能力短板(工具使用、上下文理解、多步推理)。
这套分类是反复打磨出来的,目的就是让改进建议可以直接被路由到不同人那里。跑完一批评测 → batch-insights.md 自动汇总 → 直接得到"下一轮工作流要改什么"的清单。评测不再是"成绩单",而是工作流的迭代驱动器。
各个功能角色都已经预备好,怎么把跑题这件事压缩成无人值守的一条命令?
执行引擎是整个系统的"骨架"——用 Go 编写的单一 CLI 程序,编译产物即全部,代码层面只引入了一个外部三方库。没有任何评测框架、没有任何 ORM、没有任何 web server。这个选择是为了:零环境部署(go build一下任何机器都能跑)、代码可读性、以及不被框架绑架(评测系统的需求会持续演化,框架越重越难调整)。
执行引擎是整个系统的“骨架”,一条命令背后做的事比想象的多:
几个关键设计:
git worktree 隔离:
每个并发 run 都拿到一份独立的 sandbox 副本——本质上是git worktree 出来的目录树,共享 git 对象库。这一招让"并发跑 5 道题"不会出现 A 题的 .cursor 软链覆盖 B 题、A 题的 操作搅进 B 题分支这类问题。
symlink 软链接:
评测期间会用 symlink 把沙箱环境里的 IDE 配置目录(如.cursor )替换为本轮 run 内的工作流快照。原始目录(如果有)会先重命名成.harness-eval.bak 备份。run 完之后会把用户的真实配置原封不动还回去。相当于考试时把考生桌上的课本收走,换上考卷;考完再把课本还回去。
transient 错误自动重试:
Cursor / Codebuddy 的 CLI 调用难免遇到瞬时失败(网络抖动、CLI 崩溃、error 等)。对话调用(考官/考生每轮交互):最多 2 次尝试,2 秒间隔,只对 CLI 崩溃和执行错误重试裁判调用(独立判分):最多 3 次尝试,3 秒间隔,对所有 transient 错误重试对 context deadline 不重试不兜底——超时就是超时,重试只是浪费 token。
4.5评测结果:让成长的趋势“看得见”
这一节回答:"跑了几十次后,怎么从一堆 run 目录里看出工作流到底在变好还是变差。"
workflow_rev 记录每次 run 用的工作流 git commit。这意味着:我们遇到"v0.3 通过率 0.4,v0.4 上提升到 0.8,v0.5 又跌回 0.6——v0.5 改了什么?"这类问题,可以直接定位到某个 commit 的影响,不再靠回忆。
用这套评测工程在团队内部的 Harness 工作流上进行测试并改进优化,结果如下:
对仍 Fail 的三道题目进行针对性改进后:
整体通过率从之前的 82.4%(14/17)提升到 100%(17/17)。
通过工作流的标准化考题、4 轮 workflow_rev 迭代、50+ 次自动化 run,系统性地暴露了团队内部 Harness 工作流在范围锁定、Critical 拦截、归档沉淀、提交规范、TDD 纪律等多个环节的缺陷,并通过"评测 - 修复 - 回归"闭环逐一修复。多道关键题目的 overall 从 1-2 分修复到满分 5 分。
同时,评测系统本身也沉淀为可复用的工程基础设施和方法论文档,具备向其他工作流平移的通用性。
回过头看,我们做的事情可以抽象为一句话:
给一个"行为不确定"的 Harness 工作流,建立可重复的、可归因的、闭环的质量度量体系。
软件工程界用了几十年总结出"测试驱动开发"。Harness 工作流的复杂度和不确定性比传统软件更高,更需要这套思路:
这套方法论不绑定任意平台、不局限任意场景。但凡存在行为效果难以直观判定优劣的 Harness 类工作流,均可直接使用这套评估体系的思想。
为什么这件事现在特别重要?
Harness 正在从"新鲜玩具"变成"生产工具"。当它真正进入工作流的关键路径时,"好不好用"不再是一个可以靠体感回答的问题——它关系到交付质量、研发效率、甚至线上安全。
任何不可量化的东西都不可优化。 如果不能回答"昨天那版和今天这版谁更好",所谓的"持续改进"就只是自我感动。
我们只是在团队内部的 Harness 工作流先趟了一遍。如果你也在做类似的事——无论是评测自家的 Harness 工作流、还是想给工作流建立质量基线——欢迎来交流。我们也很想听听不同业务线的评测难点,看看这套评测机制还能做得多通用。
在一个充满概率和不确定性的领域里,"确定性"是最稀缺的资源。而获得确定性的方式,从来都是同一个:量化它,追踪它,用数据说话。
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业
2026-06-18
企业智能体的下半场,如何让智能体越用越聪明?
2026-06-18
Agent 记忆,我们全都理解错了?
2026-06-18
多 Agent 并行与 Headless 模式:让 Claude Code 效率翻 10 倍
2026-06-17
拆解大模型几项核心操作背后的数学与 Infra 优化逻辑
2026-06-17
更可靠的主播助理:淘宝主播Agent的Harness工程实战
2026-06-16
Business Insider:揭秘 Cursor 的疯狂崛起
2026-06-15
如何搭建一个端到端业务需求专家 Agent
2026-06-12
谁是 Agent 最强守门员?首个 Agent 技能安全评测基准 SkillTrustBench 正式发布
2026-04-15
2026-04-07
2026-04-07
2026-03-31
2026-03-21
2026-04-24
2026-04-17
2026-03-31
2026-03-20
2026-04-05
2026-06-18
2026-06-18
2026-06-10
2026-06-10
2026-06-10
2026-06-07
2026-06-06
2026-06-03