首页
/ Deep Agents 仓库测试策略与变更验证:从包内单元测试到 CI 扇出与真实模型评估

Deep Agents 仓库测试策略与变更验证:从包内单元测试到 CI 扇出与真实模型评估

2026-09-09 20:14:03作者:丁柯新Fawn

本篇指南系统讲解 Deep Agents 多包仓库(monorepo)的测试与变更验证方法论:如何为 SDK、dcode CLI、ACP、Talon 主机与 evals 评估套件选择最合适的测试边界,如何借助各包 Makefile 运行确定性单测、集成测试、基准测试与真实模型评估,以及如何利用 CI 依赖扇出和发布检查验证跨包改动。读完你可以掌握在 libs/ 下任意包内跑对测试、写对断言、并安全合并跨包变更的完整工作流。

总体原则:在行为所属的包内工作

Deep Agents 仓库由多个独立版本化的包组成(核心 SDK、ACP、evals、code、talon 与 partners 系列),每个包都拥有自己的环境、pyproject.tomlMakefile。测试的第一原则是:在拥有被改行为的那个包内工作,不要跨包写“越权”测试。

开发时的标准起点是 开发运营 中描述的安装流程:

cd libs/deepagents
uv sync --all-groups   # 需要全部依赖组时使用 --all-groups
make help              # 以包内 Makefile 作为命令的权威来源

仓库没有根级 pyproject.toml,也不存在全局 Python 版本,uv 会按每个包 requires-python 自动准备解释器。包间依赖是可编辑(editable)的,因此开发期间一个包的改动对依赖它的兄弟包立即可见——这既是便利,也是变更验证必须覆盖跨包消费者的原因。包归属与依赖关系可参考 源码地图

写测试时的核心主张:从最接近的既有测试出发,断言用户可观察的行为,而不是某个实现内部“恰好”的调用或顺序。mock 被刻意淡化,能用真实行为验证就不用 mock;当某个场景的约定不明确时,先读附近的既有测试再动手。

选择最小且有意义的测试边界

不同的变更表面对应不同的测试位置和首条命令。文档给出了一张可直接照搬的决策表:

变更表面 测试位置与首条命令 何时升级
Deep Agents SDK libs/deepagents/tests/unit_tests/make test TEST_FILE=tests/unit_tests/middleware/test_foo.py 契约需要可选依赖、provider 或网络行为时,改用 tests/integration_tests/
dcode CLI libs/code/tests/unit_tests/make test TEST_FILE=tests/unit_tests/test_agent.py 可执行文件、子进程、ACP 传输、沙箱或 provider 本身是被测行为时,改用 make integration_test
ACP 扁平结构 libs/acp/tests/make test TEST_FILE=tests/test_agent.py 协议行为用 client double 保持确定性,除非互操作本身需要外部对端
Talon 主机 libs/talon/tests/make test TEST_FILE=tests/test_data_lifecycle.py 套件含 tests/integration_tests/,但那些仍是本地主机编排测试,不是自动连真实服务的测试
Eval 套件 libs/evals/tests/unit_tests/make test TEST_FILE=tests/unit_tests/ 真实模型的行为或质量在被测时,通过 eval CLI 或 Makefile 目标调用 tests/evals

对 SDK 源码要镜像源码布局:为 deepagents/middleware/foo.py 写的测试应当放在 tests/unit_tests/middleware/test_foo.py。ACP 和 Talon 使用各自的包内组织方式,不要强行套用 SDK 的布局。

整个决策路径可以用下面的流程图概括(原文 Mermaid 图):

flowchart TD
    Change["Change behavior"] --> External{"Does the behavior cross an external boundary"}
    External -->|"No"| Unit["Focused package unit or component test"]
    Unit --> Normal["Normal target with socket protection"]
    External -->|"Process or provider"| Integration["SDK or dcode integration test"]
    External -->|"Model quality"| EvalRun["Traced real-model eval"]
    External -->|"Sandbox runtime"| Harbor["Harbor runtime-host run"]
    Integration --> Contract["Process or network contract"]
    EvalRun --> Report["Experiment and aggregate report"]
    Harbor --> Sandbox["Selected sandbox environment"]

这条路径把确定性正确性检查(单元/组件测试)与进程或 provider 契约(集成测试)、随机模型评估(evals)和沙箱运行时实验(Harbor)清晰地区分开来。

包命令与套件边界

Deep Agents 与 dcode 的 make test 默认指向各自单测树。两个包的常规目标都启用 xdist(-n auto)、禁用 benchmark、并阻止非 Unix socket;其 make integration_test 目标选择 tests/integration_tests/、允许网络访问并施加 30 秒超时。ACP 的常规目标运行其扁平 tests/ 树,带 socket 拦截和 10 秒超时。Talon 的常规目标先运行 WhatsApp bridge 的 Node 测试,再运行其 socket 拦截的 Python 树,同样 10 秒超时。

