作者用20个日志样本对比了正则解析器与免费模型端点在检测构建错误上的表现,结果显示模型在多行隐藏错误上明显优于正则。
构建在凌晨 2 点 14 分失败。CI 日志有 41,000 行。我的第一个 grep 在第 12 行找到了一个错误。真正的失败藏在第 12,003 行。我的正则从未看到它。
那天晚上我不再信任我的解析器。我也不愿盲目信任模型。于是我建了一个基准测试,让它们公平地较量一番。
这篇文章就是那个基准测试。复制它,在你的日志上运行,大约 30 分钟就能得到你自己的数据。
免费模型端点能否比 40 行 Python 代码更好地读取构建日志?如果能,它究竟在哪些方面胜出?我要的是一个分数,而不是感觉。
两个参赛者。一份标注语料。一个评分脚本。
参赛者 A 是一个确定性解析器。Grep 风格的模式,零 AI,零意外。
参赛者 B 是一个免费模型端点。MonkeyCode 是一个开源项目,提供免费套餐,包含 10M tokens 和免费服务器选项。这使得整个评估可以免费运行。
披露:本文是 MonkeyCode 产品推广的一部分。
我构建了一个包含 20 个 log fixtures 的标注语料库。每一个都有已知答案。类别比数量更重要:
明显错误——单行失败,正则能立即捕获。
隐藏错误——多行失败,真正的消息出现在关键词之后两行。
诱饵——警告行中文件名包含"error"这个词。
干净日志——完全没有错误。正确答案应该是"no"。
WARNING: build/error_handler.py:12: unused import
用 naive 的 grep 搜索 error 会标记这一行。但它不是错误。好的分类工具必须拒绝它。
fixtures 文件是普通的 JSON:
[
{"log": "ERROR: build/main.c:12: undefined reference to `foo`", "error": true},
{"log": "WARNING: build/error_handler.py:12: unused import", "error": false}
]
40 行 Python 代码,故意写得 naive。因为那是我们大多数人会实际 ship 的:
import re
def regex_parse(log: str) -> dict:
patterns = [
(r"\berror\b|fatal|FAILED|Exception", "obvious"),
(r"undefined reference|No such file|not found", "hidden"),
]
for pattern, kind in patterns:
m = re.search(pattern, log, re.IGNORECASE)
if m:
line = log[:m.start()].count("\n") + 1
return {"error": True, "line": line, "reason": kind}
return {"error": False, "line": None, "reason": None}
快。确定性。完全不懂行。
模型收到相同的日志,截断到最后 2000 个字符。prompt 很严格:
You are a build-log triage tool. Return JSON only.
{"error": true|false, "line": int|null, "reason": "short text"}
Log:
<last 2000 chars>
harness 把客户端留成 stub。换入你端点的 SDK:
def model_parse(log: str) -> dict:
prompt = f"""You are a build-log triage tool. Return JSON only.
{{"error": true|false, "line": int|null, "reason": "short text"}}
Log:
{log[-2000:]}
"""
# response = your_model_client.complete(prompt)
# return json.loads(response)
raise NotImplementedError("Add your endpoint client here.")
两个解析器针对相同的 fixtures 运行。scorer 计算真正例、假正例和假负例:
import json, sys
def score(parser, fixtures):
tp = fp = fn = 0
for fx in fixtures:
pred = parser(fx["log"])
truth = fx["error"]
if pred["error"] and truth:
tp += 1
elif pred["error"] and not truth:
fp += 1
elif not pred["error"] and truth:
fn += 1
precision = tp / (tp + fp) if tp + fp else 0
recall = tp / (tp + fn) if tp + fn else 0
return {"tp": tp, "fp": fp, "fn": fn,
"precision": round(precision, 2),
"recall": round(recall, 2)}
fixtures = json.load(open(sys.argv[1]))
print("regex:", score(regex_parse, fixtures))
print("model:", score(model_parse, fixtures))
python compare.py fixtures.json
输出格式是这样的:
regex: {'tp': 6, 'fp': 4, 'fn': 4, 'precision': 0.6, 'recall': 0.6}
model: {'tp': 9, 'fp': 1, 'fn': 1, 'precision': 0.9, 'recall': 0.9}
这些数字是说明性的。你的日志得分会不同。重要的是正确读取模式。
多行错误。Grep 是面向行的。关键词落在第 12,003 行。真正的消息位于第 12,005 行。正则表达式报告了错误的行。
诱饵。包含"error"的文件名会产生假正例。
新的工具链。你从未编写的模式总是会漏过去。
幻觉的行号。模型有时会捏造一个不存在的行。
过度自信。它偶尔会将警告升级为错误。
非确定性。相同的日志,两次运行,两个判决。正则表达式永远不会这样。
延迟。两百个顺序调用需要几分钟,而不是毫秒。
每次分类调用使用大约 600 个 token。截断的日志是 2000 个字符,大约 500 个 token。prompt 和 JSON 回复又增加了 100 个。按照这个速率,10M 免费 tokens 覆盖大约 16,000 次分类调用。这就是这个评估值得运行的原因:测量本身就是免费的。
免费服务器选项意味着你可以在笔记本电脑之外运行 harness。不需要本地 GPU。不需要长时间运行的进程消耗你的电池。
如果你的模式已经工作正常,不要添加模型调用。你在无端增加延迟和不确定性。
如果你需要可重现的输出来进行审计,模型就是错误的工具。
如果你甚至无法标注 10 个 fixtures,你就无法测量任何东西。跳过基准测试并修复正则表达式。
二十个 fixtures 是一个冒烟测试,而不是基准测试。模型行为在不同版本之间会发生变化,所以要每月重新运行一次。我的标注是我的判断——你的日志在某些地方会有分歧。
harness 才是真正的产物。数字是你自己去收集的。
如果你想运行相同的评估而不花钱,MonkeyCode 的免费套餐(10M tokens、免费服务器)足以运行整个 harness。换入你的日志,标注 20 行,看看哪个参赛者你实际上应该信任。
然后告诉我:哪个类别首先打破你的解析器——多行错误还是诱饵?这个答案决定了是否值得构建第二轮。