仓库太大塞不进上下文,先别急着换模型:三种切片法和它们的代价

2026-07-28

数据截至 2026-07,各产品的额度与报错口径以官方最新说明为准。

大仓库喂不进去,绝大多数时候不是窗口小,而是你的仓库没有可切开的边界。 同样一个几十万行的工程,有人能让模型稳定改对一个跨三层的功能,有人连”这个字段是哪里写进去的”都问不出来,差别通常出在切片方式上,而不是选了哪家模型。换更大窗口的模型往往只是把”读不完”变成”读了但抓不住重点”,答非所问的比例可能还会上升——因为无关代码变多了,模型的注意力被稀释了。

这篇只谈仓库侧怎么切:怎么判断你遇到的是哪一类”塞不进”,三种切法分别怎么做、各自要付什么代价、什么时候该停手。工具会话里怎么开新窗口、怎么清历史,见Claude Code 上下文管理;Agent 在运行过程中自己怎么裁剪记忆,见Agent 上下文管理;具体的引用语法怎么写,见Cursor 上下文引用;项目说明文件里该沉淀什么,见CLAUDE.md 怎么写。这四篇管的是”喂的动作”,本篇管的是”喂什么进去”——先把仓库切成可喂的单元,那四篇的技巧才有东西可用。

一、先分因:三种”塞不进”是三个不同的病

不要一上来就删文件、压缩代码。先花五分钟判断现象归属,走错分支的代价是几个小时。

第一类是真的超量:你把整个目录塞进去,工具直接拒绝或提示内容过长。这种最好办,也最少见。

第二类是检索没命中:工具能跑,但它给你的答案引用的是过时文件、测试桩、或者同名的另一个实现。你追问”你看的是哪个文件”,它给出的路径你根本没让它看。这类问题的根子在索引和检索,不在窗口。相关的排查思路见Cursor 索引失败

第三类是注意力被稀释:内容进去了,检索也命中了,但模型在一堆相似代码里挑错了分支,或者把三处相似逻辑混成一处。表现是答案听起来对,落到具体行号就不对。

三类的处置动作完全不同。下面这张表按”你能观察到什么”来分:

现象大概率成因怎么验证处置动作
提示内容过长、请求被拒单次投喂量真的超限把投喂范围减半再试,若能通过即为超量按接口切片,先只给签名与类型定义
回答引用了你没提供的文件工具自带检索抓了旧索引或缓存让它列出实际读过的文件路径并逐一比对重建索引;改为显式指定文件,关掉自动检索
回答引用了已删除或改名的符号索引落后于工作区git log -3 --date=iso --format="%ad %h %s" -- <路径> 拿到最近改动时间,与你上次重建索引的时间比重建索引后重问;仍不对则手工贴当前文件
三处相似逻辑被混成一处无关同构代码进入了上下文把投喂范围缩到只含目标一处,看是否立刻答对按任务切片,只留一条调用链
改动能编译但跑不通隐式依赖(配置、注册表、DI 容器)没进上下文全仓搜类名字符串,看是否有配置文件按名字引用它按依赖切片,把字符串引用一并纳入
每轮回答质量递减会话历史挤占了有效内容开新会话、只贴同样的文件再问一遍换新会话,把结论沉淀成文档而不是靠历史
跨模块问题总答一半缺少中间层的接口定义问它”这两个模块通过什么通信”,看能否答出按接口切片,补上契约文件

表里有两处需要补一句。一是”让它列出实际读过的文件”这个验证动作,它属于自述,模型有可能把没读过的路径也报上来,所以这一步只用来快速筛掉明显不对的情况(报出的路径根本不存在、或指向另一个模块),不要拿它当读取日志用;要确凿,就看工具自己的日志或改成显式指定文件。二是最容易被忽略的”隐式依赖”那行。按名字反射、按配置装配的代码,静态依赖分析看不见,模型也看不见。这是大仓库里最常见的翻车点。

二、按依赖切片:沿调用链纵向取一条

思路是从入口或落点出发,只取真实经过的那条链。适合”这个值是怎么来的”、“这个接口谁在调”这类溯源型问题。

做法上,先定位落点,再逆向展开一到两层。纯文本手段就够用:

# 找出所有引用某符号的位置(含配置和文档里的字符串引用)
git grep -n "OrderSettlement" -- . ':!*.lock' ':!dist/*'

