☰
想做 Agent Harness 和 Eval,第一步该写什么?
2026/10/10 1:19:38 网站建设 项目流程

第一次接触 Agent Harness 和 Eval 时,我最困惑的是:应该先接模型、写工具,还是收集评测数据?看到成熟项目里的插件、权限、沙箱和评估器,又容易觉得每一块都得先做好。

后来我采用了一条比较容易推进的路径:先用一个假模型和一条用例,跑通“执行—记录—评分—报告”,再逐步接入真实模型、发布门禁和失败回流。

这篇文章以我的 sugarFreeHarnessAndEvals 项目为例,介绍这条入门路径。你只需要了解基础 Python 和 JSON,就可以先把离线链路跑起来;第一阶段不需要 API Key。

实现可以交给 Codex / Claude Code ,你负责明确目标、约束和验收标准。下面每一步都附有可直接复制的指令,按顺序发给 Agent,验收当前步骤后再继续;已有仓库时,让它先检查实现并补齐缺项。代码和命令示例用于理解与验证,不必逐行手敲。

1. 先确定要验证什么

从一个具体任务开始:

查询苹果的单价,再用计算器计算买六个苹果的总价。

假设单价是 7,最终答案应该是 42。但“答案出现 42”只是其中一个要求。我们还希望知道:模型是否真的查询了价格?计算器收到的参数是否正确?是否循环调用了很多次?工具失败后,任务还能不能结束?

这就是 Harness 和 Eval 的分工:

  • Harness 是运行底座:管理消息、调用模型、执行工具、处理错误和结束任务,同时记录执行证据。
  • Eval 是质量判断:为任务定义预期,检查运行结果,并输出通过或失败的理由。

它们不一定是某个特定框架。一个几十行的执行循环,加上几条明确的断言,也可以成为最小起点。

初学时,可以只做到图中的“评测报告”。等这一小段稳定了,再继续扩展。

可以这样告诉 Coding Agent:

请先检查当前工作区,以“查询苹果单价,再计算六个苹果总价”为例,列出答案、工具选择、参数、调用顺序和执行预算的验收标准,给出最小 Harness 与 Eval 的模块划分和分步实施方案,先把范围限定到离线执行与评测报告。

2. 第一步:搭建最小 Harness

先只选一个简单工具,例如计算器。然后让 Agent 支持两种模型响应:直接回答,或者请求调用工具。

可以这样告诉 Coding Agent:

请用 Python 实现最小 Agent Harness:统一模型接口、消息结构、工具注册与参数校验,先提供安全的基础算术计算器;Agent 支持直接回答和工具调用,将工具结果或错误放回上下文,设置最大步数,并返回答案、步骤数、工具调用数和明确终止原因,给出离线验证方式。

执行过程可以写成下面的伪代码:

# 展示执行顺序的伪代码,不是项目的完整实现。 messages = [user_message(prompt)] for step in range(max_steps): response = model.complete(messages, tool_specs) messages.append(response) if not response.tool_calls: return completed(response.content) for call in response.tool_calls: result = validate_and_execute(call) messages.append(tool_message(call.id, result)) return stopped("max_steps")

这里有三个值得先弄明白的设计。

第一,工具结果需要重新进入消息历史。模型通过下一轮请求看到执行结果,才能决定继续调用还是回答。工具报错也应当作为结构化结果返回,而不是直接让整个进程崩溃。

第二,要设置最大步数。在这个项目中,一步对应一次模型请求;一次请求可能返回多个工具调用,所以“步骤数”与“工具调用数”不是同一个指标。

第三,要明确终止原因。例如completed、max_steps、model_error。后续再加入model_timeout、tool_timeout、tool_denied等原因,Eval 才能区分任务完成和异常退出。

我的实现位于src/sugarfree/core/agent.py。模型通过统一的complete(request)接口接入,工具由注册表管理。这样更换模型供应商时,Agent Loop 不需要跟着重写。

3. 第二步:用假模型跑通,而不是立即调用 API

为了验证刚写好的执行循环,我使用了ScriptedModel:提前准备每轮模型响应,运行时按顺序返回。

可以这样告诉 Coding Agent:

请实现按顺序返回预设响应的 ScriptedModel,并保存收到的模型请求;用“调用计算器计算18 + 24,再回答42”验证 Harness,检查工具确实执行、下一轮请求包含工具结果、步骤数为2且正常结束,全程不联网,并在脚本耗尽时返回明确的模型错误。

