Harvey LAB:法律 Agent 基准的架构与评测方法
LLMAgentBenchmark评测 agent 的基准大多长在程序员的地界上:修 bug、刷题、逛网页。
法律 AI 公司 Harvey 把自家的评测开源了出来——Harvey LAB(Legal Agent Benchmark),
让 agent 坐进虚拟资料室里干真实的律师活:尽调、起草、红线、备忘录。
任务怎么造、环境怎么隔离、没有标准答案怎么判分,这套设计值得拆开看看。
定位
MIT 协议的开源基准,两部分:一个任务数据集,一个执行与评测 harness。
文档口径是 1,660 个任务、覆盖 24 个执业领域加合同板块、约 10.1 万条评分判据;
本地这份 checkout 已经数出 2,010 个任务——项目还在滚着往里加。
最大的几个领域是公司并购(156 题)、知识产权(147 题)、私募与风投(99 题)、公司治理合规(97 题)。
整个系统是文件系统优先的:没有数据库、没有 Web 服务,任务在 tasks/,结果在 results/,报告是静态 HTML。
一次基准跑的全流程:任务进沙箱执行,交付物逐条过评审,得分进对比仪表盘
(交互版,可缩放、可播放数据流轨迹。)
任务模型
一个任务就是一个目录:task.json 加一个 documents/ 资料室。task.json 里有给 agent 的指令(instructions)、工作类型(分析 / 起草 / 审阅 / 检索四种)、
期望的交付物文件名映射,以及内联的评分判据。资料室里是合成的案卷材料。
随手翻一个公司并购题感受一下分量:「审阅收购资料室的合同与内部备忘录,就控制权变更与转让条款出一份交易团队报告」——
资料室 19 份文档,交付一份 coc-analysis-report.docx,评分判据 57 条。
判据长这样,每条都是一段自然语言的通过标准:
{
"id": "C-001",
"title": "Identifies key contract as requiring change-of-control consent",
"match_criteria": "PASS if the agent identifies the key customer contract contains a change-of-control consent requirement (Section 14.3 or equivalent reference). FAIL if the agent does not mention the change-of-control consent requirement.",
"deliverables": ["red-flag-memo.docx"]
}执行链路
harness 是刻意做薄的。agent 循环全文百来行:模型思考,要用工具就调,
工具结果喂回去接着想,直到模型不再调用任何工具——没有专门的「交卷」指令,停手即交卷,上限 200 轮。
工具就六个:bash、read、write、edit、glob、grep,全部圈在任务工作区里。read 能直接消化 docx / xlsx / pptx / pdf;产出二进制交付物则靠三份技能手册(docx / pptx / xlsx),
里面备好了红线对比、加批注、模板填充、格式校验这些法律文书离不开的脚本。
模型侧是适配器制:Anthropic、OpenAI、Google、Mistral、Fireworks(Kimi、GLM 这些开源模型走它)五家,
一个 sweep 命令能把任务矩阵乘上模型矩阵并行跑完,顺手把评测和对比报告也出了。
沙箱
每个任务跑在独立的 Podman 容器里:--network=none 断网,--cap-drop=ALL 去权,
资料室以只读方式挂载,只有工作区和交付目录可写。
六个工具全部经由同一个沙箱接口落地,所以恶意构造的 docx 是在容器里被解析的,碰不到宿主机。
有个细节值得说:装着评分细则的 task.json 物理上不进容器——挂进去的只有 documents/,
agent 想偷看评分标准也无从看起,系统提示里那句「读它算违规直接判负」只是第二道保险。
评测方法
这是全项目最有观点的部分。没有标准答案文件——每条判据的 match_criteria 文本本身就是评分标准,
由一个 LLM 评审(默认 claude-sonnet-4-6,温度 0)逐条裁决:一次评审调用只看一条判据、
只读该判据声明的交付物文件,语义比对而非关键词匹配,回一个 pass 或 fail 加一段理由,全部留档可查。
all-pass 评分:逐条裁决、一票否决,差一条判据整题零分
(交互版)任务得分是二值的:
score = 1.0 if every criterion passed else 0.0
差一条判据,整题零分。 理由写在文档里,很律师:一份抓到 95% 问题但漏掉一处实质风险的尽调备忘录,
不是 95 分的备忘录,是错的备忘录。运营上要回答的问题是「这个 agent 多大概率把事情一次全做对」,
all-pass 率回答的正是这个。诊断字段(过了几条、共几条)另存,用来看差距,不进总分。
可选的双评审模式再拉一个 gpt-5.5 独立判一遍,两边取平均,缓解单一评审的口味问题。
上手
uv run python -m harness.run --model anthropic/claude-sonnet-4-6 \
--task real-estate/extract-psa-key-terms/scenario-01
uv run python -m evaluation.run_eval --run-id <run-id> --task <task-id>
uv run python -m utils.sweep --task corporate-ma --models sonnet --parallel 4跑完 results/ 下面每次运行一个目录:完整对话转录、工具调用统计、交付物、逐条判决、单跑报告;evaluation.compare 再把多模型汇成仪表盘——all-pass 率、判据池化通过率、热图、文档覆盖率、token 与成本。
边界与风险
评审自己也是个 LLM,而且默认是 claude-sonnet-4-6——用 Anthropic 的模型给包括 Anthropic 在内的所有选手判卷,
亲缘偏差是结构性的;双评审能缓解但默认不开。温度 0 保证的是同版本内可复现,评审模型一升级,历史分数就没法直接比了。
没有标准答案是把双刃剑:rubric 即真相,match_criteria 写得含糊,评测就跟着含糊,
而全套约 10.1 万条判据的质量只能靠作者功力和 CI 的格式校验兜着。
文档自己也承认 all-pass 对判据灌水极其敏感——多塞几条「锦上添花」的判据,通过率直接被拖穿,却不产生任何质量信号。
判据数量差异也让跨任务比较要小心:3 条判据的题和 57 条的题,all-pass 难度天差地别,聚合口径得看清楚再引用。
资料室是合成的。文档干净、齐全、都在一个文件夹里——真实执业里缺页的合同、口头交代的背景、
改了八版的条款清单,这些混乱不在题面上。它测的是「材料齐备时能不能做对」,不是「乱局里能不能理清」。
成本也要有数:判一个 57 条判据的任务就是 57 次评审调用,全量 2,000 个任务乘上 10 万条判据再乘模型矩阵,
sweep 一轮的账单不是小数目。好在按领域、按任务切片跑都支持,不必一上来就全量。
法律这行把「差一点」当「全错」——把 95 分算成 0 分,是这个基准最像律师的地方。