# 只看最近半年动过的文件,剔掉长期不变的历史层
# -c core.quotepath=false 是为了让中文路径原样输出,否则会变成转义码不好比对
git -c core.quotepath=false log --since="6 months ago" --name-only --format="" | sort -u > recent.txt

拿到候选集后,人工判一遍:哪些是真链路,哪些是同名巧合。链路一般不超过十来个文件,这个量级任何工具都吃得下。

代价有三条,你得认。第一,横向的相似实现全被切掉了。模型看不到平行的另外两个结算分支,它给你的改法可能只在这条链上成立。第二,隐式依赖需要人工补,上面那条 git grep 之所以要包含配置文件,就是为了捞回按字符串装配的部分,漏了就会出现”编译通过、运行时找不到实现”。第三,链条会失真。你逆向展开两层就停手,第三层的一个默认参数可能才是真正的原因。溯源问题里,链条切浅了比切错了更常见。

判断切得够不够,有个简单标准:让模型复述这条链的数据流向,如果它需要用”应该是""可能通过”这类措辞才能连上,链条就是断的。

三、按任务切片:从改动记录反推最小工作集

思路是不看代码结构,看历史上”做类似这件事时动过哪些文件”。适合”加一个同类功能”、“改一个已有行为”这类落地型任务。

历史里有现成答案。找到最近一次类似改动的提交,看它的文件清单:

# 看某次改动碰了哪些文件
git show --stat <commit>

# 统计跟某个文件经常一起变的文件(共变关系)
# 中间那个 grep -v 把目标文件本身剔掉,否则它必然占据第一名,没有信息量
git log --format="%H" -- src/order/settle.py \
  | while read h; do git show --name-only --format="" "$h"; done \
  | grep -v '^src/order/settle\.py$' \
  | sort | uniq -c | sort -rn | head -20

有两点要先说清楚:git show --name-only 默认不展开合并提交的文件清单,所以纯合并带进来的改动统计不到;另外这条命令统计的是”同一个提交里一起出现”,团队习惯是一次提交只做一件事时它最准,习惯是攒一大坨再提交时噪声会明显变多。

排除自身之后的输出通常很有说服力:跟目标文件共变次数高的,基本就是这个任务的真实工作集。它能捞到静态分析捞不到的东西——迁移脚本、文案文件、权限配置、测试夹具。这些恰恰是模型漏改之后最难发现的部分。

代价是它只反映过去,不反映现在。仓库刚做过架构调整,共变数据就会把你带回旧结构。判断办法是看共变文件的最后修改时间,如果一半以上超过一年没动,这份数据就不能直接用。

另一个代价是首次任务无历史可查。仓库里没有先例的功能,这种切法直接失效,只能退回按依赖切。

还有一条隐性成本:这种切法产出的文件集是扁平的,没有层次。模型拿到十五个平级文件,不知道哪个是主战场。所以给的时候要人工标一句”主要改动在 A,B 和 C 只需同步字段”,一句话的成本,能省掉一轮返工。

四、按接口切片:拿契约换实现

思路是只给边界定义——函数签名、类型声明、数据表结构、接口文档,不给实现体。适合跨模块设计、跨团队协作、以及模块实现你根本无权修改的情况。

在有类型系统的项目里,这种切法性价比最高。类型声明文件、接口定义文件、数据库迁移文件加起来体积很小,信息密度极高。模型看到完整的类型定义,就能推断出绝大部分调用关系,不需要看实现。

具体操作没什么技巧,就是把边界文件挑出来单独成一组,长期维护。值得做的是把这组清单写进项目说明文件里,让每次会话都能直接取用。

代价是它对实现里的坑一无所知。签名说返回一个列表,实现里在某个条件下返回空列表还是抛异常,接口切片看不出来。所以按接口切出来的方案,属于设计层面可用、落地层面待验证。指望它一次改对具体逻辑,会失望。

第二个代价是动态语言里契约往往不存在。没有类型标注的项目,函数签名不携带信息,这种切法退化成给了一堆函数名。这种项目里可以补类型标注,但那是另一个工程了,别在排查过程中顺手开这个战场。

第三个代价容易被低估:接口和实现会不同步。文档里写的字段和代码里实际的字段不一致时,模型会信文档。所以接口切片的前提是这份契约是从代码生成或被测试约束的,不是人手维护的文档。人手维护的接口文档,可信度往往不如直接读代码。

