Continue Anti-Slop 检查:识别并清理 10 种典型 AI 代码坏味道
在 AI 辅助编程普及的背景下,"AI slop"(低质量 AI 生成代码模式)正在悄悄侵蚀代码库的可读性与可维护性。Continue 仓库在 .continue/checks/anti-slop.md 中内置了一份名为 Anti-slop 的自动化检查规则,定义了 10 类典型的 AI 代码坏味道,并规定了一套保守、可执行的清理约束。读完本文,你将理解这份检查规则的完整定义与判定标准,掌握 Continue CLI 从发现、执行到结果管理的整条 checks 链路,并知道如何把同类检查移植到自己的项目中。
1. anti-slop.md:一份完整的检查规则定义
Anti-slop 检查文件位于 Continue 仓库根目录的 .continue/checks/anti-slop.md,采用 Markdown 加 YAML frontmatter 的格式,与 Continue 的 Rules 机制(见 Rules 深度文档)同源。完整文件内容如下:
---
name: Anti-slop
description: Fix AI Slop
---
I want to follow the **Anti AI-slop rule**: clean up any AI-generated code patterns that harm readability and maintainability. Please look around at the files that were changed. If there are any with "AI slop" patterns, make targeted changes to clean them up. Stick to a single file maximum. If no changes are necessary, then do nothing.
**What qualifies as AI slop in code:**
1. **Overly verbose comments** - Comments that restate exactly what the code does (e.g., `// increment counter by 1` above `counter++`)
2. **Excessive defensive programming** - Unnecessary null checks, try-catches, or validations that clutter the logic without providing real safety
3. **Redundant type annotations** - Type declarations that are already inferred by the language/compiler
4. **Boilerplate explosion** - Creating separate classes/functions/files for trivial operations that could be a simple expression
5. **Over-abstraction** - Interfaces with single implementations, factories that create one thing, strategy patterns for two options
6. **Verbose variable names that obscure intent** - e.g., `currentUserAuthenticationStatusBoolean` instead of `isAuthenticated`
7. **Unnecessary intermediate variables** - Variables used exactly once on the next line purely to "document" a step
8. **Repetitive error handling** - Copy-pasted try-catch blocks that could be consolidated
9. **Filler documentation** - JSDoc/docstrings that add no information beyond the function signature
10. **"Just in case" code** - Unused parameters, dead code paths, or features built for hypothetical future needs
**The goal:** Code should be concise, readable, and no more complex than necessary. Remove ceremony, not functionality.
这份文件由三部分构成,理解它们的分工是使用这份检查的关键:
- frontmatter 元信息:
name: Anti-slop提供显示名称,description: Fix AI Slop是一句话职责描述。Continue 的文档说明,当alwaysApply未开启时,Agent 会读取description来决定是否将该规则拉入上下文,因此一行描述必须准确传达触发时机。 - 执行指令段:开头一段英文指令定义了三个硬性执行约束:
- 只查看被修改过的文件("look around at the files that were changed"),不做全仓扫描;
- 最多修改一个文件("Stick to a single file maximum"),把改动面压到最小;
- 无问题则什么都不做("If no changes are necessary, then do nothing"),从机制上杜绝"为了改而改"。
- 10 类 slop 判定标准 + 目标声明:明确"代码应当简洁、可读、复杂程度不超过必要",最后一句 "Remove ceremony, not functionality"(移除的是仪式感,不是功能)划清了清理的边界。
值得一提的是,Continue 团队在自己仓库中实践这份规则属于典型的 dogfooding。从目录结构看,.continue/checks/ 下共有 7 份检查文件,除 anti-slop.md 外还包括 react-best-practices.md、security-audit.md、setup-scripts.md、stale-comments.md、update-agents-md.md、update-continue-docs.md,覆盖了反 AI 味、React 规范、安全审计、陈旧注释等不同审查维度。
2. 十种 AI slop 模式逐项解析
以下对原文档中的 10 类模式逐一展开,给出判定要点与对照示例,便于在实际代码审查中落地。
2.1 过度冗余的注释(Overly verbose comments)
判定要点:注释逐字复述代码行为,删除后理解成本为零。
// ❌ slop:注释与代码完全同义
// increment counter by 1
counter++;
// ✅ 好:要么删掉,要么解释"为什么"
counter++; // 补偿上一轮被吞掉的心跳
2.2 过度防御式编程(Excessive defensive programming)
不必要的空值检查、try-catch 或校验,堆叠出"虚假安全感"却从不真正触发。典型特征是:在调用方已保证类型/非空的前提下,函数入口再包一层 if (!value) return;或对不可能抛错的纯计算套 try-catch。这类代码增加阅读负担,且掩盖了真实的错误路径。
2.3 冗余类型标注(Redundant type annotations)
语言或编译器已能推断的类型再显式声明一次:const x: number = 42、let items: string[] = []。在 TypeScript 中,仅当推断结果与意图不符(如 const re = /a/ 推断为 RegExp 需要收窄、或需要显式联合类型)时才值得标注。
2.4 样板代码爆炸(Boilerplate explosion)
为一个本可以用单行表达式完成的琐碎操作,创建独立的类、函数或文件。例如为了取 a ?? b 建一个 resolveFallback.ts 工具模块;为一次性的日志格式化单开一个 30 行的 helper 文件。
2.5 过度抽象(Over-abstraction)
原文档给出三个精确信号:
- 只有一个实现的接口:
interface PaymentStrategy下只有StripePayment一个类; - 只创建一种东西的工厂:
createService(type)中永远只会传"order"; - 为两个选项设计的策略模式:只有"是/否"两条分支,却套了 Strategy + 注册表。
抽象的引入应当以"存在第二个真实实现"为前提,否则它只是提前支付的认知成本。
2.6 冗长且掩盖意图的变量名(Verbose variable names)
原文示例很典型:currentUserAuthenticationStatusBoolean 应直接叫 isAuthenticated。过长的名字往往是把"类型后缀 + 业务前缀"机械拼接的结果,读起来反而比短名字更难抓住主干。命名应以意图为中心,长度以表达力为限。
2.7 不必要的中间变量(Unnecessary intermediate variables)
变量在下一行被恰好使用一次,纯粹为了"记录步骤":
// ❌ slop
const result = computeTotal(items);
const total = result;
sendInvoice(total);
// ✅ 好
sendInvoice(computeTotal(items));
如果这个中间变量承载了值得命名的概念(后续多次引用、断言、日志),保留;否则它就是噪声。
2.8 重复的错误处理(Repetitive error handling)
复制粘贴的 try-catch 块,每个调用点各写一遍相同的 catch (e) { logger.error(e.message); }。应当收敛为统一的重试/降级工具(Continue 自身在 withExponentialBackoff 实现 中体现了"把重复恢复逻辑收敛到一处"的思路),或至少抽取共享的 handler。
2.9 填充式文档(Filler documentation)
JSDoc/docstring 只重复函数签名已经说明的信息,例如对 function parseConfig(path: string): Config 写"解析给定路径的配置文件并返回 Config"。文档注释的价值在于签名无法表达的内容:前置条件、副作用、边界行为、为什么这样实现。
2.10 "以防万一"的代码("Just in case" code)
未使用的参数、永远不会走的分支、为假设的未来需求预留的字段。这类代码是熵源:每个死分支都在消耗后续维护者的注意力,且测试无法覆盖。判断标准很简单——当前调用链上没有任何路径使用它,就删掉。
十条模式背后的统一目标:代码应当简洁、可读、复杂程度不超过必要。清理动作删除的是仪式(ceremony),不是功能(functionality)——这是 Anti-slop 检查与"随意重构"的本质区别。
3. Continue CLI 如何发现这份检查
resolveReviews.ts 定义了审查来源的三级解析顺序(见 resolveReviews):
- CLI
--agent参数:最高优先级,显式指定时只运行指定项; - Hub API:源码中
resolveFromHub已返回空数组并注释 "Hub review resolution has been removed",属于预留位; - 本地文件发现(fallback):
resolveFromLocal依次扫描 .continue/agents/ 与 .continue/checks/ 两个目录下的.md文件。
本地发现逻辑中有两个值得注意的细节:
- 同名去重规则:扫描顺序是
agents/在前、checks/在后,用Set记录已见文件名,因此同名文件agents/优先。对应的 单元测试 显式构造了agents/shared.md与checks/shared.md冲突的场景,断言保留的是 agents 版本,且 checks 目录下的anti-slop.md被解析为名为 "anti slop" 的本地审查项(测试第 62-77 行); - 显示名转换:
path.basename(file, ".md").replace(/[-_]/g, " ")把文件名中的连字符/下划线替换为空格,所以anti-slop.md在输出中显示为 "anti slop"(第 77 行)。
4. 检查的执行流程:临时 worktree 中运行 Agent
从 reviewWorker.ts 的实现看,每个审查项(含 anti-slop)在一个 fork 出的 worker 进程中执行,流程如下:
- 切换到临时 worktree:
process.chdir(config.worktreePath)。该 worktree 不安装依赖(node_modules等不存在),保证审查不会误触发构建; - 以 auto 权限初始化服务:
initializeServices传入toolPermissionOverrides: { mode: "auto" }与headless: true,Agent 拥有完整的工具访问权,可读取文件; - 构造限定范围的审查提示词:buildReviewPrompt 生成一段强约束提示,核心规则是:
- 只审查 diff 中出现的文件与被改动的行,不读取、不报告 diff 之外的内容;
- 即使是 diff 中出现的文件,也不报告未改动行的存量问题;
- 只允许对变更文件中违反审查规则的部分做修复,禁止顺手做通用改进、重构、文档或风格修改;
- 若无违规,不编辑任何文件,直接声明未发现问题并退出。
- 注入检查文件正文:当审查来源是本地
.md文件时,loadLocalAgentInstructions 读取其全部内容,以## Agent Instructions区块拼接到审查提示词之前(第 157-164 行)。也就是说,anti-slop.md 中那 10 条 slop 定义会原文进入 Agent 的上下文,成为它的"专属审查规则"。
这个注入机制解释了 anti-slop.md 执行指令与框架提示词的自洽性:文件里写的 "look around at the files that were changed"、"stick to a single file maximum" 与框架提示词中 "ONLY flag issues that exist in the changed lines"、"ONLY make edits to files listed" 形成双层约束——文件级规则与框架级规则互相兜底,共同把一次审查的改动面锁定在"改动文件 × 改动行"的最小集合内。
- 回收产物:Agent 跑完后,
captureWorktreeDiff(worktreePath)捕获 worktree 中的实际改动作为 patch,连同 Agent 文本输出(agentOutput)与耗时一起通过 IPC 回传给编排进程(WorkerResult)。这个 patch 就是最终可供审查、接受或拒绝的"建议变更"。
5. 结果管理:cn checks 的列表、接受与拒绝
checks.ts 实现了 cn checks 命令族,用于在终端管理检查会话的结果:
Usage:
cn checks [pr-url] - List checks with diffs
cn checks accept [pr-url] - Accept pending suggestions
cn checks reject [pr-url] - Reject pending suggestions
其关键行为(以 源码 为准):
- 列表模式:通过
api/checks/status?pullRequestUrl=...拉取该 PR 的所有检查状态(pending/success/failure),逐项打印名称、描述、提交信息与建议状态;若该检查产生了 commit,还会调用agents/{sessionId}/diff打印出具体 diff 供人工比对(printCheckDiff); - 接受/拒绝:对
suggestionStatus === "pending"且已有 commit 的检查项,逐条POST agents/{sessionId}/accept或/reject,完成建议变更的落地或丢弃(acceptChecks / rejectChecks); - PR URL 自动探测:不传 URL 时,从当前 git 分支解析
owner/repo(支持 HTTPS 与 SSH remote 格式),再查询 GitHub API 找到该分支对应的 open PR(detectPrUrl); - 面向 CI 的退出码:全部通过退出 0,存在失败退出 1,仍有 pending 退出 2(第 196-201 行),可直接接入 CI 流水线做门禁。
这套"Agent 产出 patch → 人工查看 diff → 显式 accept/reject"的设计,正是 "Remove ceremony, not functionality" 在流程层的体现:机器负责发现与修复,人保留最终裁决权。
6. 在自己的项目中落地 Anti-Slop 检查
在自己的仓库中启用同类检查,步骤与 Continue 自身保持一致:
- 创建检查文件:在项目根目录新建
.continue/checks/anti-slop.md,内容可直接复用本文第 1 节的完整文件;frontmatter 中name与description是必需的最小元信息,其中description应保持一行、准确概括职责(例如 "Fix AI Slop"),供 Agent 判断是否拉取该规则。如需按文件类型触发,可参考 Rules 文档中定义的globs/regex/alwaysApply属性(Rules 文档); - 理解两个目录的分工:从源码结构看,
cn review的本地发现同时扫描.continue/agents/与.continue/checks/,前者优先级更高;而.continue/rules/面向 IDE 中 Agent/Chat/Edit 模式的系统消息注入。可以把rules理解为"长期生效的行为准则",把checks理解为"面向一次变更的审查项"; - 运行与验证:Continue 仓库自身的 PR 审查实践可参考 PR Review Bot 指南 中基于 GitHub Actions 的无头(headless)调用方式,该文档要求 Node.js 20+ 环境。本地调试时同样可以在终端直接运行 CLI,检查 Agent 输出与最终 patch 是否符合预期;
- 按需扩展检查集:anti-slop 只是 Continue 内置 7 份检查之一。如果你的团队更关注安全,可参考 security-audit.md;关注 React 代码质量可参考 react-best-practices.md——每份文件都是"一段执行指令 + 一份判定清单"的同构结构,可以照此为自己的技术栈编写专属检查。
7. 边界与适用前提
- 只针对增量代码:框架提示词明确禁止报告未改动行的存量问题,因此 anti-slop 检查不会"顺手"清理历史遗留代码,它只服务于本次变更;
- worktree 中无依赖:审查环境不安装
node_modules,Agent 不能依赖运行时代码验证,只能基于静态阅读判断; - 单文件上限:即使发现多个文件存在 slop,一次审查最多修复一个文件,其余问题应留给后续审查轮次;
- 检查文件是提示词而非程序:整个机制的可靠性取决于 LLM 对规则的遵循程度,因此 10 条判定标准写得越具体(如给出反例),执行效果越稳定。
Anti-slop 检查的价值不在于"删代码",而在于给 AI 辅助开发立下一条可执行的质量底线:每次变更合入前,自动过一遍 10 种典型 AI 坏味道,且改动范围、执行时机、人工介入点全部确定。从 Continue 仓库的 resolveReviews 测试 到 checks 命令实现,这条链路的每一环都有源码佐证,开发者既可以直接复用这份检查文件,也可以基于它构建自己团队的"反 AI 味"门禁。
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

