Deep Agents 上下文检索评测任务 cb-cloud-54 深度解析:aggregation 型多文件检索、跨实体关联与聚合验证全流程
【免费下载链接】deepagentsThe batteries-included agent harness.项目地址: https://gitcode.com/GitHub_Trending/de/deepagents
本篇技术指南以 libs/evals/datasets/context-retrieval-evals/cb-cloud-54/instruction.md 这份评测任务指令文档为骨架,结合其所在的 context-retrieval-evals 数据集、Context-Bench 任务生成适配器与 LLM 裁判(model_judge)验证机制,完整拆解这一类"跨多文件语料检索、实体关联、聚合计算"评测任务的设计原理、运行环境与打分链路。读完本文,你将掌握如何解读一个 Harbor 评测任务目录(instruction/task.toml/tests/solution/environment),理解 aggregation 类任务在 Deep Agents 评测体系中的定位,并能在本地复现该任务的构建、填充与评分流程。
一、任务文档原文与解读
cb-cloud-54/instruction.md是这份评测任务给被测 Agent 的唯一任务说明,全文如下:
What is the total bank balance of the person with the most internet accounts among all residents of the same state as the owner of the pet named 'Gloria'? If there's a tie for most internet accounts, use the highest total bank balance as a tiebreaker. Use only the files under `/app/files`. Write your final answer (and nothing else) to `/app/answer.txt`.1.1 问题拆解:一次典型的多跳聚合查询
表面看这只是一句英语问句,但它实际包含一条完整的多步推理链(multi-hop reasoning + aggregation),被测 Agent 需要依次完成:
- 实体定位:在语料中找到名为 'Gloria' 的宠物,确定其主人;
- 属性关联:确定该主人所在的州(state),再枚举同州的所有居民(residents);
- 属性筛选:统计这些居民各自的互联网账户(internet accounts)数量,找出最多的那一位;
- 聚合计算:读取该居民的银行存款总额(total bank balance)作为最终答案;
- 平局处理(tiebreaker):若存在多个居民互联网账户数并列最多,则取其中银行总余额最高者的余额作为答案。
该问题在数据集的元数据中被标注为question_type = "aggregation"(见 task.toml),属于 30 个评测任务中 6 类题型(aggregation、comparison_tiebreak、temporal_reasoning、set_intersection、negation、cross_file_counting、multi_hop_chain、multi_entity_comparison)中的"聚合"类型,且同时隐含了实体关联与平局判定逻辑。
1.2 运行约束:指令的第二段是评测的关键约定
第二段是 Deep Agents 评测体系中所有 Context-Bench 任务的统一操作契约(由任务生成器统一写入,见下文源码佐证):
- 数据边界:
Use only the files under /app/files——语料被整体挂载在沙箱的/app/files目录下,Agent 只能基于本地语料作答; - 输出通道:最终答案必须原样只写入
/app/answer.txt,不得附加任何其他内容。这一约定直接对接验证器的读取逻辑:裁判程序(judge)只认/app/answer.txt这一个文件(见 judge.py 中_SUBMISSION_PATH = Path("/app/answer.txt"))。
二、任务在 context-retrieval-evals 数据集中的位置
cb-cloud-54隶属于 libs/evals/datasets/context-retrieval-evals——一个包含30 个上下文检索任务的 Harbor 数据集,其设计目标是:每次任务都把完整的 10 文件语料(共约 6.47 万行)整体提供给 Agent,因此 Agent 无法通过文件名或目录结构预判哪些文件有用,必须真正"检索 → 关联 → 聚合"才能作答。
据 README 说明:
- 任务衍生自Context-Bench(
cloud套件,合成的人物/车辆/宠物/账户记录),源数据为filesystem_cloud.jsonl(100 条记录),由libs/evals/harbor_adapters/contextbench适配器逐条生成; - 每个任务
cb-cloud-<i>对应源 JSONL 的第<i>条记录(0 起始索引),因此cb-cloud-54对应第 54 条记录; - 30 个任务是全量 100 个源任务的代表性抽样,保留了整体难度分布:抽样前 Terra 85.0%、Luna 92.0%,抽样后 85.0% / 92.2%,两个模型在 30 个选中任务上均达到了 29/30 的 pass@6。
2.1 难度与来源标注:medium 的真实含义
task.toml 中记录:
version = "1.3" [metadata] source = "contextbench" suite = "cloud" difficulty = "medium" source_difficulty = "medium" question_type = "aggregation" [environment] network_mode = "allowlist" allowed_hosts = ["astral.sh", "*.astral.sh", "github.com", "*.githubusercontent.com", "pypi.org", "*.pythonhosted.org", "api.smith.langchain.com", "api.anthropic.com", "api.openai.com", "generativelanguage.googleapis.com", "openrouter.ai", "*.baseten.co", "api.fireworks.ai", "ollama.com", "api.groq.com", "integrate.api.nvidia.com", "api.x.ai"]需要特别澄清的是(README 有明确说明):difficulty与source_difficulty是Context-Bench 源数据自带的分层标签(全数据集合计 2 easy · 10 medium · 18 hard),而不是事后根据模型表现贴的标签。cb-cloud-54为 medium 档。而calibration.json(calibration.json)是源运行与聚合结果的机器可读记录,用于--stamp-tiers将实测分层写回各任务的difficulty字段;pass_at_bare保留为 Terra 的通过率以便与既有适配器兼容。
在 30 任务清单中,cb-cloud-54的配对记录为:
| 任务 | 源难度档 | Terra pass@6 | Luna pass@6 | 题型 |
|---|---|---|---|---|
cb-cloud-54 | medium | 6/6 | 6/6 | aggregation |
(数据来源:context-retrieval-evals/README.md 中的 30 任务表;cb-cloud-54两模型均全通过,说明该任务在代表性抽样中属于两模型都能稳定解决的中等难度聚合题。)
2.2 环境网络策略:allowlist 而非断网
task.toml 的[environment]段体现了一个关键设计:评测沙箱不是完全断网,而是采用network_mode = "allowlist"。其目的是(从 adapter.py 的生成注释可以确认):langgraph/dcode 形式的 Agent 在沙箱内运行,需要访问包镜像源(astral.sh、pypi.org 等)以及所选模型厂商的 API(openai、anthropic、openrouter、groq、nvidia 等)才能完成自身引导与推理作答;而任意其他网络访问被阻断,从而仍然防止 Agent 通过联网"查答案"。api.openai.com同时也在白名单内,供验证阶段的裁判模型调用。
三、任务目录结构与每个文件的源码级由来
完整的任务目录由适配器 adapter.py 的generate_task()一次性生成。cb-cloud-54目录下各文件的职责如下:
cb-cloud-54/ ├── environment/ │ ├── Dockerfile # 沙箱镜像:python:3.12-slim + curl,并 COPY files/ 到 /app/files/ │ └── files/ # 10 文件语料(git-ignored,需 --populate 生成) ├── solution/ │ └── solve.sh # 参考答案写入脚本:printf '%s\n' '$130,196.23' > /app/answer.txt ├── tests/ │ ├── case.json # 唯一按任务提交的验证输入:{input, ground_truth} │ └── (test.sh / judge.py / rubric.txt 由 populate 从模板/源复制,git-ignored) ├── instruction.md # 任务指令(本文主体文档) └── task.toml # 任务元数据与环境配置3.1 指令文档的生成:不是手写的
instruction.md的内容由generate_task()程序化写入(见 adapter.py 中_write_task_files):
(task_dir / "instruction.md").write_text( f"{question}\n\n" "Use only the files under `/app/files`. Write your final answer (and nothing else) " "to `/app/answer.txt`.\n" )其中question直接取自源 JSONL 记录(record.get("input")),这解释了为什么每个任务的instruction.md第二段完全一致——它是框架统一追加的运行约定,第一段才是该任务独有的问题。
3.2 沙箱镜像与数据边界
environment/Dockerfile 在构建阶段(该阶段允许联网)预装curl与ca-certificates,使沙箱内 Agent 的运行时引导跳过 apt,从而把运行期的出站流量全部限制为任务白名单内的 HTTPS 请求;随后COPY files/ /app/files/将完整语料挂载进沙箱,与instruction.md中/app/files的约定一一对应。
3.3 答案真值:ground_truth 与 solve.sh 双重确认
case.json 记录:
{"input": "What is the total bank balance ...", "ground_truth": "$130,196.23"}solution/solve.sh 则给出参考答案的写入方式:
#!/bin/sh set -eu printf '%s\n' '$130,196.23' > /app/answer.txt即该任务的期望答案(最终银行总余额)为$130,196.23。注意答案带美元符号与千分位、两位小数的金额格式,这也提示:Agent 在写/app/answer.txt时对数字格式的处理会影响后续 LLM 裁判的打分宽容度(见第五节)。
四、验证机制:LLM model_judge,而非字符串比对
一个常见误区是"评测就是字符串比对"。该数据集明确不采用字符串相等判定。从 adapter.py 的注释与 templates/judge.py 的 docstring 可知:评分复刻上游 Letta letta-evals 的RubricGrader(OpenAI provider)——用一个 LLM 裁判模型对照rubric.txt打分,对措辞、人名、数字格式保持宽容,得分桶为0.0 / 0.5 / 1.0。
4.1 裁判的执行流程(judge.py 逐段解析)
templates/judge.py 是整个评分链路的沙箱内实现,其流程为:
- 读取输入:从
/tests/case.json读任务问题与 ground_truth,从/tests/rubric.txt读裁判提示词模板; - 拼装提示词:用
string.Formatter().vformat将{input}、{ground_truth}、{submission}三个占位符替换进 rubric,没有 system prompt、没有额外包装; - 调用裁判模型:通过 Chat Completions 接口(
{OPENAI_BASE_URL}/chat/completions)发送,response_format强制为 JSON Schema({score: float∈[0,1], rationale}); - 温度规则:
_temperature()实现上游规则——若裁判模型匹配o1/o3/gpt-5这类推理模型,则使用 temperature=1.0(这些模型 API 会拒绝 0.0),其余模型使用 0.0; - 重试与兜底:最多重试 5 次,任何异常最终都按上游惯例得 0.0 分;
score = clamp(score, 0.0, 1.0)强制限幅; - 写结果:将得分写入
/logs/verifier/reward.txt,test.sh只负责调用python3 /tests/judge.py。
4.2 与上游的两处有意偏差
judge.py 的 docstring 明确说明了两处由 deepagents 评测框架而非该文件决定的偏差:
- 裁判模型来自环境变量
JUDGE_MODELS(默认回退gpt-5.6-luna),而不是上游固定的gpt-5-mini; - 被评答案来自
/app/answer.txt(框架统一的答案通道),而不是上游"Agent 最后一条 assistant 消息"。
裁判模型与凭据均由框架注入验证环境(OPENAI_API_KEY、OPENAI_BASE_URL、JUDGE_MODELS、JUDGE_PROVIDER),judge.py 中不硬编码任何密钥,也从不打印密钥。
五、本地复现:填充语料并运行 Harbor 评测
由于每份任务共享的语料(environment/files/)与验证器固定文件(tests/{test.sh,judge.py,rubric.txt})在 30 个任务间逐字节相同,因此它们采用"单一来源"策略:git-ignored、不提交到仓库,仅保留一份在harbor_adapters/contextbench/vendor/与templates/中;每个任务只提交tests/case.json(问题 + 真值)。因此本地运行前必须先填充,步骤见 README:
uv run python -m harbor_adapters.contextbench.main --populate datasets/context-retrieval-evals uv run harbor run --path datasets/context-retrieval-evals ...其中--populate通过 adapter.py 的populate_corpus()完成:遍历数据集下所有source = "contextbench"的任务目录,把 vendored 语料复制进各自environment/files/,并把templates/下的test.sh、judge.py与vendor/rubric.txt复制进各自tests/,而不触碰已提交的case.json。CI(harbor.yml)在构建任务镜像前会自动执行--populate。
5.1 适配器 CLI 的三种模式
main.py 提供了互斥的三种工作模式,对应数据集运维的不同阶段:
| 模式 | 命令 | 作用 |
|---|---|---|
| 生成任务 | python -m harbor_adapters.contextbench.main --task-ids cb-cloud-54 --output-dir <dir>或--limit N | 从 vendored JSONL 按索引生成指定任务的目录结构(含 instruction.md、task.toml、case.json、solve.sh、Dockerfile)并复制语料 |
| 填充语料 | ... --populate datasets/context-retrieval-evals | 为已生成任务补齐 git-ignored 的语料与验证器固定文件 |
| 校准分层 | ... --stamp-tiers <dir> --calibration calibration.json | 用实测校准记录覆盖各任务task.toml的difficulty(仅接受 easy/medium/hard),source_difficulty保留原始标签以溯源 |
任务 ID 必须匹配cb-<suite>-<i>形式(parse_task_id()用正则^cb-(?P<suite>[a-z0-9]+)-(?P<index>\d+)$校验),其中<i>是源 JSONL 的 0 起始行索引;record_for_task_id()会在生成前先做一次 ID 有效性预检。
六、解题思路与评测设计要点(从源码结构推断)
基于任务结构与语料设计(语料本身被 git-ignored、未随仓库提交),可以从框架层面推断这类 aggregation 任务的正确解题姿势:
- 先全局检索再聚焦:由于 10 个文件全部在
/app/files,Agent 应先用文件系统工具(或 grep 类检索)定位包含 "Gloria" 的宠物记录,再沿"主人 → 州 → 同州居民 → 互联网账户数"逐跳建立实体关联,最后跨文件做聚合; - 注意平局规则:题目显式给出 tiebreaker(账户数并列时取余额最高者),这是 aggregation 与 comparison_tiebreak 两类题型的常见结合点,直接体现 README 所说的"retrieve, join, and aggregate"评测目标;
- 输出格式以 case.json 为准:期望答案
$130,196.23带符号与千分位,虽然 LLM 裁判对数字格式有宽容度(分数桶 0.0/0.5/1.0),但"只写答案、不多不少"的通道约定仍是最稳的提交方式; - 不依赖外网:
allowlist只放行包镜像与模型 API,答案必须完全来自本地语料推理,这是该评测与一般 RAG 评测的核心差异之一。
七、小结
cb-cloud-54虽只是一份两行的指令文档,却是 Deep Agents 上下文检索评测体系中"aggregation 题型"的一个完整切片:指令本身定义问题与操作契约,task.toml定义难度、题型与网络边界,case.json/solve.sh锁定真值$130,196.23,Dockerfile 定义沙箱数据挂载,而 judge.py + rubric.txt 提供对措辞与数字格式宽容的 LLM 裁判评分。理解这一条任务,即可举一反三地解读整个 context-retrieval-evals 数据集(30 个任务、6 类题型、单源语料 + 校验器复用机制),并为在 Harbor 上复现、扩展或二次开发同类检索评测提供完整的工程参考。
【免费下载链接】deepagentsThe batteries-included agent harness.项目地址: https://gitcode.com/GitHub_Trending/de/deepagents
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考