Dify 的 CLAUDE.md / AGENTS.md:为 AI 编程代理编排大型 monorepo 的分层工程指南
Dify 仓库根目录下的 CLAUDE.md 是一份写给 AI 编程代理(Claude Code 及其他遵循 AGENTS.md 约定的工具)看的"工程操作手册":它声明仓库构成、三条仓库级禁忌(gotchas)和前端工作流的引用入口。本文以该文档为核心,逐条结合仓库中真实存在的 Makefile、api/pytest.ini、api/pyproject.toml 和各级子目录 AGENTS.md 进行溯源与展开,读完后你可以完整掌握这套"根级 + 作用域级"分层代理指南是如何约束命令执行、测试策略与环境变量组织的,并能把它移植到自己的 monorepo 中。
1. CLAUDE.md 与 AGENTS.md:一个文件,两个入口
在仓库根目录下,CLAUDE.md 并不是一个独立文件,而是指向 AGENTS.md 的符号链接:
$ ls -la CLAUDE.md AGENTS.md
-rw-r--r-- 1 root root 995 AGENTS.md
lrwxrwxrwx 1 root root 9 CLAUDE.md -> AGENTS.md
这一做法的用意很直接:用同一份事实来源同时服务两类消费者——Claude 系工具读取 CLAUDE.md,而 Cursor、Codex 等遵循 AGENTS.md 约定的工具读取 AGENTS.md,二者永远一致,避免维护两份指南产生漂移。
根级指南全文非常短,只包含三部分:一段项目与仓库构成的总述、一节 Repository Gotchas(仓库级陷阱清单),以及一节 Frontend Workflow(前端工作流)。下面逐条拆解。
2. 仓库总述:六个一级子系统
根指南开宗明义地介绍了 Dify 是什么、这个 monorepo 里有什么:
Dify is an open-source platform for building LLM applications, agentic workflows, and RAG pipelines. This monorepo contains the backend API (
api/), frontend application (web/), deployment assets (docker/), standalone agent backend (dify-agent/), CLI (cli/), and end-to-end suite (e2e/).
对照仓库实际目录结构,可以补全一张"地图",帮助任何代理(或新人)快速建立空间感:
| 路径 | 职责 | 关键证据 |
|---|---|---|
api/ |
Flask 后端 API,含 controllers/、services/、core/、tasks/、migrations/ |
api/AGENTS.md 中的架构边界约定 |
web/ |
Next.js 前端应用,i18n 资源位于 web/i18n/ |
web/AGENTS.md 的包契约 |
docker/ |
Docker Compose 部署资产与环境变量模板 | docker/docker-compose.yaml、docker/envs/ |
dify-agent/ |
独立 Agent 运行时(Python) | dify-agent/AGENTS.md |
dify-agent-runtime/ |
沙箱运行时(Go) | Makefile 的 build-sandbox-runtime |
cli/ |
difyctl TypeScript CLI |
cli/AGENTS.md |
e2e/ |
Cucumber + Playwright 端到端套件 | e2e/AGENTS.md |
packages/contracts/ |
前后端共享的生成式 API 契约(@dify/contracts) |
e2e/AGENTS.md 中"regenerate @dify/contracts" |
sdks/nodejs-client/、sdks/php-client/ |
对外 SDK | sdks/README.md |
总述之后紧跟的是根指南最重要的一句路由规则:
Follow the nearest scoped
AGENTS.mdfor the files being changed.(改动哪些文件,就遵循离它们最近的那份AGENTS.md。)
从源码结构看,这套"就近路由"在当前仓库中对应的完整作用域文件集合为:
AGENTS.md # 根级
api/AGENTS.md
cli/AGENTS.md
cli/src/commands/AGENTS.md
dify-agent/AGENTS.md
e2e/AGENTS.md
e2e/features/agent-v2/AGENTS.md
packages/dify-ui/AGENTS.md
web/AGENTS.md
web/features/agent-v2/AGENTS.md
分层的收益在于:根级文件保持极小、不含易过时的细节;子级文件携带模块专属契约;代理按"改动路径"就能命中正确的约束集合,而不需要通读全部文档。
3. Gotcha 一:后端命令一律经由 uv run --project api
根指南的第一条 gotcha:
Run backend commands through
uv run --project api <command>.
这句话的底层原因是 api/ 被隔离为一个独立的 Python 项目:它拥有自己的 api/pyproject.toml 与 api/uv.lock。如果直接用系统解释器执行 python/pytest,依赖环境与版本都无法保证。因此所有后端工具链都必须经过 uv run --project api 前缀进入该项目的虚拟环境。
根级 Makefile 中的每个目标都严格践行了这条规则,可以直接复制使用:
- 格式化:
make format→uv run --project api --dev ruff format ./api - 静态检查:
make check→uv run --project api --dev ruff check ./api - 完整 Lint:
make lint→ 依次执行 ruff format、ruff check --fix、Flask 响应契约 lint(api/dev/lint_response_contracts.py)、lint-imports导入边界检查,以及dotenv-linter ./api/.env.example ./web/.env.example——最后一步会校验前后端两份.env.example之间变量的一致性,这正是下一节环境变量策略的机器化保障 - 类型检查:
make type-check→./dev/pyrefly-check-local加上 mypy,其中 mypy 显式排除了conftest.py、tests/、migrations/与 swagger 生成脚本,并启用--check-untyped-defs(见 Makefile) - 开发环境一键搭建:
make dev-setup串联三步——prepare-docker(从 docker/envs/middleware.env.example 复制出docker/middleware.env并启动docker-compose.middleware.yaml中间件)、prepare-web(复制web/.env.example为web/.env.local并pnpm install)、prepare-api(复制api/.env.example为api/.env、uv sync --dev、uv run flask db upgrade升级数据库)(见 Makefile)
4. Gotcha 二:集成测试是 CI-only,本地跑单测
第二条 gotcha:
Backend integration tests are CI-only and are not expected to run locally.
这条规则划清了本地与 CI 的验证边界,api/AGENTS.md 将其进一步细化为可执行命令:
make lint # 格式化 + lint
make type-check # 类型检查
make test # 单测
make test TARGET_TESTS=./api/tests/<path> # 定向测试
并补充两条纪律:"Docker-backed integration suites are normally CI-owned"(Docker 支撑的集成套件归 CI 所有)、"Do not start long-running services as part of routine agent work"(常规代理工作中不要启动长驻服务)。
对照 Makefile 可以看清两条测试通道的实际差别:
make test(本地通道):uv run --project api --dev pytest -p no:benchmark --timeout "$${PYTEST_TIMEOUT:-20}" -n auto api/tests/unit_tests api/providers/vdb/*/tests/unit_tests api/providers/trace/*/tests/unit_tests --ignore=api/tests/unit_tests/controllers,随后单独跑 controllers 单测并追加覆盖。并行(-n auto)、默认单测超时 20 秒(可用PYTEST_TIMEOUT覆盖)。make test-all(CI 通道):在上述基础上追加两段 Docker 套件——--start-middleware启动中间件后运行api/tests/integration_tests/{workflow,tools}与api/tests/test_containers_integration_tests(超时升至 180 秒),以及--start-vdb运行 Chroma/pgvector/Qdrant/Weaviate 四个向量库的 smoke 测试。
单元测试的本地可运行性由 api/pytest.ini 保障:addopts 注入了覆盖率采集,env 段则预置了一批纯本地 mock 凭据(如 OPENAI_API_KEY = sk-IamNotARealKey...、CODE_EXECUTION_ENDPOINT = http://127.0.0.1:8194、PLUGIN_DAEMON_URL = http://127.0.0.1:5002),使单测无需任何真实外部服务即可执行。测试目录的物理分层也印证了这一点:api/tests/unit_tests/、api/tests/integration_tests/ 与 api/tests/test_containers_integration_tests/(需要容器)三者并存,前两者可本地跑,后者 CI-only。
5. Gotcha 三:.env.example 的三层拆分策略
第三条 gotcha 是这份指南中最具信息量的一条:
Keep
docker/.env.examplelimited to variables required for a default Docker Compose deployment to start. Put optional and provider-specific settings in the matchingdocker/envs/*.env.examplefile;docker/.envoverrides those service-specific env files.
它规定了三条明确的分层规则:
- docker/.env.example 只保留"默认 Docker Compose 部署能启动"所必需的变量——保证最小部署面,避免一个巨型样例文件淹没真正的必填项;
- 可选变量与服务商专属变量下沉到 docker/envs/ 下按服务命名的
*.env.example文件——仓库当前约有 37 份这样的分服务样例(如 docker/envs/postgres.env.example 一类的按服务拆分),中间件统一样例即 docker/envs/middleware.env.example(make dev-setup的复制源); - 本地覆盖遵循固定优先级:
docker/.env> 各服务专属 env 文件。
这条约束还有机器化的守护者:make lint 会执行 dotenv-linter ./api/.env.example ./web/.env.example,防止前后端样例文件漂移;而 docker/docker-compose-template.yaml、docker/docker-compose.middleware.yaml 与 docker/generate_docker_compose 则说明 compose 文件是由模板/生成器统一产出的,进一步降低了多份 compose 之间环境变量失配的概率。
6. Frontend Workflow:把长规则放在可引用的独立文档里
根指南的最后一节只有两条,但体现了"根级指南不膨胀"的写法:
- For truncated text disclosure and native
titledecisions, follow [Truncated Text Disclosure].
其引用目标 web/docs/truncated-text-disclosure.md 是一份自成一体的操作契约,核心结论值得摘录:
- 原生
title是可选的、补充性的产品行为,不是truncate/line-clamp-*的机械配套;缺少title本身不构成无障碍缺陷; - 动手前必须四步走:追踪取值到最终渲染的 DOM 元素 → 识别完整内容披露的既有 owner → 把候选分类为
AUTO/COVERED/SKIP/REVIEW→ 只修改AUTO类候选,其余只报告不改码; AUTO必须同时满足一组强条件(单行有界纯文本、无既有 Tooltip/Popover/"展开更多"等披露 owner、不与既有交互竞争等);含敏感信息、无界用户内容、ReactNode、会产生副作用表达式的一律SKIP;- 明确禁止:仓库级 missing-title lint 规则、批量自动迁移、
ReactNode转字符串助手函数;测试侧只允许在title是显式产品契约时断言它,不得用getByTitle来"证明迁移完成"。
此外,根指南总述中的六个子系统各自还有作用域文件可进一步下钻,例如 web/AGENTS.md(i18n 键、consoleQuery/consoleClient 生成式 API、Dify UI 组件契约、Next.js 代理规则块)、cli/AGENTS.md(DifyCommand 继承体系、pnpm tree:gen 命令树生成、基于真实 Hono mock server 的行为测试)、e2e/AGENTS.md(Cucumber 运行时所有权、标签语义如 @smoke/@unauthenticated/@axe、E2E_ 前缀资源命名与 LIFO 清理注册)。这些子级指南共同构成了第 2 节"就近路由"规则的落点。
7. 这套分层指南的设计模式小结
从 CLAUDE.md 出发可以归纳出 Dify 仓库组织 AI 代理指南的几条可复用模式:
- 符号链接统一事实来源:
CLAUDE.md -> AGENTS.md,一份内容服务多个代理工具; - 根级极简 + 就近路由:根级只写"全仓恒真"的最小集合(项目构成、3 条 gotchas、1 个引用),模块细节全部下沉到作用域
AGENTS.md,以"follow the nearest"为路由规则; - 每条规则都有机器化对应物:
uv run --project api对应 Makefile 目标;"集成测试 CI-only" 对应make test与make test-all的双通道;.env.example三层拆分对应dotenv-linter与 envs 样例目录——规则不是口号,而是能被命令和 lint 守护的契约; - 长规则外链化:像 Truncated Text Disclosure 这样的多页契约独立成文,指南内只留指针,避免根文件随时间膨胀失效。
对于同样使用 AI 编程代理的 monorepo 项目,这套"符号链接入口 → 根级最小集 → 作用域指南 → 命令/lint 守护"的结构,是一份低成本、可验证、可持续演进的参考实现。
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 StartedRust0624
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