前言
从最早思考《为什么要做一个 iOS bug 自动修复的 agent 程序》,到 V3 把单文件原型重构成 AgentEngine 引擎、V4 把领域知识结构化、V5 把「完成需求开发」提成唯一 P0 并引入 Pipeline 编排基座,这个项目断断续续做了两个月。之前我写过一篇《从 Bug 修复到需求开发:iOS AutoFix Agent 的 V3-V5 演进之路》,把版本演进的脉络讲清楚了。
接下来我的重心会转到和其他同学共建另一个 Agent 项目上。所以趁记忆还热乎,给 iOS AutoFix 做一份阶段性收尾——这篇不再复述版本演进,而是把这一年踩过坑、验证过的与领域无关、可以直接搬到下一个 Agent 项目的工程经验沉淀下来。
一句话定位整个项目的终态:
V4 解决「能不能定位/修复一个 Bug」,V5 把「完成一个需求开发」提升为唯一 P0,并引入 Pipeline Orchestrator(流水线编排基座),让
分析 → 设计 → 闸门 → 实现 → 验证 → 评审以声明式、可配置、支持闸门与智能回退的流水线串起来。
V5 走到了哪里
V5 围绕三条主线展开:
| 主线 | 名称 | 角色 |
|---|---|---|
| 主线 0 | Pipeline Orchestrator | 架构基座:声明式流水线引擎 + 闸门(Gate)+ 智能回退(Rollback)+ 状态持久化 + 可观测性 |
| 主线 A | 定位与修复质量 | 继续打磨 Bug 定位/修复/经验复用,在 V5 中承担「基础设施 + 壁垒」角色 |
| 主线 B | 需求开发能力 | 从需求分析、改动点识别、多文件写入到限定场景的完整需求实现 —— V5 唯一 P0 |
落地形态是一条六阶段需求开发流水线,以一个真实 TAPD 需求为例,全程约 65 分钟、中间两次人工确认:
1 | ① 需求分析 → ② 方案设计 [HumanGate] → ③ 影响评估 |
到 V6 雏形,需求开发已经从「勾哪端就各自独立跑 Pipeline」演进到「一个需求、一次整体分析、按涉及端分别落地」——iOS / Android / Kuikly 三端共享一次跨端分析,再并发分端实现,保证跨端协议字段/事件名一致。
当前的分层架构
收尾时整个系统稳定在 5 层。理解这张图,就理解了这个 Agent 的全部骨架:
1 | 入口层(CLI / GUI / 企微机器人) |
最关键的一组抽象,也是这一年最值钱的设计决策:
- Engine 管「怎么跑」,Profile 管「跑什么」。通用的 Agentic Loop(循环、工具分发、压缩、容错)沉到 Engine,场景差异(角色、工具集、退出条件、领域知识)放进 Profile。十几种 Profile 复用同一个 Engine,新增一种任务类型只要写一个新 Profile。
- Pipeline 管「编排」。Engine 只关心单个 Agent 跑完一轮,Pipeline 负责把多个 Stage 串起来,并在阶段转换处插闸门。
可迁移的工程经验
下面这些是我打算带去下一个 Agent 项目的「行李」。它们大多与「修 iOS Bug」这件具体的事无关,是做任何 LLM Agent 都会遇到的问题。
1. 先有编排框架,再往里插能力
最容易犯的错是:先把一个个能力(分析、设计、实现)写成独立脚本,最后再用胶水代码串起来。结果就是回退、重试、断点续跑这些横切逻辑散落在各处。
V5 的做法是反过来——先建 Pipeline 编排基座,每个能力作为一个 Stage 插进去。回退、状态持久化、崩溃恢复、可观测性都由编排层统一提供。Pipeline First 是 V5 九条核心原则里的第一条,事后看这个顺序定对了。
2. 闸门是一等公民 + 智能回退
关键阶段转换处一定要设闸门(Gate),不通过则携带反馈智能回退,而不是直接失败。闸门分三类:
- MetricGate:置信度 / 编译结果等硬指标
- AIGate:用一次独立的 LLM 调用评估上一阶段产物质量
- HumanGate:人工确认(GUI 里做成倒计时确认弹窗)
回退用的是循环模型而非递归:每个 Gate 维护独立的回退计数,避免「设计→实现→验证失败→回设计→又失败」无限套娃。状态用原子写入持久化,进程崩溃能恢复到中断处。
3. 上下文工程:三个反直觉的结论
这是 Agent 工程里水最深的部分,几条经验都和直觉相反:
- 超长 system prompt 会「中段失忆」(Lost in the Middle)。主 Orchestrator 的 prompt 一度超过 350 行,模型对中间部分的遵循率明显下降。解法是分阶段注入:探索阶段只给角色+工具+方向,搜索完才注入评估规则和退出条件,准备提交时才注入「自我质疑」清单。瘦身后 exploration prompt 减了约 70%。
- 压缩会丢掉关键证据。每 5 轮压缩一次对话,会把搜索过程中发现的文件路径、行号、调用链一起压没,导致 Agent 重复搜已经找过的文件。解法是给 Agent 配一个不会被压缩的小本本(Scratchpad):通过
note_finding工具写入关键发现,标记_isScratchpad,压缩时跳过,每轮作为 system message 重新注入。 - 结构化数据 > 报告文本。子代理给主代理传「文本报告」会有信息损失且容易被误解,改成结构化的
keyFiles / codeSnippets / callChains / hypotheses / coverage后,主代理可以直接引用具体发现。
4. 置信度驱动早停,而不是机械计时器
最初的轮次控制是「第 8 轮提醒、第 12 轮强制提交」这种机械计时器——Agent 可能第 3 轮就找到答案还在空转烧 token,也可能第 12 轮没找到被强行提交。
改成置信度驱动早停:每轮自评置信度,但触发早停要三个条件同时满足——置信度 ≥ 0.8、总发现 ≥ 3、剩余方向全为低优先级。任一不满足就继续,宁可多跑也不「差点找到」漏掉根因。强制提交时打 low_confidence 标记,让下游知道定位可能不准。
5. 工具使用纪律:双层约束
让 Agent 别乱用工具,单靠 prompt 不够。有效的是双层约束协同:
- 工具 description 层(「什么时候该用」):写前置条件「使用前先 ripgrep 确认目标位置」、范围指引「命中行 ±20 行」、红线「禁止无目标读取 >100 行」。
- Prompt 层(「什么不能做」):明确列反模式——没 grep 就直接读大段、对低相关匹配反复扩大读取范围、重复读已读过的区域。
两层叠加后,ripgrep 调用从 7 次降到 4 次(**-43%**),平均每次工具调用数从 7 降到 5。
6. 写入安全是红线
只要 Agent 会改用户的代码,写入安全就不能妥协。V5 的红线是四件套:**--confirm 确认 + 事务写入 + 编译验证 + 自动回滚**。代码实现阶段所有写入打包成事务,编译不过就整体回滚,绝不留下半成品。这条在「修 Bug」时还能商量,到「需求开发」要改多文件时就是底线。
7. 改动的「精准性」比「成功率」更重要
修 Bug 最大的风险不是没修好,而是改出新 Bug 或改错位置。让 LLM 生成 diff 时,强制 search 块带上前后 1-2 行上下文确保唯一匹配,而不是只给变更行(LLM 从记忆重建代码会有细微偏差,导致静默匹配失败)。
就这一个约束 + 「最小修改原则」,让修复阶段 Token 从 60K 降到 26K(**-57%**),LLM 调用从 6 次降到 3 次。
8. 可观测性要覆盖事前/事中/事后
- 事前:靠工具 description 和 prompt 预防低效行为;
- 事中:检测工具使用比例(read_file / ripgrep > 2:1 报警)、重复搜索同一文件;
- 事后:
earlyTerminationSnapshot记录早停时跳过了哪些方向,配合evidenceHitRate/searchEfficiencyRatio/evidenceConsistencyScore等场景级指标,做基线保存和回归检测。
一组综合优化前后的实测对比(质量指标全部持平的前提下):
| 指标 | 优化前 | 优化后 | 改善 |
|---|---|---|---|
| 总 Token | 107,951 | 67,098 | -37.9% |
| 总耗时 | 272.9s | 191.0s | -30.0% |
| LLM 调用 | 18 | 13 | -27.8% |
| 主循环收敛轮次 | 3 | 2 | -33.3% |
9. 多模型评测与降级链
不要押注单一模型。V5 建了一套 10 分制的多模型评测框架(评估维度:文件定位 25% / 分析深度 25% / 实现计划 20% / 风险识别 15% / 执行效率 15%),跑同一个需求对比:
| 配置 | 综合得分 | 特点 |
|---|---|---|
| claude-opus + 内置引擎 | 8.8 | 覆盖最广,风险识别最深 |
| deepseek-4 + ACP | 8.5 | 行号级精度,零幻觉 |
| claude-4.6 + claude-internal | 8.2 | 流程最稳,但会跑偏去搜 Android 代码(项目类型上下文注入不足) |
| glm-5 + CodeBuddy SDK | 6.7 | 最快(105s),质量尚可 |
线上则用降级链(CodeBuddy Claude → GLM → DeepSeek)保可用性,并对不支持 function calling 的模型做 JSON 解析降级。评测决定选型,降级链保底。
10. 知识沉淀要分类,并设晋升路径
知识库不是把文档堆一起。V5 把知识分四类——案例 / 经验 / 记忆 / 索引,共享知识库走独立 git 仓、个人记忆落本地,每条知识带 confidence 和晋升路径(被多次验证的经验才升级为共享知识),并设入库准入条件和触发器。这样知识库才不会越长越脏。
诚实的局限:为什么在这个点暂停
核心闭环已经成形,剩下的是打磨,所以这是个合理的暂停点。但有几处确实没做完,留个记录:
- 跨端 analyze 还没真正复用(Phase B):V6 目前是把跨端整体方案作为
additionalContext注入各端,各端仍会冗余跑一遍自己的 analyze,时间有浪费。彻底的做法是用preloadedAnalysis直接跳过各端 analyze stage,但那会侵入 PipelineEngine 内部,风险大,所以先用了轻耦合方案。 - 粗筛权重是手工拍的:5 策略加权(直接路径 100 / SQLite 索引 10-40 / 目录推断 8 / Git 热点 5 / Bug 类型专项 12-15)很难对所有项目通用。正解是用历史定位数据做权重调优,或者干脆让 LLM 直接做粗筛——现在的模型能力够用了。
- 并发统计口径会串:多端并发时 token / 日志统计可能互相累加,不影响功能,但口径乱。
- 旧入口还没收尾:三个分端独立 Tab 仍可见,等新的统一入口稳定后再隐藏。
下一站:共建另一个 Agent
回头看,这一年真正沉淀下来的,不是「怎么修 iOS Bug」,而是「怎么造一个能干活的 Agent」。这两件事可迁移的程度完全不同:
- 可以直接搬过去的(与领域无关):Pipeline 编排 + 闸门 + 智能回退、Engine/Profile 解耦、上下文工程那三条、置信度早停、写入安全四件套、事前/事中/事后可观测性、多模型评测与降级、知识分类沉淀。
- 必须重做的(iOS 特化):仓库索引与页面映射表、编译验证(xcodebuild / gradle / KMP)、各端的 Profile 与领域知识。
下一个项目是和其他同学一起共建,正好可以把上面这套「与领域无关的 Agent 骨架」当成共识的起点,少走一遍弯路。iOS AutoFix 这边先告一段落,等下一个 Agent 跑起来,应该还有新的东西可以反哺回来。
总结
如果只能带走三句话:
- 先有编排,再插能力——横切逻辑(回退/续跑/可观测)必须沉到编排层。
- Engine 管怎么跑,Profile 管跑什么——这组解耦让一套引擎服务十几种任务。
- 上下文工程 + 写入安全是 Agent 能不能用的两道生死线——前者决定它聪不聪明,后者决定你敢不敢让它动你的代码。