利用免费层模型对CI日志和diff做分类,判断是flaky、regression还是环境问题,输出标签加简短理由,全程零推理成本。
失败的隐性成本
每一次红色 CI 构建都会打断团队的工作。有人必须读日志,有人必须判断:是 flaky、是 regression、还是环境问题?大多数时候这个判断是机械性的。一个小模型就能完成这项工作。本文构建了一个失败分类器。它运行在免费模型和免费服务器上。你得到的是一个今天就能运行的脚本。
失败分类的隐性成本
每一次红色的 CI 运行都会中断工作流。第一响应者阅读日志、检查 diff、然后猜测。大多数失败属于四个类别。猜测通常是对的,但它仍然要花费十分钟。乘以每个开发者和每个失败的构建,那就是巨大的时间成本。
自动化很少能提供帮助,因为工具太重了。全可观测性平台需要配置。自定义分类器需要标注数据。而免费层模型两者都不需要。
为什么小模型就够了
失败分类是一个短输入的分类任务。日志只有几百行。diff 只有几个文件。输出是一个标签和一段简短的说明。这不是重推理的工作。免费模型就能处理得很好。
MonkeyCode 是一个开源项目,正好契合这个场景。它提供免费模型和免费服务器选项。下面的流水线以零推理成本运行。该脚本使用通用 CLI 适配器,所以你可以之后切换后端。
披露:本文是 MonkeyCode 产品推广的一部分。
在写代码之前,先定义标签。使用五个类别。保持它们互斥。
表格即提示词。模型将证据映射到标签。证据字段是你需要验证的内容。
分类器脚本
下面的脚本读取 CI 日志和 diff。它将两者发送给模型。它期望一个包含三个字段的 JSON 响应:label、confidence 和 evidence。
#!/usr/bin/env python3
"""triage.py — classify a CI failure with a free-tier model."""
import json
import subprocess
import sys
from pathlib import Path
LOG_PATH = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("ci.log")
DIFF_PATH = Path(sys.argv[2]) if len(sys.argv) > 2 else Path("diff.txt")
LABELS = {
"flaky": "Test failed once, no related diff.",
"regression": "Diff touches the failing path.",
"environment": "Dependency, network, or timeout.",
"test_bug": "Assertion or fixture error.",
"unknown": "No clear signal.",
}
PROMPT = f"""
You are a CI failure triage assistant.
Read the log and the diff. Choose exactly one label.
Labels: {json.dumps(LABELS)}
Return JSON: {{"label": "...", "confidence": 0.0-1.0, "evidence": "..."}}
Evidence must quote the log. Do not invent facts.
"""
def run_cli(prompt: str) -> str:
"""Send a prompt to the model CLI via stdin and return stdout."""
result = subprocess.run(
["mc", "--free-server", "prompt"],
input=prompt,
capture_output=True,
text=True,
check=True,
)
return result.stdout
def main() -> None:
log = LOG_PATH.read_text()[-4000:]
diff = DIFF_PATH.read_text()[-2000:]
response = run_cli(PROMPT + f"\n\nLOG:\n{log}\n\nDIFF:\n{diff}")
try:
data = json.loads(response)
except json.JSONDecodeError:
print("{\"label\": \"unknown\", \"confidence\": 0.0, \"evidence\": \"Model output was not JSON.\"}")
return
print(json.dumps(data, indent=2))
if __name__ == "__main__":
main()
脚本会截断输入。长日志会丢失上下文,但尾部通常包含失败信息。diff 尾部包含最近的变更。
提示词有三个刻意设置的约束。首先,它强制输出一个标签。能回答"unknown"的模型会过度使用它。其次,它要求引用证据。这使得输出可验证。第三,它禁止编造事实。这保持模型的诚实。
你可以之后调整标签。一些团队把 regression 拆分成 logic 和 data。一些团队添加一个 migration 标签。结构保持不变:label、confidence、evidence。
在接触 CI 之前,先在一个保存的失败案例上运行脚本。命令很简单:
python triage.py ci.log diff.txt
你应该在 stdout 上看到 JSON。如果模型返回的是散文而不是 JSON,添加一个要求 JSON 的系统提示词。如果输出仍然格式错误,回退到 unknown 标签并手动审查。
一次健康的运行返回干净的 JSON。下面是一个真实的响应:
{
"label": "regression",
"confidence": 0.87,
"evidence": "test_auth.py:41 failed; diff changes validate_token() signature."
}
证据是你信任的部分。标签是一个假设。confidence 是一个提示,不是保证。
第二次运行可能返回不同的裁决:
{
"label": "flaky",
"confidence": 0.74,
"evidence": "test_orders.py:22 failed; diff touches billing.py only."
}
这就是模型发挥作用的地方。它读取 diff,发现没有关联,然后说出这一点。疲惫的开发者可能会错过这一点。
将脚本添加到你的流水线中。使用三个编号的步骤。
在 job 退出前捕获失败的日志和 diff。
运行分类器并将 JSON 写入文件。
将标签和证据作为评论发布到 commit 上。
一个最小的 CI 片段如下:
- name: Capture failure
if: failure()
run: |
tee ci.log
git diff origin/main...HEAD > diff.txt
- name: Classify failure
if: failure()
run: python triage.py ci.log diff.txt > triage.json
- name: Comment result
if: failure()
run: gh pr comment "$PR" --body "$(cat triage.json)"
这个评论给下一个开发者一个起点。它不替代判断。它省去了阅读的前十分钟。
先验证类别
不要立即将其接入 CI。先在上周的失败案例上运行。将模型标签与团队实际决定进行比较。追踪三个数字:regression 的精确率、flaky 的精确率,以及 unknown 率。
如果 unknown 率超过 30%,你的类别与日志不匹配。调整标签并重试。
验证成本很低。它只需要运行一次脚本和几分钟的阅读。它防止最坏的结果:一个自信地错误的模型。
当分类器说谎时
模型会犯错。把标签当作建议。evidence 字段才是真正的交付物。它指向日志中重要的行。
不要将其用于安全事件。不要将其用于值班告警。不要让它自动关闭 issue。这个流水线是一个分类辅助工具,不是权威。
谁不应该使用这个
没有人工审查步骤的团队会盲目信任标签。那比没有自动化更糟糕。受监管环境需要文档化的决策痕迹,而不是模型摘要。如果你的 CI 日志包含 secrets,不要将它们发送到任何远程模型。免费服务器选项改变的是成本,不是数据处理。
零成本尝试
完整流水线的运行成本为零。MonkeyCode 的免费模型和免费服务器覆盖了推理成本。脚本是一个模板。适配到你的 CI 系统和你的失败模式。
从上周的失败开始。在旧日志上运行分类器。将其标签与团队的决定进行比较。这种验证告诉你类别是否适合你的现实。如果适合,将其接入 CI,恢复每个红色构建的前十分钟。