例如计算任务可以预设两轮:

第一轮:调用 calculator,参数 expression = "18 + 24" 第二轮:回答 "The result is 42."

这样可以稳定检查 Harness 是否执行了工具、是否将结果加入上下文、是否在第二轮正常结束。工具仍然真实执行,只有模型响应被替换。

假模型测试验证的是运行底座,不是模型的推理能力。预设第二轮回答“42”,不能证明模型理解了工具结果;要检查上下文是否正确,应查看请求记录或增加相应测试。

这种区分能让排查变得清楚:执行循环有问题,就在离线环境修复;底座稳定后,再验证真实模型是否会做出正确决策。

想先体验项目,可以直接运行:

git clone https://github.com/sugarFreeWT/sugarFreeHarnessAndEvals.git cd sugarFreeHarnessAndEvals python scripts/run_eval.py evals/architecture-smoke.json

环境要求是 Python 3.11 或更高版本。上述脚本会加载本地源码,不需要先接入模型服务,也不会调用外部 API。

4. 第三步:写一条真正可以评分的用例

任务描述只是输入,还需要把“怎样算通过”写出来。

可以这样告诉 Coding Agent:

请设计 JSON 评测用例格式和离线 Runner,包含 case ID、标签、Prompt、模型脚本及预期;先生成计算器用例,再补齐直接回答、多工具串联、错误恢复和最大步数场景,运行后输出逐 case 的 report.json,保留各项判断理由,并校验重复 ID 和非法配置。

下面是一份可运行的最小评测集。将内容保存为evals/tutorial-smoke.json:

{ "name": "tutorial-smoke", "mode": "scripted", "cases": [ { "id": "calculator_addition", "tags": ["core", "tool"], "prompt": "Use calculator to compute 18 + 24.", "model_script": [ { "tool_calls": [ { "id": "calc-add", "name": "calculator", "arguments": {"expression": "18 + 24"} } ] }, {"content": "The result is 42."} ], "expect": { "termination_reason": "completed", "answer_contains": ["42"], "required_tool_calls": [ { "name": "calculator", "arguments": {"expression": "18 + 24"} } ], "required_events": ["tool.completed"], "max_tool_calls": 1, "max_steps": 2 } } ] }

运行:

python scripts/run_eval.py evals/tutorial-smoke.json --output-dir runs/tutorial-smoke

这一条用例同时表达了答案、工具参数、成功事件和执行预算。运行后,先打开runs/tutorial-smoke/report.json,查看每个评分器的判断。

入门时不用一次设计几十条用例。可以先补齐五种场景:直接回答、单工具调用、多工具串联、错误恢复、达到最大步数。每条用例都应该回答一个明确问题。

5. 第四步:把断言拆成独立评估器

如果所有判断都放进一个大函数,新增评测维度会越来越困难。我把它们拆成独立的 Scorer:

可以这样告诉 Coding Agent:

请把 Runner 中的判断拆成独立 Scorer,分别检查答案片段、终止原因、工具及参数顺序、调用与步骤上限、必需和禁止事件;每项返回名称、通过状态和诊断详情,未配置的规则跳过,全部适用规则通过才算 case 通过,并用反例验证评分器确实能检出错误,后续再接工作区变化评分。

维度检查内容例子
答案是否包含预期片段包含 42
终止状态是否按预期结束completed 或 max_steps
工具调用工具名称、参数和顺序是否符合预期先 lookup,再 calculator
执行预算步骤和调用次数是否超限最多两次调用
事件必需事件和禁止事件必须出现 tool.failed
文件变化文件产物及副作用只新增指定文件

评分结果不只返回一个布尔值,还带上诊断信息:

{ "scorer": "max_tool_calls", "passed": false, "detail": "limit 1, got 3" }

这里有两个容易误解的细节。

answer_contains只是片段匹配。“答案不是42”也包含“42”,因此它适合简单 Smoke,不能代表完整语义正确性。严格格式任务可以增加精确匹配;复杂任务则需要结构化校验、人工标注或经过校准的模型裁判。

工具请求也不等于工具成功。当前required_tools根据tool.requested判断名称是否出现;如果要求成功执行,还要检查tool.completed或更具体的结果。

