搞过智能客服项目的朋友应该都有同感:单轮问答做得再漂亮,一旦进入多轮对话,效果就很容易失控。用户上一句说“我想查话费”,你回复了余额;下一句问“那流量呢”,模型可能就不记得前面聊的是什么了。这种问题根子往往不在模型选型上,而在提示词体系的设计上。这篇就聊一聊从架构师视角做智能客服多轮对话时,提示工程里最值得投入精力的4个关键优化点。
这个方向适合正在做客服机器人、对话式AI产品、或者准备把大模型接入业务系统的团队参考。我整理的内容不是某个单一框架的使用教程,而是把多轮对话从“能用”推到“好用”的思路、模板和避坑经验。下面直接进正题。
1. 先拆解问题:为什么智能客服多轮对话容易“跑偏”
多轮对话和单轮问答有一个本质差异——它需要模型在连续对话中维护一个不断变化的“记忆状态”。单轮问答里,模型只需要理解当前这句话就够了;多轮对话里,模型必须知道“用户刚才说了什么”“我上一轮已经回答了什么”“现在这句话和之前的对话是什么关系”。
这个记忆状态稍有一点偏差,后面的回答就会跟着歪。我看到过不少项目把用户最近几轮对话直接拼到上下文里,然后让模型自由发挥,结果就是:
- 越聊越偏,模型把用户刚才提到的某个产品名当成新问题来回答;
- 重复提问,模型不记得自己已经解释过一遍;
- 信息丢失,用户说“那这个呢”的时候,模型根本不知道“这个”指代什么;
- 答非所问,上一轮话题还没结束,模型就跳到下一轮。
整个链路拆开看,问题主要出在四个环节:上下文管理、意图判定、输出结构、异常兜底。这四个环节刚好对应我要说的四个优化点。先把架构层面的整体逻辑理清楚,再逐项展开。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 优化点一:会话上下文的动态管理与加权
2.1 上下文窗口不是越长越好,而是要“够用”
很多团队觉得,模型上下文窗口大,那就把历史上所有对话都扔进去。这个想法在实际业务里很容易翻车。一方面,大部分商业模型按Token计费,窗口拉满意味着成本直线上升;另一方面,过长的历史会对模型形成噪声干扰。举个实际场景:用户在第1轮问了一个售后问题,客服答完之后,用户在第4轮又问了一个完全独立的新问题。如果不加处理地把前3轮内容全部保留,模型很容易把这两个无关话题错误地关联起来,导致第4轮的回答掺杂着第1轮的信息。
我常用的做法是设置了一个滑动的对话窗口。窗口按时间顺序保留最近的N轮完整对话,超过N轮的部分只保留一个“摘要”,摘要由系统在每轮结束后自动生成。这个摘要不是简单地把旧对话拼在一起,而是抽取关键信息,例如下面这样:
code复制<历史摘要>
用户之前咨询了【手机型号X】的订单物流状态,已回复预计3天内送达。
用户已确认订单地址,无需修改。
</历史摘要>
在Token预算有限时,摘要比原文的性价比高很多。一个5轮对话的历史摘要可能只需要100个Token,但1轮对话原文可能就有500个Token。窗口的具体轮数要看业务复杂度,我接触过的场景里,5到8轮是一个比较稳的范围。太少会丢上下文,太多会增加噪声和成本。
2.2 用优先级给上下文里的信息分层
仅仅是保留最近的几轮,还不足以解决所有问题。多轮对话里有些信息是“贯穿全程”的,比如用户在下单流程里选定的商品型号、用户主动说明的账号类型、客服在中间环节做出的承诺。这些信息如果随着窗口滑动被挤掉,后续对话一定会出问题。
因此我会把上下文拆成三层来管理:
- 核心层:用户画像、订单编号、商品型号、售后单号,这些是业务的锚点,必须全程保留;
- 工作层:最近N轮的具体问答内容,用来理解当前话题;
- 背景层:更早的历史内容,只保留摘要。
在组装Prompt时,把这层按“核心层优先、工作层其次、背景层最后”的顺序排列。这样做有两个好处,一个是让模型更关注重要的业务参数,另一个是当Token不够时,优先裁剪背景层,而不是动核心层。
2.3 实操示例:一段可复用的会话记忆模板
实际项目中我会在System Prompt里设计一个“状态区”,用于动态记录每次对话后的关键信息。下面是一个经过简化的示例:
code复制你是智能客服助手。请维护下方的“对话状态区”,每轮对话结束时更新它。
<对话状态区>
用户目标:查询订单物流
当前进度:已获取订单号,等待用户确认查询方式
已确认信息:订单号=XP20250701
待确认信息:无
</对话状态区>
请基于状态区回答用户当前问题,不要重复询问已确认信息。
当模型回答完用户问题后,它会根据对话内容更新状态区中的“当前进度”和“已确认信息”。这样下一轮消息到达时,模型打开状态区就能快速恢复记忆。这个过程有点像给模型配了一张“工作便签”,不用每次都在历史对话里找信息。
这套模板上线后,我们项目里用户重复提问的比例下降得比较明显。关键是模型不再需要从大段历史里去推理“用户刚才说过什么”,状态区直接给了它权威答案。
3. 优化点二:意图识别与主动澄清机制
3.1 意图识别要“外化”,不要指望模型自动判断
常见的做法是让模型在生成回复的同时顺便判断意图,例如在Prompt里写“根据用户的问题判断他的意图”。这种做法在小样本场景下还能凑合,但在真实客服环境里不太可靠。原因是:回复生成和意图识别本身是两个不同的优化目标。生成回复追求语义流畅自然,意图识别追求分类准确,放在同一个解码过程中互相干扰。
架构上更稳妥的做法是拆分成两步:先用意图分类Prompt单独识别用户当前输入的意图,再把意图结果和对话上下文一起交给生成模型。这样做的好处一个是能精确控制分类结果,另一个是可以在意图层面做路由,比如识别到“退款申请”就直接转去售后流程,不经过生成模型。
3.2 给“澄清”设计独立的提示路径
多轮对话里模型经常遇到语义不完整的情况。用户说“第二个呢?”,但没有“第一个”的上下文;用户说“帮我取消”,但没说要取消什么。这时候很多模型会硬猜一个答案,结果答非所问。
我意识到问题在提示词设计里:必须给模型一个明确的“不知道该问就主动问”的指令。这听着像废话,但如果你不白纸黑字写进Prompt,模型大概率会顺着上下文去猜,而不是提反问。于是在设计生成Prompt时,加了一段固定内容:
- 当用户指令不明确时,先列出缺失的信息点,再向用户提问确认;
- 每次提问最多列出3个缺失项,不要一次性抛出一堆问题;
- 提问后结束回答,等待用户补充信息,不要继续猜测。
为了让这套机制更容易维护,可以把澄清规则独立成一个Prompt模块。不同业务场景(比如售前咨询和售后报修)的澄清路径是完全不一样的,独立模块方便针对每个场景单独调优。
3.3 置信度标记与转人工判断
多轮对话里总会有模型搞不定的情况。这时候,识别模块不仅要回答问题,还要输出置信度。我给意图识别模块设计的输出格式是:
json复制{
"intent": "查询物流",
"confidence": 0.87,
"slots": {"order_id": "XP20250701"}
}
当置信度低于某个阈值(比如0.65)时,直接走人工客服。这个逻辑不能写在生成模型里靠它“自觉”判断,而是应该由系统层面的路由代码根据置信度值去决策。多轮对话里,用户的意图会在对话过程中变化,置信度阈值也要跟着变。我一般会区分“对话开始的第一轮”和“对话进行中的后续轮次”——第一轮的置信度阈值可以适当调低,因为用户刚开始描述往往是零散的;后续轮次调高,因为经过多轮交流后意图应该更明确。
4. 优化点三:结构化输出与动作编排
4.1 为什么多轮对话必须有结构化输出
纯文本输出在单轮问答里没有大问题,但在多轮对话里,纯文本会让后续流程很难接住信息。举一个实际例子:用户说“我要退货”,模型回复了一段话“您的退货申请已受理,请将商品寄回”,但系统需要知道“退货申请”这个动作是否真的执行了。如果只靠文本解析,后端的运维系统还得从一大段自然语言里提取“申请状态”。
提示工程里有一个层级的动作:让模型在回答用户的同时,输出一个机器可读的结构化动作指令。这个指令包含要执行的操作和参数。同一个回复里可以同时有给用户看的自然语言内容,以及给系统执行的JSON指令。
拿“退货申请”为例,模型输出如下:
json复制{
"reply": "您的退货申请已受理,请将商品寄回",
"actions": [
{"type": "create_refund_order", "order_id": "XP20250701", "reason_code": "not_as_described"}
]
}
自然语言部分给用户看,结构化的“actions”给系统调用。这样对话引擎就不再需要做一次额外的文本解析,直接读取JSON就能触发后端的订单处理流程。
4.2 用JSON Schema约束模型输出的设计思路
有了结构化输出,还要保证格式稳定。大模型输出的JSON有时候会缺字段、嵌套层级不对、甚至直接返回纯文本。我自己会在Prompt里附带一段JSON Schema,并用少量示例作为少样本参考。
JSON Schema的写法可以参考下面这个简化版:
json复制{
"type": "object",
"properties": {
"reply": {"type": "string"},
"actions": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {"type": "string"},
"order_id": {"type": "string"},
"reason_code": {"type": "string"}
},
"required": ["type", "order_id"]
}
}
},
"required": ["reply", "actions"]
}
把Schema放进System Prompt里,效果比只写一句“请输出JSON”稳定很多。尤其是在多轮对话场景里,模型每轮都要输出JSON,很容易在紧张的任务中跑偏。约束越多,格式漂移越少。如果后端用的是支持函数调用的模型,可以优先采用函数调用机制,让模型原生返回结构化参数,而不是手写JSON——这套路本质上是一样的,只是把约束放到了模型层而非提示词层。
4.3 动作编排与多轮状态的维护
结构化输出解决了“表单提交”的问题,但多轮对话里还有“状态流转”。比如用户申请退货,流程需要经历“确认信息—打印退货单—填写运单号—完成退款”。每一轮结束,系统要记录当前处在哪个环节,以及已经拿到了哪些信息。这里我是用“状态机+提示词”的组合:状态机负责流程流转的确定性,提示词负责每个环节的自然语言表达。
从经验来看,不要把流程逻辑硬塞进模型里让它“自己理解”。模型的强项是理解用户语言,弱项是严格遵守多步业务规则。状态机在外层管流程,模型在内层负责和用户对话。最直接的好处是:上一轮缺什么参数,下一轮该要什么信息,这些逻辑完全由代码控制,不会因为用户换了一种问法就错乱。
5. 优化点四:兜底降级与可观测性设计
5.1 多轮对话中最常见的几种“崩坏”现场
不管设计多巧妙,线上多轮对话总会遇到几个棘手场景,而且这些场景往往在测试阶段根本不会出现。
最常见的是用户中途改了话题。用户本来在咨询订单物流,突然插入一句“你们有没有那种防水的耳机”。如果上下文设计得不够好,模型会把这句话当作物流问题的补充信息,回答得驴唇不对马嘴。
第二种是“嘲讽和负面情绪”场景。用户问了三次没解决,语气带着明显不满:“你们客服到底能不能干活?”模型如果按正常的业务口径回答“抱歉给您带来不便”,很容易激怒用户。这时候需要的是让模型识别到情绪级别,并触发“安抚+快速转人工”的降级动作。
第三种是反问循环。用户问“到底什么时候能到?”模型回“请提供订单号”,用户再问“到底什么时候?”——模型又回“请提供订单号”。来来回回好几轮,用户越来越烦躁,系统原地打转。
这三个场景的共同点是,模型在既定流程里找不到出路。如果提示词里没有预留“逃生通道”,模型就会死循环。所以兜底方案必须在架构层面提前预留。
5.2 降级策略的提示词设计
我在生成Prompt里加了一条明确的降级触发规则:
code复制如果连续2轮无法获取关键信息,或用户情绪评级为负面,停止追问,转为以下兜底话术:
“好的,我帮您转接人工客服,您的问题会比较复杂,人工处理更快。”
同时输出动作:{"type": "handover_to_human"}。
这里有两个细节容易遗漏。一个是要在“无法获取关键信息”后面加一个具体的轮次阈值,而不是让模型自己判断“试几次算试几次”。另一个是兜底话术里不能只是冷冰冰地转接,最好附带一句让用户感觉被重视的话。
情绪识别这个能力,我建议单独做一个小分类模型,而不是靠提示词让大模型顺带判断。电商场景里有大量口语化表达,单靠生成模型顺带判断,准确率不太稳定。单独分类模型输出情绪等级,系统再根据等级决定是继续对话、转人工还是直接切换话术。
5.3 可观测性:记录每一轮的意图、置信度与状态迁移
多轮对话项目上线后,真正的难关不是“效果不好”,而是“不知道哪里不好”。我踩过最明显的坑是:线上用户反馈不好,但翻日志只看到一问一答的记录,根本看不出模型在哪一步丢了上下文、在哪一步判断错了意图。
后来我开始给每一轮对话都打上以下几方面的核心字段:
- 轮次序号
- 用户输入原文
- 意图识别结果和置信度
- 系统本轮采用的上下文状态区快照
- 模型输出内容
- 结构化动作列表
- 状态迁移记录(从哪个状态到哪个状态)
- 特定提示词版本号
有了这些字段,线上排查问题的时候就可以按轮次回放。比如用户说“你们客服能不能干活”这一轮,我可以看到情绪分类模型输出的等级、意图识别结果、状态机走到了哪一步,然后定位问题是出在Prompt没写清楚、状态机判断条件不对、还是分类模型误判。
部署时我还习惯给Prompt做版本管理。每次调整提示词,都会记录版本号,并在日志里随每一轮对话一起保存。后面做A/B测试时就能快速对比不同版本提示词的对话质量。客户项目里,我曾经因为某次提示词改动把“售后维修”意图全部分到了“售前咨询”里,如果没有版本号追踪,这类回归问题很难及时发现。有一个统一版本号的字段,用SQL一查就能找出影响范围。
6. 架构视角的全局复盘:4个优化点如何协同
6.1 从单轮Prompt到系统工程的转变
这4个优化点如果分开看,每一个都是局部优化。但在项目里真正起作用的是它们的协同关系。上下文管理为后面的意图识别提供准确的背景信息;意图识别把用户输入转化为结构化指令,交给状态机执行;结构化输出反哺上下文管理,让状态区能填写准确的状态信息;兜底降级则把前面三个环节没处理好的异常情况接入人工。
这套链路跑通之后,提示工程就不再是“写一段话丢给模型”那么简单,而是变成了一个完整的系统工程。说句实在话,“提示词设计师”和“提示词架构师”最大的区别就在这里。前者注重单次对话的效果,后者关心的是整条链路的稳定性和可维护性。
6.2 优化点的优先级判断
如果项目正在起步阶段,我的建议是不需要同时铺开所有4个优化点。优先级排下来大概是这样的:
第一步先做上下文动态管理,这是多轮对话的地基。连上下文都记不住,其他优化都是白搭。第二步做结构化输出,因为动作编排和状态机依赖它。第三步做意图识别外化,等业务积累了一定对话数据后再调优置信度阈值。第四步做兜底降级与可观测性,这个一般等系统有一定用户量后价值才体现出来,但日志字段设计建议从第一天就留好。
6.3 我的实操体会
做智能客服多轮对话,我一直觉得最关键的品质是克制。打住不问的克制、不猜用户意思的克制、不确定就转人工的克制。很多效果问题并不是模型不够聪明,而是我们给了它太多“发挥空间”。提示词里的每一句“你可以”“请尝试”,最终都会变成线上某个失控的角落。
把该管的逻辑管住,把该让模型发挥的空间留出来,这条平衡线画好了,多轮对话项目就已经成功了八成。项目上线后,我的习惯是定期翻一遍对话日志,把里面“用户表达绕弯”和“系统追问了三轮以上”的样本单独拎出来看。每一批这样的样本,往往都能指出下一轮提示词优化的具体方向——这套迭代方式比任何理论分析都有效。