这些约束都能在各包 Makefile 中得到印证,例如 libs/deepagents/Makefile 中:

test: ## Run unit tests
	uv run --group test pytest -n auto -vvv $(PYTEST_EXTRA) --disable-socket --allow-unix-socket $(TEST_FILE) \
		--benchmark-disable \
		$(COV_ARGS)

integration_test: ## Run integration tests
	uv run --group test pytest -n auto -vvv --timeout 30 --benchmark-disable $(TEST_FILE)

五个包的套件命令一览(原文):

cd libs/deepagents
make test TEST_FILE=tests/unit_tests/middleware/test_foo.py
make integration_test

cd ../code
make test TEST_FILE=tests/unit_tests/test_agent.py
make integration_test

cd ../acp && make test TEST_FILE=tests/test_agent.py
cd ../talon && make test TEST_FILE=tests/test_data_lifecycle.py
cd ../evals && make test TEST_FILE=tests/unit_tests/

实操建议:先用 TEST_FILE 让第一次运行范围变窄,在依赖该改动之前再跑拥有该行为的包的常规目标。Socket 拦截只能抓住意外的服务调用,但要让断言真正确定,仍然需要受控的 fake、临时文件系统或固定时间——这三者缺一不可。

异步与告警策略

五个包的 pytest 配置全部使用 asyncio_mode = "auto",因此异步测试不需要 @pytest.mark.asyncio 也能运行。dcode 在 pyproject.toml 中额外启用严格 markers 与严格配置(--strict-markers --strict-config --durations=5)、30 秒默认测试超时,以及函数作用域的异步 fixture 循环。不要为迁就新测试而削弱这些约束。

