返回博客
X24LABS

删掉我们的正则分类器:当 AI 替代你自己造的抽象

我们花了几个月搭了一套加权正则引擎,把 CI 错误分成九类并附上置信度分数。然后我们删掉了它。这是它为什么必须走、以及被什么替换的故事。

v1 到 v2 的重写大部分是关于外部形态的变化。本地而不是远程,TypeScript 而不是 Python,CLI 而不是 CI job。值得单独写一篇的那部分是内部的。v2 的代码量比 v1 少很多,而缺掉的大部分都是我们刻意删掉的。

单次最大的一次删除,是我们的错误分类器。

它做什么

v1 的分类器是一个加权正则引擎。它读 CI 日志,用大约一百五十条 pattern 跑一遍,把错误分到九个桶里:lint、格式、类型、build、CI config、test 合约、复杂类型、逻辑,以及一个兜底的 unknown。每次分类都会带一个置信度分数。每个桶对应一种修复策略。

Stitch v1 的着陆页曾经很自豪地列出这九个类别。当时我们把它当作一个功能。

我们为什么要建它

分类器回应的是一个真实的问题。语言模型调用要花 token。CI 日志庞大。如果你把整份日志发过去,你会浪费上下文,而且结果更差,因为模型得自己去找信号。如果你先预处理日志、剥掉噪声、隔离相关错误、并标注它的类别,你就能省 token 并让模型有个起跑点。

所以我们建了一个预处理器。它在模型之前运行,产出一份紧凑的载荷,大意是 “这是一个类型错误,在 X 文件第 Y 行,这是 traceback,这是调用点附近的五行”。模型随后产出修复。这套是有效的。

它为什么必须走

两件事变了。

模型变得更擅长读原始日志。Claude 和 GPT 的每 token 成本下降,解析非结构化输出的能力变强。“把精准提取出的错误发给模型” 和 “把日志整份发给模型让它自己看” 之间的差距被抹平了。提取那一步不再值回它的成本。

相比之下,我们的分类器变差了。我们有给 TypeScript、Python、Go 的 pattern,没有给 Elixir、Rust、或者用户这周正在用的某个框架的 pattern。当一条正则匹配不上的日志进来,我们会把它路由到 unknown 桶,然后还是交给模型处理。这就是暗示。如果 fallback 有效,那条专门通道就是可选的。既然专门通道是可选的,它也就是我们无缘无故在维护的一个 bug 来源。

让我们突破的那一刻,来自 v2 的本地上下文。在开发者机器上,跑 stitch run claude 意味着我们在调用 Claude Code,而它是一个可插拔的智能体,已经自带读文件、跑命令、推理错误的工具。给它一份预分类好的载荷,就像给一个资深工程师递上一份预先填好的 bug 报告,还要求他用我们的模板。他们不需要它。模板在增加摩擦。

我们用什么替换了它

两样东西。一个比之前简单得多,另一个甚至不在同一层。

Stitch v2 仍然做少量预处理,但粗粒度、也诚实。它看 job 名字,决定要不要跑这个 job。叫 deploy-prod 的是 infra,本地跳过。叫 lint 的是 verify,本地跑。每个 job 一行决策。它不假装自己知道错误会是什么。

真正的诊断搬到了智能体里。当一个 job 失败时,Stitch 用一个简短、刻意平淡的 prompt 把日志交给智能体:这是 job,这是日志,这是仓库,请修一下。智能体做它在交互会话里会做的事。读文件,跑命令,试方案。Stitch 通过重跑 job 来验证结果。

删掉的总代码量是几千行。失去的总能力是零。诊断反而更好了,因为智能体能看到整个仓库,而不是五行摘录。

教训

这个教训很不舒服,但它在围绕 AI 智能体的工具里一再出现:你为帮助模型而建的抽象,往往在模型足够好之后就不再帮忙了。如果你同时拥有预处理器和 prompt,你会走到一个预处理器让 prompt 变差的点。你不会察觉,因为是你自己建的。

我们察觉到的唯一方式,是允许自己删掉它,看会发生什么。什么也没发生。这就是关键。

系列下一篇:Stitch 2.0 即将发布。你实际会安装什么、它会做什么。

返回博客