面向 Android 手机系统软件与芯片研发的一次架构思考
我们想做一名 AI Debug Engineer。
不是一个会回答 Android 问题的聊天机器人,也不是一个能帮忙写几行代码的 Copilot,而是希望它能够逐渐接手过去由工程师完成的工作:理解问题、收集证据、提出假设、定位根因、修改代码,最后证明问题真的被解决了。
这个目标听起来并不离谱。今天的模型已经能读代码、执行命令、分析日志,也知道 Binder、HAL、SELinux、Linux kernel、Android Framework 等大量公开知识。可真正开始建设后,我们却很容易陷入一种熟悉的混乱:
- 有人把排查步骤写成 Skill;
- 有人把芯片手册摘录放进 Skill;
- 有人把历史案例总结成 Skill;
- 还有人把一整套从读日志到修改代码的推断过程也写成 Skill。
结果是 Skill 越来越多、越来越长,系统却不一定越来越聪明。出现错误时,我们甚至不知道该改模型、补知识、增加工具,还是重写某个 Skill。
问题可能不在于我们积累得不够多,而在于最开始没有分清:AI Debug Engineer 到底由什么组成。
从"能力"出发,但不要停在"知识、经验、技能"
一个自然的起点是把人的能力拆成知识、经验和技能。
这个思路有价值,因为调试确实同时需要三种东西:工程师要理解系统原理,要见过相似问题,也要会操作日志、代码、设备和调试器。
但直接把人的能力分类搬到 AI 系统里,会产生一个麻烦:"技能"这个词太宽了。
对人来说,"分析一次开机失败"可以叫技能,"使用 adb"也可以叫技能,"看到某种日志后知道下一步查哪里"仍然可以叫技能。而在 Agent 系统里,这三件事分别属于执行能力、推理判断和经验参考。如果仍然把它们都称为 Skill,系统边界很快就会消失。
所以我们最终收敛到了一个更小、也更清晰的结构:
text
┌─────────────┐
│ Agent │
│ 判断下一步 │
└──────┬──────┘
│
┌────────────┼────────────┐
│ │ │
┌───▼───┐ ┌───▼────┐ ┌───▼────┐
│ Tool │ │Knowledge│ │Experience│
│ 做动作 │ │理解系统 │ │参考过去 │
└───────┘ └────────┘ └─────────┘
最基础的 AI Debug Engineer,只需要四个核心部分:
- Agent:负责判断和决策
- Tool:负责执行动作并返回结果
- Knowledge:负责提供可依赖的系统事实
- Experience:负责提供过去问题的参考
这四个词不是为了制造新的术语,而是为了让系统出错时能够回答一个重要问题:究竟是哪一部分出了问题?
Tool:能做什么,而不是该怎么想
Tool 是最容易定义清楚的部分。它接受明确输入,执行一个动作,返回可观察的结果。
例如:
- 打开文件或搜索符号;
- 获取 logcat、dmesg、tombstone;
- 解析寄存器、反解堆栈;
- 查询设备属性和产品配置;
- 编译模块、刷写镜像、运行测试;
- 比较两个版本的代码或配置。
"打开某个文件"是 Tool;"打开后重点看哪个函数"不是 Tool。后者是 Agent 根据当前问题、系统知识和已有证据做出的判断。
这个区别很重要。Tool 应尽量确定、可复现、可测试。Agent 则必须保留选择空间。假如我们把"看到 watchdog 就固定打开 A、B、C 三个文件"写死在 Tool 中,它就不再只是执行动作,而是在偷偷替 Agent 做诊断决策。
工具可以变得很强,但不应该隐藏推理。
Knowledge:不是把 Android 百科再抄一遍
谈到知识,另一个问题马上出现:LLM 本身已经了解 Android,我们还需要建设知识库吗?
答案不是简单的"需要"或"不需要"。模型内部知识应该被视为一个很强的先验,但不是当前项目事实的权威来源。
可以把知识分成三层。
第一层是稳定、公开、通用的知识,例如 C/C++ 语义、常见并发问题、Linux 基础机制、Binder 的基本概念。大部分情况下,可以先依靠模型已有能力。
第二层是公开但与版本强相关的知识,例如某个 Android 版本的 Framework 实现、某条 AOSP 分支上的接口行为。这些内容不适合只凭模型记忆,应该在需要时查阅匹配版本的源码和文档。
第三层是企业与产品内部知识,例如私有 SoC 模块、寄存器定义、芯片 errata、Vendor HAL、产品配置、内部补丁以及未公开的设计约定。这些内容模型不可能天然知道,必须由系统提供。
因此,真正需要长期建设的知识,可以用一个不严格但很实用的公式来描述:
text
需要的知识
- 模型能够可靠掌握的通用知识
- 可由当前代码、设备和工具即时获取的事实
= 需要额外维护的内部知识
这也意味着我们不应该先启动一个"完整 Android 知识库工程"。更合理的方式是从真实调试任务出发:当 Agent 因缺少某项稳定事实而失败,再把这项缺口补成可检索、可追溯、带版本条件的知识。
还有一条原则不能省略:越接近结论的关键事实,越不能只依赖模型印象。调试中的证据可信度大致应该遵循:
text
当前设备与实验结果
> 当前分支的代码和配置
> 匹配版本的内部规范
> 匹配版本的公开文档
> 模型的既有认知
LLM 可以告诉我们"通常是什么",但最终需要回答的是"这台设备、这个版本、这次故障究竟是什么"。
Experience:不是一句"以前也遇到过"
经验和知识最容易混在一起。
"某寄存器的 bit 3 表示总线超时"是知识。它描述的是在给定版本和条件下可反复成立的事实。
"上次某项目进入休眠后偶现死机,最终发现是一个电源域关闭前仍有未完成访问"是经验。它来自一次具体事件,带有当时的上下文、行动和结果。
一条有用的经验,至少应该能回答:
- 当时是什么产品、版本和环境?
- 出现了什么现象和触发条件?
- 做了什么检查或实验?
- 观察到了什么?
- 哪个假设因此被支持或排除?
- 最终结论经过了怎样的验证?
所以经验不应该只是一个根因标签,更适合保存成一个个 Experience Episode:
text
上下文 + 触发状态 + 行动 + 观察 + 产生的影响
一张完整的 Bug 单当然可以保留,用于审计、复盘和评测;但检索时未必应该把整张 Bug 单原样塞给模型。一个复杂问题往往包含多段有价值的经验:一次无效排查、一个被排除的假设、一个关键转折点,以及最后的根因验证。把它们拆成较小的 episode,更容易在新的调试状态下被准确召回。
经验的地位也要摆正:它是新假设的来源,不是当前问题的真相。
"过去这样解决过"只说明这个方向值得检查。是否适用于眼前问题,仍然要回到当前代码、设备和实验中验证。
Agent:不是一个巨大的 Prompt
当 Tool、Knowledge、Experience 被分开后,Agent 的职责反而简单了。
它不需要装下整本 Android 百科,也不应该背诵所有历史案例。它要做的是维护当前问题的工作状态,并不断判断下一步最值得做什么。
这个工作状态至少包括:
- 已确认的事实;
- 当前假设及其依据;
- 已排除的方向;
- 已执行的动作及结果;
- 尚缺少的信息;
- 下一步想验证的问题。
然后 Agent 在一个循环里做选择:是继续推理,检索知识,寻找相似经验,调用工具收集新证据,向工程师请求信息,还是已经可以停止。
这里要区分 LLM 与 Agent。LLM 完成的是一次推断;Agent 则把模型、目标、工作状态、工具、权限和停止条件组织成一个持续的决策过程。
因此,Agent 的基础配置应该短而稳定,主要描述角色、目标、证据原则、成功条件和权限边界。Android 机制、项目资料、历史问题和具体命令不应全部堆进基础 Prompt,而应在需要时通过对应接口进入上下文。
固定 Flow,不等于固定推理
调试的大流程看起来确实很直觉:接收问题、分析、定位、修改、验证。为什么还需要把 Flow 固定下来?
因为需要固定的不是"先看哪一行日志",而是系统的外部协议。
例如,一个 Bug 可以有相对稳定的状态:
text
问题接入 → 调查中 → 根因候选 → 修复候选 → 验证中 → 已解决 / 阻塞
每个状态可以规定最少需要留下什么证据、什么情况下允许进入下一阶段、哪些动作需要审批,以及什么才算真正完成。但在"调查中",Agent 先看日志还是代码、先查知识还是案例、提出几个假设、设计什么实验,都应该保持开放。
换句话说:
固定的是状态、证据、权限和验收;开放的是假设、搜索路径与实验设计。
错误的规范会限制 AI,这个担心完全成立。因此规范最好分为三个强度:
- 必须遵守:安全边界、版本匹配、证据来源、操作权限和真实验证;
- 默认建议:高频且通常有效的检查策略,Agent 可以说明理由后偏离;
- 自由判断:假设生成、检索顺序、工具组合和新实验设计。
能用工具强制保证的约束,不要只写在 Prompt 中;能用验收结果判断的事情,不要过度规定中间步骤。
Codex Skill 到底放在哪里
到这里,"技能"和 Codex 里的 Skill 就可以彻底分开了。
在我们的架构语言中,技能若指"打开文件、采集日志、刷写设备",更接近 Tool;若指"判断该看哪里",则属于 Agent 的推理能力。
而 Codex Skill 是一种工程上的打包形式。OpenAI 官方文档将它描述为可复用工作流的创作格式:一个 Skill 可以包含指令、参考资料、资源和可选脚本,并在任务匹配时被加载。也正因为它是"包装",里面技术上什么都能放,才更需要我们自己维持语义边界。OpenAI:Build skills
一个好的 Skill 更像装配说明和入口:
- 它说明适用场景和触发条件;
- 它告诉 Agent 可使用哪些工具;
- 它指向哪些知识源和经验源;
- 它规定必要的输出、验证与安全边界。
但知识的权威正文、历史案例的源数据以及工具本身,不应该因为"方便"就全部复制进 Skill。否则同一事实会散落在几十个 Skill 中,无法独立更新,也无法判断哪份才是最新版本。
简单地说:
Skill 是组装方式,不是我们能力体系中的第五种内容。
先用 Codex,还是从头开发一个 Agent
在当前阶段,我们倾向于先把 Codex 当作 Agent 运行时,而不是立即从零开发一个完整 Agent。
原因不是 Codex 已经自动解决了领域调试,而是它已经提供了不少昂贵的通用能力:多轮任务推进、代码与工作区操作、命令执行、根据结果继续判断、权限控制以及修改后的验证。我们可以把精力集中在真正具有组织差异的部分:芯片与产品知识、调试工具、经验检索、问题状态和验收标准。
早期可以让工程师直接在 Codex 中运行真实案例,观察它在哪里失败。当流程逐渐稳定,需要接入 CI、Bug 平台、设备农场或内部系统时,再通过 Codex SDK 将同样的能力嵌入自动化流程。官方文档也将 Codex SDK 定位为对本地 Codex Agent 的程序化控制方式,可用于内部工具、工作流和 CI。OpenAI:Codex SDK
只有当我们明确需要自己的任务队列、大规模并发、跨设备调度、组织级权限、审计系统或特殊交互界面时,才有充分理由建设更完整的外层平台。即便如此,也未必需要重新实现底层 Agent 的全部能力。
这条路线的核心不是"永远依赖 Codex",而是先验证领域系统,再决定哪些基础设施值得自建。
真正重要的是可诊断的进化
这套拆分最大的价值,不是架构图变得好看,而是系统终于可以有方向地进化。
当一次调试失败,我们可以追问:
- Agent 已经拿到了足够信息,却选错了方向吗?那是决策问题;
- Agent 想做正确检查,却没有可调用的动作吗?那是 Tool 缺口;
- 关键的内部机制没有被提供,或者版本不匹配吗?那是 Knowledge 缺口;
- 组织里曾经处理过类似问题,却没有被检索出来吗?那是 Experience 或检索问题;
- 已经提出正确修复,却用错误标准宣布完成吗?那是验收与 Flow 问题。
每个真实 Bug 都可以成为一次评测。每次评测暴露一个可归因的缺口,再补到对应层中,而不是习惯性地"再写一个更长的 Skill"。
随着案例增加,一些经验会反复出现。被多次验证的因果机制,可以提炼成知识;反复有效的检查方式,可以沉淀成软性的 Playbook;频繁且确定的操作,可以进一步封装成 Tool。新的内容不是随意堆积,而是在系统中迁移到最合适的位置。
这才是"源源不断完善"真正需要的闭环:
text
真实问题
↓
Agent 调试并留下可观察记录
↓
评测结果与失败归因
↓
补 Agent / Tool / Knowledge / Experience 中的明确缺口
↓
回放旧问题,确认能力真的提高
这里记录的不是模型隐秘而冗长的思维过程,而是工程上可以检查的产物:假设、证据引用、执行动作、工具结果、排除方向、下一步实验和最终验证。
如果没有评测和回放,"积累"很容易只是仓库越来越大;有了它们,我们才知道系统是真的进化了,还是仅仅换了一种写法。
结语:先让边界清楚,再让能力增长
建设 AI Debug Engineer 最诱人的做法,是尽快收集知识、编写 Skill、导入历史 Bug,再期待规模产生智能。但对于 Android 系统软件和手机芯片调试,这条路很容易造出一个内容庞大、边界含糊、无法定位自身问题的系统。
我们目前得到的结论反而非常朴素:
Agent 负责判断,Tool 负责行动,Knowledge 负责解释系统,Experience 负责提示过去。
外层 Flow 管住状态、证据、权限与验收,内层推理保持自由;Codex Skill 负责组装和复用,而不是吞掉所有内容;Codex 可以先作为通用 Agent 内核,领域能力围绕它逐步生长。
这并不是最终架构,更不是已经证明可以覆盖所有 Android Bug 的答案。它只是把一个模糊的问题变成了可以逐项建设、逐项评测、逐项修正的工程系统。
但也许这正是起点最需要完成的事:在追求让 AI 像资深工程师一样工作之前,先让我们能够说清楚------它这一次为什么做对,又为什么做错。