Deep Agents Eval 测试套件全解析:从轨迹断言到外部基准的端到端评测体系
2026/9/10 5:53:54 网站建设 项目流程

Deep Agents Eval 测试套件全解析:从轨迹断言到外部基准的端到端评测体系

【免费下载链接】deepagentsThe batteries-included agent harness.项目地址: https://gitcode.com/GitHub_Trending/de/deepagents

导读

本文围绕 Deep Agents SDK 评测体系的测试核心——libs/evals/tests/evals/目录展开,系统讲解其目录结构、pytest 基础设施、基于轨迹(trajectory)的"正确性 + 效率"双轨断言模型、LLM-as-judge 自动打分、JSON 效率报告插件,以及 MemoryAgentBench、tau2-bench airline 与 BFCL v3 等外部基准的接入方式。读完本文,你将掌握如何阅读、运行、扩写这套真实调用 LLM 的端到端行为评测套件,并理解每个组件背后的源码实现与数据流。

目录全景:tests/evals 承担什么角色

tests/evals/是 Deep Agents SDK 的行为评测测试套件所在目录。它与传统单元测试不同:每个 eval 都会用一个真实的 LLM 运行一个 agent,捕获完整轨迹(工具调用、文件变更、最终回复),然后从正确性和效率两个维度打分。目录的官方说明见 tests/evals/README.md,其明确列出的组件包括:

组件职责
conftest.pypytest 夹具:--model选项、model/model_name夹具、LangSmith 实验元数据
utils.py核心框架:AgentTrajectory、断言类、TrajectoryScorerrun_agent
llm_judge.py基于 openevals 的 LLM-as-judge 断言
pytest_reporter.py自定义 pytest 插件,产出效率总结报告
fixtures/静态测试数据(如摘要种子消息)
data/基准样本数据(FRAMES、Nexus、BFCL v3)与 BFCL API 实现
memory_agent_bench/MemoryAgentBench(ICLR 2026)评测运行器
tau2_airline/tau2-bench 航空领域评测(来自 Sierra Research 的 tau-bench,MIT License)

从仓库结构看,该目录与libs/evals/tests/unit_tests/(纯单元测试)并列,体现了"单元测试验证机制、evals 验证端到端行为"的分层设计。根 libs/evals/README.md 将其定位为"面向 Deep Agents SDK 的端到端行为评测套件",并说明每个 eval 都会把完整轨迹与正确性、效率分数同步到 LangSmith。

基础设施层:conftest.py 的 pytest 集成

conftest.py 是整套评测的 pytest 入口,承担三个关键职责。

1. 启动即校验:LangSmith 追踪与模型必须就绪

pytest_configure在会话启动阶段就强制两条前置条件,不满足则直接pytest.exit

  • 必须开启 LangSmith 追踪LANGSMITH_TRACINGLANGSMITH_TRACING_V2LANGCHAIN_TRACING_V2LANGCHAIN_TRACING任一为"true"即可,否则整套测试被跳过并给出明确报错;
  • 必须传入--model:每个 eval 都依赖真实模型,缺失时直接退出。

此外它还注册了三类自定义 marker:

config.addinivalue_line("markers", "eval_category(name): ...") config.addinivalue_line("markers", "eval_tier(name): ...") config.addinivalue_line("markers", "repl(*allowed): ...")

其中eval_category用于分组(如memorytool_use),eval_tier用于区分baseline(回归门槛)与hillclimb(进度追踪)两个层级,repl用于声明可选的 REPL 后端(与--repl quickjs搭配)。

2. 全套 CLI 选项

pytest_addoption暴露了评测专用的命令行参数:

选项说明
--model必填,评测使用的模型标识,如--model claude-sonnet-4-6
--eval-category可重复,只运行指定类别的 eval,如--eval-category memory --eval-category tool_use
--eval-category-exclude可重复,排除指定类别(优先于 include)
--eval-tier可重复,只运行指定层级的 eval
--openrouter-provider逗号分隔的 OpenRouter 供应商白名单,如MiniMax,Fireworks
--openrouter-allow-fallbacks允许 OpenRouter 在列表供应商不可用时回退,默认严格不回退
--openai-reasoning-effort仅对 OpenAI 模型生效,取值为minimal/low/medium/high/xhigh
--repl可选 REPL 中间件,当前支持quickjs

