写在前面
如果你看过AI编码助手的演示,可能会被它们快速检索代码库、修复Bug的能力所震撼。但你注意到没有?那些Demo几乎都有一个共同前提:代码就在眼前,Agent只需要读取它、修改它。

然而,现实中的研发环境往往复杂得多。
以一家汽车软件公司为例,它们的代码库可能这样组织:Android应用层每个功能模块对应一个独立Git仓库(设置、空调、导航……合计数十个);Android系统框架层按照AOSP方式拆分为数百个子仓库,通过repo和Manifest来管理;QNX实时系统有自己的Manifest;MCU固件则是另一套完全独立的仓库体系。
此时,Agent收到一条“修复蓝牙连接断开的Bug”的指令。它面临的第一个问题,不是“如何修改”,而是“代码在哪里”。这个问题的难度,实际上被严重低估了。
问题的三个递进层次
层次一:仓库发现(Repo Discovery)
最直观的挑战:面对几十上百个仓库,Agent凭什么知道该访问哪个?
有些团队的做法是为每个仓库编写一段自然语言描述,告诉Agent这个仓库负责什么业务。这是个合理的起点,但存在几个系统性问题:
- 自然语言描述很难被精准匹配——尤其是任务描述和仓库描述使用了不同术语时
- 描述随代码升级容易过时,维护成本始终存在
- 缺乏结构化元数据,Agent无法按技术栈、业务域做精细筛选
- 对于AOSP-style的大型Manifest仓库,“这个Manifest管理了什么”和“某个功能位于哪个子仓库”之间,仍然存在鸿沟
层次二:跨仓库依赖识别(Cross-Repo Dependency)
真实的业务需求往往跨越多个技术栈的多个仓库。一个“空调设置温度”的功能,其依赖链路可能是这样的:
Android 设置 App(应用层仓库)↕ IPC / AIDLAndroid 系统服务(框架层子仓库)↕ HAL 接口BSP HAL 实现(BSP 层子仓库)↕ 底层通信协议MCU 固件(MCU 仓库)
Agent如果只找到了应用层仓库,修改了UI逻辑,却没有意识到下层接口也需要同步更新,结果就是“本地编译通过、集成测试失败”的不完整修改。
仅靠单仓库的描述无法解决这个问题——需要显式地建立仓库间的依赖关系。
层次三:仓库获取与上下文构建(Repo Fetch & Context)
找到仓库之后,Agent还需要“获取代码”。
对于应用层的单仓库,一个git clone就够了。但对于AOSP-style的代码库,流程要复杂得多:
- 先获取Manifest仓库
- 确定当前任务需要哪几个子仓库(全量sync成本极高,时间、存储、网络都不允许)
- 执行
repo sync -c,再加上精选的子仓库列表 - 理解跨子仓库的代码依赖
这对Agent的工具能力要求,远远不止一个简单的git clone。
解法方向一:结构化 Repo Registry
本质上是将当前自由文本的描述升级为结构化Repo Registry——每个仓库入口就是一个YAML对象:
repo_id: android-app-settingsdisplay_name: 设置应用tech_stack: android-appcomponent_type: applicationbusiness_domains:- 系统设置- WiFi- 蓝牙- 显示fetch_type: git-singlefetch_url: ssh://gerrit.example.com/settings-appdependencies:- android-framework-systemui- android-bsp-display-halowners:- team: android-app-team
结构化带来的核心价值是:Agent可以按照tech_stack、business_domains做精准过滤,而不是在大量自然语言中做模糊匹配;dependencies字段则为跨仓库联动提供了基础。
跨技术栈依赖图
在Repo Registry之上,可以显式维护仓库间的依赖关系。当Agent定位到一个仓库时,平台自动查询依赖图,提示那些可能需要同步修改的关联仓库。
依赖关系的来源有两种:
- 手动维护:由熟悉架构的人来标注,准确但有维护成本
- 从构建系统导出:AOSP的构建系统知道哪些模块依赖哪些接口,理论上可以半自动提取
标准化仓库获取协议
将“如何获取这个仓库”从自然语言描述,转变为机器可执行的fetch spec:
fetch_type: repo-manifestmanifest_repo: ssh://gerrit.example.com/platform/manifestmanifest_branch: mainrequired_projects:- platform/frameworks/base- platform/packages/apps/Settingssparse_checkout: true
Agent调用平台提供的统一fetch工具,传入repo_id,平台根据fetch spec完成实际的代码获取操作,底层git/repo的差异就被屏蔽掉了。
解法方向二:全量预置工作区
另一种思路:保持仓库的组织方式不变,但将所有仓库的代码预先同步到Agent可以直接访问的共享工作区。Agent启动时无需下载,直接在本地搜索和修改。
这个方案解决了两个问题:
- 消除了仓库发现——所有代码在同一文件系统,搜索工具直接跨栈运行
- 消除了下载等待——代码预置完毕,Agent可以立即开工
但Token消耗问题并没有消失。代码在本地,不等于代码在Agent的上下文窗口里。Agent仍然需要依靠代码搜索工具来定位相关文件,选择性地将少量相关代码段加载到上下文。搜索工具的建设需求,在全量预置方案和多仓库方案中,其实是一样的。
还有一个反直觉的问题:搜索范围变大之后,噪音也会随之增加。在多仓库方案中,Agent已经定位到正确仓库后再搜索,候选结果集合很小;但在全量预置方案中,同一个关键词可能在几百个文件里命中,Agent的判断负担反而更重了。
工程实施的关键
几百GB的代码,不应为每个Agent实例复制一份。合理的架构是:
只读共享镜像(全量代码)↓ OverlayFS(CoW 文件系统)Agent 工作区(薄层,只存储写操作的差量)
每个Agent实例读代码时零下载成本,写代码时在自己的薄层上隔离,互不干扰。这个架构在K8s容器环境下已经有成熟的实现路径了。
提交回原仓库的流程则需要专门设计:Agent完成修改后,平台需要识别哪些文件属于哪个原始仓库,将变更拆开提交,各自走Code Review流程。这不是零成本的。
三种方案的对比
| 维度 | 多仓库 + 结构化 Registry | 全量预置工作区 |
|---|---|---|
| 仓库发现难度 | 通过 Registry 改善 | 完全消除 |
| 下载等待 | 每次任务需要下载 | 完全消除 |
| 跨层搜索 | 需跨仓库操作 | 本地直接支持 |
| Token 效率 | 需代码搜索工具 | 同左,问题未消除 |
| AOSP 兼容性 | 完全兼容 | 完全兼容 |
| 存储成本 | 按需下载 | 数百 GB,需共享镜像 + OverlayFS |
| 提交回原仓库 | 自然 | 需平台做变更分拆 |
| 实施难度 | 低,渐进改善 | 中,基础设施有成熟方案 |
| 推荐程度 | 主线方案 | 高频 Agent 任务场景下有价值 |
核心结论其实很清楚:两个方案都无法绕开那个根本需求——Agent需要高质量的代码搜索和导航工具。
解法方向三:代码知识图谱
无论仓库如何组织,Agent在代码工作中面临的根本限制始终存在:上下文窗口装不下大型代码库,必须有选择地加载相关代码段。而这个“选择”本身,就需要消耗token来搜索和判断。
代码知识图谱的思路是:提前用静态分析将代码的结构化关系建成数据库。那些原本需要Agent读取大量文件才能推断出来的关系——谁调用了谁、接口在哪里实现、模块间如何依赖——都变成了可以直接查询的结构化数据。
分层分析,成本可控
不同分析层次的成本差距很大:
| 分析层次 | 内容 | 工具 | 成本 |
|---|---|---|---|
| 符号索引 | 函数/类/变量的定义位置 | tree-sitter,速度极快 | 低 |
| 调用图 | 哪些函数调用哪些函数 | clangd / language server | 中 |
| 接口实现映射 | AIDL/HIDL 接口与实现的对应关系 | 静态分析 + 规则 | 中 |
| 模块依赖图 | 模块/库级别的依赖关系 | 构建系统导出 | 低 |
| 语义向量索引 | 代码语义的向量表示,支持模糊搜索 | embedding 模型 | 高 |
对于几百GB的代码,符号索引、调用图、接口映射这几层,使用tree-sitter和clangd做静态分析,速度可达每分钟百万行级。全量分析可能需要数小时,但这是一次性的离线成本。
语义向量索引成本较高,建议做选择性覆盖:只对公开接口、头文件、关键模块做embedding,实现文件内部细节按需处理即可。
高价值查询场景
知识图谱能给出精准答案、节省大量token的场景,包括:
- “这个AIDL接口有哪些实现类?”——接口实现映射直接返回
- “哪些模块调用了AudioFlinger::setStreamVolume?”——调用图直接返回
- “修改这个HAL接口会影响哪些上层模块?”——依赖图反向查询
- “这个函数定义在哪个文件的哪一行?”——符号索引直接返回
在没有知识图谱的情况下,Agent需要用grep搜索大量文件然后自行推断,消耗大量token,结果还不一定准确。
对于车载软件那种典型场景(App → Framework → HAL → MCU的跨层分析),知识图谱的价值尤其突出。AIDL/HIDL接口是各层之间的明确契约,静态分析能准确建立“接口定义 → 接口实现 → 接口调用方”的完整图谱,这恰恰是Agent做跨层代码修改时最需要的信息。
技术选型参考
| 功能 | 推荐工具 | 理由 |
|---|---|---|
| C/C++ 符号和调用图 | clangd + bear(编译数据库) | AOSP 场景最成熟 |
| Ja va/Kotlin 符号分析 | tree-sitter-ja va | 覆盖应用层 |
| 多语言符号索引 | tree-sitter | 支持 C/C++/Ja va/Kotlin,速度快 |
| 图数据存储 | Neo4j 或 DGraph | 天然适合调用关系查询 |
| 语义向量索引 | Qdrant + CodeBERT(本地部署) | 避免 embedding API 费用 |
| 接口实现映射 | 自定义规则 + AIDL/HIDL 解析 | 通用工具不理解 Android HAL 语义 |
特别注意:AIDL和HIDL接口映射建议单独建索引。Android的跨层接口定义规范明确,编写专用解析器成本低、准确率高。
一些未解决的问题
仓库元数据的持续维护:Repo Registry是静态维护的,但代码仓库的职责会随项目演进而变化。如何建立机制让仓库描述和实际代码保持同步,目前还是个开放问题。理想的状态是把这些信息纳入研发平台动态维护,Agent通过MCP等协议向平台查询,而不是维护本地静态文件。
隐性知识的显式化:跨技术栈的依赖关系(尤其是App层到MCU层的完整调用链),目前主要存在于资深开发者的脑中。如何将这些隐性知识结构化地写入依赖图?需要各技术栈的专家投入,这不是能自动化解决的问题。
多仓库任务中的Agent协作:当一个任务横跨Android应用、系统框架、MCU三个仓库时,这三部分代码改动应该由同一个Agent完成,还是由多个专栈Agent分别完成再汇合?这涉及到Agent Platform的架构设计,目前还没有清晰的答案。
总结
多仓库多技术栈下的代码定位,是AI Agent落地企业研发环境时最容易低估的工程挑战。解决方案可以分三个层次渐进推进:
- 结构化Repo Registry:把自然语言描述升级为机器可处理的YAML元数据,显式建立跨仓库依赖关系,这是成本最低的起点
- 全量预置工作区:消除仓库发现和下载等待,适合Agent任务频繁触发的场景,需要OverlayFS等基础设施支持
- 代码知识图谱:从根本上改变Agent获取代码关系的方式,把“读文件推断”变成“查数据库直取”
无论选哪个方向,有一点是确定的:Agent定位相关代码段的能力,取决于搜索和导航工具的质量,而不是代码的物理存放位置。
