Langflow AGENTS-example.md 解析:一份面向 AI 编码代理的开发规范模板,从权衡优先级到交付前检查清单
AGENTS-example.md 是 Langflow 仓库根目录提供的一份语言无关、框架无关的开发标准示例模板,定义了从核心理念、设计原则、代码质量、架构分层,到测试、评审与交付前检查的完整工程规范。本文以该模板为骨架逐节拆解其规则体系,并结合 Langflow 仓库中的实际配置(ruff、pytest、pre-commit、codecov)与配套的 docs/agents/ 文档、后端代码评审 Skill,展示这份模板在真实项目中如何被适配、落地与强制执行,帮助读者掌握"为 AI 编码代理编写可执行开发规范"的方法。
模板的定位:为什么 Langflow 需要一个 AGENTS-example.md
Langflow 仓库中存在多份面向 AI 编码代理(coding agent)的指引文件,分工明确:
- AGENTS.md:项目级代理指引,描述项目概览、前置条件(Python 3.10–3.14、uv、Node.js)与常用命令(
make init、make unit_tests、make alembic-revision等); - CLAUDE.md:说明本项目采用 AGENTS.md 作为向 AI 代理提供上下文的标准,通过
@AGENTS.md导入指令让 Claude Code 自动加载; - AGENTS-example.md:本文主角——一个标注为 "EXAMPLE" 的参考模板,作者明确提示"使用需谨慎(Use at your risk)",建议在采纳前先按项目需要适配;
- docs/agents/PHILOSOPHY.md、docs/agents/ARCHITECTURE.md、docs/agents/TESTING.md 等:Langflow 团队将模板思想"适配"到本项目后的实际产出(如十项项目哲学、单向依赖边界、组件测试基类);
- .agents/skills/backend-code-review/SKILL.md:一个直接消费该模板规则的评审 Skill,其中明确引用了 "per AGENTS-example.md" 的文件行数限制,并把模板中的测试反模式(Liar、Mirror、Giant、Mockery……)逐条写进了评审规则。
可以看出,Langflow 的用法是:模板提供通用规则骨架,项目文档注入领域细节,工具链(Skill/pre-commit)负责强制检查。这种"模板 + 适配 + 执行"的三层结构,是本文解读该模板的主线。
核心理念:权衡优先级与基本规则(第 1 节)
模板开宗明义地给出了冲突发生时的权衡优先级(Trade-off Priority):
- 正确性(Correctness) —— 代码做它该做的事;
- 简洁与可读性(Simplicity and readability) —— 代码易于理解;
- 可测试性(Testability) —— 代码易于测试;
- 性能(Performance) —— 代码足够快;
- 抽象与复用(Abstraction and reuse) —— DRY。
这个排序的潜台词是:性能排第 4、抽象排第 5,意味着"先写对,再写快,最后才谈优雅"。模板结尾也再次强调:"当拿不准时,选择简单;当权衡发生时,遵循第 1 节的优先级。先构建正确性,之后再做优化,永远保持可测试。"
与之配套的基本规则(Ground Rules)共有五条:
- 修改前先阅读并理解现有代码;
- 遵循项目既有的模式与约定;
- 需求含糊时,先提问再写代码;
- 优先增量交付:先核心逻辑,再边界情况,最后打磨;
- 不过度工程化,为今天的需求而建,不为假想的未来而建。
Langflow 自身的项目哲学文档 docs/agents/PHILOSOPHY.md 正是这种"先问该不该做,再问怎么做"思路的产物,其中第 11 条信条要求"没有证据证明改动有效,就不算修复"(先复现失败、再证明修复消除了它),与模板中"需求含糊时先提问"一脉相承。
设计原则:SOLID、DRY、KISS、YAGNI(第 2 节)
SOLID:附"常见误区"的实现版解释
模板没有停留在 SOLID 的口号上,而是为每条原则配了"规则 + 常见错误"两列的表格:
| 原则 | 规则 | 常见误区 |
|---|---|---|
| SRP 单一职责 | 每个类/函数/文件只有一个变化理由。需要用到"和/或"来描述它时,就该拆分 | 把 SRP 误解为"一个类一个函数"。SRP 指的是一个变化轴 |
| OCP 开闭原则 | 通过写新代码而非改旧代码来增加行为;在预期变化的地方用多态或策略模式 | 过早抽象导致的过度工程。OCP 只适用于有证据表明需求会变化的地方 |
| LSP 里氏替换 | 子类必须遵守父类契约;"is-a" 关系不严格时优先组合而非继承 | 重写父类方法却直接抛 NotImplementedError 或什么都不做 |
| ISP 接口隔离 | 定义小而面向角色的接口;客户端只依赖它用到的方法 | 创建一个含 15+ 方法的"上帝 service"接口 |
| DIP 依赖倒置 | 在模块边界依赖抽象而非具体实现;领域逻辑永远不 import 基础设施 | 把 DIP 等同于"用依赖注入就行"。DIP 是反转源码依赖的方向 |
值得注意的是 Langflow 的架构文档 docs/agents/ARCHITECTURE.md 将 DIP 落成了可检查的硬规则:依赖图只能单向流动(langflow → langflow-base → lfx,lfx 禁止 import langflow.*),并给出反例——"在 src/lfx/ 中 from langflow.services.deps import session_scope" 是违规写法,正确做法是在 lfx 内定义接口、由应用层注入实现。这正是模板中"依赖倒置"从概念到边界的典型适配。
DRY:三条红线
- 只有当完全相同的业务规则在 3 处以上重复时(Rule of Three)才抽取共享逻辑;
- 配置、常量、schema 定义保持单一事实来源;
- 宁可重复,也不要错误的抽象。两段看起来相似但服务于不同业务目的的代码不是重复——强行合并会制造意外的耦合。
"错误的抽象"被明确定义为:过早泛化、目的不清、把无关关注点耦合在一起。
KISS 与 YAGNI:反过度工程的量化口径
- 选择满足当前需求的最简实现;标准库优先于自造轮子;
- "一次普通的函数调用胜过元编程;只需要数据分组时,字典胜过类";
- 不要"以防万一"地引入设计模式、抽象或框架;
- 只有存在具体的当前需求时才实现功能;至少有两个具体用例之前,不要搭"通用/可扩展"框架;
- 定期删除投机性代码和无用的 feature flag;
- "三行相似代码优于一个过早的抽象。"
代码质量:命名、类型、不可变性与函数设计(第 3 节)
命名规则
- 名字应揭示意图,回答"它为什么存在、它做什么";
- 函数用动词:
get、create、update、delete、validate、format、parse; - 布尔量用前缀:
is、has、can、should; - 除非业界通用(
id、url、api),否则不用缩写; - 禁用泛化命名:
data、result、obj、thing、temp、misc、utils; - 名字中不出现 "and"、"or"、"then"——出现即暗示承担了多个职责。
这套命名规则在 Langflow 的评审 Skill .agents/skills/backend-code-review/SKILL.md 中被逐条复刻为检查项("Functions should use verbs (get, create, validate). Booleans should use prefixes (is_, has_, can_, should_)"),说明它已超出"示例"范畴,成为实际评审依据。
强类型、不可变性与早返回
- 强类型:处处使用强类型,避免
any/object/dynamic/Object;公共函数必须有带类型的参数与返回值;不许为了编译通过而强转any。 - 不可变:默认不可变(
const、readonly、final、frozen、tuple、frozenset);转换函数返回新对象而非原地修改;绝不向外暴露可变内部集合,只返回副本或只读视图;函数内部的可变局部变量没问题——危险的是可变共享状态。 - 早返回与守卫子句:在函数顶部校验前置条件并尽早 return/throw;通过取反条件、提前返回来降低嵌套;让"快乐路径"停留在最低缩进层级。
- 无魔法值:重复出现的数字和字符串抽成命名常量,用描述性变量名替代内联字面量。
- 注释:不注释显而易见的代码;注释只解释 WHY,绝不解释 WHAT;不留注释掉的代码(那是版本控制的工作);TODO 注释必须带 ticket 引用。
- 函数:保持短小、单一抽象层级;一个函数做一件事,做两件事就拆分;不用切换行为的布尔参数,而是拆成两个具名函数;每次改动都清除死代码和未使用的 import。
从仓库的 lint 配置看,这些规则有工具兜底:pyproject.toml 中 ruff 配置 select = ["ALL"]、line-length = 120、pydocstyle.convention = "google",即几乎启用全部 lint 规则族,并用 Google 风格 docstring 约定约束注释。
架构:分层职责、依赖注入与 DDD 的适用边界(第 4 节)
关注点分离
- 领域、应用、基础设施三层关注点必须分离;
- 领域/业务逻辑对框架、数据库、HTTP 层的 import 数量必须为零;
- 副作用(I/O、日志、指标)留在系统边缘,业务逻辑保持纯粹;
- 层与层边界上使用 DTO 或值对象——永远不要把 ORM 模型或 HTTP 请求对象传进业务逻辑。
分层职责表
模板用一张 "CAN / CANNOT" 表格把五类层的边界钉死:
| 层 | 可以 | 不可以 |
|---|---|---|
| Handler/Controller | 接收输入、委派给 service、返回输出 | 包含业务逻辑、直接调数据库 |
| Service/Orchestrator | 协调操作、执行业务规则 | 感知 HTTP/传输细节、直接执行 SQL |
| Repository/Data Access | 执行查询、映射数据 | 做业务决策、调用外部 API |
| Helper | 转换数据、校验、格式化 | 产生副作用、做 I/O、维护状态 |
| External Client | 与外部服务通信 | 包含业务逻辑、访问数据库 |
Langflow 的 docs/agents/ARCHITECTURE.md 给出了这张表的"项目化"版本——Service vs Utility vs Component 三分类:Service(继承 services/base.Service、经 services/factory.py 注册、有生命周期与共享连接的单例)、Utility(无共享状态、无生命周期的纯/近纯函数,放 helpers/ 或 lfx/utils/)、Component(画布上用户可见的节点子类)。它甚至点破了模板隐含的陷阱:"MyHelperService 这种其实只是一堆函数的 service" 是坏味道——工具函数就该是函数,不该套 service 壳。
依赖注入的五条细则
- 通过构造函数或方法参数注入依赖,让所有依赖显式化;
- 注入的是 I/O 边界(数据库、HTTP 客户端、文件系统、时钟),使它们在测试中可替换;
- 组合根(composition root)留在应用入口,与业务逻辑分离;
- 一个类需要注入超过 ~4 个依赖时,说明它职责过载,应拆分;
- 只注入有副作用或随环境变化的东西;纯工具函数不需要注入。
DDD:仅在值得时应用
模板对领域驱动设计持谨慎态度:仅当领域复杂度确凿地证明有必要时才引入 DDD;Entity、Value Object、Aggregate 只在真正创造价值时使用;错误与不变量也应建模为领域的一部分。这与 Langflow 哲学第 2 条"每个后端能力必须落到画布上(如果不能被非 Python 用户以组件形式接线,它属于 SDK 而非平台)"的克制气质一致——先问"值不值",再谈"怎么建"。
文件结构:行数上限、职责分文件与命名禁区(第 5 节)
单文件生产代码上限
| 指标 | 指南 |
|---|---|
| 代码行数(不含 import、类型定义、文档) | ~500 行(到 ~530 可接受;600+ 是红旗) |
| 承担不同职责的函数 | 最多 5 个 |
| 承担相同职责(同前缀)的函数 | 最多 10 个 |
| 文件内主类 | 1 个 |
| 小型相关类(异常、DTO、枚举) | 5 个(且同类型) |
Langflow 评审 Skill 对这一节做了显式背书:"生产文件超过 ~500 行……600+ 行应视为红旗……测试文件超过 ~1000 行应按逻辑分组拆分(per AGENTS-example.md)",并解释了 500 行阈值背后的理由:行数越大的文件缺陷率越高、评审越慢,且是多重职责(SRP 违规)的信号。
单职责文件与"一句话测试"
每个文件必须有一个存在理由和一个变化理由。判据是一个测试:能否不用"和/或",用一句话描述这个文件的用途?
按职责前缀分文件
函数必须按职责类别分组,不同前缀的函数不允许共存于同一文件:
| 职责 | 函数前缀 | 对应文件 |
|---|---|---|
| Types/Models | 类型定义、接口、无逻辑的类 | {feature}_types |
| Constants | MAX_*、DEFAULT_*、枚举 |
{feature}_constants |
| Validation | validate*、check*、is_valid* |
validation |
| Formatting | format*、build*、serialize*、to_* |
formatting |
| Parsing | parse*、extract*、from_* |
parsing |
| External calls | fetch*、send*、call*、request* |
{service}_client |
| Data access | save*、load*、find*、delete*、query* |
{feature}_repository |
| Orchestration | 主入口、协调 | {feature}_service |
| Handlers | 端点、控制器、视图 | {feature}_handler |
反过度拆分与命名禁区
- 不要把 1–2 个合计不到 20 行的琐碎函数拆成独立文件;
- 私有辅助函数(
_func)留在使用它的文件里;一行工具函数不单独成文件; - 拆分时机 = 出现清晰、可复用的职责;合并时机 = 拆分只增加复杂度而无收益;
- 永远不用泛化文件名的独立文件:
utils、helpers、misc、common、shared。评审 Skill 给出的解释是:叫utils.py的文件会在几个月内变成 50+ 个函数的垃圾场——每个函数组应放进以职责命名的文件(formatting.py、validation.py)。
模板最后给出一张标准模块结构图:
feature/
├── {feature}_service # Orchestration
├── {feature}_types # Type definitions
├── {feature}_constants # Constants and enums
├── helpers/
│ ├── validation # ONLY validation functions
│ ├── formatting # ONLY formatting functions
│ └── parsing # ONLY parsing functions
├── services/
│ └── {external}_client # ONLY external API communication
├── repositories/
│ └── {feature}_repository # ONLY data persistence
└── handlers/
└── {feature}_handler # ONLY request handling
错误处理:错误是 API 契约的一部分(第 6 节)
模板给出七条错误处理铁律:
- 显式处理预期错误,零静默失败;
- 不用泛型异常(
Exception、Error、object),使用领域相关错误类型; - 抛出/返回的错误必须带上下文(什么失败、什么输入导致的、如何修复);
- 错误是 API 契约的一部分;
- 在系统边界校验输入,非法数据快速失败;
- 区分可恢复错误与致命异常;
- 绝不静默地"修正"非法输入——用清晰消息拒绝它。
配套的正反例代码:
# BAD
try:
result = do_something()
except:
pass
# GOOD
try:
result = do_something()
except ValidationError as e:
logger.warning("Validation failed", extra={"error": str(e), "field": e.field})
raise DomainError(f"Invalid input: {e.field}") from e
要点在于 GOOD 写法做了三件事:捕获具体异常类型、把失败记入结构化日志(带 field 上下文)、以 raise ... from e 保留异常链——而不是吞掉异常假装一切正常。
安全:默认拒绝与边界校验(第 7 节)
模板的安全清单可以归纳为四组:
输入与信任边界
- 在边界处净化并校验所有用户/外部输入;
- 永远不信任系统边界之外的数据;
- 使用白名单(allowlist)而非黑名单——默认拒绝,只接受已知安全的模式;
- 复杂结构用 schema 校验库(Pydantic、zod、JSON Schema),不手写校验;
- 服务端校验永远不可省略——客户端校验只是 UX 便利,不是安全措施。
密钥管理
- 密钥不进代码,使用环境变量或 secret manager;
- 不允许硬编码 API key、token、密码。
数据访问与错误输出
- SQL 一律参数化查询,禁止字符串拼接;
- 面向最终用户的错误消息不暴露内部细节。
测试数据
- 测试中使用伪造/匿名数据,绝不使用真实用户数据。
在 Langflow 仓库中,"密钥不进代码"这条规则由工具链闭环执行:.pre-commit-config.yaml 配置了 detect-secrets 钩子(以 .secrets.baseline 为基线扫描),评审 Skill 也把"硬编码密钥"列为 Critical 级发现项。
可观测性:结构化日志、级别与 PII 零容忍(第 8 节)
日志原则
- 使用结构化日志(key-value / JSON),而非格式化字符串;
- 在关键决策点与边界处打日志,而不是在紧循环内部;
- 日志应包含:操作名、相关 ID、结果(成功/失败)、必要时耗时;
- 全代码库保持字段名一致。
日志级别
| 级别 | 使用场景 |
|---|---|
| ERROR | 有东西坏了,需要人工介入 |
| WARN | 降级但可自恢复 |
| INFO | 重要的业务事件 |
| DEBUG | 诊断细节,生产环境关闭 |
PII 零容忍
- 绝不记录:邮箱、用户名、手机号、住址、token、密码;
- 允许的标识符:
auth_id、user_id、internal_id; print()/console.log()不得携带用户数据——它们会流向生产日志。
Langflow 的评审 Skill 把这一节细化到了具体实现层面:统一使用 lfx.log.logger 的异步日志方法(adebug、ainfo、awarning、aerror、aexception),禁用 print() 与标准库 logging;允许的 ID 进一步收敛为 user_id、flow_id、session_id;异常输出统一用 {e!s} 表示法。
测试:测试代码就是生产代码(第 9 节)
模板在此节的立场很强硬:"Test code is production code. 它接受同样的打磨、评审与质量标准。"
核心原则
- 为所有核心逻辑写单元测试;
- 遵循 AAA(Arrange-Act-Assert)结构:一个测试一个 act、一个逻辑断言;
- 测试必须相互独立、确定性、不依赖执行顺序;
- Mock 或 fake 掉所有外部依赖(DB、API、文件系统、时间、随机性);
- 测试命名清晰:
should_[expected]_when_[condition]。
不仅要验证,还要"进攻"
这是本模板最有辨识度的部分。模板明确指出:快乐路径测试只是地基,单独的快乐路径测试是不够的。必须写主动"进攻"代码、寻找缺陷的对抗性测试:
- 意外的输入类型:
None、""、[]、{}、0、-1; - 边界值:最大 int、最大长度、恰好等于上限、超过上限一格;
- 畸形数据:缺字段、多字段、错误类型、非法格式;
- 错误状态:依赖失败时会发生什么?
- 验证"不该发生的事":被禁止的状态被正确拒绝;
- 错误消息与类型:不只验证"它失败了",还要验证它如何失败。
并给出两条纪律:
- 基于需求/规格写测试,而不是照抄源码当前的行为——这是捕捉"代码偏离预期"型 bug 的唯一手段;
- 测试失败时先问代码是不是错了,而不是默默改断言去迁就现状——不理解原因就改断言是被禁止的。
测试文件规则
| 指标 | 指南 |
|---|---|
| 每文件行数 | ~1000 行指南——超过可考虑拆分,但覆盖单一模块时非强制 |
| 每文件测试数 | 无硬性上限——只在覆盖无关行为时才拆分 |
| Setup(Arrange) | 每测试 ~20 行上限(超出则抽取为 helper/factory) |
原则是按逻辑边界拆分测试文件,而不是按任意行数:一个文件对应一个模块/服务,即使 800+ 行也完全没问题。
覆盖率:80% 目标,75% 底线
- 目标 80%,最低可接受 75%,低于 75% 视为任务未完成;
- 重点盯分支覆盖(
if/else两侧、所有catch块),而非仅仅是行覆盖; - 没有断言的高覆盖率毫无价值——每个测试至少有一个有意义的断言;
- 覆盖率必须实际运行并展示(后端和前端都要)。
模板给出的三类运行命令:
# Python
pytest tests/your_tests.py --cov=src/module_under_test --cov-report=term-missing --cov-branch -v
# JavaScript/TypeScript (Jest)
npx jest tests/your_tests.test.ts --coverage --collectCoverageFrom="src/module/**/*.{ts,tsx}"
# JavaScript/TypeScript (Vitest)
npx vitest run tests/your_tests.test.ts --coverage
所有创建的测试必须通过:零失败、零异常;禁止禁用/跳过/删除测试来掩盖失败;禁止留下"以后再修"的测试;覆盖率不足 75% 就继续补测试、重跑,直到达标。
不该测什么:简单 getter/setter/trivial mapper 不值得测;实现细节(方法调用顺序、内部状态)应该测行为来替代;不要用无意义断言堆覆盖率。
七大测试反模式(禁止)
| 反模式 | 问题 |
|---|---|
| The Liar(骗子) | 测试通过,但没有验证它声称要验证的行为 |
| The Mirror(镜子) | 测试读源码然后断言代码恰好做了什么——零 bug 产出 |
| The Giant(巨无霸) | 50+ 行 setup、多个 act、几十个断言——应拆成 5+ 个独立测试 |
| The Mockery(陪练) | mock 多到测试实际只测了 mock 的搭建过程 |
| The Inspector(窥探者) | 与实现细节耦合,任何重构都把它打碎 |
| The Chain Gang(连环犯) | 测试依赖执行顺序或共享可变状态 |
| The Flaky(飘忽者) | 不改代码时有时过有时挂 |
结合仓库实际可以看到这套规范的"适配版"取舍:docs/agents/TESTING.md 针对模板中"mock 掉所有外部依赖"一条做了项目级修正——Langflow 明确"避免 mock、优先打真实依赖"(因为"mock 通过、生产失败"曾让团队付出多个发布周期的代价),mock 只保留给 LLM(MockLanguageModel)与真正不稳定且与被测逻辑正交的依赖;需要凭据的测试打 @pytest.mark.api_key_required 交给 CI 门控。而 codecov.yml 揭示了另一个现实:仓库当前的整体覆盖率目标(backend 55%、lfx 60%、frontend 10%、patch 40%)是"渐进式改进"的定位,与模板"新代码 80%/75%"的绝对要求并存——前者管存量大盘,后者管每次新增的交付质量。模板命令在 Langflow 中的对应物是 AGENTS.md 中的 make unit_tests(并行)、make unit_tests async=false(串行)、uv run pytest path/to/test.py,pytest 的 marker(api_key_required、security、real_services 等)统一声明在 pyproject.toml 的 [tool.pytest.ini_options] 中。
代码评审:Blocker 优先的评审顺序(第 10 节)
模板定义了评审的固定优先级(先处理阻断项):
- 安全与 PII —— 日志无 PII、无硬编码密钥、输入有校验;
- DRY —— 无重复的类型、类、函数或逻辑;
- 文件结构 —— 行数上限被遵守、职责已分离;
- 架构 —— 单一职责、分层正确;
- 代码质量 —— SOLID、强类型、错误处理;
- 测试 —— 快乐路径与对抗性测试兼备、覆盖率达标;
- 可观测性 —— 结构化日志、无 PII。
针对测试的四个灵魂拷问:
- 是否同时有快乐路径测试与对抗性测试?
- 如果有人改坏了逻辑,这些测试能否抓住回归?
- 是否存在没被覆盖的边界情况或失败模式?
- 如果我删掉一行业务逻辑,是否至少有一个测试会失败?
对遗留代码的态度是"不传染":不延续坏模式(即使周围代码很烂,新代码也要写好的);不未经评审就从遗留代码复制粘贴;在可能的范围内把新代码与遗留隔离。Langflow 的 docs/agents/ARCHITECTURE.md 对这一点有精确表述:"langflow/base/ 是遗留目录,不要再往里面加东西"——新共享原语一律进 src/lfx/src/lfx/base/。
文档:C4 分级与功能文档必备章节(第 11 节)
何时写文档
- 功能实现完成之后再生成功能文档;
- 文档与代码同仓存放(Markdown);
- 使用通用语言(ubiquitous language)——文档、代码、沟通中使用同一套术语。
C4 文档分级
| 级别 | 读者 | 内容 |
|---|---|---|
| Context (L1) | 产品/干系人 | 系统在其环境中的位置 |
| Container (L2) | 两者 | 应用、数据库、队列 |
| Component (L3) | 工程团队 | 内部服务细节 |
功能文档的八个必备章节
- Overview —— 摘要、业务背景、限界上下文;
- 通用语言术语表 —— 领域术语并附代码引用;
- 领域模型 —— 聚合、实体、值对象、事件;
- 行为规格 —— Gherkin 场景(快乐路径、边界、错误);
- 架构决策记录(ADR) —— 上下文、决策、后果;
- 技术规格 —— 依赖、API 契约、错误码;
- 可观测性 —— 指标、日志、仪表盘;
- 部署与回滚 —— feature flag、迁移、回滚预案。
交付前检查清单:代码出门前的最后一道闸(第 12 节)
模板要求:交付任何代码前,逐项验证全部清单项。
Critical(阻断项)
- [ ] 任何日志、打印、webhook 消息中无 PII
- [ ] 代码中无密钥或凭据
- [ ] 无重复的类型、类或逻辑(DRY)
- [ ] 无生产文件超过 ~500 行 / 测试文件超过 ~1000 行
- [ ] 同一文件内无混合职责前缀的函数
- [ ] 所有用户输入在系统边界被校验
Important(必须修复)
- [ ] 每个文件/函数单一职责
- [ ] 恰当的错误处理(无静默失败、错误信息有意义)
- [ ] 强类型(无
any、object、dynamic) - [ ] 类型放专属 types 文件、常量放专属 constants 文件
- [ ] 领域逻辑独立于框架/基础设施
Testing(强制)
- [ ] 所有核心逻辑有单元测试
- [ ] 快乐路径与对抗性测试同时存在
- [ ] 所有创建/修改的测试通过——零失败
- [ ] 覆盖率报告已运行且输出已展示(后端与前端)
- [ ] 覆盖率 ≥ 75%(目标 80%)
- [ ] 无测试反模式(Liar、Mirror、Giant、Mockery、Inspector)
Quality(应该修复)
- [ ] 关键决策点有结构化日志
- [ ] 注释解释 WHY 而非 WHAT
- [ ] 无过度工程(没有 1–2 个琐碎函数单独成文件的文件)
- [ ] 未延续遗留坏模式
Pre-Commit
- [ ] Linter 已对所有改动文件运行——零错误
- [ ] Formatter 已对所有改动文件运行——零 diff
- [ ] 类型检查器已运行(如适用)——零错误
这份"Pre-Commit"清单在 Langflow 仓库中有完整的工具链对应物,恰好构成对模板第 12 节最有力的注脚。.pre-commit-config.yaml 中挂着的钩子包括:ruff check --fix 与 ruff format(对应 Linter/Formatter 零错误零 diff)、detect-secrets(对应"无硬编码密钥")、biome check 与 staged 的 no-any 检查(对应前端强类型)、以及一批 Langflow 自研检查脚本——如 check_component_env_writes.py(组件禁止写 os.environ,呼应"可变共享状态是危险")、Alembic 迁移的 Expand-Contract 校验等。评审 Skill 中的"Pre-Commit Verification"一节则把它变成评审动作:make format_backend(先格式化再评审,避免格式噪音掩盖真实改动)、make lint(lint 期发现的类型错误比生产崩溃便宜一个数量级)、make unit_tests(失败测试意味着改动破坏了既有行为,调查是代码错还是测试错)。
如何把这份模板落到自己的项目:从 Langflow 的实践看
把全文 12 节串起来读,AGENTS-example.md 实际上给出了一条可复制的落地路径,而 Langflow 仓库本身就是它的示范工程:
- 模板先行,标注"示例"身份。仓库根目录放 AGENTS-example.md,显式声明"Use at your own risk,采纳前请适配",避免它被误当成项目强制规范;
- 适配层注入领域细节。docs/agents/ 下的 PHILOSOPHY / ARCHITECTURE / TESTING 等文档保留了模板的骨架(权衡、分层、对抗性测试、检查清单),但把通用规则替换成了可判定的项目事实:单向依赖图、组件版本兼容契约(
file_names_mapping)、"避免 mock"策略、make unit_tests命令; - 工具链兜底强制。pyproject.toml(ruff
select = ["ALL"]、120 行宽、pytest marker 与 90s 超时)、codecov.yml(分组件的覆盖率目标与阈值)、.pre-commit-config.yaml(ruff/biome/detect-secrets/迁移校验钩子)把"软规则"变成提交前自动执行的"硬门禁"; - 评审 Agent 消费同一套规则。.agents/skills/backend-code-review/SKILL.md 将模板的规则(500 行上限、75%/80% 覆盖率、七大测试反模式、PII 零容忍)转写成带 Critical/Suggestion/Nit 分级输出格式的评审 Skill,让人工评审与 AI 评审共用同一把尺子。
需要强调的适用前提:该模板自称"语言无关、框架无关",其表格中的命令(pytest/Jest/Vitest)与类名(如 Component)是示意性的;具体阈值(500 行、75% 覆盖率、~4 个注入依赖上限)是经验性指南而非行业标准,采纳时应以团队现状校准——正如 Langflow 把整体覆盖率目标设为渐进式的 55%/60%/10%,而把 75% 底线只施加于新交付的代码。模板结尾的三句话可以作为收尾原则:"拿不准时选择简单;权衡时遵循优先级;先正确,后优化,永远可测。"
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 StartedRust0623
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