
1. context-mode 到底在解决什么问题1.1 从一次模型答非所问说起上周我在调试一个内部用的 AI 工单分类服务模型用的是市面上主流的大语言模型效果一直还行。但有个场景特别诡异用户提交一条我忘记密码了的工单系统偶尔会回一句请提供您的账号 ID以便我们核实身份。看起来没什么问题但这条回复是在用户已经在上一条消息里贴出账号 ID的情况下给出的。我翻了一下日志发现事情没这么简单那条带账号 ID 的消息被系统塞进了一个单独的历史记录字段而没有进入模型实际读取的当前对话上下文。模型根本没看见账号 ID当然只能干巴巴地再要一遍。这就是典型的 context-mode——上下文模式——设计出了问题。所谓 context-mode不是一个具体的算法也不是某个开源项目的名字而是我习惯用来概括我们如何组织、筛选、切换喂给模型的那部分输入信息的整套策略。它决定了一件事模型在当前这轮请求里到底看到了什么、看不到什么。这个例子也说明很多 AI 产品跑得不够聪明不是模型不够强而是上下文的组织方式太粗暴。要么一股脑全塞进去要么只取最新一条消息完全没有模式可言。1.2 窗口大小不等于有效上下文做 AI 应用的工程师几乎都会先关注预算和性能。你采购模型的时候第一眼看的一定是上下文窗口32k、128k、200k……数字越大越安心。但我踩过几次坑之后发现上下文窗口只是容量上限不是有效上下文的质量保证。窗口告诉你能装多少而 context-mode 解决的是怎么装最好用。装得下和装得好是两码事。打个比方窗口就像你的办公桌。桌子的面积决定了你最多能摊开多少份文件但如果你把 200 份文件乱七八糟全堆上去找一份关键的合同反而比只有 5 份文件摆放整齐时更慢。模型也是一样它需要在成千上万的 token 里抓住真正重要的那几条信息。token 越多干扰越多出错的可能性越大。我在实际项目里有一个比较直观的经验同一个任务把上下文从 8000 token 精简到 3000 token输出准确率有时候反而提升 10% 以上。原因很简单——多余的无关文档把模型的注意力带偏了。所以真正值得花时间的不是纠结买 32k 还是 200k 的窗口而是设计一套 context-mode让你手里的每一个 token 都有价值。窗口是硬件context-mode 是软件软件的优化空间远大于硬件的参数数字。1.3 上下文的四种形态与三种失效方式为了后面讨论方便我先把上下文这件事拆成四种最常见的形态持久上下文模型每次调用都必须携带的信息比如系统提示词、产品规则、人格设定。特点是稳定、不变、省不掉。会话上下文同一轮多轮对话里的历史消息比如用户之前问了什么、你答了什么。特点是不断累积、会变长。局部上下文当前操作直接相关的信息片段比如正在修改的那个函数、正在处理的那份文档。特点是精准、量小、必须放核心位置。检索上下文从外部知识库、数据库、文件里临时拉取的内容比如 RAG 的召回结果。特点是动态、按需、可能包含噪声。这四种形态组合在一起构成了模型当前这个时刻的完整输入。Context-mode 要做的就是为不同任务选择不同的形态组合并且决定它们的排列顺序、截断策略、刷新频率。上下文在工程上最常见的失效方式也有三种超窗截断消息太多窗口装不下把最早的关键信息挤掉了。位置淹没没有超窗但关键信息埋在长文本中间模型迷失在中间。污染覆盖存在矛盾或过时的信息新信息没能覆盖掉旧信息模型被带偏。这三种失效都不是靠加大窗口能彻底解决的。加大窗口只会推迟超窗但会让位置淹没和污染覆盖变得更严重。所以我越来越倾向于把 context-mode 当成一个独立的设计环节而不是附加在 prompt 后面的一段临时拼接。2. 位置偏差与价值密度窗口内也有黄金地段2.1 Lost in the Middle模型也有注意力高地如果你没听过 Lost in the Middle 这个术语我强烈建议你先记住它。这是 2023 年一篇论文里提出的现象后来在很多模型的实测中都被复现当一段长文本的关键信息位于中间位置时模型对它的利用效率明显低于位于开头或结尾的信息。你可以理解为模型的注意力天然倾向于两头中间位置的信息容易被一带而过。这跟人看长文本的体验非常像开头和结尾印象最深中间读完就忘。这个现象对 context-mode 设计的影响极大。我见过很多团队的 prompt 结构是这样的先是系统提示词然后是一大堆检索文档最后才把当前用户问题放在尾部。看起来没问题但如果你检索到了 10 段文档真正的答案恰好是第 5 段那模型很可能在前面的系统提示词、后面的用户问题上分配更多注意力第 5 段反而被忽视了。站在实操的角度我总结了一套摆放原则最开头放全局性最强的指令比如角色、任务、硬性规则。靠近开头或靠近结尾放当前任务最需要的核心信息比如用户问题、关键约束。中间放辅助性参考材料比如背景知识、备选方案说明、历史对话摘要。2.2 Token 预算的分配谁是核心、谁是陪衬既然窗口内存在黄金地段那么 token 预算怎么分配就成了 context-mode 设计的第一道数学题。我的习惯是把整份上下文预算按 1:2:1 的比例粗分大约 25% 留给系统指令和全局约束大约 50% 留给当前任务的核心上下文大约 25% 留给参考材料、备选内容、对话历史摘要。当然这个比例不是死的。有些任务比如代码续写局部上下文要占到 70% 以上有些任务比如客服回复需要大量产品文档支撑参考材料的占比就会更高。但无论如何你要有一个预算意识而不是让所有内容随缘拼装。我在给团队评审 prompt 的时候经常问一个问题如果这个上下文只能保留 1/3你会砍掉哪些部分如果对方回答不上来说明他根本没有做过优先级排序。Context-mode 的底层思维就是做取舍而不是做加法。2.3 价值密度用信噪比体检每一条上下文我还有一个习惯几乎是职业病了——每次调试 AI 请求的时候会把完整的上下文打印出来逐条问这段内容对当前任务的输出有没有直接贡献这个习惯帮我发现了很多铺张浪费的上下文。最常见的是把整个商品详情页、整本文档、整个代码文件都塞进窗口。你想想模型只是要回答这个商品支不支持货到付款你却把商品图片的 alt 文本、SKU 编号、供应商信息全给它。这些信息不是没用而是对当前问题来说没有直接关系多了反而稀释了真正有用的信息。所以我在团队里推行一个概念叫上下文价值密度——单位 token 对输出质量的贡献度。高密度上下文是直接命中问题答案的内容低密度上下文是可能有帮助但大概率用不上的内容。Context-mode 的核心工作之一就是不断提高价值密度把低密度的内容挡在窗口之外或者降级为按需检索。具体做的时候我一般分三步第一步把当前任务可能需要的上下文类型列全写上类型、来源、大致 token 量、关联度评分。第二步按关联度排序给每类上下文定一个必须携带或者检索时携带的标签。第三步在代码里用开关控制而不是写死在拼接逻辑里。3. 把 context-mode 落地成四种可执行模式3.1 全局指令模式一次配置处处生效第一种模式适合那些每次调用都必须遵守的信息。我把系统提示词、产品红线、角色设定、输出格式要求全部放进去。它的特点是稳定、不常变、必须全量携带。但稳定不等于冗长。我见过不少团队把系统提示词写到 3000 字从公司愿景一直写到具体文案风格看着面面俱到实际上模型记不了那么多真正关键的规则反而被淹没。全局指令模式的关键是把指令写薄。我自己的做法全局指令只保留三部分——你是谁角色的身份与职责范围。你要做什么当前任务的目标与边界。你绝对不能做什么底线规则宁可少也不能含糊。至于那些可以参考资料语气要友好之类的泛泛表述能砍就砍。真正要做到的是让模型一眼抓住重点而不是读一篇小作文。另外全局指令的内容也不是完全不更新。当产品规则发生变化时你需要在代码层面对这条上下文做版本管理确保线上运行的是最新版本而不是靠人肉去改每个调用的拼接逻辑。3.2 命中检索模式按需召回而不是全量塞入第二种模式就是我们常说的 RAG检索增强生成。它解决的是全局指令模式覆盖不了的问题——当知识量太大、无法全部常驻窗口时就要靠检索把最相关的信息拉到上下文里。但我要说一句可能会得罪人的话很多 RAG 项目之所以效果差不是检索技术不行而是检索结果没有经过 context-mode 的筛选就直接拼进上下文。命中检索模式不只是从向量数据库里捞出 top-k 条而是要经过下面这几步处理查询改写把用户问题转成更利于检索的查询语句。重排序向量相似度排名未必准确要用 rerank 模型做二次精确排序。裁剪去重把重复、矛盾、与问题无关的段落剔除。追加引用来源在检索结果后标注来源文档让模型知道它读的是哪份材料。只有经过这四步检索出来的内容才算是干净的上下文。否则向量库捞出来的 top-5 里很可能有两条是相似但过时的旧文档模型读完会得出自相矛盾的结论。我实测过一个客服场景加了一路重排序之后答案准确率从 61% 提升到 78%。改动并不大就是多了一个 rerank 步骤。这个收益比换一个更大的模型明显得多还省钱。3.3 局部注入模式让模型聚焦当前任务第三种模式适合代码助手、文档写作辅助这类场景。它的特点是把当前正在处理的内容放在上下文的核心位置让模型把注意力集中在当下。以代码助手为例。用户正在编辑某个函数的中间部分此时你给模型喂的信息应该是当前文件的结构与关键函数签名。当前光标位置附近的代码片段。最近修改的几个相关函数。当前用户指令。而不要做的是把整个项目仓库的代码全塞进上下文或者像某些工具那样试图把用户打开的所有文件都带上。这不仅浪费 token还会让模型变得无所适从——它收到的信息太多反而不知道当前任务的边界在哪里。我自己在实现这个模式时会用一个聚焦窗口的概念。所谓聚焦窗口就是一个滑动窗口它只覆盖用户最近操作的文件、最近调用的函数、最近查看的文档。这个窗口不是固定不变的而是随用户动向实时更新。Cursor 编辑器的 符号引用机制本质上就是在做这个事你先显式地告诉工具我这段话关注的是这个文件工具再把对应的上下文拉进来。3.4 增量流式模式别在长任务里无限囤积历史第四种模式专门用来处理长对话、长任务的场景。比如多轮客服对话、持续一个小时的智能体交互如果不做裁剪历史消息会源源不断地灌进上下文。增量流式模式的核心思路是随着对话推进旧消息不能原样保留而应该被压缩成摘要或者干脆丢弃。具体做法我一般这样安排最近的 5-10 轮消息原样保留这是模型理解当前意图的关键信息。更早的消息每 5 轮压缩成一条摘要存进一个独立的摘要字段。当摘要越来越多时对摘要再做二级压缩只保留结论性内容。这套思路跟 Git 的提交历史有点像最新的提交是全量 diff老提交只留下 commit message。你不需要为了知道用户最初发过一封邮件而把那封邮件全文都带在身上只需要一个摘要用户反馈了发票金额问题情绪不满就足够了。这个模式最难平衡的是压缩的尺度。摘要太细省不了多少 token摘要太粗关键细节丢失模型后续理解会出偏差。我通常在压缩阶段加一条硬性约束压缩时必须保留用户明确提到过的数字、时间、产品名称和负面情绪表达。这些是最容易被模型忘记、但对后续任务影响最大的信息。3.5 四种模式怎么选一张对比表把上面四种模式放在一起对比选型思路会更清晰模式名称适用场景上下文规模刷新频率核心风险全局指令模式角色设定、产品规则、输出约束小数百 token低频仅规则变更时更新指令写得过长、互相矛盾命中检索模式知识问答、客服、文档问答中数百到数千 token每次请求动态检索召回噪声、旧文档干扰局部注入模式代码补全、文档编写、文件摘要小聚焦窗口内随用户操作实时切换窗口聚焦范围过窄、丢失跨文件依赖增量流式模式多轮对话、智能体长任务先涨后平靠压缩控制持续变化每次请求都变摘要过粗、关键信息丢失绝大多数应用都不是只用一种模式而是把几种模式串起来。比如电商客服全局指令模式提供接待规范命中检索模式提供商品知识增量流式模式管理多轮历史。Context-mode 的模式二字本质上是让你像搭乐高一样根据不同服务需求选不同的上下文构件。4. 实操怎么用 context-mode 组装一次高质量请求4.1 从零搭建一个上下文组装器前面聊了这么多概念这里给一份可以照着用的实现骨架。我用 Python 写一个简单的上下文组装类你可以把它当作模板改造。from dataclasses import dataclass, field from typing import List, Dict, Optional dataclass class ContextComponent: name: str # 组件名称如 system_rule content: str # 组件内容 priority: int # 优先级数字越小越靠近开头 max_tokens: int # 该组件最多允许的 token 数 required: bool # 是否必须携带False 表示可以按需裁剪 dataclass class AssemblyRequest: components: List[ContextComponent] query: str # 当前用户输入 budget: int 8000 # 本次请求的上下文预算 def build(self) - List[Dict[str, str]]: messages [] total_cost count_tokens(self.query) # 按优先级排序先处理重要组件 sorted_components sorted( self.components, keylambda c: c.priority ) for comp in sorted_components: if total_cost self.budget: if comp.required: # 必带组件超预算时做强制截断 trimmed truncate_tokens(comp.content, comp.max_tokens) messages.append({role: system, content: trimmed}) total_cost count_tokens(trimmed) continue # 预算充足全量添加 messages.append({role: system, content: comp.content}) total_cost count_tokens(comp.content) # 用户问题永远放在最后紧贴当前任务 messages.append({role: user, content: self.query}) return messages这个类做得比较粗糙但核心逻辑是通用的先按优先级排序再加必带、可裁剪机制最后保证用户问题占据结尾位置。实际生产环境中你还可以加入缓存、压缩、检索等步骤但骨架不需要变。4.2 组装顺序的黄金法则组装器的优先排序决定了模型先看到什么、后看到什么。我常用的顺序是系统指令全局规则、角色、输出格式。当前任务描述明确本次请求想让模型做什么。高价值检索结果与当前任务强相关的参考信息放在靠近系统指令的位置。辅助性材料背景知识、备用方案、次要文档。历史对话摘要如果有多轮对话放压缩后的摘要而不是全量历史。用户的最新输入永远放在最后模型会把它作为最直接的指令来源。这个顺序不是拍脑袋定的而是尽量贴合模型的注意力规律。开头和结尾是最有效的两个锚点把最核心的内容放在锚点上把容易干扰的信息藏在中间。4.3 一个完整的案例给工单系统配置 context-mode用上面那个工单分类的例子我完整走一遍配置过程。场景用户提交一条工单我昨天买的耳机右耳没声音想换货。传统做法是直接把用户问题 全部聊天记录 全部商品说明扔给模型。这会导致模型既可能被聊天记录里的旧问题干扰也可能因为在商品说明里找不到换货政策而给出错误答复。我用 context-mode 重构之后是这样的全局指令模式系统提示词写你是电商售后客服必须基于公司换货政策回答不得承诺超出政策范围的赔偿方案优先级最高必带。命中检索模式根据用户问题检索耳机换货政策售后时效说明两篇文档只带回与换货条件强相关的段落。这里的检索不是按整篇文档召回而是按段落召回。局部注入模式如果用户同时提交了订单截图需要 OCR 出订单号、购买时间、商品名作为独立字段注入上下文方便模型核实身份。增量流式模式如果这是一轮多轮对话把前面的消息压缩为用户反馈右耳无声已提供订单号为 xxxx而不是把上一条完整的消息原文带进来。这样组装出来的上下文比原来的体积小很多但每条信息都直接服务于是否能换货这个判断。我用这个方案上线之后工单分类的准确率提升明显同时每次请求的 token 消耗平均降了 40% 左右。5. 实测翻车点context-mode 最容易出问题的三个环节5.1 缓存失效你以为命中了其实每次都从头计算很多模型服务提供 prompt 缓存功能同样的前缀内容在短时间内重复使用时可以大幅降低成本。这个功能相当实用我在多个部署环境里都验证过。但它是把双刃剑。问题出现在什么时候呢当你的 context-mode 组装顺序轻微变化时——哪怕是系统提示词末尾多了一个空格、一个标点变化——前缀缓存就会失效所有调用都变成全量计费账单直接翻倍。更隐蔽的问题是如果你在组装时把当前时间或者动态内容拼在了前缀中间而不是前缀末尾会导致每次请求的前缀都不同。我排查过一个项目明明加了缓存成本却没有任何下降最后发现就是因为他们把当前日期放进了系统提示词而且不是放在末尾——每天都多一个字节前缀每次都变。正确的做法是把所有稳定内容放在最前面动态内容尽量放在靠近末尾的位置给缓存留足可复用的前缀空间。如果动态内容确实需要放在开头那就把动态部分单独抽出来用占位符替换在最终组装时才填充。5.2 摘要压缩信息没丢但语境感丢了增量流式模式里压缩历史消息是一种必然手段但摘要风险很低、收益很大的观点实在是大错特错。我遇到过这样一次事故用户跟客服 AI 沟通换货前面聊了大概 20 轮AI 已经答应可以换货。到了第 21 轮用户说那我把耳机寄回去结果 AI 回复请提供订单号以便我们核实——因为压缩摘要时把已同意换货这个关键进展给丢了系统又从零开始走流程。这说明一个问题摘要压缩不能只想着保留用户说过什么还要保留**系统已经承诺过什么**。后者往往是业务流程的关键节点一旦丢失AI 就会做出前后矛盾的行为。我的改进方案很笨但有效压缩时把历史消息分成用户信息和系统承诺两个维度分别记录再合并成摘要。每一次压缩都要确保这两个维度分别更新而不是统一压成一段对话进展。5.3 新旧信息冲突窗口里同时躺着两份互相矛盾的规则第三个翻车点最隐蔽也最让人头疼。当你的 team 更新了一份产品规则而旧版规则还在向量库里的时候模型可能同时检索到新旧两版输出结果自相矛盾。我见过一个具体案例一款 App 的新版隐私政策是默认不收集用户位置旧版是默认收集用户位置以提高服务质量。结果某次客服问答系统同时检索到了两版政策模型给出的答复前半句说不收集后半句又说位置信息用于改善推荐用户直接被绕晕了。要解决这个问题靠 RAG 自身很难因为语义太相似了向量检索时往往两个都要。我目前用的是两板斧第一板斧在文档入库时增加生效时间戳检索时强制过滤掉已经过期或已下架的版本。第二板斧在检索结果中加入生效状态标签比如当前生效已废弃并要求模型只依据当前生效标签的内容作答。这本质上是在 context-mode 里加入元信息层让模型能够分辨哪些上下文可信、哪些不可用。6. 进阶把 context-mode 变成团队的工程资产6.1 从 prompt 管理升级到上下文契约当团队里只有两三个人写 prompt 的时候把字符串写在代码里也能跑。但一旦产品复杂起来、prompt 开始被多处调用、不同业务方开始各自维护一套系统提示词时混乱几乎是必然的。我建议把 context-mode 抽象成一份上下文契约。所谓契约就是每一个上下文组件的定义名称、来源、更新频率、必须携带或按需检索、字段格式、安全等级。这份契约可以由不同团队各自维护但通过统一的 schema 进行约束。// 一个简单的上下文契约 schema 示例 type ContextComponentSchema { id: string; // 组件唯一 ID trigger: string; // 连接器或事件决定何时注入 content: string; // 内容或内容拉取逻辑 version: string; // 内容版本号 priority: number; // 优先级控制排序 ttl: number; // 缓存时间单位秒 rules: string[]; // 附加规则如过期文档禁止注入 };有了这份契约之后不论是后端工程师、算法工程师还是产品经理都能清楚地知道模型在某个场景下应该携带什么信息而不是靠线下口头同步。6.2 三个必须盯住的观测指标做上下文工程没有数据反馈就只能靠感觉正确。我在每一次上线前和后都会盯三个指标上下文利用率实际携带的 token 数除以窗口上限。利用率太低说明上下文太薄可能缺少关键信息利用率太高说明有冗余需要裁剪。关键信息命中率人工标注一组这个任务必须知道的信息检查模型实际收到的上下文里是否包含它们。这是我最看重的指标因为它直接衡量 context-mode 的质量。重复回答率与矛盾率统计模型在多轮对话中出现重复询问已给信息和前后矛盾的比例。这两个指标异常基本就是上下文管理出了问题要么是压缩丢了信息要么是旧信息没有正确覆盖。这三个指标都不需要复杂的数据平台用日志统计就能看到趋势。但恰恰是这种看似简单的观测很多团队都没做结果只能靠用户投诉来发现问题。6.3 自动化回归把上下文质量变成 CI 的一部分我最后分享一个正在做、也强烈建议你尝试的事情把 context-mode 的验证自动化接入 CI/CD 流程。具体方法不复杂针对每条业务场景准备一组标准的测试用例每个用例包含完整的对话历史和人工标注的最优回答。每次改动上下文组装逻辑、压缩策略、检索参数之前跑一遍这组测试看输出是否仍然符合预期。这套测试不需要追求 100% 的语义等价我们只需要关注两类指标关键信息是否仍然保留比如是否同意换货这个结论是否出现明显矛盾比如同时建议邮寄退货和线下门店办理。我在团队里跑这套回归测试大概已经半年了效果非常明显。它最大的价值不是阻止你犯错而是让你在改动的瞬间立刻知道自己犯了错不用等上线之后被用户骂。如果你刚接触 context-mode我建议你不要一开始就追求把四个模式全部落地。先花一周时间做一个简单的上下文日志审计把你现在线上应用的每次请求上下文打印出来逐条检查哪些信息有用、哪些没用、哪些缺失了关键点。这个审计过程本身就能让你发现一大堆可以立刻改进的问题。等你把这些问题修完再回头看模式这件事你会有完全不同的理解。