智能客服系统的多轮对话架构意图管理、槽位填充与上下文压缩一、为什么七轮对话的客服比单轮更难工程化——多轮对话的核心挑战单轮问答的智能客服只需要理解当前问题并返回答案即可。多轮对话场景下用户可能在一个对话中先查询订单状态再修改收货地址最后要求开具发票——三个意图在七轮对话中交叉出现。如果系统无法感知对话的状态流转当用户说把刚才那个地址改成公司地址时客服无法确定刚才那个地址指的是什么。从工程角度看多轮对话架构面临三个核心挑战。意图漂移识别——用户在对话中可能从查询意图切换到投诉意图系统需要实时感知意图变更并调整响应策略。槽位跨轮继承——用户在第一轮提供了订单号第三轮不应当再次询问系统需要维护一个跨轮对话的槽位状态。上下文窗口管理——当对话超过 20 轮时全量历史消息会超出大模型的 Context 窗口限制需要在不丢失关键信息的前提下对历史消息进行压缩。当前主流的解决方案是将多轮对话系统拆解为三个协同模块意图管理器负责对话状态追踪槽位引擎负责信息抽取与填充上下文压缩器负责在 Token 预算内保持关键信息的覆盖率。三者的协同质量直接决定了多轮对话的成功率。二、三层协防——意图流转、槽位引擎与上下文压缩的架构设计意图管理器的核心职责是维护对话状态机。当用户从订单查询切换到投诉退款时意图管理器需要做两件事一是确认意图漂移的置信度足够高避免因用户的一句这也太慢了吧就误判为投诉意图二是在意图切换时重置与该意图无关的槽位退款意图不需要订单物流的槽位。槽位填充引擎采用识别-校验-追问的三段式流程。NER 模块从用户输入中抽取实体信息填充到槽位中槽位完整性检查模块对比当前意图的必有槽位列表发现缺失则触发追问。追问策略需要设计得自然——请问您的订单号是多少比缺少必填字段 orderId的用户体验更好。上下文压缩器是 Token 预算的守门员。当对话超过 15 轮时全量历史消息可能达到 4K 至 6K Token与 LLM 回复所需的生成 Token 合并后容易超出窗口。压缩策略通常采用关键轮保留 早期轮摘要的混合模式最近 5 轮保留完整消息更早轮次通过 LLM 生成结构化摘要在摘要中重点覆盖实体信息订单号、产品名和关键决策点用户选择了什么。三、Java 后端的多轮对话引擎——基于 Spring Boot 的生产级实现import com.fasterxml.jackson.databind.ObjectMapper; import java.time.Instant; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; /** * 多轮对话状态管理器——协调意图、槽位与上下文的生命周期 */ public class DialogueStateManager { private static final int MAX_CONTEXT_TOKENS 4096; private static final int RECENT_ROUNDS_KEEP 5; private static final ObjectMapper MAPPER new ObjectMapper(); // Session 级别的对话状态存储 private final ConcurrentHashMapString, DialogueSession sessions new ConcurrentHashMap(); /** * 处理一轮对话意图识别 → 槽位填充 → 上下文压缩 → 生成回复 */ public DialogueResponse processTurn(String sessionId, String userInput, LLMService llmService) { if (sessionId null || userInput null || userInput.trim().isEmpty()) { throw new IllegalArgumentException(sessionId 和 userInput 不能为空); } DialogueSession session sessions.computeIfAbsent(sessionId, k - new DialogueSession(sessionId)); // 第一步意图识别 IntentResult intent identifyIntent(userInput, llmService); handleIntentSwitch(session, intent); // 第二步槽位填充 MapString, String extractedSlots extractSlots(userInput, intent, llmService); session.mergeSlots(extractedSlots); ListString missingSlots checkRequiredSlots(intent, session); // 如果关键槽位缺失生成追问 if (!missingSlots.isEmpty()) { return buildSlotPrompt(intent, missingSlots, session); } // 第三步上下文压缩 String compressedContext compressContext(session, llmService); // 第四步生成最终回复 String reply llmService.generate(buildFinalPrompt( intent, session.getSlots(), compressedContext)); // 更新会话状态 session.appendTurn(new DialogueTurn(userInput, reply, intent.getCurrentIntent())); return new DialogueResponse(reply, intent.getCurrentIntent(), intent.getConfidence()); } /** * 意图识别——输出当前意图、置信度和是否为意图漂移 */ private IntentResult identifyIntent(String userInput, LLMService llmService) { String prompt String.format( 你是意图识别器。分析以下用户输入返回 JSON 格式: {\intent\: \订单查询|物流追踪|投诉退款|发票申请|一般咨询\, \confidence\: 0.0-1.0}\n用户输入: %s, userInput); String llmOutput llmService.generate(prompt); try { IntentResult result MAPPER.readValue(llmOutput, IntentResult.class); if (result.getConfidence() 0.6) { result.setCurrentIntent(一般咨询); } return result; } catch (Exception e) { // JSON 解析失败时降级为一般咨询 return new IntentResult(一般咨询, 0.5); } } /** * 处理意图切换——高置信度切换时清除非共享槽位 */ private void handleIntentSwitch(DialogueSession session, IntentResult intent) { String previousIntent session.getCurrentIntent(); if (previousIntent ! null !previousIntent.equals(intent.getCurrentIntent())) { if (intent.getConfidence() 0.8) { System.out.printf(意图切换: %s → %s (置信度: %.2f)%n, previousIntent, intent.getCurrentIntent(), intent.getConfidence()); // 清除非共享槽位保留订单号、用户名等通用槽位 session.retainSharedSlots(getSharedSlots()); session.setIntentSwitchCount(session.getIntentSwitchCount() 1); } else { // 置信度不足暂时维持原意图 intent.setCurrentIntent(previousIntent); } } session.setCurrentIntent(intent.getCurrentIntent()); } /** * 上下文压缩——最近 N 轮保留原文早期轮次压缩为摘要 */ private String compressContext(DialogueSession session, LLMService llmService) { ListDialogueTurn history session.getHistory(); if (history.size() RECENT_ROUNDS_KEEP) { return history.stream() .map(DialogueTurn::toPromptString) .collect(Collectors.joining(\n)); } // 分期处理最近 5 轮保留原文 int splitIndex history.size() - RECENT_ROUNDS_KEEP; ListDialogueTurn earlyTurns history.subList(0, splitIndex); ListDialogueTurn recentTurns history.subList(splitIndex, history.size()); // 早期轮次生成结构化摘要 String earlySummary; if (session.getEarlySummary() ! null) { // 已有摘要增量更新 earlySummary session.getEarlySummary(); } else { String earlyText earlyTurns.stream() .map(DialogueTurn::toPromptString) .collect(Collectors.joining(\n)); String summaryPrompt 请将以下对话历史压缩为结构化摘要 重点保留订单号、产品名称、用户决策、关键时间节点。\n earlyText; earlySummary llmService.generate(summaryPrompt); session.setEarlySummary(earlySummary); } StringBuilder context new StringBuilder(); context.append(【历史对话摘要】\n).append(earlySummary).append(\n); context.append(【最近对话】\n); recentTurns.forEach(turn - context.append(turn.toPromptString()).append(\n)); return context.toString(); } /** * 检查必有槽位是否完整 */ private ListString checkRequiredSlots(IntentResult intent, DialogueSession session) { MapString, ListString intentRequiredSlots Map.of( 订单查询, List.of(orderId), 物流追踪, List.of(orderId), 投诉退款, List.of(orderId, complaintReason), 发票申请, List.of(orderId, invoiceType) ); ListString required intentRequiredSlots.getOrDefault( intent.getCurrentIntent(), Collections.emptyList()); ListString missing new ArrayList(); MapString, String slots session.getSlots(); for (String slot : required) { if (!slots.containsKey(slot) || slots.get(slot) null) { missing.add(slot); } } return missing; } private MapString, String extractSlots(String userInput, IntentResult intent, LLMService llmService) { // 调用 NER 模型或 LLM 提取实体 String prompt 从以下文本中提取订单号(orderId)、手机号(phone)、 投诉原因(complaintReason)、发票类型(invoiceType)等实体 输出 JSON。\n文本: userInput; try { String output llmService.generate(prompt); SuppressWarnings(unchecked) MapString, String slots MAPPER.readValue(output, Map.class); return slots; } catch (Exception e) { return Collections.emptyMap(); } } private DialogueResponse buildSlotPrompt(IntentResult intent, ListString missingSlots, DialogueSession session) { String slotNames missingSlots.stream() .map(this::slotDisplayName) .collect(Collectors.joining(、)); String prompt 请问您的 slotNames 是什么; return new DialogueResponse(prompt, intent.getCurrentIntent(), intent.getConfidence(), missingSlots); } private String buildFinalPrompt(IntentResult intent, MapString, String slots, String context) { return String.format( 你是一个专业的智能客服。当前意图: %s。已知信息: %s。\n 对话上下文:\n%s\n请生成回复:, intent.getCurrentIntent(), slots, context); } private String slotDisplayName(String slotKey) { return Map.of( orderId, 订单号, phone, 手机号, complaintReason, 投诉原因, invoiceType, 发票类型 ).getOrDefault(slotKey, slotKey); } private SetString getSharedSlots() { return Set.of(orderId, phone, username); } // --- 内部数据类 --- interface LLMService { String generate(String prompt); } static class DialogueSession { private final String sessionId; private final Instant createdAt; private String currentIntent; private String earlySummary; private int intentSwitchCount; private final MapString, String slots new LinkedHashMap(); private final ListDialogueTurn history new ArrayList(); DialogueSession(String sessionId) { this.sessionId sessionId; this.createdAt Instant.now(); } void mergeSlots(MapString, String newSlots) { if (newSlots ! null) slots.putAll(newSlots); } void retainSharedSlots(SetString sharedKeys) { slots.keySet().retainAll(sharedKeys); } void appendTurn(DialogueTurn turn) { history.add(turn); } void setCurrentIntent(String intent) { this.currentIntent intent; } void setEarlySummary(String summary) { this.earlySummary summary; } void setIntentSwitchCount(int count) { this.intentSwitchCount count; } String getCurrentIntent() { return currentIntent; } MapString, String getSlots() { return slots; } ListDialogueTurn getHistory() { return history; } String getEarlySummary() { return earlySummary; } int getIntentSwitchCount() { return intentSwitchCount; } } static class DialogueTurn { private final String userInput; private final String assistantReply; private final String intent; private final Instant timestamp; DialogueTurn(String userInput, String assistantReply, String intent) { this.userInput userInput; this.assistantReply assistantReply; this.intent intent; this.timestamp Instant.now(); } String toPromptString() { return 用户: userInput \n客服: assistantReply; } } static class IntentResult { private String currentIntent; private double confidence; IntentResult(String intent, double confidence) { this.currentIntent intent; this.confidence confidence; } String getCurrentIntent() { return currentIntent; } double getConfidence() { return confidence; } void setCurrentIntent(String intent) { this.currentIntent intent; } } static class DialogueResponse { private final String text; private final String intent; private final double confidence; private final ListString missingSlots; DialogueResponse(String text, String intent, double confidence) { this(text, intent, confidence, Collections.emptyList()); } DialogueResponse(String text, String intent, double confidence, ListString missingSlots) { this.text text; this.intent intent; this.confidence confidence; this.missingSlots missingSlots; } } }代码中有几个工程上的关键决策。意图切换时的置信度阈值设为 0.8低于阈值时维持原意图避免用户随口一句抱怨就触发意图切换。通用槽位订单号、手机号在意图切换时保留因为它们在多个意图间共享。早期轮次的上下文压缩不是每轮都重新调用 LLM 做摘要而是增量更新——首次生成摘要后缓存减少 LLM 调用成本。四、多轮对话的工程陷阱——从 Token 超支到意图振荡的防御策略第一个常见陷阱是意图振荡。当用户输入我的订单怎么还没到你们太慢了意图识别器可能连续在物流追踪和投诉退款之间跳转。解决方案是引入意图切换的冷却期——同一个会话在 3 轮对话内不允许两次切换意图。第二个陷阱是槽位追问的恶性循环。当客户反复无法提供订单号时系统不应无限追问。合理的设计是设置最大追问次数通常 3 次超过后由人工客服接管或提供替代方案如您可以使用手机号查询订单。第三个陷阱是上下文压缩的信息丢失。当早期轮次的摘要丢失了关键的用户偏好信息如我不要红色款后续回复可能完全忽略这一约束。压缩器应当对不同类型信息分层处理实体信息如订单号强制保留偏好信息如颜色选择优先保留闲聊信息酌情丢弃。对话长度超过 30 轮时即使经过摘要压缩Token 消耗仍然可能触及 LLM 的 Context 窗口上限。此时需要引入更激进的策略——将对话切分为子对话每个子对话独立维护上下文子对话间通过结构化摘要建立连接。五、总结智能客服的多轮对话架构是一个由意图管理、槽位填充、上下文压缩三个子系统协同构成的工程体系。意图管理保证对话不跑偏槽位引擎保证信息不丢失上下文压缩器保证 Token 不超支。三者的协同质量决定了用户会不会在第六轮对话后愤怒地转人工。从实现优先级看建议先做好槽位填充引擎——这是多轮对话结构化程度的量化指标。槽位覆盖率越高后续的意图管理和上下文压缩的难度越低。意图管理的重点是防振荡上下文压缩的重点是防信息丢失。三个模块不是独立的而是通过 Session 状态共享编织成一个有机整体——每一次对话流转都是三者之间的一次精密协调。
智能客服系统的多轮对话架构:意图管理、槽位填充与上下文压缩
发布时间:2026/7/16 16:53:22
智能客服系统的多轮对话架构意图管理、槽位填充与上下文压缩一、为什么七轮对话的客服比单轮更难工程化——多轮对话的核心挑战单轮问答的智能客服只需要理解当前问题并返回答案即可。多轮对话场景下用户可能在一个对话中先查询订单状态再修改收货地址最后要求开具发票——三个意图在七轮对话中交叉出现。如果系统无法感知对话的状态流转当用户说把刚才那个地址改成公司地址时客服无法确定刚才那个地址指的是什么。从工程角度看多轮对话架构面临三个核心挑战。意图漂移识别——用户在对话中可能从查询意图切换到投诉意图系统需要实时感知意图变更并调整响应策略。槽位跨轮继承——用户在第一轮提供了订单号第三轮不应当再次询问系统需要维护一个跨轮对话的槽位状态。上下文窗口管理——当对话超过 20 轮时全量历史消息会超出大模型的 Context 窗口限制需要在不丢失关键信息的前提下对历史消息进行压缩。当前主流的解决方案是将多轮对话系统拆解为三个协同模块意图管理器负责对话状态追踪槽位引擎负责信息抽取与填充上下文压缩器负责在 Token 预算内保持关键信息的覆盖率。三者的协同质量直接决定了多轮对话的成功率。二、三层协防——意图流转、槽位引擎与上下文压缩的架构设计意图管理器的核心职责是维护对话状态机。当用户从订单查询切换到投诉退款时意图管理器需要做两件事一是确认意图漂移的置信度足够高避免因用户的一句这也太慢了吧就误判为投诉意图二是在意图切换时重置与该意图无关的槽位退款意图不需要订单物流的槽位。槽位填充引擎采用识别-校验-追问的三段式流程。NER 模块从用户输入中抽取实体信息填充到槽位中槽位完整性检查模块对比当前意图的必有槽位列表发现缺失则触发追问。追问策略需要设计得自然——请问您的订单号是多少比缺少必填字段 orderId的用户体验更好。上下文压缩器是 Token 预算的守门员。当对话超过 15 轮时全量历史消息可能达到 4K 至 6K Token与 LLM 回复所需的生成 Token 合并后容易超出窗口。压缩策略通常采用关键轮保留 早期轮摘要的混合模式最近 5 轮保留完整消息更早轮次通过 LLM 生成结构化摘要在摘要中重点覆盖实体信息订单号、产品名和关键决策点用户选择了什么。三、Java 后端的多轮对话引擎——基于 Spring Boot 的生产级实现import com.fasterxml.jackson.databind.ObjectMapper; import java.time.Instant; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; /** * 多轮对话状态管理器——协调意图、槽位与上下文的生命周期 */ public class DialogueStateManager { private static final int MAX_CONTEXT_TOKENS 4096; private static final int RECENT_ROUNDS_KEEP 5; private static final ObjectMapper MAPPER new ObjectMapper(); // Session 级别的对话状态存储 private final ConcurrentHashMapString, DialogueSession sessions new ConcurrentHashMap(); /** * 处理一轮对话意图识别 → 槽位填充 → 上下文压缩 → 生成回复 */ public DialogueResponse processTurn(String sessionId, String userInput, LLMService llmService) { if (sessionId null || userInput null || userInput.trim().isEmpty()) { throw new IllegalArgumentException(sessionId 和 userInput 不能为空); } DialogueSession session sessions.computeIfAbsent(sessionId, k - new DialogueSession(sessionId)); // 第一步意图识别 IntentResult intent identifyIntent(userInput, llmService); handleIntentSwitch(session, intent); // 第二步槽位填充 MapString, String extractedSlots extractSlots(userInput, intent, llmService); session.mergeSlots(extractedSlots); ListString missingSlots checkRequiredSlots(intent, session); // 如果关键槽位缺失生成追问 if (!missingSlots.isEmpty()) { return buildSlotPrompt(intent, missingSlots, session); } // 第三步上下文压缩 String compressedContext compressContext(session, llmService); // 第四步生成最终回复 String reply llmService.generate(buildFinalPrompt( intent, session.getSlots(), compressedContext)); // 更新会话状态 session.appendTurn(new DialogueTurn(userInput, reply, intent.getCurrentIntent())); return new DialogueResponse(reply, intent.getCurrentIntent(), intent.getConfidence()); } /** * 意图识别——输出当前意图、置信度和是否为意图漂移 */ private IntentResult identifyIntent(String userInput, LLMService llmService) { String prompt String.format( 你是意图识别器。分析以下用户输入返回 JSON 格式: {\intent\: \订单查询|物流追踪|投诉退款|发票申请|一般咨询\, \confidence\: 0.0-1.0}\n用户输入: %s, userInput); String llmOutput llmService.generate(prompt); try { IntentResult result MAPPER.readValue(llmOutput, IntentResult.class); if (result.getConfidence() 0.6) { result.setCurrentIntent(一般咨询); } return result; } catch (Exception e) { // JSON 解析失败时降级为一般咨询 return new IntentResult(一般咨询, 0.5); } } /** * 处理意图切换——高置信度切换时清除非共享槽位 */ private void handleIntentSwitch(DialogueSession session, IntentResult intent) { String previousIntent session.getCurrentIntent(); if (previousIntent ! null !previousIntent.equals(intent.getCurrentIntent())) { if (intent.getConfidence() 0.8) { System.out.printf(意图切换: %s → %s (置信度: %.2f)%n, previousIntent, intent.getCurrentIntent(), intent.getConfidence()); // 清除非共享槽位保留订单号、用户名等通用槽位 session.retainSharedSlots(getSharedSlots()); session.setIntentSwitchCount(session.getIntentSwitchCount() 1); } else { // 置信度不足暂时维持原意图 intent.setCurrentIntent(previousIntent); } } session.setCurrentIntent(intent.getCurrentIntent()); } /** * 上下文压缩——最近 N 轮保留原文早期轮次压缩为摘要 */ private String compressContext(DialogueSession session, LLMService llmService) { ListDialogueTurn history session.getHistory(); if (history.size() RECENT_ROUNDS_KEEP) { return history.stream() .map(DialogueTurn::toPromptString) .collect(Collectors.joining(\n)); } // 分期处理最近 5 轮保留原文 int splitIndex history.size() - RECENT_ROUNDS_KEEP; ListDialogueTurn earlyTurns history.subList(0, splitIndex); ListDialogueTurn recentTurns history.subList(splitIndex, history.size()); // 早期轮次生成结构化摘要 String earlySummary; if (session.getEarlySummary() ! null) { // 已有摘要增量更新 earlySummary session.getEarlySummary(); } else { String earlyText earlyTurns.stream() .map(DialogueTurn::toPromptString) .collect(Collectors.joining(\n)); String summaryPrompt 请将以下对话历史压缩为结构化摘要 重点保留订单号、产品名称、用户决策、关键时间节点。\n earlyText; earlySummary llmService.generate(summaryPrompt); session.setEarlySummary(earlySummary); } StringBuilder context new StringBuilder(); context.append(【历史对话摘要】\n).append(earlySummary).append(\n); context.append(【最近对话】\n); recentTurns.forEach(turn - context.append(turn.toPromptString()).append(\n)); return context.toString(); } /** * 检查必有槽位是否完整 */ private ListString checkRequiredSlots(IntentResult intent, DialogueSession session) { MapString, ListString intentRequiredSlots Map.of( 订单查询, List.of(orderId), 物流追踪, List.of(orderId), 投诉退款, List.of(orderId, complaintReason), 发票申请, List.of(orderId, invoiceType) ); ListString required intentRequiredSlots.getOrDefault( intent.getCurrentIntent(), Collections.emptyList()); ListString missing new ArrayList(); MapString, String slots session.getSlots(); for (String slot : required) { if (!slots.containsKey(slot) || slots.get(slot) null) { missing.add(slot); } } return missing; } private MapString, String extractSlots(String userInput, IntentResult intent, LLMService llmService) { // 调用 NER 模型或 LLM 提取实体 String prompt 从以下文本中提取订单号(orderId)、手机号(phone)、 投诉原因(complaintReason)、发票类型(invoiceType)等实体 输出 JSON。\n文本: userInput; try { String output llmService.generate(prompt); SuppressWarnings(unchecked) MapString, String slots MAPPER.readValue(output, Map.class); return slots; } catch (Exception e) { return Collections.emptyMap(); } } private DialogueResponse buildSlotPrompt(IntentResult intent, ListString missingSlots, DialogueSession session) { String slotNames missingSlots.stream() .map(this::slotDisplayName) .collect(Collectors.joining(、)); String prompt 请问您的 slotNames 是什么; return new DialogueResponse(prompt, intent.getCurrentIntent(), intent.getConfidence(), missingSlots); } private String buildFinalPrompt(IntentResult intent, MapString, String slots, String context) { return String.format( 你是一个专业的智能客服。当前意图: %s。已知信息: %s。\n 对话上下文:\n%s\n请生成回复:, intent.getCurrentIntent(), slots, context); } private String slotDisplayName(String slotKey) { return Map.of( orderId, 订单号, phone, 手机号, complaintReason, 投诉原因, invoiceType, 发票类型 ).getOrDefault(slotKey, slotKey); } private SetString getSharedSlots() { return Set.of(orderId, phone, username); } // --- 内部数据类 --- interface LLMService { String generate(String prompt); } static class DialogueSession { private final String sessionId; private final Instant createdAt; private String currentIntent; private String earlySummary; private int intentSwitchCount; private final MapString, String slots new LinkedHashMap(); private final ListDialogueTurn history new ArrayList(); DialogueSession(String sessionId) { this.sessionId sessionId; this.createdAt Instant.now(); } void mergeSlots(MapString, String newSlots) { if (newSlots ! null) slots.putAll(newSlots); } void retainSharedSlots(SetString sharedKeys) { slots.keySet().retainAll(sharedKeys); } void appendTurn(DialogueTurn turn) { history.add(turn); } void setCurrentIntent(String intent) { this.currentIntent intent; } void setEarlySummary(String summary) { this.earlySummary summary; } void setIntentSwitchCount(int count) { this.intentSwitchCount count; } String getCurrentIntent() { return currentIntent; } MapString, String getSlots() { return slots; } ListDialogueTurn getHistory() { return history; } String getEarlySummary() { return earlySummary; } int getIntentSwitchCount() { return intentSwitchCount; } } static class DialogueTurn { private final String userInput; private final String assistantReply; private final String intent; private final Instant timestamp; DialogueTurn(String userInput, String assistantReply, String intent) { this.userInput userInput; this.assistantReply assistantReply; this.intent intent; this.timestamp Instant.now(); } String toPromptString() { return 用户: userInput \n客服: assistantReply; } } static class IntentResult { private String currentIntent; private double confidence; IntentResult(String intent, double confidence) { this.currentIntent intent; this.confidence confidence; } String getCurrentIntent() { return currentIntent; } double getConfidence() { return confidence; } void setCurrentIntent(String intent) { this.currentIntent intent; } } static class DialogueResponse { private final String text; private final String intent; private final double confidence; private final ListString missingSlots; DialogueResponse(String text, String intent, double confidence) { this(text, intent, confidence, Collections.emptyList()); } DialogueResponse(String text, String intent, double confidence, ListString missingSlots) { this.text text; this.intent intent; this.confidence confidence; this.missingSlots missingSlots; } } }代码中有几个工程上的关键决策。意图切换时的置信度阈值设为 0.8低于阈值时维持原意图避免用户随口一句抱怨就触发意图切换。通用槽位订单号、手机号在意图切换时保留因为它们在多个意图间共享。早期轮次的上下文压缩不是每轮都重新调用 LLM 做摘要而是增量更新——首次生成摘要后缓存减少 LLM 调用成本。四、多轮对话的工程陷阱——从 Token 超支到意图振荡的防御策略第一个常见陷阱是意图振荡。当用户输入我的订单怎么还没到你们太慢了意图识别器可能连续在物流追踪和投诉退款之间跳转。解决方案是引入意图切换的冷却期——同一个会话在 3 轮对话内不允许两次切换意图。第二个陷阱是槽位追问的恶性循环。当客户反复无法提供订单号时系统不应无限追问。合理的设计是设置最大追问次数通常 3 次超过后由人工客服接管或提供替代方案如您可以使用手机号查询订单。第三个陷阱是上下文压缩的信息丢失。当早期轮次的摘要丢失了关键的用户偏好信息如我不要红色款后续回复可能完全忽略这一约束。压缩器应当对不同类型信息分层处理实体信息如订单号强制保留偏好信息如颜色选择优先保留闲聊信息酌情丢弃。对话长度超过 30 轮时即使经过摘要压缩Token 消耗仍然可能触及 LLM 的 Context 窗口上限。此时需要引入更激进的策略——将对话切分为子对话每个子对话独立维护上下文子对话间通过结构化摘要建立连接。五、总结智能客服的多轮对话架构是一个由意图管理、槽位填充、上下文压缩三个子系统协同构成的工程体系。意图管理保证对话不跑偏槽位引擎保证信息不丢失上下文压缩器保证 Token 不超支。三者的协同质量决定了用户会不会在第六轮对话后愤怒地转人工。从实现优先级看建议先做好槽位填充引擎——这是多轮对话结构化程度的量化指标。槽位覆盖率越高后续的意图管理和上下文压缩的难度越低。意图管理的重点是防振荡上下文压缩的重点是防信息丢失。三个模块不是独立的而是通过 Session 状态共享编织成一个有机整体——每一次对话流转都是三者之间的一次精密协调。