写在前面
大多数 AI 编码助手的演示都从一个隐含前提出发:代码就在眼前,Agent 只需要读它、改它。
但在真实的企业研发环境里,这个前提经常不成立。
一个汽车软件公司的研发体系,可能同时维护着这样的代码结构:Android 应用层的每个功能模块一个独立 Git 仓库(设置、空调、导航......数十个);Android 系统框架层按 AOSP 方式拆成数百个子仓库,靠 repo 工具和 Manifest 管理;QNX 实时系统有自己的 Manifest;MCU 固件又是完全独立的仓库。
当 AI Agent 收到一条"修复蓝牙连接断开的 Bug"的指令时,它面对的第一个问题不是"怎么改代码",而是"代码在哪"。这个问题的难度,被严重低估了。
问题的三个递进层次
层次一:仓库发现(Repo Discovery)
最直观的困难:面对几十上百个仓库,Agent 怎么知道该去哪个?
有团队的做法是给每个仓库写一段自然语言描述,告诉 Agent 这个仓库是做什么的。这是合理的起点,但有几个系统性局限:
- 自然语言描述难以被精确匹配,尤其是任务描述和仓库描述用了不同的术语时
- 描述随代码演进容易过时,维护成本持续存在
- 没有结构化元数据,Agent 无法按技术栈、业务域做过滤
- 对 AOSP-style 的大型 Manifest 仓库,"这个 Manifest 管了什么"和"这个功能在哪个子仓库"之间,仍有鸿沟
层次二:跨仓库依赖识别(Cross-Repo Dependency)
真实业务需求往往横跨多个技术栈的多个仓库。一个"空调设置温度"的功能可能是这样的链路:
markdown
Android 设置 App(应用层仓库)
↕ IPC / AIDL
Android 系统服务(框架层子仓库)
↕ 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 对象:
yaml
repo_id: android-app-settings
display_name: 设置应用
tech_stack: android-app
component_type: application
business_domains:
- 系统设置
- WiFi
- 蓝牙
- 显示
fetch_type: git-single
fetch_url: ssh://gerrit.example.com/settings-app
dependencies:
- android-framework-systemui
- android-bsp-display-hal
owners:
- team: android-app-team
结构化的核心价值:Agent 可以按 tech_stack、business_domains 做精确过滤,而不是在大量自然语言里做模糊匹配;dependencies 字段为跨仓库联动提供了基础。
跨技术栈依赖图
在 Repo Registry 基础上,显式维护仓库间的依赖关系。当 Agent 定位到一个仓库时,平台自动查询依赖图,提示可能需要同步修改的关联仓库。
依赖关系来源有两种:
- 手动维护:由了解架构的人标注,准确但有维护成本
- 从构建系统导出:AOSP 的构建系统知道哪些模块依赖哪些接口,理论上可以半自动提取
标准化仓库获取协议
把"如何获取这个仓库"从自然语言提升为机器可执行的 fetch spec:
yaml
fetch_type: repo-manifest
manifest_repo: ssh://gerrit.example.com/platform/manifest
manifest_branch: main
required_projects:
- platform/frameworks/base
- platform/packages/apps/Settings
sparse_checkout: true
Agent 调用平台提供的统一 fetch 工具,传入 repo_id,平台根据 fetch spec 完成实际的代码获取操作,屏蔽底层的 git/repo 差异。
解法方向二:全量预置工作区
另一个思路:不改变任何仓库的组织方式,把所有仓库的代码预先同步到 Agent 可以直接访问的共享工作区。Agent 启动时不需要下载,直接在本地搜索和修改。
这个方案解决了两个问题:
- 消除仓库发现问题------所有代码在同一文件系统,搜索工具直接跨栈运行
- 消除下载等待------代码预置后 Agent 可立即开始工作
但它没有解决 Token 消耗问题。代码在本地 ≠ 代码在 Agent 的上下文窗口里。Agent 仍然需要靠代码搜索工具定位相关文件,选择性地将少量相关代码段加载进上下文。搜索工具的建设需求,在全量预置方案下和多仓库方案下是完全一样的。
另一个反直觉的问题:搜索范围变大后,噪音也会增加。在多仓库方案里,Agent 已经定位到正确仓库后再搜索,候选结果集合小;在全量预置方案里,同一个关键词可能在数百个文件里命中,Agent 的判断负担反而加重。
工程实施的关键
数百 GB 的代码不应该为每个 Agent 实例复制一份。合理的架构是:
markdown
只读共享镜像(全量代码)
↓ 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 场景最成熟 |
| Java/Kotlin 符号分析 | tree-sitter-java | 覆盖应用层 |
| 多语言符号索引 | tree-sitter | 支持 C/C++/Java/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 定位相关代码段的能力,取决于搜索和导航工具的质量,而不是代码的物理存放位置。
欢迎访问 PrimeSkills ------ 一个精心策划的 AI Agent 与技能市场,所有内容均经过真实企业级工作流验证。没有噱头,只有真正有效的东西。
更多实用知识和有趣产品,欢迎访问我的个人主页