告警即错误(warnings as errors) 是跨包的硬性约定:每个包都把 "error" 放在 pytest filterwarnings 的第一位,其后条目是经过评审的 allowlist。因此任何未被接受的告警都会导致失败——在测试内触发则该测试失败,import 时触发则 collection 失败,pytest 配置期(常见于插件)触发甚至可能以 INTERNALERROR 中止整个运行。正确做法是先修复可处理的告警;若某个预期告警确实无法避免,用 @pytest.mark.filterwarnings 把它限定在单个测试内,包级配置只留给有充分理由的类别性/第三方例外。allowlist 的注释风格可见 libs/deepagents/pyproject.toml,例如 "ignore:Passing \model=None` to `create_deep_agent`:DeprecationWarning"` 这类带模块限定的条目。

CI 为主维护者保留逃生舱:带 bypass-warnings-check 标签的 PR 可以 -W default 运行 pytest。该可复用 workflow 实时读取标签,读取失败时按失败关闭(fail closed);push 与 merge-group 运行没有 PR 标签上下文,永远强制告警即错误。请把这个标签视为临时分诊手段,而不是“这条告警可接受”的背书。

保护行为的测试缝(Test Seams)

文档明确了各包为保证并行确定性而维护的测试缝:

  • Deep Agents 的单元 fixture 在每条测试前重置 deprecation 告警去重状态和缓存的视频依赖探测结果,并在每个 session 启动一次内置 profiles。当新增进程级全局缓存、惰性注册表或 monkeypatch 依赖探测的测试时,要保留或扩展这些重置点——并行执行不能让观察结果依赖测试顺序
  • ACP 的 FakeACPClient 记录 session 更新与权限请求,使协议断言可以覆盖输出和权限决策而无需真实客户端。
  • Talon 的 RecordingChannel 记录输出与生命周期调用,并在消息处理器注册前拒绝注入输入;其命名集成流使用内存 channel 和脚本化 agent,就是为了在无 channel 服务的情况下演练主机生命周期与路由。
  • dcode 的集成树以独立启动的可执行文件为契约:ACP 冒烟测试通过 stdin/stdout 启动 deepagents --acp --no-mcp,初始化协议、创建 session,并在清理阶段终止子进程——进程内单测无法建立这种“可执行文件到协议”的边界。

基准测试是独立的性能信号

基准测试与正确性测试是两套完全不同的信号,不要把性能测量塞进普通正确性测试里仅仅为了让它随 make test 跑。Deep Agents 把基准放在 tests/benchmarks/,dcode 从 tests 中选择带 benchmark marker 的测试;两者都让常规测试目标保持无基准,并提供专门的测量目标:

make benchmark      # pytest benchmark marker
make bench          # benchmark marker under CodSpeed
make bench-memory   # memory_benchmark marker under CodSpeed

在仓库层面,make -C libs bench-all 会为 Deep Agents 与 dcode 各跑一遍 bench(QuickJS 也有包级基准目标,但不在该扇出目标内)。这些 Makefile 目标是基准调用的唯一权威来源——本地与 CI 的复用 workflow 都调用它们,改基准方式就改 Makefile,CI 自动继承。

真实模型评估与 Harbor

libs/evals 把普通 socket 拦截测试命令放在 tests/unit_tests 上;真实模型评估位于 tests/evals,运行要求开启 tracing 且显式传入 --modeldeepagents-evals CLI 是单次运行、重复试验、报告聚合、图表、目录/模型组维护与发现的统一入口,runtrials 可以从 --model 或环境变量 DEEPAGENTS_EVALS_MODEL 取模型。从源码看,该 CLI 定义在 libs/evals/deepagents_evals/cli.py 中,子命令包括 runtrialsaggregateradarcatalogmodel-groupslist,并且统一了退出码:0 成功、1 eval 失败、2 配置错误(如缺 --model)、3 无可用报告。

原文给出的完整运行示例:

cd libs/evals
export LANGSMITH_TRACING=true
export LANGSMITH_API_KEY=...
export DEEPAGENTS_EVALS_MODEL=<model-id>

deepagents-evals list categories
deepagents-evals run
deepagents-evals trials --trials 3

# Makefile alternatives
make evals MODEL=<model-id>
make evals-trials MODEL=<model-id> TRIALS=3

细节要点(均有源码印证):

  • 类别与层级过滤会拒绝不在已收集测试中的取值,且类别排除优先于包含。
  • 报告器输出总计、按类别的结果、失败项、耗时、实验链接与效率数据。由于报告器在写完报告后可能重写测试 session 的失败退出状态(pytest_reporter 会把 session.exitstatus 重写为 0),重复实验请使用 CLI 的聚合结果判断成败:trialsaggregatecounts.failed.mean 非零时失败(对应 cli.pyEXIT_EVAL_FAILURES = 1 的语义)。
  • Harbor 目标是运行时主机实验,不是包的 pytest 集成测试。stage-harbor-local-deps 会先把检出的 SDK、dcode、ACP、QuickJS 源码暂存到 sandbox 运行目录(见 libs/evals/Makefile),再进行选定的 Docker、Modal、Daytona、Runloop 或 LangSmith sandbox 运行。Harbor 的 LangGraph agent 在执行 shell 操作时会从环境中剥离 provider 与 LangSmith 凭据——改动 agent 到 sandbox 的交接时必须保留这一秘密边界。操作流程见 运行评估

跨包、CI 与发布验证

兄弟包可能通过可编辑本地依赖接收 SDK 改动,因此既要验证被改的包,也要验证直接消费者。CI 编码了最小扇出:SDK 改动会触发 Deep Agents、dcode、ACP、Talon、evals 以及可编辑安装它的 partner 包过滤器;dcode 改动还会触发 Talon;workflow/action 基础设施改动包含在每个包过滤器中。在 PR 上只有匹配的包任务运行;推送到 main 则运行完整包 CI 集合。

CI 常规单元矩阵同时定义了兼容性预期:

Python 版本矩阵
Deep Agents、ACP 3.11 – 3.14(Deep Agents 另加 Windows 3.13 leg)
dcode、Talon 3.12 – 3.14
evals 3.12、3.13

实操顺序:先跑本地所属包测试,合并前再确认受影响的消费者与受支持的平台行为已被覆盖。

依赖或 lockfile 改动时,在 libs/ 下运行仓库级检查:

make -C libs lock-check
make -C libs lint

发布敏感的 SDK 改动前,验证 libs/code/pyproject.toml 中精确的 deepagents== 版本钉扎:当 dcode 需要新 SDK 功能时,必须在同一改动里同步 bump 该钉扎。发布 PR 是包级别的,合并一个 PR 即在必检通过后发布该包;因此要测试发布消费方路径,而不只是产出方包。完整发布流程见 开发运营

变更验证清单

将上面的策略浓缩为六步清单,作为每次改动合并前的自检:

  1. 识别可观察行为、边界与失败模式;写测试前先检查最近的既有测试。
  2. TEST_FILE 运行最窄的邻近测试,再跑所属包的常规目标。
  3. 保持常规覆盖确定:重置全局状态、使用临时路径与固定时间,让 double 记录可观察的输出与生命周期事件。
  4. 仅在必要时升级:进程/provider 契约用集成测试,真实模型质量用 evals,沙箱主机行为用 Harbor。绝不用基准测试充当正确性测试。
  5. 对共享 SDK、dcode、workflow、依赖或发布改动,运行受影响的消费方包与仓库扇出检查;相关时验证 dcode 的 SDK 钉扎。
  6. 解决告警而非放宽过滤;把 bypass-warnings-check 视为临时分诊,以告警即错误作为最终闸门。

这套方法论的核心是“在正确边界上运行正确的信号”:用 socket 拦截和固定时间保住确定性,用集成测试验证进程与网络契约,用 evals 度量随机模型质量,用 Harbor 演练沙箱运行时——每一类信号各有其位,互不越界。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
docsdocs
暂无描述
Markdown
900
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
927
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.94 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
603
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
396
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.04 K
527