news 2026/9/10 8:34:52

context-mode:大模型对话中的上下文编排与工程实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
context-mode:大模型对话中的上下文编排与工程实践

1. 为什么需要 context-mode:从一次线上事故说起

先讲一个我实际经历过的场景。之前给一家企业做智能客服系统,业务方提了个需求:用户咨询时,如果能知道“他刚才在浏览哪个页面”“当前是售前还是售后阶段”“是否已经确认过订单信息”,回答会准很多。听起来不难对吧?结果做的时候才发现,真正的难点不在于“多轮对话”,而在于“上下文是怎么混进来的”。

系统对接了三个数据源:用户的实时浏览行为、CRM里的历史工单、以及大模型的多轮会话记录。一开始我们图省事,把所有信息一股脑拼进提示词里,结果问题立刻暴露:用户明明在问退货流程,系统却因为历史工单里有“退款金额争议”的记录,把回答带偏成了“请您联系财务核对”。更离谱的是,有一次会话里同时出现了“新品推荐”和“物流异常”两个话题,模型直接宕机式输出了一篇两不像的回复。

这就是典型的上下文污染。我当时的第一反应是:得给这个系统加一个“context-mode”,也就是上下文模式,让研发人员能显式定义当前会话到底该用哪些上下文、按什么优先级组合、到什么时候应该丢弃。后来我花了差不多三周时间把整套方案落地,效果立竿见影:回答准确率从68%提到了91%,上下文Token消耗降了40%左右。

这篇文章就把整个过程拆开来讲。适合谁看?如果你正在做AI对话类应用、智能客服、知识库问答,或者你在用LangChain、Semantic Kernel这类框架、但发现“上下文”越来越难管,这篇应该能给你一些可直接拿来用的思路。不涉及晦涩的算法推导,偏工程实践,但会把原理讲透。

2. context-mode 的核心设计思路

2.1 先搞清楚“上下文”到底包含几层

很多刚接触的人会把“上下文”简单理解成“历史聊天记录”,这是最大的误区。我在实际设计context-mode时,把上下文拆成了四个独立维度:

  • 会话上下文(Session):当前用户与系统之间多轮对话的内容,包括问题、回答、追问、修正等,是最基础的一层。
  • 场景上下文(Scene):用户当前所处的位置或阶段,比如“在商品详情页”“正在提交订单”“已进入售后流程”,这决定了对话的目标与策略。
  • 业务上下文(Business):从外部系统(CRM、ERP、订单系统等)拉取的结构化数据,比如用户积分、订单状态、历史工单。
  • 知识上下文(Knowledge):从知识库/文档中检索到的、与当前问题相关的片段,通常经过向量召回或关键词匹配。

context-mode 最核心的思想,就是把这四层拆开、并且允许每一层单独配置开关、权重和生命周期。而不是像之前那样,一股脑全塞进去。

2.2 三种基础模式,对应不同的交互场景

在设计时我没有一开始就做很复杂的规则引擎,而是先定义了三种基础模式,覆盖了绝大多数业务场景:

严格模式(Strict Mode)

只使用会话上下文,忽略场景和业务上下文。适合那种“就事论事”的问答场景,比如FAQ解答、政策咨询。严格模式的好处是输出稳定、Token消耗低、几乎不会跑偏。

智能模式(Smart Mode)

默认模式,同时启用会话+场景+业务上下文,但设置了优先级,场景上下文优先于业务上下文。适合智能客服、售前导购这类复杂场景,既能理解用户意图,又能结合用户画像给出个性化回答。

知识增强模式(Knowledge Mode)

在智能模式的基础上,额外启用知识上下文,也就是把向量检索的结果拼入提示词。适合知识库问答、内部文档检索。这里要注意,知识上下文一旦引入,检索质量和上下文拼接顺序会显著影响最终效果。

这三者不是三选一,而是支持按轮次动态切换的。比如用户一开始问“这个手机多少钱”,可以用严格模式;当识别到用户意图是“比较两款手机”时,自动切到智能模式并拉取业务上下文;如果用户问“保修政策是什么”,则临时切到知识增强模式。

2.3 为什么要有独立的上下文模式层

有人说,那我直接在代码里写if判断,根据用户输入切换不同的提示词不就完了?理论上可以,但工程上完全不可行。原因有三:

第一,提示词会爆炸。每个业务场景都要精心构造不同的提示词模板,功能多了以后,维护成本指数级上升,改一个词可能影响几十个场景。

第二,上下文来源不一致。会话上下文存在Redis里,业务上下文存在数据库里,知识上下文需要实时检索向量库。如果没有一个统一的管理层,每次拼接上下文都得写一遍获取逻辑,重复代码极多,还容易漏。

