← 返回资讯
陈默
AI 行业分析师
已审核

上下文窗口越大,代码质量反而越差

上周用 Cursor 0.42.3 重构一个老项目的订单模块,我干了件蠢事——把整个 3000 行的 `order-service.ts` 直接拖进 Composer,让它帮我整理。结果它吭哧吭哧生成完,我一看,中间 500 行的退款状态机逻辑没了。直接没了。

上下文窗口越大,代码质量反而越差

上下文窗口越大,代码质量反而越差


上周用 Cursor 0.42.3 重构一个老项目的订单模块,我干了件蠢事——把整个 3000 行的 order-service.ts 直接拖进 Composer,让它帮我整理。结果它吭哧吭哧生成完,我一看,中间 500 行的退款状态机逻辑没了。直接没了。

也不是注释掉了,就是凭空消失了。

我愣了半天才反应过来:它觉得那是冗余代码,给我“优化”掉了。

靠。


最近朋友圈和即刻上全是 AI 编程的内容,各种“百万 token 上下文”的宣传,Claude 3.5 Sonnet 出了 200K,Gemini 1.5 Pro 直接干到 1M。但说实话,我去年踩过的坑告诉我一个事:窗口再大,你不做结构化分块,出来的代码就是一坨。不是比喻,是真的能跑但没法看的那种。

先说个数据

去年 12 月我们组内部做了个实验,挺扎心的。

同一个需求文档,三组人用不同的方式喂给 AI 编程工具(当时用的是 Claude 3.5 Sonnet 20241022):

结果:A 组产出的代码首次通过率 31%,B 组 47%,C 组直接到 68%。

更离谱的是 A 组出了 3 次 bug——AI 把不同模块的变量名搞混了,生成的代码编译能过、TypeScript 类型检查也不报错,但逻辑完全错误。其中有一个是把 order.refundAmountpayment.refundAmount 当成同一个字段用了,线上跑了三天才发现。这种 bug,查起来想死。

长上下文窗口不是银弹,它更像一个巨大的桌子。你把所有东西都摊在上面,找东西反而更慢。


我踩过最惨的坑

2024 年 11 月初,我在做一个电商系统的订单模块重构。那时候刚用上 Claude 的 200K 上下文,心想牛逼了,直接把整个订单模块(大概 8000 行)加上相关的用户模块、支付模块的接口定义全丢进去,让 AI 帮我重构。

第一次生成出来,看着挺像那么回事。跑起来也没报错。TypeScript 编译过了,ESLint 也没告警。

上线两天后,财务那边炸了。

有一批订单的退款金额算错了。用户买了两件商品,用了一张满减券,结果退款的时候按照商品原价退了,没扣掉优惠券分摊的部分。大概多退了 3 万 2 千多块钱。

排查了一整天。最后发现是 AI 在处理长上下文时,把旧版退款逻辑(2023 年的版本,按商品原价退)和新版逻辑(2024 年 3 月改的,按实付金额退)混在一起了。两个版本的代码都在上下文窗口里——一个在文件底部的注释掉的代码块里,一个在正式逻辑里。AI 没有正确判断哪个是当前生效的版本,它“自作主张”取了个中间值。

等等,这里我要更正一下——不是取了中间值,准确说是它在计算退款基数时用了新版逻辑,但在处理优惠券分摊时又跳回了旧版逻辑的 fullRefund 分支。两个版本的代码片段在 embedding 空间里靠得比较近,模型就当成同一套逻辑来理解了。

教训:上下文不是越多越好,噪音太多反而干扰判断。尤其是注释掉的旧代码,必须删干净再喂。

后来我改了策略,每次只喂当前模块的核心文件,加上明确的接口契约作为边界。再也没出过这种问题。


结构化分块到底怎么做

经过这小半年各种项目的折腾,我总结了一套还算实用的分块策略。不保证是最优解,但起码在我自己的项目里能用。分享出来大家批判。

第一层:项目级分块

别把整个项目的文件树丢给 AI。这玩意儿看到 200 个文件会懵的。

我现在的做法是维护一个 project-context.md,大概 200-300 行,放在项目根目录。包含这些东西:

这个文件相当于给 AI 的“项目说明书”,每次对话都带上。实测能让 AI 的上下文理解准确率提升 30% 左右——这是我自己记的,不一定严谨,但体感很明显。

第二层:模块级分块

这个是最关键的一层。我的规则很粗暴:一个模块的上下文不超过 3000 行。

为什么是 3000?不是拍脑袋。

我们组用不同大小的上下文做过对比测试,数据大概是这样:

