Deep Agents 仓库测试策略与变更验证:从包内单元测试到 CI 扇出与真实模型评估
本篇指南系统讲解 Deep Agents 多包仓库(monorepo)的测试与变更验证方法论:如何为 SDK、dcode CLI、ACP、Talon 主机与 evals 评估套件选择最合适的测试边界,如何借助各包 Makefile 运行确定性单测、集成测试、基准测试与真实模型评估,以及如何利用 CI 依赖扇出和发布检查验证跨包改动。读完你可以掌握在 libs/ 下任意包内跑对测试、写对断言、并安全合并跨包变更的完整工作流。
总体原则:在行为所属的包内工作
Deep Agents 仓库由多个独立版本化的包组成(核心 SDK、ACP、evals、code、talon 与 partners 系列),每个包都拥有自己的环境、pyproject.toml 和 Makefile。测试的第一原则是:在拥有被改行为的那个包内工作,不要跨包写“越权”测试。
开发时的标准起点是 开发运营 中描述的安装流程:
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 且显式传入 --model。deepagents-evals CLI 是单次运行、重复试验、报告聚合、图表、目录/模型组维护与发现的统一入口,run 与 trials 可以从 --model 或环境变量 DEEPAGENTS_EVALS_MODEL 取模型。从源码看,该 CLI 定义在 libs/evals/deepagents_evals/cli.py 中,子命令包括 run、trials、aggregate、radar、catalog、model-groups 与 list,并且统一了退出码: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 的聚合结果判断成败:trials与aggregate在counts.failed.mean非零时失败(对应 cli.py 中EXIT_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 即在必检通过后发布该包;因此要测试发布消费方路径,而不只是产出方包。完整发布流程见 开发运营。
变更验证清单
将上面的策略浓缩为六步清单,作为每次改动合并前的自检:
- 识别可观察行为、边界与失败模式;写测试前先检查最近的既有测试。
- 用
TEST_FILE运行最窄的邻近测试,再跑所属包的常规目标。 - 保持常规覆盖确定:重置全局状态、使用临时路径与固定时间,让 double 记录可观察的输出与生命周期事件。
- 仅在必要时升级:进程/provider 契约用集成测试,真实模型质量用 evals,沙箱主机行为用 Harbor。绝不用基准测试充当正确性测试。
- 对共享 SDK、dcode、workflow、依赖或发布改动,运行受影响的消费方包与仓库扇出检查;相关时验证 dcode 的 SDK 钉扎。
- 解决告警而非放宽过滤;把
bypass-warnings-check视为临时分诊,以告警即错误作为最终闸门。
这套方法论的核心是“在正确边界上运行正确的信号”:用 socket 拦截和固定时间保住确定性,用集成测试验证进程与网络契约,用 evals 度量随机模型质量,用 Harbor 演练沙箱运行时——每一类信号各有其位,互不越界。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0634
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
jforgamejforgame是一个一站式游戏服务器开发框架。包含游戏服务器开发所需要的各种组件,比如网关,socket服务端与客户端,自定义高效消息编解码,游戏热更新,游戏通用工具等等。包含游戏服,跨服,匹配服,后台管理系统等实现,同时提供大量业务案例以供学习。亦可用于其他socket应用,例如及时聊天等。Java01
fizz-gateway-nodeAn Aggregation API Gateway in Java . FizzGate 是一个基于 Java开发的微服务聚合网关,是拥有自主知识产权的应用网关国产化替代方案,能够实现热服务编排聚合、自动授权选择、线上服务脚本编码、在线测试、高性能路由、API审核管理、回调管理等目的,拥有强大的自定义插件系统可以自行扩展,并且提供友好的图形化配置界面,能够快速帮助企业进行API服务治理、减少中间层胶水代码以及降低编码投入、提高 API 服务的稳定性和安全性。Java00
certd开源SSL证书管理工具;全自动证书申请、更新、续期;通配符证书,泛域名证书申请;证书自动化部署到阿里云、腾讯云、主机、群晖、宝塔;https证书,pfx证书,der证书,TLS证书,nginx证书自动续签自动部署JavaScript00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00