夜雨聆风学习资料网

ARTICLE · 1332838

源码视角:Claude Code / Codex 是怎么把上下文压下去的

源码视角:Claude Code / Codex 是怎么把上下文压下去的

📚 验证成本转移系列 · 第4/10篇

10 篇讲透一件事:AI 把写代码的成本压到近乎零,但软件工程的总成本没降,只是从「生产」转移到了「验证」

✅ 第1篇:被误读的一年:AI 让开发变快还是变慢,这个问题本身就有问题

✅ 第2篇:瓶颈转移:从「写不出」到「审不过来」

✅ 第3篇:Harness Engineering:2026 年最重要但最少被讨论的词

⏳ 第4篇:源码视角:Claude Code / Codex 是怎么把上下文压下去的

⏳ 第5篇:移动端特写:超大代码库里的 AI Coding,难在哪

⏳ 第6篇:重构的真相:代码一行没动,问题却解决了

⏳ 第7篇:写代码退居其次之后,工程师站在哪

⏳ 第8篇:从 Vibe 到 Agentic:一套方法体系的生长史

⏳ 第9篇:AI 审 AI:当审查本身也交给机器

⏳ 第10篇:收尾:2026 年,一个移动端开发者该带走什么

📰 科技要闻

• 一位 05 后写下个人数码编年史,称只想回应自己当时的感受与记忆,只因那时满腔少年心气、对一切充满好奇、对未来满怀憧憬。

• 「本周看什么」栏目盘点最近值得一看的 9 部作品,其中电影《戴高乐之战》发布本周新预告并确认引进(11月2日)。

• 在洛杉矶气温升至 90 多度、需躲避酷热之际,UV 追踪设备 Gem 让作者重新思考阳光对健康而不只是对外表的影响。

一、灶台就那么大:上下文为什么是核心问题

把上下文窗口想成后厨那张备料台:做一桌席,每道工序都要摊开料碟、锅和写着“盐放过了”的便签,台面就那么大,越往后越没地方。只剩两条路——把早先的碟子推下台,或换张大台、每道工序把碟子重新端一遍。前者丢东西,后者累死人。长任务里天天在发生。

换成工程话:一次跨十几个文件的重构不是一轮对话,是几十轮。每轮请求都要把系统提示、历史来回、工具回显、读过的文件重新打包发过去;一次 cat 或 grep 的回显就是几千 token,吃窗口的是它,不是你那句 prompt。轮次线性堆上去,窗口迟早见底,外面那层调度壳子(Harness)就得拍板:截断,还是滑窗。

截断的代价很隐蔽:模型忘了开头定的接口约定、忘了哪个文件动过,于是重复改、改回旧逻辑、往错处下刀,擦屁股的还是你。全量重喂或换个标称更大的窗口,账单和首 token 延迟一起涨,钱花在反复搬同一批旧内容上;模型对长文中段的注意力本来就衰减,多花钱买回来的仍是个会漏的脑子。

我的判断:大窗口是止痛药,不是解法。拿窗口尺寸当 Harness 的本事夸,等于拿灶台大小夸厨子。

所以决定长任务能不能跑到底的,不是窗口标称多少 K,而是每一步让模型看见什么、扔掉什么、怎么交接。用大窗口硬扛?我不用;把力气花在裁剪和交接上?值得,这是整条链子最硬、也最容易被跳过的一块。裁剪当然也会失真、会剪错,这笔账后面算。本篇要拆的两种策略,说到底都在答同一件事:怎么让台面上只留当下要用的那几样。

策略 A:流水线式,每段只带够用的上下文

把它想成中央厨房的切配台。洗菜的只见要洗的菜,切配的只拿到洗好的菜加一张配菜单,炒菜的只接一配料碗——没人把整冰箱堆到自己台面上。让这条线不断掉的从来不是记忆,是那张单和那只碗:几克、什么刀口、送哪个灶,字段写死,下一道不必猜上一道当时在想什么。流水线式上下文就是这意思:不指望模型记住全程,只保证每段交接时把该带的带齐。

落到实现上,先把长任务切成固定阶段——定位、改码、跑测、复盘——每段开工重新组装一份最小上下文,只放本段输入;段末不传对话历史,传一个字段固定的结构化对象。我自己的交接单长这样(示意,不是任何产品的源码):

{  "stage": "patch",  "inputs": ["Foo.java:42"],  "facts": {"root": "NPE@init"},  "next":  "test",  "drop":  ["chat_history"]}

这里 drop 和 facts 一样重要:显式声明丢掉什么,才没人偷偷把历史又塞回来。

我愿意用它,是因为三件事终于可预期:上下文体积有上限、成本算得清、某段崩了只重跑那一段而不是整轮重来。排查尤其省事——你能精确复现模型当时看见了什么,比「整段塞进去祈祷别截断」踏实得多。

