在 monorepo 里用 AI 编程,关键不是”把整个仓库塞进上下文”,而是先划清上下文边界:每次任务锁定一到两个包,跨包信息只提供契约(类型、接口、构建依赖)而不是源码。否则模型会把别的包里同名符号当成正确引用,改完编译不过、测试全红。
为什么 monorepo 特别容易让 AI 编程翻车
单仓库单包时,AI 靠文件名和 import 就能猜对结构。 monorepo 里这套直觉失效,原因有三个。
1. 同名符号太多,模型默认选”最常见的那个”
utils/format.ts 这种路径在 monorepo 里可能有十几个。模型没看到包边界时,往往引用了语义相近但归属错误的那一个。更麻烦的是它不会报错——它会顺手”帮你”新建一个同名文件。
2. 目录边界 ≠ 构建边界 ≠ 发布边界
物理上相邻的两个目录,构建时可能属于完全不同的 target;发布时又是另一套分组。 AI 看到的是目录树,看不到 nx graph / turbo.json 里的依赖图,于是漏改下游包。
3. 上下文预算被无关包吃光
Anthropic 在 Claude Code 最佳实践里说得很直白:上下文窗口是最需要管理的资源,填得越满性能越差,模型会开始”忘记”前面的指令。在 monorepo 里做一次无约束的全局检索,几万 token 就出去了,真正要改的那个包反而没被认真读。
三层上下文策略:清单、契约、证据
把上下文拆成三层,按需加载,而不是一次性全给。
| 层级 | 内容 | 加载时机 | 典型体积 |
|---|---|---|---|
| 清单层 | 包列表、每个包的职责一句话、构建命令 | 会话开始,长期驻留 | 1–3K token |
| 契约层 | 跨包暴露的类型、接口、事件 schema | 改动涉及跨包调用时 | 2–8K token |
| 证据层 | 具体函数体、调用链、历史变更 | 定位到具体文件时按需取 | 按需,用完即弃 |
清单层:给 AI 一张”地图”而不是”地形”
在项目根目录维护一份简短的包清单,让 AI 每次先读它。关键是写清职责边界,而不是罗列文件。
# PACKAGES.md(示例)
packages/
core-api # 对外 HTTP 接口,唯一允许直接读 db 的包
domain # 领域模型与规则,不允许 import 任何 UI 包
web-admin # 管理后台,只能通过 core-api 客户端取数
shared-types # 跨包契约的唯一来源,改动需同步更新版本
契约层:跨包只给类型,不给实现
AI 要知道 createOrder(input: CreateOrderInput): Promise<Order> 长什么样,但不需要读 createOrder 的三百行实现。把 shared-types 里的定义贴给它,比让它自己去 grep 更快也更准。
证据层:让 AI 自己取,但限定搜索范围
Anthropic 内部团队的做法是”先探索、再规划、再编码”,探索阶段明确限定目录。在 monorepo 里这一步必须显式给边界:
# 好的提问方式
claude "只在 packages/domain 和 packages/shared-types 里查找
订单状态机的定义,列出所有状态迁移,不要读其他包"
# 差的提问方式
claude "订单状态在哪定义的"
第二种问法,模型会在整个仓库做模糊匹配,最后给你一个”看起来对”的答案。这也是为什么代码库检索策略比模型能力更影响结果。
可复制的四步操作流程
- 先让 AI 读 PACKAGES.md 并复述:这个功能涉及哪几个包、边界在哪。复述错了就重来,别急着写代码。
- 进入只读模式,限定目录做探索,让它输出”要改的文件清单 + 每个文件的改动理由”。
- 把 shared-types 里相关的契约贴进上下文,让它基于契约写实现,禁止它自行新增跨包导出。
- 用受影响包构建命令验证:
nx affected --target=build或turbo build --filter=...[origin/main],而不是全量构建。
验证:让改动自然收敛
monorepo 的验证一定要”按影响范围”分级,全量跑太慢,不跑又漏。
- 类型检查:全量跑,最快也最能抓住跨包破坏。
- 单元测试:只跑受影响包及其下游。
- 集成/端到端:只跑涉及改动入口的那条链路。
把这三条写进项目的 AI 规范文件(如 CLAUDE.md 或 Cursor 规则),AI 就会自动遵守。配置方式可以参考项目级规则文件的写法,再接到 CI 流水线里的自动评审上。
一份可直接抄的 CLAUDE.md 片段
# Monorepo 规则
- 改动前先读 PACKAGES.md,确认所属包
- 跨包调用只允许通过 shared-types 暴露的契约
- 禁止新增跨包相对路径 import
- 修改完成后必须运行: pnpm nx affected --target=test
- 不确定边界时先提问,不要猜测
规模再大一点怎么办
Spotify 用 Claude Agent SDK 做全量代码迁移时,做法是”把提示词本身版本化进 Git”,再由编排系统按仓库触发。这个思路在 monorepo 里同样成立:与其让 AI 每次重新理解仓库,不如把”这次要做什么”沉淀成可复用的、带边界声明的任务模板。同时配合上下文窗口的压缩与清理策略,长任务才不会中途失忆。
monorepo 里要不要给 AI 开全局语义检索?
AI 改错了包,怎么最快发现?
包太多,PACKAGES.md 撑爆上下文怎么办?
子智能体能解决跨包问题吗?
参考来源:anthropic.com(Claude Code 最佳实践、 Anthropic 内部团队用法、 Spotify 迁移案例)。本文基于上述材料重新组织并补充 monorepo 场景下的实操经验。










评论 (0)