pytest_collection_modifyitems会依据eval_category/eval_tiermarker 对收集到的测试项做过滤,并且如果传入的 include/exclude 值与测试中实际存在的 marker 值对不上,会直接以返回码 1 退出,避免"静默跑空"。

3. 夹具体系

  • model_name:由pytest_generate_tests--model参数化到每个测试;
  • model:通过init_chat_model构建真实模型实例。源码中对 OpenRouter 前缀强制设置 120 秒超时(规避 SDK 默认 5 秒读超时导致的 TCP 悬挂),对openai:前缀显式开启use_responses_api,并可叠加reasoning_effort
  • langsmith_experiment_metadata(session 级):记录model、运行日期与deepagents_version,作为 LangSmith 实验元数据。

核心框架:utils.py 的轨迹与断言模型

utils.py 是整个评测框架的心脏,约 1500 行,定义了从轨迹建模到断言执行的全套机制。

AgentTrajectory 与 AgentStep

@dataclass(frozen=True) class AgentStep: index: int # 从 1 开始计数 action: AIMessage # agent 的输出,可能包含工具调用 observations: list[ToolMessage] @dataclass(frozen=True) class AgentTrajectory: steps: list[AgentStep] files: dict[str, str] @property def answer(self) -> str: ... # 最后一步的文本内容 def pretty(self) -> str: ... # 人类可读的轨迹摘要

AgentTrajectoryagent.invoke()的原始结果(messages 列表 + files 通道)规整为结构化数据:AIMessage构成 step、ToolMessage挂到对应 step 的 observations,files则统一转换为dict[str, str](兼容字符串与{content: ...}两种表示)。pretty()生成的格式会被llm_judge.py直接复用作为判官 prompt 的输入。此外_strip_common_zero_width会剔除零宽字符,避免模型插入的隐形 Unicode 破坏字符串比对。

双轨断言体系:正确性硬校验 + 效率软记录

框架定义了两种断言基类:

  • SuccessAssertion(正确性):违反即通过pytest.fail硬失败测试;
  • EfficiencyAssertion(效率):只记录日志、永不让测试失败。

每个断言都实现check(trajectory) -> booldescribe_failure(trajectory) -> str,失败信息会附带完整轨迹打印,便于定位问题。

正确性断言(SuccessAssertion),工厂函数与字段如下:

断言核心字段语义
FinalTextContainstext,case_insensitive最终回复必须包含子串
FinalTextExcludestext,case_insensitive最终回复不得包含子串
FinalTextContainsAnytexts,case_insensitive最终回复至少包含一组等价措辞之一(如 "unknown"/"no data"/"n/a"),用于检验"诚实承认信息缺失"而非幻觉编造
FinalTextMinLengthn最终回复去除空白后至少 n 字符,用于过滤"凑字数的收尾话术"
FileEqualspath,content轨迹中的文件内容与期望完全一致
FileContains/FileExcludespath,substring文件包含/不包含某子串
FileAbsentpath文件路径必须不存在(比FileExcludes更严格——删除操作会把 key 从 files 通道中整个移除)
ToolCalledname,step,args_contains,args_equals轨迹中必须存在匹配的工具调用
ToolNotCalled同上轨迹中不得出现匹配的工具调用(硬失败版,用于拦截"无目标却反射性调用 update_goal"之类的行为)

工具调用的选择器有两条构造期防线(_validate_tool_call_selector):step必须为正(1-indexed),args_containsargs_equals互斥,避免歧义匹配让ToolNotCalled空转通过。

效率断言(EfficiencyAssertion)