五、什么情况下别再折腾

排查最贵的不是走错路,是走错路还不肯回头。以下几种情况,停手比继续调整投喂方式更划算。

同一个问题换过三种切法还是错,停。 这时候大概率不是上下文问题,而是这段代码本身没有可理解的结构——比如逻辑分散在配置、存储过程、定时任务三处,任何切法都拼不出全貌。正确动作是自己读一遍,把结论写成一段说明,下次直接给这段说明,不再给代码。

改动涉及三个以上模块的公共契约,停。 这类改动的正确起点是先定契约再改实现,让模型在没有确定契约的情况下跨模块改,产出的补丁通常互相不兼容。先把接口敲定,再一个模块一个模块地做。

你已经开始为了迁就工具而重构目录结构,停。 为了让 AI 好读而挪文件,收益短期、成本长期,团队里其他人的心智模型也要跟着改。真要重构,用重构自身的理由去立项。

回滚点要提前留。开工之前建一个分支或者一个工作区副本,改坏了直接丢掉,别在原地一点点往回修——AI 生成的补丁往回修的成本经常高于重做。这也是并行开多个改动时用独立工作区的原因,见Claude Code worktree 并行

判断”该换条路”的信号也很明确:如果你已经把问题描述得足够清楚,清楚到你自己看着描述就知道该怎么改了,那就自己改。这时候用工具的边际收益是负的。

六、避坑清单

把整个目录一次性塞进去。 为什么会踩:默认觉得给得越多越保险。怎么避:先给最小集,不够再补。补比删便宜——删已经进入上下文的内容通常需要开新会话。

忘了 lock 文件和构建产物。 为什么会踩:递归投喂目录时默认包含全部文件,依赖锁文件和打包产物动辄几万行,把有效内容挤出去。怎么避:投喂前先确认排除规则生效,把 distbuildnode_modules*.lock、快照测试的基准文件都排掉。

信任工具自带检索的结果不做核对。 为什么会踩:检索通常不报错,只是悄悄给了不相关的文件。怎么避:每次让它先列出实际读过的文件路径,你扫一眼再让它动手。这一步成本极低,能挡掉大半的答非所问。

用旧索引问新代码。 为什么会踩:切分支、拉取远端更新之后索引没跟上,工具读的是旧版本。怎么避:切完分支先重建索引,或者当次直接用显式文件引用绕过索引。

把生成的补丁直接合进主干。 为什么会踩:跨文件补丁看着自洽,冲突标记 <<<<<<< 只会在合并时暴露,逻辑不一致连标记都没有。怎么避:在独立分支跑通测试再合,且合之前自己读一遍 diff。

用”重构一下这个模块”这种范围模糊的指令。 为什么会踩:模糊指令让模型自己决定工作集,它一定会扩大范围。怎么避:指令里写死要改哪几个文件、不许动哪几个。

为了省事把敏感配置一起贴进去。 为什么会踩:密钥常和普通配置放在同一个文件里,整文件投喂就带出去了。怎么避:投喂配置文件之前先过一遍,替换成占位值。这条没有例外。

指望换成海外的更强工具就好了。 为什么会踩:把结构问题当能力问题。另外要说清楚:主流海外 AI 编程工具的官方服务对中国大陆有区域限制,不支持直连使用,市面上确实存在第三方中转,但可靠性、合规性和数据流向都无法核实,这里不做任何推荐。把仓库切好,能用的工具就够用了。

收束

三种切法的选择其实很好记:溯源问题按依赖切,落地任务按任务切,跨模块设计按接口切。选错的代价不是失败,是多花一轮。真正致命的只有两件事——隐式依赖漏了,以及把无关的同构代码一起塞了进去。

开工前过一遍这个自检清单:

  • 我要问的是溯源、落地,还是设计?对应的切法选对了吗?
  • 排除规则生效了吗?锁文件、构建产物、快照基准都排掉了吗?
  • 按名字装配的配置文件搜过了吗?
  • 有没有平行的相似实现,我这次是故意不给它的吗?
  • 分支或工作区副本建好了吗,改坏了能一键丢弃吗?
  • 我能说出这次预期要改哪几个文件吗?说不出就先别开始。

这份清单跑一遍不到三分钟,省下的通常是半天。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。