项目当前有 23 条确定性 Regression 用例,使用core、tool、recovery、workspace、security等标签统计覆盖和通过率。标签可以重叠,不能把标签数量相加当成总用例数。

6. 第五步:记录轨迹,让失败可以被解释

当一条用例失败时,最终答案往往不足以定位原因。需要看到中间发生了什么。

可以这样告诉 Coding Agent:

请为 Harness 增加结构化事件和逐 case JSONL 轨迹,再实现不调用外部模型的 ReplayModel,复用记录的模型响应并重新执行工具,严格比较每一步模型请求;用正常回放和修改请求后的失败场景验证一致性,演示时仅使用本地确定性工具。

我给 Harness 增加了事件记录,每个 case 输出一份 JSONL 文件:每行一个事件,包含运行 ID、顺序号、时间、事件类型和载荷。

一个缺少参数后恢复的场景,可以观察到这样的关键事件:

model.responded:请求 calculator,但缺少 expression tool.requested tool.failed:参数校验失败 model.responded:重新提供正确参数 tool.requested tool.completed model.responded:返回最终答案 run.completed

这里能验证 Harness 是否正确暴露错误、是否允许继续执行、是否最终正常结束。脚本模型中的“修正”是我们预置的;真实模型能否自主修正,需要另做在线验证。

有轨迹后,就可以实现 ReplayModel:读取记录中的模型响应,重新运行 Agent 和工具。项目的严格回放还会逐步比较模型请求,检测消息历史或工具定义是否发生变化。

python scripts/run_replay.py evals/tutorial-smoke.json ` --trajectories runs/tutorial-smoke/trajectories ` --output-dir runs/tutorial-smoke-replay

回放复现的是保存下来的响应,不是让模型再生成一次答案。工具仍会执行,所以外部 API、时间相关数据或有副作用的操作,需要固定环境、替身或隔离措施;不要默认所有轨迹都能安全重放。

7. 第六步:接入真实模型,验证决策能力

离线链路稳定后,再接 DeepSeek 等真实模型。在线用例不再设置model_script,由模型自主选择工具、生成参数和回答。

可以这样告诉 Coding Agent:

请实现 OpenAI 兼容模型适配器和 live 评测入口,复用现有 Harness 与 Scorer,密钥只从环境变量读取,日志及错误信息脱敏;先准备少量真实用例和配置检查,确认配置可用后先跑1轮,再累计3轮到不同目录,生成通过率、步骤、调用数、Token和耗时对比,鉴权失败时停止多轮调用。

在本项目里,配置当前 PowerShell 的环境变量后即可运行:

$env:DEEPSEEK_API_KEY = "你的密钥" python scripts/run_live_eval.py evals/deepseek-smoke.json ` --temperature 0 ` --output-dir runs/deepseek-tutorial-run1

密钥通过环境变量读取,不写入用例文件。首次接入时先确认鉴权成功,再做多轮验证,避免重复消耗时间和额度。

在线评测重点检查模型是否理解任务、遵循 Prompt、选择合适工具,以及利用工具结果完成任务。JD 中的 Skills 在本项目里主要对应工具与插件能力,尚未实现独立的 Skill 文档运行时。

多轮运行应使用不同输出目录,再对比报告:

python scripts/run_benchmark.py ` runs/deepseek-tutorial-run1/report.json ` runs/deepseek-tutorial-run2/report.json ` runs/deepseek-tutorial-run3/report.json ` --output runs/deepseek-tutorial-benchmark.json

该命令只读取已有报告。后两份报告需要先分别运行在线评测生成。比较时尽量固定用例、Prompt、工具定义和模型配置,记录版本与评测集哈希,才能解释差异。

我使用deepseek-chat、temperature=0做了三轮验证:每轮 10/10 通过,平均步骤数均为 1.80,平均工具调用数均为 0.90。这只是当前样本下未观察到失败;即使温度为零,也不能据此保证所有运行都确定或所有场景都稳定。

8. 第七步:让评测成为发布门禁

当回归集稳定后,就可以让测试结果约束版本变更。

可以这样告诉 Coding Agent:

请基于现有离线报告实现可配置的发布门禁,检查通过率、最小用例数、核心case、关键标签、回放结果一致性、基线用例删除、新增失败及步骤和工具调用增长;输出诊断报告,失败返回非零退出码,接入离线CI,并用删除用例和新增失败等反例验证阻断,不自动更新基线来绕过失败。