断言字段语义
AgentStepsn轨迹恰好 n 个 agent step
ToolCallRequestsn工具调用请求总数恰好为 n
MaxToolCallRequestsn工具调用请求数至多 n,用于捕获"简单任务却 cargo-cult 规划工具"的退化
ToolCallname,step,args_contains,args_equals特定工具调用发生过(软记录)

TrajectoryScorer:两段式构建器

@dataclass(frozen=True) class TrajectoryScorer: _success: tuple[SuccessAssertion, ...] = () _expectations: tuple[EfficiencyAssertion, ...] = () def success(self, *assertions: SuccessAssertion) -> TrajectoryScorer: ... def expect(self, *, agent_steps=None, tool_call_requests=None, tool_calls=None) -> TrajectoryScorer: ...

.success()追加硬校验,.expect()追加效率期望,二者都返回新的 scorer(不可变风格)。_assert_expectations在断言执行时:先通过_log_efficiency把实际/期望的agent_stepstool_call_requests写入 LangSmith feedback(t.log_feedback),再依次执行成功断言,任一不满足即pytest.fail并记录correctness=0

run_agent / run_agent_async:统一入口

def run_agent(agent, *, query, model, initial_files=None, scorer=None, thread_id=None, eval_metadata=None, extra_state=None) -> AgentTrajectory:

run_agent封装了"构造输入 → 记录 LangSmith 输入 → invoke → 记录输出 → 构建轨迹 → 执行断言"的完整链路,支持同步/异步两个版本。extra_state允许注入中间件持有的状态(如{"rubric": "..."}RubricMiddleware使用),thread_id缺省时自动生成 UUID。每个 eval 测试本质上就是"创建一个 agent、调一次run_agent、挂一个TrajectoryScorer"。

扩展正确性手段:llm_judge.py 的 LLM-as-judge

当字符串断言无法覆盖语义标准时,llm_judge.py 提供了基于 openevals 的LLMJudge断言:

def llm_judge(*criteria: str, judge_model: str = _DEFAULT_JUDGE_MODEL, include_tool_calls: bool = False) -> LLMJudge:

关键设计:

  • 逐条独立判分:每个 criterion 通过create_llm_as_judge独立评估,所有 criterion 通过断言才算成功,任一失败即硬失败;
  • 并发评估:多个 criterion 通过ThreadPoolExecutor(最多 8 个 worker)并行调用,结果按下标归位,保证失败信息确定有序;单条失败不会短路其余评估;
  • 两种上下文include_tool_calls=False(默认)时判官只看 agent 的文本回复,适合评判"说了什么";True时判官看到完整轨迹(工具调用 + 文本),适合评判"做了什么"(如是否真的写了文件)。判官 prompt 明确提示"工具调用是真实执行过的动作,应视为行为证据";
  • 失败诊断describe_failure给出逐条判分注释;llm_judge_all_passed聚合分数回写 LangSmith;
  • 健壮性:轨迹为空、判官调用抛错、openevals 返回结构异常都会给出明确报错。

默认判官模型为claude-sonnet-4-6(见_DEFAULT_JUDGE_MODEL)。

结果落盘:pytest_reporter.py 与效率指标

pytest_reporter.py 是注册在conftest.py中的 pytest 插件(pytest_plugins = ["tests.evals.pytest_reporter"]),在会话结束后汇总并输出 JSON 报告。

核心行为

  • 改写 pytest 退出码pytest_sessionfinish中,当单个 eval 失败(exitstatus=1)但确有测试运行时,会把会话退出码改写为 0。这是有意为之——评测失败不应中断 CI 流水线,后续聚合/报告步骤必须照常执行;但如果一个测试都没跑(配置错误或收集崩溃),则保留非零退出码"响亮失败";
  • 效率数据收集:通过_evals_utils._on_efficiency_result回调接收每个测试的EfficiencyResult,并补记duration_spassed
  • LangSmith 实验链接:会话开始时若设置了LANGSMITH_TEST_SUITE,会预创建 LangSmith 实验并在终端输出公开/内部对比链接;
  • 失败明细:每个失败测试记录test_namecategoryfailure_message(超长信息截断到 30k 字符,约 7500 token),供后续--retry-failed复用。

