Dify 前端开发规约详解:web/AGENTS.md 中的 Agent 工作流、包契约与 Next.js 生成规则
2026/9/7 2:55:33 网站建设 项目流程

Dify 前端开发规约详解:web/AGENTS.md 中的 Agent 工作流、包契约与 Next.js 生成规则

【免费下载链接】difyBuild Agentic workflows, RAG pipelines, with rich AI model and tool support on one collaborative workspace. Deploy on cloud, VPC, or self-hosted, so teams move from prototype to production without rebuilding the stack.项目地址: https://gitcode.com/GitHub_Trending/di/dify

本文以 web/AGENTS.md 为骨架,完整拆解 Dify 前端(web/packages/dify-ui/)面向人类和 AI Agent 的双层开发规约:从测试与静态检查文档的路由规则、i18n 与生成式 API 客户端的包契约,到 Dify UI 组件库的各项"canonical contract",再到next dev自动写入的 Next.js 破坏性变更警示块。读完后可按仓库真实路径定位每一条契约的落点文件,在贡献 Dify 前端代码时保持与工程体系一致。

一、文档定位:一份写给 Agent 的前端工作手册

web/AGENTS.md 位于 Web 应用目录根部,是 Dify 前端唯一的"Agent 入口规约"。它的结构分为三层:

  1. Frontend Workflow——规定哪些文档、哪些技能(skill)在什么场景下加载;
  2. Package Contracts——规定用户可见文案、API 调用、UI 组件、表单、可访问性等跨功能契约;
  3. Next.js 生成规则块——由next dev自动写入并维护的警告区,提示当前 Next.js 版本存在与训练数据不一致的破坏性变更。

这种"流程 + 契约 + 工具链警示"的组合,使文档既约束人的 PR 行为,也约束 AI 编码助手的工具选择。

二、Frontend Workflow:文档与技能的路由规则

原文第一条工作流要求非常克制——只在对应工作场景下加载对应文档

  • web/docs/test.md只在处理前端测试工作时读取;
  • web/docs/lint.md只在运行或修改静态检查时读取。

这两个文件在仓库中均真实存在且内容完备:

  • web/docs/test.md 声明自己是web/下自动化测试的 single source of truth,定义了"何时该写测试"(保护可观察契约:用户交互、导航与 URL 状态、加载/成功/错误/空态、可访问性语义、可复现回归的 bug fix),并给出两条显式测试项目(unit走 happy-dom,browser走 Playwright Chromium)与标准命令:
# happy-dom;省略路径则运行整个 unit 项目 vp test run --project unit path/to/spec-or-directory # Browser Mode;省略路径则运行整个 browser 项目 vp test run --project browser path/to/spec.browser.spec.tsx # 诊断性覆盖率报告;不是验收目标 vp test run --project unit --coverage path/to/spec-or-directory
  • web/docs/lint.md 说明vp check(Oxfmt 格式化 + Oxlint 规则 + TypeScript 诊断)与 ESLint 非代码文件兜底的分工,根命令为pnpm check/pnpm check:fix

技能(skill)路由同样按场景触发:

  • how-to-write-component:只在实现涉及组件归属、状态、数据流、effect 或交互边界决策时加载;纯测试、纯文案、纯样式改动不得加载;
  • frontend-code-review:仅在显式的前端评审/审计请求(含测试评审)时使用;
  • frontend-testing:编写或修改 Vitest、React Testing Library 测试时使用。

文档还特别强调:web/docs/test.md是 Web 自动化测试策略的唯一事实来源,"Skills may route and execute that policy but must not redefine it"——技能只能路由和执行该策略,不能重定义它。这保证了策略演进只需改一处。

三、Package Contracts:七条跨功能契约逐条解析

3.1 用户可见字符串必须走 i18n 键

规约第一条即国际化契约:面向用户的字符串必须使用web/i18n/en-US/下的键;新增或重命名键时,必须同步更新所有受支持语言的正确本地化值

仓库中 web/i18n/ 目录下实际维护了 24 个语言目录(ar-TNde-DEen-USes-ESfa-IRfr-FRhi-INid-IDit-ITja-JPko-KRlo-LAnl-NLpl-PLpt-BRro-ROru-RUsl-SIth-THtr-TRuk-UAvi-VNzh-Hanszh-Hant)。这意味着一次键的重命名可能波及 24 个文件树——契约把"多语言完整性"从自觉行为升级为硬性规则。

测试侧同样配套:web/test/i18n-mock.ts 提供createReactI18nextMock(在需要自定义翻译时加载),共享的react-i18nextmock 全局加载,测试默认不依赖真实翻译文件。

3.2 后端调用:只允许生成式 consoleQuery / consoleClient

