从 Bug 修复到需求开发:iOS AutoFix Agent 的 V3-V5 演进之路
在上一篇文章里,我解释了为什么选择自建 Agent 架构,而不是做 IDE 插件——核心在于要拥有”流程控制权”,而不是”平台适配权”。
那篇文章写完后,系统先后经历了 V3、V4、V5 三个大版本的迭代。我打算在这篇里把这段演进过程讲清楚:每一版做了什么、为什么这么做、踩了什么坑、优化思路是什么。
一、V3:从单文件到引擎架构
问题起点
V2 的核心问题是:代码全部堆在一个 1100+ 行的 PreciseLocator.ts 文件里。prompt 编排、工具分发、Agent 循环、评估逻辑全部混在一起,新增任何功能都要触碰这个巨型文件。更重要的是,V2 在复杂 Bug 上能力不足——8 轮搜索子代理、每轮只有 2-3 个工具调用,对于需要追踪多层调用链的 Bug,搜索深度明显不够。
架构重构:AgentEngine + TaskProfile
V3 的首要任务是拆文件,但不是机械地拆,而是找到正确的分层方式:
1 | AgentEngine(通用执行核心) |
Engine 负责”怎么跑”,Profile 负责”跑什么”。新增任务类型只需实现新 Profile,不用改 Engine。这个模式后来证明极其有价值——V4、V5 的十几种 Profile 都复用了同一个 Engine。
子代理能力强化
架构拆完之后,重点放在增强子代理的搜索能力:
| 维度 | V2 | V3 | 变化 |
|---|---|---|---|
| 搜索轮次 | 8 轮 | 12 轮 | +50% |
| 每轮工具调用 | 2-3 个 | 5-9 个并行 | +200% |
| 工具执行方式 | 串行 for | 限流并发(limit=4) | token -37% |
| 搜索工具 | ripgrep + read_file | + find_files | 增加文件名维度 |
其中最有意思的是强制提交轮的上下文压缩。V2 时,子代理在强制提交轮(第 14-15 轮)拿到的是完整的历史消息,约 100K tokens。LLM 面对这么大的上下文,实际有效处理率只有 33%。V3 把强制提交轮的 context 压缩为约 15K 的结构化摘要,有效率直接从 33% 拉到 100%。
渐进式催促机制
V2 只有两段式控制:第 8 轮”给你个激励”,第 14-15 轮”强制提交”。V3 换成了 4 级渐进催促:
1 | Round 1-7: 自由搜索 |
从用户体验角度看,这很像给实习生分配任务时的跟进节奏——不是一上来就催,而是在关键节点给出提示,让他有机会自我调整。
性能对比
| 指标 | V2 | V3 |
|---|---|---|
| 总耗时 | ~283s | ~163s(**-42%**) |
| LLM 调用次数 | ~22 次 | ~17 次(**-23%**) |
| Token 消耗 | ~125K | ~95K(**-24%**) |
二、V4:领域知识沉淀与可观测性
V4 是个大版本,做的事情很多,但有一条清晰的主线:**从”能定位”到”知道自己在做什么”**。
引擎内核的四个关键优化
基于对 V3 的深度审查(是的,我用 LLM 跑了一次完整的 Code Review),发现了四个核心问题,V4.3 全部解决:
1. 分阶段 Prompt 注入
V3 的主 Orchestrator 有 350+ 行系统 prompt,角色定义、工具说明、bug 类型分类、退出条件全部揉在一起。LLM 在超长 prompt 中有著名的”Lost in the Middle”问题——中间部分指令遵循率明显下降。
解决方案是把 prompt 按任务阶段动态注入:
- 探索阶段:只给角色 + 工具 + bug 类型
- 分析阶段:注入评估规则
- 提交阶段:注入三问法和退出条件
2. Scratchpad 结构化草稿板
V3 每 5 轮压缩一次对话,问题是关键的文件路径、行号、调用链会在压缩中丢失,Agent 可能在后续轮次重复搜索已经找过的文件。
V4 引入了 Scratchpad:Agent 可以通过 note_finding 工具主动记录关键发现,这些记录被标记为 _isScratchpad: true,压缩时跳过,每轮注入为 system message。等于给 Agent 配了一个不会被压缩的小本本。
3. 置信度驱动退出
把”第 12 轮强制停”改为”置信度 ≥ 0.85 时可提前停”。不只是节省 token,更重要的是让 Agent 的退出决策与任务完成质量真正绑定,而不是靠”计时器”。
4. 结构化搜索结果传递
V3 的子代理提交一份文本报告,主 Agent 从文本里提取发现——有信息损失,且可能误解。V4 改为结构化数据传递:
1 | interface StructuredSearchResult { |
BugTypePolicy:领域知识结构化
V3 有 11 种 bug 类型的支持,但相关的搜索策略、分析维度、退出条件都硬编码在一个巨大的 switch-case 里。V4 把它重构为 BugTypePolicy 注册表——每种 bug 类型有独立的策略文件,PolicyRegistry 负责查找和组合。
这解决了一个本质问题:领域知识和执行逻辑分离。现在增加一种新的 bug 类型,只需要写一个策略文件,不用改 Agent 循环代码。
RAG 知识库
V4 引入了两层知识系统:
- ModuleDoc:模块级别的架构文档,通过 DocGen 命令自动生成
- CaseDoc / FixRecord:历史成功修复案例,每次 fix 成功后自动入库
RAG 检索有 7 条并行路径(全文检索 / 路径匹配 / 符号检索 / 依赖关系 / 案例模块 / 历史修复记录 / Knot 知识库),每条路径独立计算命中率,可以在 Metrics 里看到哪条路径最有价值。
Metrics 可观测性体系
V4 构建了完整的可观测性体系:
- 搜索质量指标:工具调用分布、计划命中率、方向利用率、证据一致性分数
- 成本效率指标:每结果 Token 消耗、按模型估算 USD 花费
- 历史聚合报告:
npm run metrics,按命令/日期汇总,sparklines 趋势可视化 - 基线评测:
--save-baseline保存基线,--check-baseline回归检测
这套体系解决了一个关键问题:不再靠”感觉”来判断系统有没有在变好。每次改动都能用数据说话。
多架构 TrackingModel
iOS 项目有 MVC、MVVM、VIPER 等多种架构,每种架构的数据流层次不同,定位路径也不同。V4 把追踪模型做成了可配置的 ArchitectureProfile:
1 | MVC_CGI: UI → 数据装配 → Model → CGI 层 |
在 autofix.config.json 里指定 projectArchitecture,系统自动选用对应的追踪策略。
三、V5:从 Bug 修复到需求开发
V5 是一次战略转向。V4 解决了”能不能修 Bug”的问题;V5 的目标是:能不能让系统完成一个完整的 iOS 需求开发任务。
Pipeline Orchestrator:声明式流水线
V5 引入了 Pipeline Orchestrator 作为核心架构基座。这是把”一组命令串起来执行”升级为”声明式流水线编排”的关键一步。
一条 Pipeline 长这样:
1 | [需求分析] → Gate → [方案设计] → Gate → [影响评估] → [代码实现] → [编译验证] → [代码审查] |
每个 Gate 可以是三种类型之一:
- MetricGate:置信度阈值、编译结果等指标判断
- AIGate:LLM 评估方案可行性或代码质量
- HumanGate:人工确认(复用 acknowledge 机制,不打断 terminal)
Gate 失败时,系统不是直接报错,而是带着失败反馈回退到指定阶段重新执行。回退上限是双层计数(每个 Gate 独立计数 + 全局总计数),防止死循环。
一个设计细节:纯 Nullability 编译错误(Swift 的 optional 相关警告)不消耗有效 retry 配额。这是因为这类错误往往是模型生成代码时的习惯问题,让它多试几次通常能自己修好,不应该把宝贵的 retry 机会浪费在这上面。
需求开发流水线实战
以一个真实案例说明整个流程:【歌手主页】歌手写真切换交互+基础体验优化(TAPD 需求)。
流水线六个阶段:
1 | ① Analyze(需求分析) |
全程大约 1 小时 5 分钟,中间两次人工确认(方案设计后、代码实现前),其余步骤全部自动执行。
多模型评测框架
V5 做了一件有意思的事:用同一个需求,分别跑 5 种不同的模型 + 接入方式组合,用 Claude Sonnet 4.6 进行 10 分制精评,验证不同配置下的效果差异。
评估维度:文件定位准确性(25%)、分析深度(25%)、实现计划质量(20%)、风险识别(15%)、执行效率(15%)。
结论很有意思:
| 配置 | 综合得分 | 特点 |
|---|---|---|
| claude-opus + 内置引擎 | 8.8 | 覆盖最广、风险识别最深 |
| deepseek-4 + ACP | 8.5 | 行号级精度、零幻觉 |
| claude-4.6 + claude-internal | 8.2 | 流程最稳定,但效率问题(搜索了大量 Android/Kotlin 代码) |
| glm-5 + CodeBuddy SDK | 6.7 | 最快(105s),质量尚可 |
V2 效率垫底的根因很清楚:项目类型上下文注入不足——LLM 不知道这是 iOS/ObjC 项目,搜索方向跑偏了。这类问题很容易修,但靠人工 review 日志才能发现。有了系统化的评测框架,这类问题能够被量化追踪。
四、优化思路总结
经历三个版本的迭代,我对 Agent 系统优化形成了几个比较稳定的判断:
1. 双层约束优于单层
改 LLM 行为有两个层面:工具 description 和 Prompt。只改其中一层,效果有限。
以工具使用纪律为例,V5 在工具的 description 里加了明确的使用边界(”使用前先 ripgrep 确认位置,禁止无目标读取 >100 行”),同时在 Prompt 里加了反模式禁止列表。两层协同,ripgrep 调用减少了 43%。
工具层侧重”什么时候该用”,Prompt 层侧重”什么不能做”,互相补充。
2. 保守设计比激进触发安全
以早停机制为例,V5 的场景 B(高质量证据早停)有三个条件必须同时满足:
- 至少有一个方向置信度 ≥ 0.8
- 总发现数 ≥ 3
- 剩余方向全为低优先级
任何一个条件单独满足,都不触发早停。这个保守设计多次避免了因为”差点找到了”而漏掉根因的情况。
3. 事前 + 事中 + 事后的可观测性层次
优化 Agent 有三个时机:
- 事前:通过工具 description 和 Prompt 预防低效行为
- 事中:检测工具使用比例(如 read_file / ripgrep 比 > 2:1 报警)
- 事后:早停快照记录跳过了哪些方向,用于效果回溯
这个分层体系比单纯看”成功率”有价值得多,因为它告诉你为什么成功或失败。
4. 修复精准性比修复成功率更值得关注
V5 优化前,修复阶段 LLM 调用 6 次,Token 60K+,核心原因是搜索块(search block)匹配到了多个位置,修错了地方,然后重试。解决方案很简单:要求 search 块必须包含前后 1-2 行上下文,确保唯一匹配。
改动 10 行代码,修复阶段 Token 下降 57%,LLM 调用从 6 次降到 3 次。
真正的成本不是模型调用费,而是重试浪费的时间。 把匹配精准度做好,比提升模型能力更有效率。
五、当前架构全景
经过 V3-V5 的演进,系统的层次结构已经比较清晰:
1 | ┌──────────────────────────────────────┐ |
入口层做薄,Pipeline 层负责编排,Engine 层通用复用,Profile 层封装差异,知识层持续沉淀。
六、下一步
还有几件事没做完:
1. 并行搜索方向:目前搜索方向是串行执行的,彼此独立的方向完全可以 Promise.all。预期对 3+ 方向的任务,总耗时能再降 40-60%。
2. 搜索结果缓存:同一仓库对同一 pattern 的搜索结果,绑定 Git commit 做缓存。对重复出现的 Bug 类型(比如 accessibility 问题),可以显著减少重复检索。
3. 模型分级调度:现在不同环节用的模型没有明显区分。提取结构化信息、消息摘要这类简单任务,用小模型足够了;精确定位和代码生成,才需要最强模型。分级后成本可以进一步下降。
4. 反馈闭环:CR 通过或拒绝的结果,目前没有系统性地回流到知识库。这个闭环一旦建起来,系统会越来越像”会学习的故障处理平台”。
七、一些反思
回看这三个版本的演进,有几个观察:
架构重构的价值超出预期。 V3 把 1100 行单文件拆成 Engine + Profile 模式,当时以为只是”代码整洁”的问题。但到 V4 需要支持 11 种 bug 类型策略、10+ 种 Profile,到 V5 需要在 Pipeline 里复用这些 Profile 时,这个架构决策带来的红利远远超过了重构的成本。
可观测性不是锦上添花。 没有 Metrics 之前,优化靠直觉。有了 Metrics 之后,每个优化都能量化,失败案例能被追溯,回归能被检测。这套体系建得越早越好。
领域知识的密度决定系统价值。 通用 Agent 框架(循环、工具分发、压缩)是基础设施,可以替换;BugTypePolicy、多架构 TrackingModel、修复策略库、历史案例这些领域资产,才是真正的壁垒。前者越薄越好,后者越厚越值钱。
从”工具”到”流水线”是个重要的认知跃迁。 单个命令(locate / fix / cr)组合使用时需要大量手工协调;Pipeline 把这些步骤声明式地串起来,加上 Gate 和回退,系统才真正具备”替程序员执行一整套流程”的能力,而不只是”帮程序员在 IDE 里更快操作”。
这条路还很长,但方向是对的。