报告中的关键指标

插件计算并通过终端与 JSON 报告输出的指标包括:

指标计算方式说明
correctnesspassed / total整体正确率
category_scoreseval_category分组的 passed/total分类正确率
step_ratiosum(actual_steps) / sum(expected_steps)步数效率比(无期望时为空)
tool_call_ratiosum(actual_tool_calls) / sum(expected_tool_calls)工具调用效率比
solve_ratepassed 且含步数期望与耗时的测试的 expected_steps/duration 均值,失败测试计 0求解速率
median_duration_s各测试 call 阶段耗时的中位数延迟表现

JSON 报告可通过--evals-report-file或环境变量DEEPAGENTS_EVALS_REPORT_FILE指定路径;报告包含created_atsdk_versionmodel、计数、上述指标、experiment_urlsexperiment_linksfailures数组。

数据资产:fixtures/ 与 data/

  • fixtures/:静态测试数据,例如summarization_seed_messages.json,供摘要类测试复用。
  • data/benchmark_samples/:三个精选外部基准子集的样本——frames_final.json(检索类)、nexus_final.json(推理类)、bfcl_v3_final.json(函数调用类)。external_benchmarks.py 从这些文件各选取 5 个高难度用例并断言数量,构成curated_external_hard子集:
    • FRAMES(检索/文件回退):_create_file_backed_agent用固定 system prompt 构造 agent,initial_files注入工作区文件,用"归一化子串存在"断言评分(忽略空白、大小写与引号变体);
    • Nexus(推理):同样采用文件回退 + 文本评分;
    • BFCL v3(有状态工具调用):data/bfcl_apis/下实现VehicleControlAPIMessageAPITradingBotTravelAPITicketAPI五个有状态 API 类。评测把 API 的公开方法包装为StructuredTool注入 agent,用MemorySaver做多轮对话,最后重放 ground truth 调用到全新实例上,逐属性对比模型实例与真值实例的最终状态——即"状态一致性"评分,比比对调用字符串更严格。

外部基准接入(一):memory_agent_bench/

memory_agent_bench/ 是 MemoryAgentBench(ICLR 2026)的评测运行器,核心是 configs.py 中的DatasetConfig数据类:

@dataclass(frozen=True) class DatasetConfig: split: str # HuggingFace 数据集 split,如 Conflict_Resolution source: str # 与数据集 metadata.source 匹配的来源标识 chunk_size: int = 4096 # 记忆阶段每个文本块的 token 预算 max_samples: int = 1 # 评估的上下文样本数上限 max_questions: int | None = None # 每个样本的提问数上限

配置按四个能力维度组织,每个维度配多档上下文长度或难度:

维度配置示例关注点
Conflict_Resolution(单跳/多跳)CR_SH_6K~CR_SH_262KCR_MH_6K~CR_MH_262K事实合并与冲突消解
Test_Time_Learning(ICL)TTL_BANKING77TTL_CLINIC150TTL_NLUTTL_TREC_COARSE/FINETTL_RECSYS测试期学习/上下文内分类
Accurate_RetrievalAR_RULER_QA1/QA2AR_LONGMEMEVALAR_EVENTQA_FULL/64K/128K精确检索(含多跳与时间推理)
Long_Range_UnderstandingLRU_INFBENCH_SUMLRU_DETECTIVE_QA长程理解(生成与推理问答)

ALL_CONFIGS汇总全部 23 个配置;CI_CONFIGS提供面向 CI 的高信号子集(每类 2 个、偏向更难变体,注释中给出了如"SH@64K 在合理上下文长度下梯度良好""MH@6K 是最便宜的近零 canary"等选型理由)。配套的 data_utils.py 负责从 HuggingFace 拉取数据并按配置切分,eval_utils.py 提供评测逻辑,test_memory_agent_bench.py 将其接入 pytest。

外部基准接入(二):tau2_airline/

