导学阶段-上下文管理策略
本文整理自 CNB 上的开源课程 《Vibe Agent:从零搭建 Coding Agent》。课程讲义、示例代码与练习仓库均在 CNB 上公开,文中示例出自该仓库对应章节。
上下文炸掉之后,要把它压回去。课程给了四种做法,从删历史到重建历史,复杂度一级一级往上走。
截断:留下头和尾
最直接的做法是保留 system 消息和最近 N 条,中间全丢。十行代码能写完,不产生额外调用,token 总量有硬上限。
1 | def truncate(messages, keep_last=6): |
课程 demo 的默认值是保留最近 6 条。优点是简单,缺点也在这里:N 条之前的用户偏好和硬约束直接消失,中间的多步推理链一起没掉。用户会感觉 AI 突然忘了说过的话。截断适合短对话,客服问答、一次性脚本生成这类任务,关键上下文本来就在最近几轮里。
这里有个必须避开的坑。如果对 messages 整体切片,system 消息也会被切掉,角色约束没了,输出立刻失稳。system 永远不参与截断,这条要在代码里显式保证。
摘要:用一次调用换浓缩记忆
比截断聪明一步:中间那段不直接删,调一次 LLM 压成摘要,再拼上 system 和最近 N 条原文。用一次调用的成本,换回对话历史的浓缩版本。
1 | SUMMARY_PROMPT = """把下面的对话压缩成 100 字以内的中文摘要。 |
两个技巧让摘要更可靠。一是明确告诉模型保留什么、丢弃什么,上面这段 prompt 就是。二是让摘要输出结构化,按用户偏好、已确认事实、未决问题分行写,后续轮次的模型更容易找到需要的那条信息。
代价是多一次 LLM 调用的成本和延迟,摘要质量取决于模型,重要事实可能在压缩里被并成一句话。后者比前者更难发现:摘要读起来通顺,细节已经没了。
分层:重要事实单独拎出来
截断靠位置,摘要靠运气,分层靠规则。做法是把上下文分成四层:原始 system 原样保留,关键长期事实单独抽成一条,早期对话压成摘要,最近 N 轮保持原文。前面两层永不参与压缩。
事实怎么抽?课程用一个关键词表兜底:过敏、预算、重要、对了、一定要、不要。用户消息里命中这些词的,整条提出来放进事实槽。
1 | KEY_FACT_KEYWORDS = {"过敏", "预算", "重要", "对了", "一定要", "不要"} |
demo 在一段 28 轮的历史里埋了两个事实:第 2 轮的“我对花粉过敏”,最后一轮的“预算是每天 500 元”。截断能保住预算,因为它在末尾;过敏在第 2 轮,直接砍掉。摘要能不能保住要看 LLM 怎么写那 100 字。分层把两条都提到事实槽里,位置再远也在。
四种策略处理完的规模与保留情况:
| 策略 | 条数 | 字符数 | 关键事实 |
|---|---|---|---|
| 不处理 | 57 | 923 | 两条都在 |
| 截断 | 7 | 106 | 过敏被砍掉 |
| 摘要 | 8 | 207 | 取决于摘要写法 |
| 分层 | 9 | 267 | 两条都保住 |
字符数从 923 压到一百到两百多,条数只剩个位数,差距全在关键事实有没有留下。
这套做法有边界。关键词表覆盖不到隐式约束,比如“我不能吃辣”里没有任何预设词,抽不出来。升级方案是用小模型做实体抽取,或者让大模型单独跑一次事实抽取,准确率提高,成本和延迟也上去。
分层适合多轮长对话,尤其是用户会不断补约束的场景:旅行规划里预算和偏好反复调整,Coding Agent 边写边改需求。这类硬限制必须跨过所有工具调用一直保留。
GSSC 与选型
前三种都是单点优化,GSSC 把上下文构造拆成四个阶段:Gather 从对话历史、Memory、工具输出和文件系统收集候选信息,Select 按当前任务相关度打分丢掉低相关的,Summarize 压缩长内容,Compose 按 system、facts、examples、context 的结构拼成最终 messages。
这是 Anthropic、Cursor、Claude Code 这类产品背后的上下文管线。适用场景是信息源不止对话历史,还包括文档、代码库和 Skills 的时候。每个阶段可以单独调优、单独观测,也能进 CI 做回归。代价是设计和维护成本,小项目用不上。
四种策略怎么选,课程给了一张对照表:
| 任务特点 | 推荐策略 |
|---|---|
| 演示、短对话 | 截断 |
| 中等长度、单一任务 | 摘要 |
| 多轮、用户不断补约束 | 分层 |
| 多信息源的生产系统 | GSSC |
实际工程里通常组合使用。主链路走分层,保证重要事实不丢;system 和事实槽两层永不压缩;早期摘要层内部再跑一遍 select 和 summarize,按相关度决定压什么。课程 lab 实现的是分层,因为它覆盖了最主要的一类失效:早期约束在长对话里消失。
四种策略的复杂度递增,丢信息的风险递减,没有一种是通用解。判断标准回到任务本身:这段对话里最重要的信息是什么,它出现在第几轮,会不会在压缩时被丢掉。
这对搭 Agent 意味着什么
上下文管理是每轮都要做一次的决策,不是启动时配一次的参数。截断适合先跑通,分层是长任务的下限,GSSC 把这件事变成可维护的工程。任务跑长之后还能不能稳定拿到早期约束,是检验方案的直接标准。
本轮对话管住了,跨会话的偏好和习惯还在外面。那是 Memory 系统要处理的事。