但它不该是默认答案。阶段是预先定死的,碰上路径不确定的活(偶现 crash 得在定位和验证之间来回跳)立刻显笨;更麻烦的是交接单本身就是瓶颈,字段没设计到,那条信息永久丢了,后面再聪明也补不回。我的判断很直接:阶段能枚举清楚、形态稳定的批量活——仓库迁移、按模板生成、批量格式化——用它收益最大;探索性任务别硬套,那是策略 B 的地盘。

策略 B|一路累积,到点交一次班

拿值夜班交接理解最顺。上一班八小时里的每一条告警、每一次试探,不可能全口述给下一个人;真交班就一张纸:3 床术后平稳、两点有台定时重启、那条报警是误报别管。拿着它,下一班无缝接上,整晚的监控流水直接归档。摘要式上下文就是这个动作:任务不切段,历史照常往上堆,堆到窗口快满、或刚跨过一个里程碑,把这一大段压成一份能揣着走的短记忆,原稿扔掉,带摘要继续跑。Claude Code 的 compact 是典型——不是清空重来,是让模型写一份「到目前为止」交给下一个自己。

我愿意为它的灵活买单。探索性调试、啃没文档的遗留代码,事先根本不知道该在哪儿划线,流水线硬切只会把线索切碎;摘要不挑时机,到点就压,反倒兜得住。但我不迷信:摘要是模型自己写的有损压缩,而写摘要的那个「自己」,恰恰不知道后面会用到什么。当时无关的一个变量名、一次跳过的 corner case,压完就没了;更糟的是原稿已丢,想回溯都没底稿。策略 A 丢信息是明着丢,B 是悄悄丢,后者更难查。

所以我给摘要打了个补丁:不许自由发挥写散文,逼它按硬事实字段交班,试探过程、废话、自我纠正这些软叙述一律压掉。

# HANDOFF / 只留硬事实goal:     一句话目标changed:  file:line, file:lineblocking: 报错原文,别复述rejected: 方案 + 否决原因next:     下一步一件事

粒度上我吃过亏:压太狠,一句「已修复登录态」就让它跑去改缓存,改错地方还理直气壮。摘要里必须留 file:line、报错原文这种能核对的锚点——可被验证的才叫短记忆,否则只是读着通顺的幻觉。至于压缩率,别信现成数字,按自己的任务试出来才算数。

流水线还是错题本:先问你敢不敢赌那一步

把这事想成备考。流水线是按章节复习:每章只带本章的公式清单,章末填一张交接卡交给下一章,写清哪些结论已定、从哪接手。可控、可预算、崩在哪章一眼定位;代价是题目一跨章节,卡上没记的那个知识点就断了。摘要式是一本错题本:一路做一路压进去,翻到哪页都能接着想,不预设顺序;但压缩有损——你当时为什么选错、草稿上那步被划掉的试算,最后往往只剩一个正确答案。

落到 Harness,差别不在省不省 token,在输入可不可预期。流水线每段喂什么、花多少、失败落在哪段都能写死、能断言;需求一改切分就得跟着改,这才是它"灵活度低"的来源,不是笨。摘要不预设任务形状,长任务能一直滚;但 handoff 等于让模型给自己的历史写读书笔记——漏一个约束,后面全建立在缺了它的历史上,越往后越难对账。失真会累积,不是一次性事故。

想给"摘要失真有多常见"找量化结论:本篇要点未指定 arXiv 编号,未能在 arXiv 核到该编号,我不编数字撑场面。按工程经验,选择就三行:

场景
选谁
为什么
能列清步骤:迁移、重构、CI 批处理
流水线
摘要的灵活性在这里是纯负担
列不清:探索性调试、需求还在变
摘要
流水线会被现实撞歪
长任务但有硬约束:接口签名、已否决方案
混合
骨架走流水线,约束字段不进压缩

我的判断:别把摘要当默认。handoff 没有第二份原始记录可校验,写错了甚至不知道从哪步开始错;那张笨交接卡至少能断言、能回滚。上下文工程里"可验证"比"省 token"值钱。真混着用,只压过程、别压约束——这是我会守的线。

📱 落到 Android:按改动文件裁上下文

把整个 module 塞进上下文,等于出差把整个衣柜拖上飞机:行李箱就那么大(窗口),多带的每件都是超重费(token)。我按「这次要办的事」收拾——改动的文件是主角,带全文;它 import 的类算配件,只带一跳;配件怎么用,带张接口清单(类签名)就够,函数体那点走线没必要拆开。

落到代码就是个裁剪器:吃 git diff 吐出来的改动文件,吐一个拼好的上下文串。三步,骨架如下,解析细节我省略了。

