deepagents 评估管线中的数据 vendoring 实践:Context-Bench filesystem-cloud 语料库的引入与复刻
【免费下载链接】deepagentsThe batteries-included agent harness.项目地址: https://gitcode.com/GitHub_Trending/de/deepagents
导读
本文深入剖析 deepagents 仓库的 Harbor 评估适配器中,如何将 Letta 的 Context-Bench filesystem-cloud 评测语料库以 "vendoring(数据内置)" 的方式固化到本仓库,从而保证评测任务可复现、评分口径与上游一致。你将掌握libs/evals/harbor_adapters/contextbench/vendor/目录的完整构成、从原始 JSONL 记录生成 Harbor 任务的底层实现,以及 LLM 评分器judge.py逐字复刻上游model_judge评分细则的全部细节。
Context-Bench 与 deepagents 评估管线的关系
Context-Bench 是面向 LLM Agent 的上下文检索与跨文件推理评测基准,其filesystem-cloud子集模拟了一个"纯文本文件系统":语料由一批存储合成人物数据的.txt文件构成(如people.txt、pets.txt、vehicles.txt、bank_accounts.txt等),每个问题要求 Agent 阅读并交叉引用多个文件才能得出答案,覆盖多跳推理、比较与并列、否定推理等题型。
在 deepagents 中,Context-Bench 被接入 Harbor 评测框架(任务生成、沙箱构建、验证评分的整套体系),相关代码位于 libs/evals/harbor_adapters/contextbench/:
vendor/:内置(vendored)的 Context-Bench 原始数据与评分细则;adapter.py:将原始记录转换为自包含 Harbor 任务的核心实现;main.py:命令行驱动入口;templates/:任务无关的验证器模板(test.sh与judge.py)。
vendor/README.md正是描述这份内置数据来源与构成的核心文档,即本文主体。
vendor 目录的内容构成
vendor/README.md 明确指出:该目录从 Letta 的letta-evals仓库(main分支)内置了 Context-Bench filesystem-cloud 语料,共包含三部分工件。实际目录内容如下:
libs/evals/harbor_adapters/contextbench/vendor/ ├── README.md # 本文所述的来源与归属说明 ├── LICENSE # 上游 Apache-2.0 许可证原文(未修改) ├── filesystem_cloud.jsonl # 100 条评测记录(每行一条 JSON) ├── files/ # 语料库文本文件(共 10 个) └── rubric.txt # 上游 model_judge 使用的评分细则(逐字复刻)filesystem_cloud.jsonl:评测记录
对应上游路径letta-leaderboard/filesystem-agent/datasets/filesystem_cloud.jsonl,当前仓库内置了100 条记录(每行一条 JSON)。以第一条为例:
{"input": "Who owns more vehicles: the person with the highest total bank balance among residents of the same state as the owner of pet 'Eduardo', or the person with the highest total bank balance among residents of the same state as the owner of pet 'Victor'?", "ground_truth": "Linda Robbins", "agent_args": {"tags": [], "extra": {"required_files": ["pets.txt", "addresses.txt", "bank_accounts.txt", "vehicles.txt", "people.txt"], "question_type": "multi_hop_chain", "difficulty": "hard"}}}每条记录含三个关键字段:
| 字段 | 含义 | 在 deepagents 中的去向 |
|---|---|---|
input | 评测问题文本 | 写入每个任务的instruction.md与tests/case.json |
ground_truth | 标准答案 | 写入solution/solve.sh与tests/case.json |
agent_args.extra | 元数据(difficulty难度标签、question_type题型、required_files依赖文件) | 写入task.toml的 metadata,required_files用于定位所需语料文件 |
files/:合成人物语料
对应上游路径letta-leaderboard/filesystem-agent/files/*.txt,共 10 个文本文件:
addresses.txt bank_accounts.txt credit_cards.txt employments.txt insurance_policies.txt internet_accounts.txt medical_records.txt people.txt pets.txt vehicles.txt这 10 个文件是每次评测时注入沙箱的完整"文件系统",Agent 只能据此作答。从 adapter.py 的_copy_corpus可以看到,生成任务时会按字典序将vendor/files/下所有.txt文件拷贝到任务的environment/files/目录。
rubric.txt:逐字复刻的评分细则
对应上游路径letta-leaderboard/filesystem-agent/rubric.txt。它是上游model_judge的评分提示词,deepagents 选择原样保留、逐字复刻,而不是重写一套自有评分标准——原因在 README 中写得很清楚:让本仓库的验证器与上游用同一把尺子打分。关于这一点,adapter.py 的注释进一步佐证:
Grade exactly as upstream Letta letta-evals does: an LLM
model_judgeagainst the vendoredrubric.txt(措辞/姓名/数字容错,分档 0.0/0.5/1.0),NOT string equality.
细则的核心要点包括:
- 只允许 0.0 / 0.5 / 1.0 三档,禁止 0.25、0.75 等部分得分;
- 数字格式等价:
"$145,315.33"="145315.33"; - 数字单词等价:
"2 dogs"="two dogs"="2"; - 姓名大小写不敏感:
"john smith"="John Smith"; - 措辞差异可接受:
"Risk manager"与完整句式表述等价; - 单位可隐含:问车辆数时
"4"="4 vehicles"="4 cars"; - 0.5 仅授予明确拒绝作答(refusal),不授予尝试失败;
- 评分只看最终答案,中间推理出错但最终答案正确仍给 1.0。
许可证归属
README 声明:源仓库以 Apache-2.0 许可,其未修改的LICENSE文件已一并内置(即vendor/LICENSE),且上游不提供NOTICE文件。这是数据 vendoring 必须具备的合规细节——把第三方数据连同许可证原文一起固化,保证后续使用有据可依。
为什么"复刻 rubric"如此重要:验证器与上游评分对齐
评分口径的一致性是整套 vendoring 设计的核心动机。作为证据,templates/judge.py 的模块文档字符串声明它是上游letta_evals/graders/rubric.py(OpenAI provider)的RubricGrader的沙箱内复刻实现,逐条复刻了上游行为:
- 提示词构造:直接以
rubric.txt为提示词模板,用string.Formatter().vformat替换{input}、{ground_truth}、{submission}三个占位符——无系统提示词、无额外包装; - 响应格式:通过 Chat Completions 调用,并使用与上游
_JudgeResponse.model_json_schema()镜像的json_schema响应格式(score: number ∈ [0,1]+rationale: string,见 judge.py); - 温度规则:上游对
o1/o3/gpt-5系列推理模型调用温度 1.0(这些模型拒绝 0.0 温度,会返回 400),其余模型 0.0,见_temperature(); - 分数处理:
score = clamp(score, 0.0, 1.0),任何异常一律记 0.0,与上游"异常即 0.0"一致;且对HTTPError会透出 API 返回的原因(如不支持的 temperature 400)便于排查; - 失败重试:最多重试 5 次,全部失败才记 0.0。
同时文档也诚实标注了两处刻意偏离上游的实现,均由 deepagents 评测框架注入而非本文件硬编码:
- 评判模型来自环境变量
JUDGE_MODELS(如gpt-5.6-luna),而非上游固定的gpt-5-mini; - 待评提交是
/app/answer.txt(框架的答案通道),而非 Agent 的最后一条消息。
凭证与模型选择全部来自验证器环境注入(OPENAI_API_KEY、OPENAI_BASE_URL、JUDGE_MODELS、JUDGE_PROVIDER),密钥不落盘、不打印。入口模板 test.sh 只有两行:set -eu后直接执行python3 /tests/judge.py,由judge.py自己把分数写入/logs/verifier/reward.txt。
从 vendored 数据到 Harbor 任务:adapter 的生成链路
vendor/README.md 中提到的../adapter.py与../templates/judge.py是整个数据消费端。核心实现在 adapter.py,几个关键函数构成了完整链路:
任务 ID 约定:cb- -
任务 ID 形如cb-cloud-1,由_TASK_ID_RE = ^cb-(?P<suite>[a-z0-9]+)-(?P<index>\d+)$解析,其中<index>是filesystem_<suite>.jsonl的从零开始的行号(parse_task_id)。record_for_task_id据此从vendor/filesystem_<suite>.jsonl按行号取回对应记录。
generate_task:生成一个自包含任务
generate_task(adapter.py)接收source_jsonl、source_files_dir、output_dir、task_id、line_index,产出如下目录结构:
<output_dir>/cb-cloud-1/ ├── environment/ │ ├── Dockerfile # python:3.12-slim + 预装 curl/ca-certificates │ ├── .dockerignore # 排除 .env、*.pem、credentials.json 等敏感文件 │ └── files/ # 从 vendor/files/ 拷贝的 10 个 .txt 语料 ├── instruction.md # 问题 + 固定指令(只准用 /app/files,答案写入 /app/answer.txt) ├── solution/ │ └── solve.sh # printf '%s\n' '<ground_truth>' > /app/answer.txt ├── tests/ │ ├── test.sh # 模板拷贝 │ ├── judge.py # 模板拷贝 │ ├── rubric.txt # vendor/rubric.txt 拷贝 │ └── case.json # 唯一按任务提交的文件:{"input", "ground_truth"} └── task.toml # 任务元数据 + 沙箱网络策略几个值得注意的安全与工程细节:
generate_task会校验task_id必须是单一路径组件(Path(task_id).name != task_id即拒绝),杜绝路径逃逸;重跑时直接shutil.rmtree旧目录实现"干净重建";- 记录形状校验:
agent_args必须为 dict、input/ground_truth必须为 str,否则抛TypeError(防止损坏的 JSONL 静默产生坏任务); - Dockerfile 在构建阶段预装 curl(构建期有网络),使沙箱内 Agent 运行时无需执行 apt,运行期出口流量全部收敛为 HTTPS,配合下方 allowlist 白名单使用;
.dockerignore排除.env、*.pem、*.key、credentials.json等凭据类文件,防止数据泄露。
populate_corpus:单源复制与 git 忽略
由于语料和验证器文件在每个任务间字节级相同,设计上采用"单源 + 每任务 git 忽略"策略:
- 语料单源存于
vendor/files/; - 验证器固定文件(
test.sh、judge.py、rubric.txt)单源存于templates/与vendor/rubric.txt; - 每个任务只提交
tests/case.json(问题 + 标准答案),其余由populate_corpus(adapter.py)在运行前统一生成。
populate_corpus扫描数据集目录下的所有*/task.toml,仅处理直接子目录且source = "contextbench"的任务,为其补上environment/files/与tests/下的全部固定文件。这正是 README 强调的 "single-sourced, git-ignored" 设计:一个语料副本,供全部任务共享,Harbor 构建时再展开。
stamp_calibrated_tiers:用实测档位覆盖难度
Context-Bench 自带的difficulty标签是源数据的主观标注。校准实验结束后,stamp_calibrated_tiers(adapter.py)读取校准 JSON({tasks: {task_id: {"tier": "easy"|"medium"|"hard"}}}),把实测档位写回每个task.toml的difficulty字段,同时保留原标签到source_difficulty以便溯源。三档取值由_VALID_TIERS = {"easy", "medium", "hard"}强制校验。
生成的 task.toml 实例
以 cb-cloud-1/task.toml 为例:
version = "1.3" [metadata] source = "contextbench" suite = "cloud" difficulty = "easy" source_difficulty = "easy" question_type = "comparison_tiebreak" [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"]网络策略采用allowlist(白名单,而非禁网):沙箱内 Agent 需要访问包镜像(PyPI、astral/uv)与所选模型的 API 端点才能启动与作答,但任意网页仍被阻断,从而既保证可运行、又防止直接查答案。白名单只含模型供应商 API 域名,绝不含任何答案来源。
CLI 使用方式
main.py 提供命令行驱动,三种互斥模式:
# 1. 按任务 ID 生成(校验 ID 并逐条生成) python -m harbor_adapters.contextbench.main \ --output-dir datasets/context-retrieval-evals \ --task-ids cb-cloud-1 cb-cloud-2 cb-cloud-3 # 2. 不指定 ID 时用 --limit 生成云套件前 N 个任务 python -m harbor_adapters.contextbench.main \ --output-dir datasets/context-retrieval-evals --limit 10 # 3. 展开单源文件(运行 harbor run 前必须执行) python -m harbor_adapters.contextbench.main --populate datasets/context-retrieval-evals # 4. 用校准结果覆盖难度档位(需要 --calibration JSON) python -m harbor_adapters.contextbench.main \ --stamp-tiers datasets/context-retrieval-evals --calibration calibration.json其中--populate与--stamp-tiers均与--task-ids/--limit互斥,--stamp-tiers强制要求--calibration;既不传--task-ids也不传--limit时抛ValueError。生成流程会先用record_for_task_id前置校验每个 ID,再逐条调用generate_task。
数据实测:filesystem_cloud.jsonl 的题型分布
从内置的 filesystem_cloud.jsonl 观察,题型标签(question_type)覆盖多跳链式推理(multi_hop_chain)、比较与并列裁决(comparison_tiebreak)、否定推理(negation)等类型,难度标签分为easy/medium/hard。问题示例:
- 多跳:
"Who owns more vehicles: the person with the highest total bank balance among residents of the same state as the owner of pet 'Eduardo', or ...?" - 否定:
"Among all people who live in the same state as the owner of the vehicle with license plate '7D U3378', who does NOT own any pets?"
此类问题迫使 Agent 真正"打开多个文件、逐字段关联",而非依赖模型记忆,这正是 Context-Bench 用于上下文检索与长文件操作评测的价值所在。
测试保障
vendoring 的消费逻辑有单测覆盖,见 test_contextbench_adapter.py 与 test_contextbench_main.py。前者以最小 fixture 数据集验证generate_task产出的完整目录结构——包括environment/Dockerfile与.dockerignore内容、instruction.md的固定指令、solution/solve.sh的答案写入方式、tests/下验证器文件的存在性、case.json的 JSON 内容,以及task.toml的逐字节比对;后者验证 CLI 参数互斥、--limit生成规则等。测试还显式断言 rubric 模板中包含{input}、{ground_truth}、{submission}三个占位符,从测试层面锁死了"逐字复刻 rubric"这一核心约定。
小结
libs/evals/harbor_adapters/contextbench/vendor/的 README 虽短,背后却是一套完整的数据工程原则:评测数据必须与评测代码同库、评分细则必须与上游逐字一致、大体积单源数据必须 git 忽略并在运行前展开。这套做法让 deepagents 的 Context-Bench 评测既可在 Harbor 上端到端复现,又能保证与 Letta 官方评分口径可比——这正是 vendoring 在 LLM 评测工程中的典型价值:可复现、可审计、可对齐。
【免费下载链接】deepagentsThe batteries-included agent harness.项目地址: https://gitcode.com/GitHub_Trending/de/deepagents
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考