规约明确要求:新后端调用与已迁移界面必须使用@/service/client中生成的consoleQuery/consoleClientAPI,禁止新增手写 REST helper、DTO 镜像、基于 mock 的 app 状态,或直接修改生成契约

从源码结构看,这条契约有明确实现落点 web/service/client.ts:

  • 类型全部来自@dify/contracts/api/console/**/types.gen@dify/contracts/consoleconsoleRouterContract,即契约类型是生成的(.gen后缀),而非手写镜像;
  • 客户端基于 oRPC 体系构建:createORPCClient+OpenAPILink@orpc/openapi-client/fetch),再经createTanstackQueryUtils接入 TanStack Query,这就是consoleQuery的形态;
  • 请求上下文扩展了 TanStack Query 的 operation context,加入 Dify 特有的keepalivesilent标志(见 client.ts#L68-L71),并封装了 SSE 流式生成streamWorkflowGeneration

因此"不要手写 REST helper"不是风格偏好,而是保证 DTO 类型、OpenAPI URL 归一化(normalizeConsoleOpenAPIURL)、认证头注入(getMarketplaceHeaders等)只有一条代码路径。

3.3 Dify UI 原语:子路径导入与焦点指示

规约要求优先使用@langgenius/dify-ui/*原语、data attribute 与设计令牌,选择原语时从 Dify UI 包索引入手,并在最终可聚焦元素上保留可见焦点指示

包索引即 packages/dify-ui/README.md。从源码结构看,该包有两条与规约直接对应的硬性设计:

  • 故意没有根 barrel——只能按公共子路径导入,如@langgenius/dify-ui/button@langgenius/dify-ui/dialog@langgenius/dify-ui/field,样式入口@langgenius/dify-ui/styles.css在消费方根样式表引入一次;
  • 大部分交互原语是 Base UI 无头组件的"薄而有主见的封装",Dify 自研原语使用语义化 HTML、cvacn与设计令牌,包虽为 workspace 私有,但其公共子路径被当作稳定的包边界对待。

README 的 Primitives 表按 Actions / Controls / Display / Feedback / Form / Layout / Media / Navigation / Overlay and menu / Search and pick 十类列出全部子路径(./button./icon-button./form./input-group./dialog./infotip所在的 overlay 家族等),是"选原语"的唯一入口清单。

3.4 搜索输入框:SearchInput 复合组件优先

规约:当 Web 的SearchInput复合组件的搜索、清除、IME 契约与功能匹配时,复用它;否则遵循 canonical 的 Input Group 契约

这里的关键是"契约匹配才复用":SearchInput封装了搜索、清除按钮与 IME(输入法组合)边界这三件事,只有功能同时命中该组合契约时才使用,避免为了"看起来像搜索框"而引入不匹配的复合行为。不匹配时退回更底层的 Input Group 契约文档 packages/dify-ui/src/input-group/README.md,其覆盖复合输入解剖、共享表面归属、DOM 顺序、焦点与可交互 addon。

3.5 保存与提交流:真实表单边界

规约要求为保存/提交流程建立真实表单边界,带可见标签与可访问错误:当 Dify UI 的Form结构化提交与校验契约是归属方时使用它,否则使用原生 form;契约详见 packages/dify-ui/docs/forms.md。

从 dify-ui 文档索引看,forms.md 负责 native submit 边界、值归属、field、label 与 error 的契约划分——这与 AGENTS.md 中"要么 Dify UI Form 拥有契约,要么原生 form"的二选一表述互补:文档定义契约内容,AGENTS.md 定义选择决策。

3.6 按钮与图标按钮:不得用 Web 包装层掩盖契约

规约:遵循 canonical 的 Button 契约 与 IconButton 契约,覆盖操作语义、loading、可访问名称与原语组合;不得添加一个 Web 包装层来隐藏这些契约

从 packages/dify-ui/README.md 的组件指南表可确认两条契约的具体职责:Button 覆盖"操作语义、submit 与 link 的选择、loading 与 disabled 的区分、内容间距";Icon Button 覆盖"可访问名称、装饰性 glyph、外观归属与原语组合"。规约中"不得在 Web 层包一层"的禁令,正是防止web/出现一个与契约文档不同步的中间封装。

3.7 可访问名称、描述与弹层

两条契约引用同一套命名与弹层文档:

  • 命名:选择或修改可见标签、ARIA 命名、描述、视觉隐藏文本时遵循 packages/dify-ui/docs/accessible-names-and-descriptions.md。规约补充了一条职责边界——"Web 拥有本地化与功能特定的状态播报;不得在本地重新定义 Dify UI 的命名契约"。也就是说 dify-ui 文档定义"命名的来源、描述、覆盖与安全的 label 移除",web/只负责本地化与功能级状态播报。
  • 弹层:原语选择、portal、焦点与层叠遵循 packages/dify-ui/docs/overlays.md;"信息图形打开解释性内容"的场景复用 Web 的Infotip复合组件;不得引入一个在 Web 层重新实现 Dify UI 弹层行为的通用包装。

3.8 自定义 SVG 图标

规约末尾:自定义 SVG 图标遵循 packages/iconify-collections/README.md;不得在web/app/components/base/icons/src/下添加生成的 React 图标。仓库中packages/iconify-collections/目录实际包含 500 余个 SVG 源文件与多个 JSON 集合描述,即图标资产以 iconify 集合形式集中管理,而不是把构建产物 React 图标散落在应用目录里。

四、Next.js 生成规则块:由next dev自动维护的警示区

web/AGENTS.md 尾部被<!-- BEGIN:nextjs-agent-rules --><!-- END:nextjs-agent-rules -->包裹着一段机器维护的文本,这是整篇文档中最特殊的部分:

  1. "This is NOT the Next.js you know"——声明当前 Next.js 版本存在破坏性变更,API、约定与文件结构都可能与模型训练数据不同;写任何代码前,先读node_modules/next/dist/docs/中相应指南(从本文件所在目录解析;monorepo 中next包可能从仓库根不可见),并注意弃用通知;
  2. 自解释的维护机制——该块由next dev写入并在被删除后重新加回,校验逻辑位于node_modules/next/dist/server/lib/generate-agent-files.js。文档给出了一条实用的 diff 建议:"从 diff 中移除它只会重新产生未提交的变更;把它随你的改动一起提交,才能保持工作树干净"。

这个设计值得借鉴:Next.js 官方把"AI 助手可能按旧知识写码"这一风险,变成了工具链自动写入 AGENTS 类文件的固定机制——规约不是靠人记得更新,而是每次next dev运行时自我刷新。

五、契约到文件的路径速查

下表汇总 AGENTS.md 中每个契约指向的真实文件,便于按图索骥:

契约/工作流条目契约文件(仓库根相对路径)
前端测试策略(唯一事实来源)web/docs/test.md
静态检查策略web/docs/lint.md
Dify UI 包索引(选原语起点)packages/dify-ui/README.md
Button 契约packages/dify-ui/src/button/README.md
IconButton 契约packages/dify-ui/src/icon-button/README.md
Input Group 契约packages/dify-ui/src/input-group/README.md
Form 契约packages/dify-ui/docs/forms.md
可访问名称与描述packages/dify-ui/docs/accessible-names-and-descriptions.md
Overlay 契约packages/dify-ui/docs/overlays.md
自定义 SVG 图标packages/iconify-collections/README.md
生成式 API 客户端实现web/service/client.ts
国际化键web/i18n/(en-US为键基准,24 个 locale 同步)

六、实操要点小结

  • 动测试之前先读 web/docs/test.md,并记住web/下测试必须显式--project unit--project browser;裸vp test会同时跑两个项目,不是标准 Web 测试命令。覆盖率只是诊断信号,文档未定义任何百分比门槛。
  • 动静态检查之前先读 web/docs/lint.md:根命令pnpm check(Oxlint 规则基线在lint.config.ts,非代码文件兜底在eslint.config.mjs,Oxlint 历史错误基线在oxlint-suppressions.json);Oxlint 与 ESLint 的 disable 注释互不通用。
  • 加一条用户可见文案:先在web/i18n/en-US/定义键,再把 24 个 locale 目录全部补齐。
  • 加一次后端调用:走@/service/client的生成契约(oRPC + OpenAPI + TanStack Query),不手写 REST helper,不碰.gen契约。
  • 加一个组件:先到 packages/dify-ui/README.md 的子路径表里找原语,命中SearchInput复合契约就复用,否则按 Input Group / Button / IconButton / Form / Overlay / 命名契约文档决策,并保留可见焦点指示;图标只进 iconify 集合,不进app/components/base/icons/src/
  • 写任何 Next.js 相关代码前:读node_modules/next/dist/docs/里的当前版本文档,因为 AGENTS.md 中那个自更新块明示了"这不是你训练数据里的 Next.js"。

整体来看,web/AGENTS.md 的价值不在罗列规则,而在于把"哪个契约归谁所有"(Dify UI 拥有原语与命名契约,Web 拥有本地化与功能播报,docs/test.md拥有测试策略)写得毫无歧义,并用生成式代码、机器维护的 Next.js 规则块和 24 语言 i18n 目录这些仓库事实,为每条规则提供了可核验的落点。

【免费下载链接】difyBuild Agentic workflows, RAG pipelines, with rich AI model and tool support on one collaborative workspace. Deploy on cloud, VPC, or self-hosted, so teams move from prototype to production without rebuilding the stack.项目地址: https://gitcode.com/GitHub_Trending/di/dify

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

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

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

立即咨询