Dify Agent(dify-agent)包开发规范:标准命令、Docstring 本地契约与测试边界实践
Dify 仓库中的 dify-agent 包承载了 Dify Agent 运行时——在 FastAPI 服务背后托管由 Agenton 组合的 Pydantic AI 运行实例。本文基于该包的开发者指南 dify-agent/AGENTS.md,完整解读其文档字符串(docstring)本地契约规则、四个标准包命令的底层实现、uv 环境与 Pydantic v2 的依赖约束,以及"测试稳定行为、守护真实依赖边界"的测试组织原则,并结合 Makefile、pyproject.toml 与导入边界测试源码给出可验证的实现证据,帮助你在修改 dify-agent 包时严格遵循其既定边界而不会破坏客户端安全导入等核心不变量。
dify-agent 包与其开发者指南的定位
dify-agent 包同时面向两类使用者:作为库被 dify_agent.Client 客户端直接导入的调用方,以及以 dify_agent.server.app:app 形式运行的 Agent 后端服务。按 dify-agent/README.md 的说明,框架无关的 Agenton 代码位于 src/agenton 与 src/agenton_collections,而 Dify 特化的运行时源码位于 src/dify_agent,两者文档分别见 docs/agenton/guide/index.md 与 docs/dify-agent/index.md。
dify-agent/AGENTS.md 是这个包面向 AI 编码助手与人类开发者的行为守则,全文虽短,但规定了四层约束:docstring 契约、标准命令、环境要求、测试与边界原则。以下逐一展开。
Docstring 与非显式注释是"本地契约"
AGENTS.md 开篇给出的规则是:在修改行为之前,先阅读周边的 docstring 和非显而易见的注释;它们是本地契约,只应在其所拥有的行为(owned behavior)真正变化时才更新,并且必须与当前代码保持对齐;当改动触及公共运行时契约时,还应阅读 docs/dify-agent/index.md。
这条规则在源码中有直接体现。以 src/dify_agent/init.py 为例,其模块 docstring 声明了"客户端安全顶层导出"契约:
"""Client-safe top-level exports for the Dify Agent package.
Default installs must be able to import ``dify_agent`` without pulling in server
runtime adapters or their optional dependencies. Server-only adapter entry points
remain under ``dify_agent.adapters.llm``.
"""
该契约的含义是:默认安装下导入 dify_agent 不得拖入服务端运行时适配器及其可选依赖(如 FastAPI、Redis、各 LLM 供应商 SDK)。docstring 不只是说明文字,而是与导入边界测试共同构成可执行的约定——一旦行为变更,docstring 必须同步更新,否则契约与实际代码脱节。
四个标准包命令及其底层实现
AGENTS.md 要求所有包命令在 dify-agent/ 目录下执行,并给出四个核心命令。对照 dify-agent/Makefile 可以看到每个目标的确切动作:
| 命令 | 用途 | Makefile 中的实际执行 |
|---|---|---|
make check |
Lint 检查 | uv run --project . python -m ruff check . |
make fix |
格式化并自动修复 lint | 先 ruff format .,再 ruff check --fix . |
make typecheck |
类型检查 | basedpyright --level error src examples tests |
make test |
运行本地测试与文档/示例测试 | uv run --project . --extra server python -m pytest tests |
几个值得注意的实现细节:
- 所有命令都通过
uv --directory "$(PROJECT_DIR)" run --project .执行,即强制使用包自带的 uv 虚拟环境,避免系统解释器混入; typecheck使用 basedpyright 且级别为--level error,检查范围精确限定为src、examples、tests三个目录,与 pyproject.toml 中[tool.pyright]的include = ["src", "examples", "tests"]一致;make test显式带--extra server安装服务端可选依赖,因为tests目录中既有tests/local/本地测试,也有tests/docs/下校验文档示例输出的测试(test_examples.py、test_snippets.py);- Makefile 还提供
serve/dev(uvicorn 启动dify_agent.server.app:app,8000 端口)、docs/docs-serve(MkDocs 构建,见 mkdocs.yml)等运维与文档目标,但 AGENTS.md 列出的四个命令才是日常开发闭环的最小集合。
另外,pyproject.toml 中 [tool.pytest.ini_options] 配置了 addopts = ["--import-mode=importlib"],注释解释了原因:多个测试模块在不同目录共享同名文件名(如 test_layer.py、test_client.py),默认 prepend 导入模式按 basename 推导模块名会导致导入文件不匹配。因此运行 pytest 时若手动执行,也应带上 --import-mode=importlib(Makefile 的 make test 已通过 ini 选项覆盖)。
uv 环境与 Pydantic v2 的硬性约束
AGENTS.md 明确要求:使用包自己的 uv 环境,并使用 Pydantic v2 API。这一约束在 dify-agent/pyproject.toml 中有对应的硬边界:
requires-python = ">=3.12,<4.0",且 ruff 配置target-version = "py312",即代码只允许使用 Python 3.12 语法特性;- 核心依赖锁定为
pydantic>=2.12.5,<2.13(v2 系)、pydantic-ai-slim>=2.30.0,<3.0.0、httpx==0.28.1等; - 服务端可选依赖(
[server]extra)包含fastapi==0.136.0、redis、uvicorn[standard]、e2b、logfire等——这正是导入边界测试要隔离的对象; - 开发依赖组(
dev)含ruff、basedpyright、pytest、pytest-mock、pytest-examples,文档组(docs)含mkdocs-material等。
AGENTS.md 还有一条容易被忽视的要求:在集成、实现或 mock 某个运行时契约尚未在本仓建立的 API 之前,先检查该依赖的当前源码或官方文档。这意味着不允许凭记忆假设第三方库行为,尤其在 mock 边界上——mock 的字段、方法签名必须对照依赖的真实契约核实。
测试组织:镜像 src 结构,只测稳定行为
AGENTS.md 的 "Tests And Boundaries" 一节给出四条原则,每条都能在仓库中找到对应的落地机制:
1. 本地测试放在 tests/local/ 下,并镜像 src/ 包结构。 实际目录 dify-agent/tests/local/ 严格对应源码分层:tests/local/agenton/、tests/local/agenton_collections/、tests/local/dify_agent/(下含 client/、layers/、runtime/、server/、storage/、agent_stub/ 等子包)、tests/local/shellctl/,与 src/ 下的顶层包一一对应。
2. 测试稳定行为和真实依赖边界,不得用本地 mock 声称覆盖了真实网络、框架接线、序列化或第三方运行时。 这一原则通过 pytest marker 在机制上得到区分:pyproject.toml 定义了 markers = ["integration: requires a real external service or exercises multiple concrete adapters"],需要真实服务的测试统一放在 tests/integration/ 目录(如 run_local_integration.sh、test_working_environment.py、test_e2b_transport_soak.py),与 make test 默认运行的 tests 路径中的本地测试隔离。
3. 测试、公共文档与本地契约随行为变更保持一致。 文档侧的示例输出由 tests/docs/test_examples.py 校验,make update-examples 目标(pytest --update-examples tests/docs/test_examples.py)用于重写文档中的示例输出,保证 docs/dify-agent/ 下文档与行为不脱节。
4. 保留现有运行时与层(layer)的所有者,不要为绕过它们而添加通用工具或兼容边界。 这条"架构防腐"原则由 tests/local/dify_agent/test_import_boundaries.py 以自动化方式强制。该文件实现了 _run_import_check 助手:在子进程中用 guarded_import 包装 builtins.__import__,对一组"被封禁"的模块(命中即抛 ModuleNotFoundError)设置拦截,然后在干净命名空间里导入待测模块并执行断言。以 test_dify_agent_root_import_is_client_safe 为例,封禁列表包括 fastapi、redis、openai、anthropic、google、pydantic_settings、dify_agent.server、dify_agent.runtime 等服务端模块,随后断言 from dify_agent import Client 成立且 'Client' in dify_agent.__all__——这正是前文 docstring 契约的可执行验证。类似地,test_protocol_and_dify_plugin_exports_do_not_import_server_only_modules 验证各 layer 包(dify_agent.protocol、dify_agent.layers.*)的导出配置类在封禁服务端模块的前提下仍可导入;test_agenton_collection_roots_do_not_eagerly_import_pydantic_ai_implementations 则验证 agenton_collections 根不会急加载 Pydantic AI 实现。这些边界测试就是"不得添加通用工具或兼容边界绕过既有 owner"的守门员:任何试图在 dify_agent 顶层或 protocol/layer 包中悄悄引入服务端依赖的重构,都会在 make test 中直接失败。
小结
dify-agent/AGENTS.md 虽然篇幅精炼,但它定义了 dify-agent 包完整的质量闭环:docstring 作为本地契约约束"改代码先读契约";make check / make fix / make typecheck / make test 四个命令(dify-agent/Makefile)覆盖 lint、格式化、basedpyright 类型检查与测试;uv 环境与 Pydantic v2、Python 3.12 的锁定约束(dify-agent/pyproject.toml)划清依赖边界;tests/local/ 镜像 src/ 的结构、真实边界测试外置 tests/integration/、以及 test_import_boundaries.py 的导入封禁机制,共同保证客户端安全导出、运行时与 layer 的所有权不被侵蚀。遵循这些约定,对 dify-agent 的修改才能既通过全部本地检查,又不破坏其作为库与作为服务双重身份之间的依赖隔离。
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 StartedRust0626
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00