第三,排查极其困难。上线后如果模型回答出了问题,你怎么定位?到底是提示词写错了,还是历史消息取多了,还是检索结果相关度太低?没有上下文模式这一层抽象,你面对的就是一坨无法观测的“输入拼接”。

所以context-mode本质上不是“一个功能”,而是一种上下文编排层(Context Orchestration Layer)。它把“获取上下文”、“裁剪上下文”、“拼接上下文”、“生命周期管理”这四件事统一收口,向上对业务方暴露简洁的配置接口,向下屏蔽各数据源的差异。这样做之后,任何一次Prompt构造都可以被记录、被复现、被审计,这比“能跑通”重要得多。

3. context-mode 的工程实现细节

3.1 配置驱动的上下文策略

我最终采用的方式是“配置驱动”,没有把逻辑写死在代码里。每一类对话流程对应一份YAML或JSON配置,研发人员只需要修改配置,就能调整上下文的使用策略。

mode: smart context_sources: session: enabled: true max_turns: 6 max_tokens: 1200 scene: enabled: true resolver: from_url_and_session priority: high business: enabled: true resolver: from_crm_api required_fields: - user_id - order_status - membership_level cache_ttl: 300 knowledge: enabled: false fallback: on_business_failed: degrade_to_session_only on_scene_unknown: use_default_scene

这份配置的核心在于每个上下文源都有独立的开关、获取方式和容量上限。max_turns控制会话历史取多少轮,max_tokens控制这块上下文最多占多少Token,超了就要做截断或摘要。fallback则定义了异常情况下的降级策略,避免因为某个数据源挂了导致整个对话不可用。

这里我踩过一个坑:一开始max_turnsmax_tokens只限了会话源,没限业务源。结果某个大客户的CRM接口返回了极长的订单历史,一次性把7k Token吃满了,模型输出质量严重下降。后来统一对所有上下文源都加了上限,问题才解决。

3.2 上下文裁剪策略:截断、摘要与多级压缩

上下文窗口有限,而真实业务里用户的历史消息、引用文档、业务数据往往是海量的。context-mode 必须内置裁剪策略,我把它分为三级:

第一级:数量截断

最朴素的做法,只保留最近N轮对话。N的取值需要考虑两个因素:模型的最大上下文窗口,以及回答所需的最少信息量。以主流模型的32k窗口为例,如果知识库检索结果占8k,业务上下文占4k,那么留给会话历史的就只有20k左右,按每轮约1k Token算,一般保留10-15轮。

第二级:重要性保留

简单截断的缺点是,用户可能在前面几轮提到关键信息,直接丢掉会导致语义断裂。所以我会按“消息类型”做加权保留:系统消息、带有结构化信息的消息(如订单号、地址)、用户明确表达偏好或情绪的消息,优先级高于闲聊类消息。哪怕超过轮数限制,这些高优先级消息也会被保留。

第三级:摘要压缩

当历史信息实在太长、且重要信息分散在各处时,就用LLM做一次“增量摘要”,把前文压缩成几百字的短摘要,再接上最近几轮完整对话。我记得有一次用户连续问了二十多分钟,历史记录累积超过15k Token,摘要压缩后只剩1.2k,既保留了关键信息又大幅降低了Token成本。

def build_context(session_history, scene, business_data, knowledge_hits): session_part = trim_history( session_history, max_turns=current_mode.session_turns, max_tokens=current_mode.session_tokens, high_priority_keys=["order_id", "address", "preference"], ) # 场景源不超长就直接拼,超长则只保留scene_id scene_part = scene.to_prompt_fragment() # 业务源优先展示结构化字段;长文本字段做摘要 business_part = f"user_id={business_data.user_id}\n" business_part += f"order_status={business_data.order_status}\n" if len(business_data.remark) > 200: business_data.remark = summarize(business_data.remark, max_tokens=100) business_part += f"remark={business_data.remark}\n" # 知识源保留topK个结果,且按相关度倒序 knowledge_part = "\n\n".join( [h["content"] for h in sorted(knowledge_hits, key=lambda x: x["score"], reverse=True)[:topK]] ) return assemble_prompt(mode=current_mode, parts={ "instruction": current_mode.system_prompt, "scene": scene_part, "business": business_part, "knowledge": knowledge_part, "session": session_part, })

3.3 模式的动态切换与场景识别

严格、智能、知识增强三种模式的静态定义只是基础,真正让context-mode发挥作用的是“对话过程中能够自动切换”。我在实现里加了一个轻量级的意图识别模块,每一次用户消息进来,会先做一个快速分类,判断当前对话属于哪种状态。