// skeleton: changed + deps + sigs// ... resolve() / decl omittedclass CtxTrimmer(    private val proj: Project) {    fun build(        changed: List<KtFile>    ): String {        val sb = StringBuilder()        // 1. changed: full text        changed.forEach {            sb.append(it.text)        }        // 2. direct deps: one hop        val deps = changed            .flatMap { it.imports }            .map(proj::resolve)            .distinct()        // 3. deps: signatures only        deps.forEach {            val s = signature(it)            sb.append(s)        }        return sb.toString()    }    private fun signature(        c: KtClass    ): String {        // header + method decls        val decls = c.methods            .joinToString("\n") {                it.decl            }        return c.header + decls    }}

我的判断:别当银弹。裁到只剩签名,AI 看得见fun pay(),看不见里面怎么扣款,碰上要改方法内部的活就会改错地方——粒度的账就在这儿:剪太狠省了 token,把判断依据也一起剪没了。我的做法是签名留一跳、顶多两跳,再往外整段换摘要。但论「省 token 又不丢主线」,它比无脑塞整个 module 强得多,是我实测收益最直接的一处改动。

踩坑:摘要会失真,裁过头会改错地方

整理错题本这事我干过:把一道压轴题的解法浓缩成一句「用韦达定理」,当时觉得够狠够省。期末再翻,四个字还在,可为什么设而不求、哪一步最容易翻车,全想不起来了。handoff 摘要就是这种有损压缩——它留得住「做过什么」,压掉的偏偏是「为什么这么改」。下一个 agent 拿着结论没有理由,最省事的做法就是照结论再推一遍,于是把当初特意绕开的 deprecated API 又请了回来。我吃过这亏,现在只把摘要当省 token 的便签,绝不当事实用。

裁剪是另一把刀。为了让窗口小一点,把「看着无关」的基类、接口、资源文件一并砍掉,AI 少读了 8k token,转头在父类里本该复用的方法上手写了一遍。这类错最阴:能编译、读着还挺顺,review 一眼扫过去看不出来,等跑起来才发现改在了错地方。

粒度怎么定?我不拿行数或 token 数当尺子,卡「一跳依赖」:当前改动文件显式 import 到的工程内源码,原样留、不摘要;二阶以外才压成类签名。命不中就当没这一跳,宁可漏给也别递归乱抓。三个入参依次是改动文件、java 根目录、工程包名:

import refrom pathlib import Pathdef one_hop_deps(chg, root, pkg):    """一跳依赖:只跟当前文件的显式 import。"""    hits = set()    pat = r'^import\s+([\w.*]+)'    txt = Path(chg).read_text()    fs = re.findall(pat, txt, re.M)    for fqn in fs:        if not fqn.startswith(pkg):            continue        if fqn.endswith('.*'):            continue        rel = fqn.replace('.', '/')        f = Path(root, rel + '.kt')        if f.exists():            hits.add(str(f))    return hits

值不值得?值得,但有条线:能重新读回原文的,随便压;压了就再也还原不回来的决策依据——某次为何绕开某 API、某个线上 bug 的成因——我宁可多烧几千 token 原样揣着。我的判断是宁可裁保守点,也别让 AI 在错地方写出「看着很对」的代码:后者排查起来的成本,比省下的那点上下文贵一个数量级。

收尾:上下文工程是 Harness 里最值钱的一块

出远门收拾行李,箱子就那么大。新手比的是谁箱子大,老手比的是带什么、扔什么——同样 20 寸,有人三天够用还走得轻快,有人塞满用不上的东西,到地方翻箱倒柜找不着充电线。长任务的上下文窗口就是这只箱子:决定你能不能走到终点的,从来不是窗口标称多少 K,而是每一轮往里装什么、到点丢什么、怎么把有用的交到下一轮手上。

把这个系列往前翻,模型能力趋同之后差距只能从工程层找(第 3 篇说的 Harness),而工程层里最不显眼的恰恰是这块。它不像工具调用、不像跑测试那样有个「看得见的功能」,改好了没人夸,改坏了却让后面每一步都多付代价:窗口撑爆、账单往上走、改错地方还理直气壮。前面拆的流水线、handoff 摘要、一跳裁剪,说到底都在答同一件事——让模型每一步只看见该看的。

我的判断:这是 Harness 里最容易被低估、但杠杆最长的一块。换 prompt 技巧常常只对一类任务生效,裁剪和交接策略改对一次,之后每一轮请求都省、每一次改动都更准,收益是复利式往后滚的。也别把它神化成「求一个最优压缩率」:我不追那个数字,我要的是喂进去的东西可预期、能断言、能回溯——可验证比省 token 值钱,这是整篇我守的线。

落到我们这行尤其如此。Android 一次改动往往跨好几个文件,还得编译、装真机才验得动,上下文喂错一次的返工成本比后端高一截。所以在「验证成本转移」这条链上,我会先把上下文工程补起来:它不产出功能,却决定了前面所有收益能不能兑现。

回首页,搜索你想要的文档

在首页搜索框输入关键词,快速定位资料

文库首页搜索指引 立即搜索

相关学习资料