周一早晨,你醒来发现有五个严重的 Bug 报告。你的审查监控工具已经完成了它的工作。它捕捉到了每一个崩溃报告,每一个愤怒的一星评论,以及每一条“点击保存时应用卡死”的反馈。你很清楚哪里出了问题。但你不知道该去哪里查找。
这就是我在构建第一个流水线后遇到的瓶颈。它能毫无压力地监控应用评论和传入的崩溃日志,并将每条反馈分类到整齐的桶中:Bug、崩溃或功能请求。仪表盘看起来很健康,但实际的调试过程却并非如此。
知道 Bug 的存在仅仅是漫长旅程的第一步。我仍然需要打开 IDE,使用 grep 遍历模块,将堆栈跟踪与当前代码库进行交叉引用,并在脑海中重建失败路径。当工单不断堆积,而咖啡还很烫时,这种手动“考古”会消耗你根本无法承受的时间。我需要流水线不仅仅是标记问题,我需要它去调查问题。
因此,我围绕一个单一目标重建了系统:接收原始的 Bug 报告并返回经过验证的诊断结果。不是一段 LLM 的随笔,而是一个结构化的发现结果,指明文件名、指向代码行、评估风险并建议修复方案。以下是它的实现过程。
为什么结构化优于聊天日志
我使用 PydanticAI 构建了这个调查 Agent。原因很简单。当你要求语言模型对代码进行推理时,它的默认输出是一段友好的文本流。这可能对人类读者有帮助,但对下游脚本来说毫无用处。我需要一个机器可读的契约。
该 Agent 返回一个包含四个特定字段的验证数据模型:根本原因、受影响的文件、建议的更改,以及对复杂性和风险的评估。如果模型缺失字段或幻觉出一个文件路径,验证就会失败,我会立即发现。这种严谨性保证了流水线的可靠性。
为了进行实际的侦探工作,该 Agent 仅获得四个只读工具,除此之外别无其他。它可以利用 grep 搜索代码、读取文件的特定行范围、列出目录内容,以及定位类或函数等符号。只读是关键部分。我不希望一个拥有写权限的 Agent 在凌晨两点到处乱逛我的代码库。先理解,后编辑。
代码库地图:工具之前的上下文
第一版 Agent 虽然准确,但成本极高。它消耗 Token 的速度就像一个在原地打转的游客。模型会先调用 list-dir,然后是 grep,接着是读取文件,然后再调用 list-dir,通过一次次昂贵的 Token 消耗,缓慢地构建项目的结构模型。
解决方法是在 Agent 开始工作之前生成一个紧凑的代码库地图(repo map)。这张地图是对代码库的高度概括:关键文件、它们的主要功能或类,以及主要模块是如何连接的。把它想象成给 Agent 一个 GPS,而不是让它通过不断试错来发现道路。
有了这张地图作为上下文,Agent 就不会浪费调用去确认 src/utils/parser.ts 是否存在。它已经了解了地形,会直接奔向冒烟的脊线。这一项改动彻底消除了“漫游阶段”。
工具漏斗:强制得出结论
即使有了地图,Agent 仍可能犹豫不决。它会发现一个可疑文件,然后自我怀疑,接着再次搜索,然后阅读另一个文件,陷入“再检查一次”的死循环。我需要一种方法来强制推进进度。
我实现了一个三阶段的工具漏斗,随着任务的推进,限制 Agent 可以执行的操作。
第一阶段是探索。Agent 可以完全访问所有四个工具。它可以进行搜索、浏览和读取任何它认为需要的东西,以在推理中复现 Bug。
第二阶段是深入调查。一旦 Agent 识别出了可能的故障点,它就会失去发现类工具。它只能读取文件。不再能使用 grep,不再能列出目录。在这个阶段,它必须研究已经找到的代码,并构建其证据链。
第三阶段是输出。所有工具都被锁定。Agent 不再能查询代码库。它必须坐下来编写报告。这防止了陷入“让我再检查一下”的无尽螺旋。
这个漏斗将每次分析的平均工具调用次数从 40 多次降低到了大约 10 次左右。Agent 变得更快、更便宜,而且矛盾的是,它变得更有信心了,因为它必须对结论负责。
保持后端的可替换性
我不想将系统硬编码为仅依赖单一的模型提供商。我会根据任务使用不同的引擎。有时使用 Claude Code,有时使用 Grok Build,有时则使用当前最便宜的引擎。为了保持核心逻辑与提供商无关,我将工作分成了两个阶段。
第一阶段是探索。编程 Agent(可以是任何能力出众的模型)读取 repo map,使用工具,并生成一份原始的 Markdown 报告。这是最昂贵的思考环节。
第二阶段是结构化。一个廉价且快速的 LLM 获取该 Markdown 并将其重新格式化为严格的 Pydantic 模型。这个阶段几乎不需要推理能力。它仅仅是提取和格式化,因此可以在轻量级硬件上运行。
由于边界清晰,我可以更换后端而无需触动校验逻辑。Markdown 报告充当了探索性大脑与我实际使用的结构化输出之间的通用适配器。
真正奏效的方法
这种设置改变了我处理传入 Issue 的方式。分类层仍然负责将 Bug 与功能需求区分开来,但现在分析层会在其后立即接手。当我打开编辑器时,文件路径、行范围以及建议的修改已经准备就绪。我仍然会手动审查所有内容。这是辅助,而非自动驾驶。但以往那种上下文收集工作...