场景识别我用了两条腿走路:

一是规则兜底。比如URL匹配,如果用户是从/order/detail/12345页面发起的会话,那场景上下文直接定为“订单详情咨询”;如果用户点击了“申请售后”按钮,场景是“售后流程”。这些规则简单、可靠、零延迟,适合高频且路径清晰的场景。

二是模型兜底。当规则无法判断场景时,用小模型对用户首条消息做分类。你不需要在整个对话过程中反复调用大模型,只在会话开始或用户换话题时触发一次就行。这里可以用一个几千样本的分类模型,也可以直接调大模型API,但一定要控制调用频率,否则延迟和成本都受不了。

动态切换还有一个容易忽略的问题:切换时要同步清理旧上下文。比如用户上一轮还在走“退货流程”的业务上下文,这一轮突然问“那你们有没有新品推荐”,如果不把旧的业务上下文清掉,模型大概率会继续往退货方向带节奏。我在代码里对模式切换定义了一个context_refresh事件,一旦场景变化,相关上下文源自动失效,下一次组装时重新拉取。

3.4 Token预算如何算

在做context-mode时,我养成了“先算预算,再写代码”的习惯。这里给一个可以照抄的计算模板。

假设模型窗口是32k,安全系数建议留20%余量,也就是实际可用约25k。需要用到的上下文源包括:

  • 系统提示词:约2k,固定
  • 场景上下文:约1.5k,固定
  • 业务上下文:约3k,平均
  • 知识检索结果:约6k,最多
  • 用户当前消息:约1k,可变

那么会话历史最多能占用的Token就是25 - 2 - 1.5 - 3 - 6 - 1 = 11.5k。如果你的对话平均每轮1k Token,那最多只能放11轮左右。这个数字就是后续配置里max_turnssession_tokens的取值依据。

这套计算方法帮我避免过很多次“上线后才发现上下文被截断得厉害”的尴尬。先定预算,再定参数,不要拍脑袋

4. context-mode 上线后的高频问题与排查技巧

4.1 对话“跑偏”了,如何快速定位是哪个上下文源的问题

AI对话系统最让人头疼的就是“明明刚才还好好的,怎么突然答非所问”。context-mode 带来的一个巨大好处是:由于上下文被显式拆分成多个源,排查时可以逐个排除。

我的排查顺序是固定的:

  1. 先看场景上下文是否正确。打开日志,看当前场景识别成了什么。如果用户问售后,场景却识别成了售前,那答案基本必歪。
  2. 再看业务上下文是否有脏数据。比如订单状态字段过时了、用户身份拿错了,模型基于错误的数据给出“合理但错误”的回答。
  3. 然后看知识上下文的检索结果。最常见的问题是检索出来的topK文档和用户问题只存在字面匹配、没有语义相关性。
  4. 最后才怀疑会话历史。看是否因为历史消息过长导致早期关键信息被截断,或者模式切换时旧上下文没有清干净。

我一般会在日志里给每个上下文源加上唯一的标记ID,例如ctx:session:12ctx:business:998,这样在排查时可以直接看到最终拼进Prompt的每一块内容是什么、来自哪里。上下文要可观测,否则出了问题只能靠猜

4.2 多轮对话中Token消耗激增,怎么压

Token消耗过高通常有两个原因:一是历史消息越积越多,二是一次检索返回的文档太多。

如果你用的是截断策略,但发现仍超出预期,要重点检查是不是“重要性保留”环节出了问题。我遇到过一种情况:用户每轮都发“好的”“嗯”,这些消息虽然没有实际信息量,但仍然占Token。后来我对消息做了“有效内容判断”,纯语气词或极短消息直接跳过,不进上下文,Token消耗立刻降了一截。

知识库方向,一个非常管用的优化是为知识片段设置更细的切分粒度。不要整篇文档一梭子喂进去,按标题、段落、甚至语义块切分,检索时按块召回,每块控制在300-600字左右。这样做之后,同样的问题检索结果更精准,Token占用也更低。

另外还有一个容易被忽视的点:缓存的业务上下文不要放太长时间。有些接口的数据是频繁变动的(比如物流轨迹),如果把旧数据缓存5分钟,用户就会觉得“回答不实时”。但反过来,如果每个上下文源都实时请求,延迟又会飙升。我最后的策略是“高频变动的字段实时查,低频字段走缓存”,双管齐下,效果最好。

4.3 模式切换生效了,但模型还是“记着”上一轮的内容

这个坑我调试了好几天。代码逻辑上模式切换后旧上下文源已经被清除了,但模型回答里仍然带着上一轮的错误信息。后来发现原因不在拼接层,而在模型的服务端会话缓存