先设置最简单的规则:必需用例全部通过,失败就返回非零退出码。再逐步增加覆盖和退化检查:

  • 用例总数不能低于要求,核心 case 必须存在;
  • 关键能力标签必须达到通过率阈值;
  • 当前报告与严格回放报告的 case 结果一致;
  • 基线用例不能被静默删除,不允许新增失败;
  • 平均步骤数和工具调用数不能明显增加。

项目的gates/offline-release.json要求至少 23 条用例、整体通过率为100%,并将平均步骤和工具调用增长上限设为0.25。当前配置展开后共20项检查。

阈值要跟场景相适应。对于小型确定性核心回归,要求100%合理;面对有概率波动的真实模型,应考虑重复采样、容忍区间和人工复核。耗时也受网络影响,不宜直接照搬确定性门禁。

完整离线流程可以一条命令执行:

python scripts/run_ci_checks.py --output-dir runs/ci

它包含组件测试、架构评测、Regression、严格回放、基准比较和发布门禁。组件测试使用 Python 标准库unittest;GitHub Actions 在 Python 3.11 和3.13上运行相同入口。

基线也需要审核:只有明确知道预期为什么改变,才更新它。否则一次错误的基线更新就可能把真实退化变成“新的正常状态”。

9. 第八步:让真实失败变成回归资产

最后一个问题是:发现失败之后怎么办?

直接把每个失败都加入正式评测集,会混入鉴权错误、网络问题、评估器误判和偶发噪声。因此我先把失败收集到候选 Inbox,再审核:

可以这样告诉 Coding Agent:

请实现失败Inbox与collect、review、promote、reconcile命令,按套件、case、失败评分器和终止原因去重,重复收集同一报告不增计数,并对保存信息脱敏;候选须经人工审核,正式回归用例经人工确认后关联,只有关联case在验证报告中通过才关闭问题,用模拟报告验证幂等性和状态流转。

项目根据“套件、case、失败评分器、终止原因”计算 SHA-256 指纹。重复收集同一报告不会重复计数;不同运行中的同类失败会合并并累计出现次数。

这个指纹合并的是相同失败形态,并不是自动识别根因。人工仍需看轨迹判断问题性质,必要时拆分候选。

有效问题审核通过后,手工编写有明确预期的确定性用例,再用promote关联;修复后运行 Regression,通过reconcile检查关联用例,确认通过才将状态变为resolved。

# 从一次真实评测报告收集失败;全部通过时不会产生候选。 python scripts/manage_failures.py collect ` runs/deepseek-tutorial-run1/report.json ` --inbox runs/failure-inbox.json

我遇到过的真实问题是 API 401。它属于鉴权故障,审核后没有提升为 Agent 行为回归。但错误响应中出现了掩码密钥线索,这又促使我完善脱敏逻辑。失败可以带来新的工程改进,但需要先判断它究竟是什么问题。

目前闭环机制已有测试验证;最近三轮真实评测全部通过,因此没有人为制造“真实模型缺陷已修复”的案例。

10. 如果今天开始,你可以按这个顺序做

阶段先完成什么怎样确认做到了
最小 Harness一个假模型、一个工具、最大步数调用工具后能正常结束
最小 Eval五种基础场景、独立评分器每次失败都有具体原因
可复现诊断事件记录、JSONL、严格回放离线复盘并发现请求变化
真实验证少量在线用例、多轮对比区分框架问题和模型行为问题
发布约束版本化基线、门禁、CI注入回归时检查确实失败
持续改进失败 Inbox、审核、回归关联问题关闭有验证报告支撑

这套项目当前的规模是23条离线 Regression、10条真实 Smoke。2026年10月8日上传前的本地验证运行了67项测试,其中1项因Windows环境限制跳过,其余通过;Regression及回放均为23/23,门禁20/20。

这些数字说明当前实现可以运行和验证,不代表覆盖已经充分。后续更值得投入的是业务任务、Prompt变体、更准确的语义评估,以及模型版本变更时的对照实验。

如果你还不知道该给 Coding Agent 下达第一个什么任务,可以从本文的计算器用例开始:让它实现最小循环、运行用例、留下证据并评分,再要求它用错误参数验证失败诊断。你要掌握的是每一步为什么做、怎样验收,再让 Agent 完成实现。这就是一套 Harness 与 Eval 的起点。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询