具体操作上,我会给每个模块维护一个 module-context.md,包含这个模块的核心接口定义、依赖关系、数据模型。然后实际喂给 AI 的时候,用这个文件 + 当前要修改的 2-3 个核心文件。

举个例子,改订单模块的退款逻辑时,我给 AI 的是:

1. module-context.md(订单模块的接口定义和依赖,大概 400 行)

2. refund-service.ts(退款服务,要改的文件,800 行)

3. payment-adapter.ts(支付适配器的接口定义,只给接口,不给实现,150 行)

总共 1350 行。AI 处理起来很轻松,而且不会乱猜支付模块的内部实现。

第三层:函数级分块

这个是从一个 GitHub 项目学到的思路,好像是叫 code-to-context,Star 不多但思路很对:用 AST 工具把代码文件解析成函数级别的结构化数据。

比如一个 2000 行的 service 文件,我会先用 tree-sitter 写的脚本把它拆成这样的 JSON:

JSON
{
 "file": "order-service.ts",
 "summary": "订单核心业务逻辑",
 "functions": [
 {
 "name": "createOrder",
 "signature": "(params: CreateOrderDTO): Promise<Order>",
 "summary": "创建订单,包含库存校验、价格计算、优惠券核销",
 "dependencies": ["inventoryService.checkStock", "couponService.validate"],
 "lines": "45-120"
 }
 ]
}

嗯...这个比较复杂,展开说一下。

这个结构化的索引文件大概只有原文件的 1/5 大小,但包含了 AI 需要的核心信息——函数名、签名、依赖关系、功能摘要。实际用的时候,先让 AI 看这个索引,它自己判断需要哪些函数的完整代码,我再按需提供。

这个做法的好处是,AI 的“注意力”被引导到真正相关的代码上,而不是在 2000 行里大海捞针。 我觉得这可能是整套策略里最有用的一步。


一个意外的发现

做这套分块策略的过程中,我发现一个有意思的事情。

分块越细,AI 越敢说“不知道”。

以前我把整个模块丢进去,AI 几乎从来不会说它需要更多信息。它会硬着头皮猜。猜错了也不告诉你。但用了结构化分块后,AI 会主动说“这个函数依赖了 couponService.validate,能给我那个模块的接口定义吗?”

这其实是个好事。AI 的“自信”往往和上下文的信息密度成反比——信息越杂乱,它越容易产生虚假的确定感。据我了解,这跟 transformer 的注意力机制有关,信息噪声大的时候模型倾向于“平滑”处理,反而掩盖了不确定性。


工具推荐

说几个我在用的工具,都是免费的:


最后说两句

AI 编程现在最大的瓶颈不是模型能力,而是我们怎么组织信息喂给它。

长上下文窗口给了我们一个错觉,以为可以偷懒不用做信息筛选了。但实际上,信息的质量远比数量重要。 这句话我说过很多次了,但每次踩坑都有新的体会。

我现在带的新人,第一件事就是教他们怎么给 AI 准备上下文,而不是怎么用 AI。这个习惯养成了,效率提升比换什么模型都明显。我们组有个实习生,来的时候啥都不会,但上下文组织得特别好,一个月之后产出快赶上两年经验的了。

你们在实际项目里是怎么处理长上下文的?有没有踩过类似的坑?或者有更好的分块策略?评论区聊聊,我最近正好在优化这块的流程,想听听大家的经验。


#AI编程 #长上下文窗口 #结构化分块 #研发效能 #工程实践

Edit:没想到这么多兄弟有同感。评论区有人提到用 RAG(检索增强生成)做代码检索的思路,把代码库向量化之后按语义检索相关片段再喂给 AI,这个方向很有意思。我下周试试用 LanceDB 做本地向量索引,到时候回来更新效果。还有老哥推荐了 aider/map 命令,说能自动做函数级上下文映射,我也去看看。

659
10992 阅读
2 评论
分享
链接已复制
编辑说明

本文由 MakeSense 编辑团队撰写并审核。文中引用的数据和观点均经过交叉验证,如有疏漏欢迎在评论区指正。最后更新:2026年06月27日 18:14

陈默

AI 行业分析师

前某大厂 AI 实验室研究员,关注大模型技术演进和商业化落地。写过 200+ 篇行业分析,擅长从产品视角拆解技术趋势。

读者评论 2

老李 昨天
有个小问题想请教,文中提到的那个方案在大规模场景下性能怎么样?
回复 点赞 (5)
运营小陈 4天前
转发到团队群了,大家都觉得有参考价值。
回复 点赞 (4)