拆解 harness9(10):Benchmark 驱动开发

关于 harness9

harness9 是一款 Local-First、轻量级、功能完备、生产可用的通用 Go Agent 框架。

⭐ Star 是对开源工作最直接的支持,欢迎提 Issue 和 PR。


TL;DR

  • SWE-bench 测的是"根据 Issue 产出补丁能否通过隐藏测试",不是 Agent 是否写出了看似合理的解释。
  • Terminal-Bench 通过 Harbor 把终端任务、容器环境、Agent 生命周期和 verifier 结果组合成一个可复现 trial。
  • harness9 的 runner 显式隔离隐藏 test_patch;它只用于官方判分与运行后分析,不会进入 Agent 上下文。
  • 轨迹分析把 agent/harness9.log、补丁和 verifier 输出对齐,才能区分内核缺陷、环境问题与模型判断问题。
  • SWE-bench 的环境自举、验证关卡、停滞提示和生成重试,都来自具体失败轨迹,而不是经验性堆功能。
  • M1 已交付 production-ready Agent Harness;M2"本地 Agent OS"是路线图,不是当前 benchmark 结果或已交付能力。

本文你将学到

  • 你将看清 SWE-bench 与 Terminal-Bench 分别验证什么,以及二者为什么需要不同的接入边界。
  • 你将理解 test_patchFAIL_TO_PASS 等评测字段为何只能用于分析,不能泄露给 Agent。
  • 你将看清 runWithVerificationGate 如何把"改完即收尾"变成一次受限的真实验证续跑。
  • 你将掌握如何用轨迹、补丁和 verifier 三类证据给问题归因,而不是把所有失败归咎于模型。
  • 你将区分 M1 已交付的 Agent Harness 基线和 M2 本地 Agent OS 的路线图边界。

为什么不只看分?

单一 resolved rate 只能说明某次采样的结果。它不能说明 Agent 是否真的执行了测试,也不能说明失败来自 Agent 内核、任务镜像,还是模型本身的推理偏差。

harness9 把 benchmark 看成工程反馈回路。任务进入 AgentEngine、工具和 Sandbox;运行留下补丁或终端状态;verifier 给出外部判决;轨迹分析再把结论映射回 Prompt、依赖自举、超时与重试策略。

这里的关键不是把 benchmark 当作 CI 的一个数字门槛,而是把它当成观测接口。没有足够的中间证据,分数变化无法指导下一次改动。

两类任务差在哪?

SWE-bench 的输入是仓库、基线提交和 Issue 描述。Agent 在隔离工作区内修改代码,runner 收集 git diff,官方评估器再把隐藏测试补丁应用到副本上判分。cmd/swebench/dataset.go 解析 test_patchFAIL_TO_PASSPASS_TO_PASS,但注释明确规定:它们绝不在 Agent 运行时暴露或应用。

看这一段数据边界。ProblemStatement 是输入;TestPatch 是评测器的私有输入。两者不能混在同一个 Prompt 里。

go 复制代码
// TestPatch 是评测阶段注入的隐藏测试补丁。同样**绝不**在 Agent 运行时应用。
TestPatch string `json:"test_patch"`

Terminal-Bench 则要求 Agent 在真实终端任务中交付最终状态。Harbor 负责启动任务环境、调用适配器和运行 verifier;reward 记录在 trial 的 verifier/reward.txt。它不以 Git patch 作为唯一产物,环境与命令执行本身就是任务的一部分。

这也是两个 benchmark 可以共用 Agent 内核、却不该共用一套"完成"定义的原因:SWE-bench 产出可评分补丁,Terminal-Bench 产出可验证的环境状态。

SWE 怎么接入?

runInstance 的路径很直白:宿主机用 git clone --filter=blob:none 获取基线,再创建绑定 workDir 的 Docker Sandbox,注册文件与 shell 工具,最后以 RunStream 执行 Agent。它以独立 context 收集 diff,因此主执行被取消时仍能保留已完成的修改。

go 复制代码
_ = exec.CommandContext(diffCtx, "git", "-C", tmpDir, "add", "-A", "-N").Run()
patchOut, _ := exec.CommandContext(diffCtx, "git", "-C", tmpDir, "diff").CombinedOutput()
patch := strings.TrimSpace(string(patchOut))

git add -A -N 不是装饰。新建文件默认不在普通 git diff 中;intent-to-add 让它以新增 hunk 进入 model_patch,否则一个真实修复可能在提交评分前被静默丢掉。

runner 还为 benchmark 设定专用边界:未显式配置时使用 python:3.11,通过 BootstrapCmd 执行 editable 安装和 pytest 自举;工具侧将 bash 超时放宽到 300 秒;上下文侧启用 TokenBudgetCompactor,预算为模型窗口的 55%;引擎侧启用 WithGenerateRetry(4, 2*time.Second) 与默认 80 Turn 上限。这些都服务于同一目标:让失败能被观察、能恢复、也能在失控前停止。

验证怎么强制?

第一轮 SWE-bench 轨迹曾暴露一个比"模型不会修代码"更基础的问题:24 条轨迹没有一条真正执行测试,全部被缺 pip 或依赖的环境拦住。静态分析能偶然命中简单题,却没有反馈把复杂修复收敛到正确位置。

