iOS AutoFix Agent 阶段性收尾:可迁移的 Agent 工程经验沉淀

前言


从最早思考《为什么要做一个 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
2
① 需求分析 → ② 方案设计 [HumanGate] → ③ 影响评估
→ ④ 代码实现 [事务写入 + 自动回滚] → ⑤ 编译验证 → ⑥ 代码审查

到 V6 雏形,需求开发已经从「勾哪端就各自独立跑 Pipeline」演进到「一个需求、一次整体分析、按涉及端分别落地」——iOS / Android / Kuikly 三端共享一次跨端分析,再并发分端实现,保证跨端协议字段/事件名一致。

当前的分层架构


收尾时整个系统稳定在 5 层。理解这张图,就理解了这个 Agent 的全部骨架:

1
2
3
4
5
6
7
8
9
入口层(CLI / GUI / 企微机器人)

Pipeline Orchestrator —— 按什么顺序跑、卡在哪个闸门、错了怎么退

AgentEngine —— 单个 Agent 怎么跑:循环控制 / 工具分发 / Scratchpad / 对话压缩 / 置信度退出

TaskProfile —— 这一步跑什么:BugLocate / Fix / FeatureAnalyze / FeatureDesign / FeatureImplement / CodeReview / ImpactAnalysis

知识 & RAG 层 —— ModuleDoc + CaseDoc + FixRecord,7 路并行召回 + Knot 知识库

最关键的一组抽象,也是这一年最值钱的设计决策:

  • 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 跑起来,应该还有新的东西可以反哺回来。

总结


如果只能带走三句话:

  1. 先有编排,再插能力——横切逻辑(回退/续跑/可观测)必须沉到编排层。
  2. Engine 管怎么跑,Profile 管跑什么——这组解耦让一套引擎服务十几种任务。
  3. 上下文工程 + 写入安全是 Agent 能不能用的两道生死线——前者决定它聪不聪明,后者决定你敢不敢让它动你的代码。
-------------本文结束感谢您的阅读-------------

欢迎关注我的其它发布渠道