Claude Code 用于 CI 失败:提示模型引用失败输出、建议最小修复并添加回归测试以防止重复发生。

CI 失败通常并不神秘。日志会告诉你停止的地方、哪个命令失败以及错误信息。好的日志包含栈追踪、带有文件和行号的编译器错误,或者显示哪个断言失败的测试报告。有时你还会看到类似“expected X, got Y”的 diff 式线索,或明确的失败步骤如 lint、build 或 migrate database。
真正的问题是人(和 AI)常把日志当背景噪音。如果你粘贴一大段日志然后问“修复方法”,很多模型会跳到熟悉的解释而不是读最后有意义的几行。当错误看起来常见(“module not found”、“timeout”、“permission denied”)时,猜测会更糟:你可能得到一次大改写、一个新依赖或“试着更新全部”的建议,而这些并不匹配真实失败。
目标不是“用某种办法让它通过”。更简单的目标是:
在实践中,“最小修复”通常是这些之一:单处几行的代码修改、缺失的 import 或错误的路径、对 CI 环境明显不合适的配置值,或者撤回一次意外的破坏性改动而不是重新设计代码。
后续的测试也很重要。CI 通过一次并不等于防止重现。如果失败来自某个边缘情况(空输入、时区、舍入、权限),请添加一个在修复前会失败、修复后会通过的回归测试。这把一次性的救火变成了一道护栏。
大多数糟糕修复都源于上下文缺失。如果你只粘贴最后一条红色行,模型就要猜测之前发生了什么,而猜测常变成改写。
目标是提供足够细节,让别人能从第一个真实错误一路看明白到结尾,然后尽量少改动。
尽量按原样粘贴以下内容(能逐字粘贴尽量逐字):
go test ./...、npm test、flutter test、golangci-lint run)。用简单的语言列出约束。如果你要一个很小的修复,要明确写出来:不重构、不改行为(除非必须)、把补丁限制在失败相关区域。
一个简单例子:CI 在 lint 步骤失败,恰好发生在一次依赖升级之后。粘贴从第一个警告开始的 lint 输出,包含 CI 使用的命令,并说明单个包版本变更。这通常足以建议一行配置调整或小范围代码修改,而不是格式化半个仓库。
如果你想要可以直接复制粘贴的结构,通常如下就够了:
CI command:
Failing output (full):
Recent changes:
Constraints (smallest fix, no refactor):
Flaky? (runs attached):
当模型在 CI 中未命中要点时,通常是因为你的提示允许它去猜测。你的任务是让它用确切的失败输出“展示其思路”,然后只落实最小改动以让任务通过。
要求证据与一个小计划。一个好的提示会强制模型做五件事:
不确定是可以接受的,但隐藏的不确定会浪费时间。
把下面这段粘贴在你的 CI 提问顶部:
Use ONLY the evidence in the CI output below.
1) Quote the exact failing lines you are using.
2) Give ONE sentence: the most likely cause.
3) Propose the smallest fix: 1-3 edits, with file paths.
4) Do NOT do formatting/renames/refactors or "cleanup".
5) List uncertainties + the one extra detail that would confirm the diagnosis.
如果日志写着“expected 200, got 500”并伴随栈追踪到 user_service.go:142,这个结构会把响应推向该函数和一个小范围的守护或错误处理改动,而不是端点的重新设计。
最快的办法是用一个强制引用日志、在约束内作答并在缺信息时停下的提示。
You are helping me fix a CI failure.
Repo context (short):
- Language/framework:
- Test/build command that failed: <PASTE THE EXACT COMMAND>
- CI environment (OS, Node/Go/Python versions, etc.):
Failing output (verbatim, include the first error and 20 lines above it):
<PASTE LOG>
Constraints:
- Propose the smallest possible code change that makes CI pass.
- Do NOT rewrite/refactor unrelated code.
- Do NOT touch files you do not need for the fix.
- If behavior changes, make it explicit and justify why it is correct.
Stop rule (no guessing):
- If the log is incomplete or you need more info (missing stack trace, config, versions, failing test name), STOP and ask only the minimum questions needed.
Your response format (follow exactly):
1) Evidence: Quote the exact log lines that matter.
2) Hypothesis: Explain the most likely cause in 2-4 sentences.
3) Smallest fix: Describe the minimal change and why it addresses the evidence.
4) Patch: Provide a unified diff.
5) Follow-up: Tell me the exact command(s) to rerun locally to confirm.
Then, write ONE regression test (or tweak an existing one) that would fail before this fix and pass after it, to prevent the same failure class.
- Keep the test focused. No broad test suites.
- If a test is not feasible, explain why and propose the next-best guardrail (lint rule, type check, assertion).
减少来回沟通的两个细节:
最快丢失时间的方式是接受一个“清理”型补丁,它一次修改五处。事先定义“最小”:让失败的作业通过的最小 diff,风险最低且验证最快。
一个简单的规则很有效:先修症状,再决定是否值得做更广的重构。如果日志指向一个文件、一个函数、一个缺失的 import 或一个边缘情况,就把注意力放在那里。避免“顺手改”的修改。
如果确实需要备选方案,只问两种且仅两种:“最安全的最小修复” vs “最快的最小修复”。你想要的是权衡,而不是一大堆选项。
还要要求本地验证与 CI 匹配:让对方给出 CI 运行的相同命令(或最接近的等价命令),这样你能在几分钟内确认:
# 运行 CI 所运行的同一单元测试目标
make test
# 或 CI 使用的确切脚本
npm test
如果回复建议的是大改动,就逼问:“展示能修复失败断言的最小补丁,且不要包含无关的格式化或重命名。”
没有测试的修复是在打赌不会再遇到同样的问题。总是要求一个在修复前失败、修复后通过的回归测试。
明确“好”的标准:
一个有用的模式是要求四件事:放测试的位置、测试命名、测试覆盖的行为,以及简短说明为什么它能防止将来的类似错误。
可复用的附加要求:
举例:CI 显示当 API handler 收到空字符串 ID 时 panic。不要只要求“为这一行写测试”。要求一个覆盖无效 ID(空字符串、空白字符、错误格式)的测试。最小修复可能是返回 400 的守护语句。回归测试应断言多个无效输入的行为,这样将来有人重构解析逻辑时 CI 会立刻报错。
如果项目已有测试约定,请写明;如果没有,要求它模仿同包或同文件夹中附近的测试,并保持新测试简洁可读。
粘贴包含错误及其上方 20–40 行的 CI 日志部分。还要粘贴 CI 失败时使用的精确命令和关键环境信息(操作系统、运行时版本、重要标志)。
然后让它用白话复述失败并指出日志中证明该结论的行。如果它不能引用日志,就说明它没真正读日志。
要求先给出能让失败命令通过的最小代码改动。反对重构。应用之前让它列出:
应用补丁并在本地运行导致失败的精确命令(或在相同 CI 作业中运行)。如果仍然失败,只粘贴新的失败输出并重复。把上下文减到最小有助于回答更聚焦。
一旦绿灯,新增一个能在修复前失败、修复后通过的测试。保持针对性:一个测试,一条目的。
再次运行包含新测试的命令以确认你不是把错误“静默”了。
要求简短的提交信息和 PR 描述,包含:出错内容、改动、如何验证以及哪个测试防止复发。审阅者看到清晰理由会更快通过。
一个常见场景:本地一切正常,某个小改动让 CI 在运行时失败。下面是一个来自 Go API 的简单例子:handler 开始接受仅包含日期的值(2026-01-09),但代码仍只按完整的 RFC3339 时间戳解析。
这是应该粘贴的简短片段(保短但包含错误行):
--- FAIL: TestCreateInvoice_DueDate (0.01s)
invoice_test.go:48: expected 201, got 400
invoice_test.go:49: response: {"error":"invalid due_date: parsing time \"2026-01-09\" as \"2006-01-02T15:04:05Z07:00\": cannot parse \"\" as \"T\""}
FAIL
exit status 1
FAIL app/api 0.243s
现在用一个强制引用日志、最小修复并带测试的提示:
You are fixing a CI failure. You MUST use the log to justify every claim.
Context:
- Language: Go
- Failing test: TestCreateInvoice_DueDate
- Log snippet:
<PASTE LOG>
Task:
1) Quote the exact failing line(s) from the log and explain the root cause in 1-2 sentences.
2) Propose the smallest possible code change (one function, one file) to accept both RFC3339 and YYYY-MM-DD.
3) Show the exact patch.
4) Add one regression test that fails before the fix and passes after.
Return your answer with headings: Evidence, Minimal Fix, Patch, Regression Test.
一个好的回答会指出解析格式不匹配,然后在一个函数里做一处小改动(例如在 invoice.go 中的 parseDueDate)先尝试 RFC3339,再回退到 2006-01-02。不要重构或新增包。
回归测试是护栏:发送 due_date: "2026-01-09" 并期望 201。如果以后有人移除回退解析,CI 会以相同的失败类别再次报错。
最快浪费一小时的方式是只给出问题的裁剪视图。CI 日志很吵,但有用的部分通常在最后错误之前约 20 行左右。
一个陷阱是只粘贴最后一行红字(例如“exit 1”),而隐藏了更早的真实原因(缺失环境变量、快照失败或第一个崩溃的测试)。解决办法:包括失败命令和第一个真实错误所在的日志窗口。
另一个时间陷阱是允许模型一路“清理”代码。额外的格式化、依赖升级或重构会让审查更难、也更容易引入其他问题。解决办法:把范围锁定为让失败步骤通过所需的最小代码改动,并拒绝任何无关改动。
一些需要警惕的模式:
如果你怀疑是非确定性问题,不要用重试掩盖。去掉随机性(固定时间、设定 RNG 种子、隔离临时目录),让信号清晰。
在推送前做一次简短的理智检查,目标是确保改动是真实、最小且可复现的,而不是一次侥幸通过。
最后,运行比单个失败作业稍宽一些的测试(例如 lint+单元测试)。常见的问题是某个修复通过了原始作业,但破坏了其它目标。
如果你希望这个方法长期为你节省时间,就把提示和响应格式当作团队流程的一部分。目标是可重复的输入、可重复的输出,减少“神秘修复”破坏其它功能的情况。
把最佳提示保存为仓库片段并固定到团队聊天里。目标是:
如果你偏好以聊天为中心的工作流,可以在 Koder.ai 里运行相同的修复-测试循环,使用快照来试验,准备好后再把源码导出回常规仓库。
先看第一个真正失败的地方,而不是最终的 exit 1。
让它证明它确实读了日志。
使用这样的约束:
把默认目标设为让失败步骤通过的最小补丁。
通常意味着:
在 CI 绿灯前,避免“清理”类的大改动。
粘贴足够的上下文以便重现失败,而不是仅仅最后一行红色日志。
包括:
go test ./..., npm test, flutter test 等)。可以——用简单明了的语言重复你不允许的内容并把它写进约束里。
示例约束:
这样可以让回复更聚焦、更易于审查。
先修最早出现的真实错误。
不确定时,让模型识别日志中的第一个失败步骤并优先处理它。
把不稳定性视为需要消除随机性的信号,而不是增加重试次数。
常见的稳定方法:
一旦变为确定性,最小修复通常就很明显了。
要求给出 CI 使用的精确命令,然后在本地运行它。
如果本地难以复现,要求在仓库里给出一个最小重现用例(一个触发相同错误的单测或目标)。
写一个重点明确的回归测试:修复前会失败,修复后会通过。
好的目标包括:
如果是 lint/构建失败,把等同的“测试”变成更严格的 lint 规则或检查。
使用快照或回滚机制使实验易于撤销。
一个实用的循环:
在 Koder.ai 中使用快照可以快速迭代,而不会把实验性的改动混入最终补丁。