runWithVerificationGate 因而不改通用 Agent loop,而在 benchmark runner 中观察 bash 事件。Agent 自然结束、且没有检测到测试命令时,runner 才注入一次 verifyGateText 并沿用同一 MemorySession 续跑。

go 复制代码
ranTest, runErr := streamOnce(ctx, eng, w, userPrompt)
if !ranTest && ctx.Err() == nil {
    ranTest2, err2 := streamOnce(ctx, eng, w, verifyGateText)
    runErr = errors.Join(runErr, err2)
    if !ranTest2 { /* 记录环境或行为问题 */ }
}

这不是无限循环。它只触发一次,仍受实例超时和 MaxTurns 约束。looksLikeTestRun 还只对 bash 的 command 字段做启发式判断,避免把 grep pytest 误当作验证。

Terminal 怎么接入?

Terminal-Bench 2.0 的接入被收敛在 benchmarks/terminal_bench/harness9_agent.pyHarness9Agent 继承 Harbor 的 BaseInstalledAgentinstall() 把静态编译的二进制上传到 /usr/local/bin/harness9run() 上传多行 instruction 文件,再非交互执行二进制。

python 复制代码
await self.exec_as_agent(
    environment,
    command=(
        f"{_BINARY_REMOTE_PATH} --prompt-file {_INSTRUCTION_REMOTE_PATH} "
        f"> {_RUN_LOG_REMOTE_PATH} 2>&1"
    ),
    env=run_env,
    timeout_sec=_ABSOLUTE_TIMEOUT_SEC,
)

这里的重点是超时权责。Harbor 的外层会按各任务 task.tomlagent.timeout_sec 裁决;适配器只保留 4 小时绝对兜底,避免一个统一的内层数字抢先终止声明了更长预算的任务。

install() 还主动安装 ca-certificates。这来自轨迹复核:部分官方镜像没有系统 CA 信任链,静态 Go 二进制访问 HTTPS 会确定性失败;把它当作网络抖动来重试没有意义。

轨迹怎么反推?

轨迹不是调试日志的副产品。agent/harness9.log 给出每轮 LLM 输出、工具调用和耗时;SWE-bench 的 git diff 或 Terminal-Bench 的最终环境给出实际行为;verifier/ctrf.jsontest-stdout.txtreward.txt 给出外部判决。三者必须一起读。

这套证据链也约束结论的力度。一个重跑由 0 翻到 1,可能是模型采样差异;只有同一失败模式被定位、修复并通过针对性复跑,才可以归因于 harness 改动。相反,镜像缺少 CA、任务预算不足和 Agent 未验证产物,分别属于环境、评测适配与模型行为,不能被包装成"引擎进步"。

SWE-bench Lite 的同 seed=1、同 24 实例对比记录为 16/24 到 19/24,真实测试运行实例从 1/24 到 18/24,且无回归。这个变化的价值不只是 +12.5pp,而是从"静态猜测"恢复为"测试反馈驱动"的闭环。

M1 到 M2

M1 是 v1.0.0 的 production-ready Agent Harness 基线:ReAct engine、Provider、Tools、Sandbox、Human-in-the-Loop、MCP、Web、Planning、Skills、Sub-Agent、AutoDev、短期与长期记忆、Eval、SWE-bench、Terminal-Bench、OpenTelemetry、TUI、CLI 与双语站点都已纳入交付范围。

M2 是"本地 Agent OS"的路线图,不是已交付能力,也不是 benchmark 得分的外推。它要解决的是多 Agent runtime、角色与权限、任务图与调度、worktree-session-sandbox 生命周期、可恢复状态、冲突感知的文件所有权、分层记忆及 provenance、benchmark/regression dashboard 与操作面板。

M2 的前提不是先堆一个调度器,而是继续保留 M1 已建立的可验证性:每个角色做了什么、改了哪个 worktree、在什么 Sandbox 中运行、用过哪条记忆、由哪个 benchmark 证明没有回归,都必须能追溯。

结语

把 benchmark 当作证据链,Agent 才能从"偶尔答对"走向"每次改动都能解释"。

相关推荐
塔能物联运维1 小时前
**塔能两相液冷:量化交易场景下的硬核验证,±1.5℃控温如何转化为真金白银**
人工智能·fpga开发·两相液冷
hithithithithit1 小时前
图像编辑维度分类一览表
人工智能
塔能物联运维1 小时前
塔能两相液冷:600W/cm²极限能力,为下一代AI芯片预留充足散热余量
人工智能·算法·两相液冷
番石榴AI1 小时前
轻量化本地 Agent 方案分享:PocketBot 开源,支持目标拆解与自动化
agent·个人智能助手
网易云信1 小时前
从"能不能用"到"用来干什么"——2026 企业 AI 认知的三级跳
人工智能·agent
万悉科技1 小时前
时尚品牌GEO技术选型指南:LLM原生优化与AI记忆构建全解析
数据库·人工智能·搜索引擎
FBI HackerHarry浩1 小时前
AI大模型开发V2第四阶段机器学习概述
人工智能·python·机器学习
蓦然回首却已人去楼空1 小时前
Build a Large Language Model (From Scratch) 第7章 通过微调遵循人类指令
人工智能·语言模型·自然语言处理
数字融合2 小时前
透明化视场时空配准视频孪生多视域空间融合技术
人工智能·音视频·virtualenv