AI 客服系统的架构进阶:从单轮问答到多轮分析的工程化升级路径

📅 2026/7/14 12:10:17
AI 客服系统的架构进阶:从单轮问答到多轮分析的工程化升级路径
AI 客服系统的架构进阶从单轮问答到多轮分析的工程化升级路径一、单轮客服的困境用户说了三次还是不对一个电商客服 AI 上线后一个常见场景暴露了单轮问答的局限用户我的订单怎么还没发货 — AI 查询订单状态回复已发货用户不是这个是昨天刚下的那单 — AI 重新查询所有订单回复发货列表用户算了我要退款 — AI 回复请问您要退哪个订单的款三轮对话后用户放弃并转人工。问题不是 AI 不能回答单个问题而是它无法在跨轮对话中维护上下文、跟踪用户意图的演变。第一轮用户抱怨发货第二轮澄清了具体订单第三轮转向了退款——AI 需要在每一轮对话中利用前几轮积累的信息来做更精准的判断。二、多轮对话的上下文管理架构sequenceDiagram participant U as 用户 participant CG as 对话网关 participant SM as 会话管理器 participant NLU as NLU 意图引擎 participant KB as 知识库 participant BS as 业务系统 U-CG: 我的订单怎么还没发货 CG-SM: 获取会话上下文(session_id) SM--CG: 上下文(空, 新会话) CG-NLU: 意图识别 NLU--CG: intent: ORDER_STATUS, entities: [] CG-BS: 查询最近订单 BS--CG: 返回最近 5 个订单 CG-SM: 保存上下文(关注订单列表) CG--U: 您最近有 5 个订单, 请问是哪一单 U-CG: 昨天刚下的那单 CG-SM: 获取会话上下文 SM--CG: 上下文(订单列表 上一轮意图) CG-NLU: 意图识别(带上下文) NLU--CG: intent: ORDER_CLARIFY, entities: [date:昨天] CG-CG: 从上下文的订单列表中筛选日期匹配 CG-SM: 保存上下文(聚焦订单#12345) CG--U: 订单#12345(昨天), 状态: 已发货, 预计明天到达 U-CG: 我要退款 CG-SM: 获取会话上下文 SM--CG: 上下文(订单#12345) CG-NLU: 意图识别(带上下文) NLU--CG: intent: REFUND_REQUEST, target: 订单#12345 CG-BS: 发起退款(订单#12345) CG--U: 已为订单#12345提交退款申请2.1 会话上下文的存储结构// 会话上下文的存储结构支持多层信息的增量积累 type SessionContext struct { SessionID string json:session_id UserID string json:user_id Turns []TurnRecord json:turns // 对话历史 Entities map[string]interface{} json:entities // 累积的实体信息 FocusOrder *OrderSummary json:focus_order // 当前聚焦的订单 IntentStack []string json:intent_stack // 意图栈支持子意图嵌套 CreatedAt time.Time json:created_at UpdatedAt time.Time json:updated_at } type TurnRecord struct { TurnID int json:turn_id UserMsg string json:user_msg BotMsg string json:bot_msg Intent string json:intent Actions []string json:actions // 本轮执行的操作 } // 会话管理器负责上下文的持久化、读取和更新 type SessionManager struct { store StorageBackend ttl time.Duration } func (sm *SessionManager) GetOrCreate( ctx context.Context, sessionID string, userID string, ) *SessionContext { session, err : sm.store.Get(ctx, sessionID) if err ! nil || session nil { session SessionContext{ SessionID: sessionID, UserID: userID, Turns: make([]TurnRecord, 0), Entities: make(map[string]interface{}), IntentStack: make([]string, 0), CreatedAt: time.Now(), } } return session } func (sm *SessionManager) UpdateContext( ctx context.Context, session *SessionContext, turn TurnRecord, entities map[string]interface{}, ) error { session.Turns append(session.Turns, turn) // 合并新实体到已有上下文中 for k, v : range entities { session.Entities[k] v } session.UpdatedAt time.Now() // 写入存储并设置过期时间 return sm.store.Set(ctx, session.SessionID, session, sm.ttl) }2.2 意图切换的上下文继承多轮对话中意图切换是常态。上一轮的查订单意图切换到退款意图时需要将前几轮积累的订单信息带入新的意图处理// 意图处理器支持上下文继承 type IntentRouter struct { handlers map[string]IntentHandler } func (r *IntentRouter) Route( ctx context.Context, intent string, session *SessionContext, userMsg string, ) (*BotResponse, error) { handler, ok : r.handlers[intent] if !ok { return r.fallbackResponse(), nil } // 将当前意图推入意图栈用于后续的切换检测 if len(session.IntentStack) 0 || session.IntentStack[len(session.IntentStack)-1] ! intent { session.IntentStack append(session.IntentStack, intent) } // 构建带上下文的处理请求 req : IntentRequest{ Intent: intent, UserMessage: userMsg, Context: session.Entities, // 继承历史上积累的所有实体 FocusOrder: session.FocusOrder, PreviousIntent: r.previousIntent(session), } resp, err : handler.Handle(ctx, req) if err ! nil { return nil, fmt.Errorf(意图 %s 处理失败: %w, intent, err) } // 更新聚焦实体 if resp.FocusOrder ! nil { session.FocusOrder resp.FocusOrder } return resp, nil }三、端到端的延迟控制多轮对话的另一个性能挑战是延迟每轮对话可能需要查询订单、调用知识库、请求 NLU 服务。串行执行的总延迟是各步骤延迟之和。// 并行化多数据源查询降低单轮延迟 func (h *OrderStatusHandler) Handle( ctx context.Context, req *IntentRequest, ) (*BotResponse, error) { // 同时启动多个查询 type queryResult struct { orders []Order err error queryType string } results : make(chan queryResult, 2) // 查询1系统订单 go func() { orders, err : h.orderService.QueryByUser(ctx, req.UserID, 10) results - queryResult{orders, err, system} }() // 查询2最近交互记录用于理解上下文 go func() { history, err : h.historyService.GetRecent(ctx, req.UserID, 5) // 转为统一格式 results - queryResult{history, err, history} }() // 带超时的收集结果 var systemOrders, recentHistory []Order for i : 0; i 2; i { select { case r : -results: if r.err ! nil { log.Printf(查询 %s 失败: %v, r.queryType, r.err) continue } if r.queryType system { systemOrders r.orders } else { recentHistory r.orders } case -ctx.Done(): return nil, ctx.Err() } } return h.buildResponse(systemOrders, recentHistory, req.Context) }并行化让两路查询的总延迟从T1 T2变为max(T1, T2)在依赖查询较多的场景中可缩短 30-50% 的响应时间。四、边界与权衡上下文膨胀多轮对话的上下文数据持续增长序列化后的体积可能影响 Redis 的读写延迟。设置了实体数量上限30 个 key和历史轮次上限20 轮超过阈值自动清理最旧的记录。意图识别的歧义放大带上下文的意图识别在聚焦错误对象时会连续犯错。例如第一轮错误地将退款聚焦到了订单 A第二轮用户说不对时模型可能基于错误上下文做出更错误的判断。需要在每轮 NLU 输入中重新提供用户的原始消息而非只传递上一轮的意图摘要。会话持久化的存储成本30 分钟的会话 TTL 乘以 10000 并发用户 约 500MB Redis 内存。使用压缩gzip/snappy可以减少 60-70% 的存储开销。五、总结AI 客服从单轮升级到多轮的核心工程挑战是上下文管理——如何在对话状态中积累用户信息并在每一轮对话中正确利用这些信息。关键组件是会话上下文存储SessionContext Redis、意图栈跟踪IntentStack、实体增量合并Entities map。实施从上下文盲区最大的场景开始——即当前单轮模型错误率最高的对话类型通常是含有多实体引用、意图切换的场景。在 A/B 测试中对比多轮上线前后的转人工率、平均对话轮次和用户满意度评分。多轮不是做了就一定更好——如果上下文质量差错误实体积累多轮可能比单轮更糟。