tau2_airline/ 是 tau2-bench 航空领域的 vendored 实现(源自 Sierra Research 的 tau-bench,MIT License)。目录下的data/db.jsonpolicy.mdtasks.json为领域数据,且按约定必须与上游逐字节一致(不得重新格式化,详见 libs/evals/AGENTS.md)。

其核心是多轮对话编排器 runner.py:run_multi_turn让 deepagents agent 与 LLM 驱动的用户模拟器(UserSimulator)来回对话,最多 30 轮(DEFAULT_MAX_TURNS),记录完整 transcript、工具调用日志与终止原因(user_stop/max_turns):

for turn in range(max_turns): trajectory = run_agent(agent, query=user_msg, model=model, thread_id=thread_id) agent_msg = trajectory.answer ... user_msg = user_sim.respond(agent_msg)

配合 domain.py(领域模型与工具)、user_sim.py(用户模拟器)、evaluation.py(结果评分),test_tau2_airline.py 将整套流程接入评测套件。这种"agent 对打 LLM 模拟用户"的范式,能覆盖自由对话式任务,弥补单轮 prompt 评测的不足。

如何运行这套评测

虽然tests/evals本质上是 pytest 测试,仓库推荐通过规范入口deepagents-evals(console script,定义于 libs/evals/pyproject.toml 的[project.scripts])运行,详见 libs/evals/AGENTS.md:

# 单模型单次运行 deepagents-evals run --model claude-opus-4-7 # 按类别与层级过滤,输出 JSON 报告 deepagents-evals run --model openai:gpt-5.5 \ --eval-category memory --eval-tier baseline --report evals_report.json # 多次运行并聚合统计 deepagents-evals trials --model openai:gpt-5.5 --trials 3 # 只重跑上次失败的用例 deepagents-evals trials --model openai:gpt-5.5 --trials 1 \ --retry-failed trial_runs/trials_summary.json

CLI 还提供list(发现类别/层级/模型/eval)、aggregate(聚合离线报告)、radar(生成雷达图)、catalog/model-groups(检查生成文档)等子命令;DEEPAGENTS_EVALS_MODEL环境变量可省略--model。运行前置条件与 pytest 层一致:必须开启 LangSmith 追踪(LANGSMITH_TRACING=true+LANGSMITH_API_KEY),并配置与所选模型匹配的 provider key。make evals MODEL=...make evals-trials MODEL=... TRIALS=...仍可用(CI 采用的形式),CLI 是其超集。

CLI 的退出码语义是自动化接入的关键:0表示成功;1表示存在评测失败(由trials_summary.json中聚合的counts.failed.mean > 0判定,而非单次 pytest 退出码——因为 pytest_reporter 会把单次运行的退出码改写成 0);2表示配置错误或生成文档过期;3表示没有可用报告。请勿解析人类可读输出,应以退出码与trials_summary.json驱动自动化。聚合报告的 JSONC 结构(metricscountscategory_scorestrials各字段及每 trial 的experiment_urlspytest_returncode)均记录在 libs/evals/AGENTS.md 中,可供读者直接参考对接。

延伸阅读

  • libs/evals/tests/evals/README.md——本套件的目录索引与组件清单
  • libs/evals/tests/evals/utils.py——轨迹与断言核心框架
  • libs/evals/tests/evals/conftest.py——pytest 集成与模型夹具
  • libs/evals/tests/evals/pytest_reporter.py——效率报告插件
  • libs/evals/tests/evals/external_benchmarks.py——FRAMES/Nexus/BFCL v3 精选子集评测
  • libs/evals/AGENTS.md——deepagents-evalsCLI 用法、退出码与报告 schema
  • libs/evals/README.md——评测套件总览
  • libs/evals/EVAL_CATALOG.md、libs/evals/MODEL_GROUPS.md——完整 eval 清单与模型分组目录

【免费下载链接】deepagentsThe batteries-included agent harness.项目地址: https://gitcode.com/GitHub_Trending/de/deepagents

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询