很多模型API支持传入conversation_id来维持多轮记忆,如果你在切换context-mode时没有换一个新的会话ID,服务端仍会保留旧的消息记录,相当于你拼接的只是“可见”上下文,而模型实际“看到”的还有一截历史。

解决方式有两种:一是切换模式时生成新的conversation_id;二是如果同一会话需要保留部分上下文,则要在服务端手动清洗历史,只保留你想让模型看到的那部分。从工程稳妥性来看,我更推荐直接换新会话ID,然后把你认为有价值的历史消息以普通文本形式写进系统提示词里。这样就完全掌控了模型“看到”什么,不会被服务端缓存干扰。

4.4 一张问题速查表,直接保存到团队Wiki里

症状可能原因排查步骤
回答偏离主题场景识别错误或未切换检查scene resolver输出,确认当前场景值
回答过于啰嗦历史轮数太长,低价值消息多降低max_turns,增加有效内容判断
上下文太长报错Token预算没算好用公式重算各源上限,加截断
模式切换后仍沿用旧数据服务端会话缓存未清理切换模式时替换conversation_id
知识库回答空洞检索相关度低或切块太大检查topK与片段长度,优化切分策略
业务数据滞后缓存TTL过长缩短高频字段缓存时间或实时查询

这张表基本覆盖了我上线后的绝大多数工单。后来我把这套排查流程沉淀成了团队的SOP,新同学碰到类似问题,先照着表过一遍,基本能解决80%的Case,不用再来敲我门。

5. 关于 context-mode 的几条实战忠告

做得越多,越觉得context-mode不是“写个配置”那么简单,它本质上是给AI系统建立一套“信息过滤规则”。有几条心得,我觉得比代码本身更值钱。

第一,宁可少给上下文,也不要多给。我之前总觉得上下文越丰富回答越聪明,结果经常因为一个无关字段把答案带沟里去。信息越多,模型越容易“想太多”。context-mode的真正价值在于“克制的融合”,而不是“无限的堆叠”。

第二,上线前一定要做回归测试集。不要只测手工挑的几个好例子。我从实际对话日志里抽了200条,按业务场景分类,做成自动化回归集,每次改策略就跑一遍,准确率下降自动报警。有了这个底子在,后续调参才有安全网,不然改一次坏一次,改到后期根本不敢动。

第三,模式配置要收敛,不要追求无限灵活。我见过有的团队把context-mode做成了一套可视化编排工具,支持任意拖拽组合,结果没人能维护。灵活性和复杂度是伴生的,你要什么就要承担什么。对我目前遇到的大部分业务来说,三种基础模式加一套fallback规则已经足够了,再多就是过度设计。

最后再分享一个我后来一直在用的技巧:每次要调整context-mode之前,我会先把当前用户的完整上下文导出成纯文本,人肉读一遍,看哪些信息是真正有用的、哪些是干扰项。这个方法听起来原始,但极其有效。因为只有你真的站在“模型视角”去看眼前这堆输入时,才会理解为什么有些莫名其妙的回答会冒出来。这个习惯帮我省掉了不计其数的线上Debug时间。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 8:32:37

RNOH横竖屏切换实战:从尺寸监听到状态恢复的完整适配方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 8:32:06

2026企业级AI Agent竞争版图:四类玩家的工程较量与落地路线

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 8:31:43

绝缘子自爆检测实战:从滑动窗口切图到YOLOv8训练

简介:这是一份面向电力巡检与计算机视觉研究者的绝缘子自爆点目标检测数据集,由无人机或巡检机器人在塔内作业时拍摄,聚焦玻璃绝缘子串上自爆缺陷的定位与识别,既可用于独立检测任务,也可衔接语义分割流程。全部数据共…

作者头像 李华
网站建设 2026/9/10 8:31:21

亚马逊选品新思路:用供给断层找出真正能打的产品机会

选品这事,做亚马逊的很少有不犯迷糊的。早期大家习惯看需求端——关键词搜索量、类目体量、增长率,觉得只要“有量”就有得做。我在这个框架下吃过不少亏:有的品需求确实大,一进市场才发现头部链接已经是月销几万的老牌大卖&#…

作者头像 李华
网站建设 2026/9/10 8:29:28

context-mode:智能体调用MCP的上下文传递协议解析

1. 什么是 context-mode:一个被严重低估的智能体通信底层范式“context-mode”这个词最近在开发者社区里频繁冒头,但几乎没人说清楚它到底是什么。我第一次在蓝湖MCP服务的调试日志里看到context-mode: full这行配置时,还以为是某个内部开关的…

作者头像 李华