AI 赋能传统业务:智能客服系统的对话引擎设计与工程实践

cover

一、规则引擎的困局:当客服场景遇上长尾问题

传统智能客服基于意图识别 + 槽位填充的规则引擎,对高频标准问题(如"查快递"、"退换货")处理效率极高。但当用户提出长尾问题(如"我买的花瓶碎了,但包装完好,是物流还是商品质量问题"),规则引擎的意图分类准确率骤降,最终只能转人工。统计数据显示,头部 20% 的意图覆盖了 80% 的流量,但剩余 80% 的长尾意图消耗了 60% 的人工客服资源。

大语言模型为长尾问题提供了新解法:通过 RAG 检索知识库中的相关政策,让模型基于检索结果生成回答,而非依赖预定义的规则树。但 LLM 不是万能药——它的响应延迟(2-5 秒)远高于规则引擎(<100 毫秒),且存在幻觉风险。工程化的关键在于:让规则引擎和 LLM 协同工作,各取所长。

flowchart TB
    Input[用户消息] --> Classifier{意图分类器}
    Classifier -->|置信度 > 0.9<br/>高频意图| RuleEngine[规则引擎<br/>槽位填充 + 模板回复]
    Classifier -->|置信度 0.5-0.9<br/>模糊意图| Clarify[澄清确认<br/>追问关键信息]
    Classifier -->|置信度 < 0.5<br/>长尾问题| RAG[LLM + RAG<br/>检索知识库生成回答]

    RuleEngine --> QualityCheck{质量检查}
    Clarify --> Classifier
    RAG --> QualityCheck

    QualityCheck -->|通过| Output[回复用户]
    QualityCheck -->|不通过| Human[转人工客服]

    Output --> Feedback[用户反馈收集]
    Feedback --> KnowledgeBase[知识库更新]

二、混合对话引擎的核心机制

2.1 三层路由架构

混合对话引擎的核心是三层路由:规则层处理高频标准意图,LLM 层处理长尾问题,人工层兜底无法自动回答的场景。路由决策由意图分类器的置信度驱动——置信度高于阈值走规则引擎,低于阈值走 LLM,LLM 回答不通过质量检查则转人工。

2.2 RAG 增强的回答生成

当请求路由到 LLM 层时,系统先从知识库中检索相关政策文档,再将检索结果作为上下文注入 Prompt。这种 RAG 模式显著降低了幻觉率——模型的回答基于真实文档,而非凭空生成。关键在于检索质量:如果召回的文档与问题不相关,模型仍可能产生误导性回答。

sequenceDiagram
    participant User as 用户
    participant Gateway as 对话网关
    participant Classifier as 意图分类器
    participant Rule as 规则引擎
    participant RAG as RAG引擎
    participant LLM as 大语言模型
    participant QA as 质量检查

    User->>Gateway: "花瓶碎了但包装完好怎么处理"
    Gateway->>Classifier: 意图分类
    Classifier-->>Gateway: 置信度 0.3 → 路由至 LLM

    Gateway->>RAG: 检索知识库
    RAG-->>Gateway: 返回3条相关政策文档

    Gateway->>LLM: Prompt + 检索文档 + 用户问题
    LLM-->>Gateway: 生成回答

    Gateway->>QA: 质量检查
    QA-->>Gateway: 通过
    Gateway->>User: 回复:"根据退换货政策第3条..."

三、生产级代码实现

3.1 混合对话引擎核心

import asyncio
import logging
import time
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Dict, List, Optional

logger = logging.getLogger(__name__)


class RouteType(Enum):
    RULE = "rule"         # 规则引擎
    LLM_RAG = "llm_rag"  # LLM + RAG
    HUMAN = "human"       # 人工客服
    CLARIFY = "clarify"   # 澄清确认


@dataclass
class IntentResult:
    """意图分类结果"""
    intent: str
    confidence: float
    slots: Dict[str, Any] = field(default_factory=dict)


@dataclass
class DialogContext:
    """对话上下文:维护多轮对话的状态"""
    session_id: str
    history: List[Dict[str, str]] = field(default_factory=list)
    intent_stack: List[IntentResult] = field(default_factory=list)
    collected_slots: Dict[str, Any] = field(default_factory=dict)
    turn_count: int = 0


class HybridDialogEngine:
    """混合对话引擎:规则引擎 + LLM/RAG + 人工兜底

    设计考量:
    - 三层路由:规则引擎处理高频意图,LLM 处理长尾,人工兜底
    - 置信度阈值可动态调整,根据业务时段和客服负载弹性切换
    - 所有自动回答必须通过质量检查,不通过则转人工
    """

    # 路由阈值
    HIGH_CONFIDENCE = 0.9   # 高于此值走规则引擎
    LOW_CONFIDENCE = 0.5    # 低于此值走 LLM

    def __init__(
        self,
        intent_classifier,
        rule_engine,
        rag_engine,
        llm_client,
        quality_checker,
    ):
        self.classifier = intent_classifier
        self.rule_engine = rule_engine
        self.rag = rag_engine
        self.llm = llm_client
        self.qa = quality_checker

    async def handle(
        self,
        user_message: str,
        context: DialogContext,
    ) -> Dict[str, Any]:
        """处理用户消息的完整流程"""
        start_time = time.time()
        context.turn_count += 1
        context.history.append({"role": "user", "content": user_message})

        # Step 1: 意图分类
        intent_result = await self.classifier.classify(user_message, context)
        context.intent_stack.append(intent_result)

        # Step 2: 路由决策
        route = self._route(intent_result)
        logger.info(f"路由决策: intent={intent_result.intent}, "
                     f"confidence={intent_result.confidence:.2f}, route={route.value}")

        # Step 3: 执行对应处理逻辑
        if route == RouteType.RULE:
            response = await self.rule_engine.execute(intent_result, context)
        elif route == RouteType.LLM_RAG:
            response = await self._handle_with_rag(user_message, intent_result, context)
        elif route == RouteType.CLARIFY:
            response = await self._handle_clarify(intent_result, context)
        else:
            response = {"text": "正在为您转接人工客服,请稍候...", "route": "human"}

        # Step 4: 质量检查(仅对自动回答)
        if route in (RouteType.RULE, RouteType.LLM_RAG):
            qa_result = await self.qa.check(response, user_message, context)
            if not qa_result.passed:
                logger.warning(f"质量检查未通过: {qa_result.reason}")
                response = {
                    "text": "抱歉,我无法确定准确答案,正在为您转接人工客服。",
                    "route": "human",
                    "qa_failure_reason": qa_result.reason,
                }

        latency_ms = (time.time() - start_time) * 1000
        response["latency_ms"] = latency_ms
        response["route"] = route.value
        context.history.append({"role": "assistant", "content": response.get("text", "")})

        return response

    def _route(self, intent_result: IntentResult) -> RouteType:
        """基于置信度的路由决策"""
        conf = intent_result.confidence
        if conf >= self.HIGH_CONFIDENCE:
            return RouteType.RULE
        elif conf >= self.LOW_CONFIDENCE:
            return RouteType.CLARIFY
        else:
            return RouteType.LLM_RAG

    async def _handle_with_rag(
        self,
        user_message: str,
        intent_result: IntentResult,
        context: DialogContext,
    ) -> Dict[str, Any]:
        """LLM + RAG 处理长尾问题"""
        # 1. 检索知识库
        docs = await self.rag.search(user_message, top_k=3)

        # 2. 构建增强 Prompt
        context_text = "\n\n".join(
            f"[文档{i+1}] {doc['content']}" for i, doc in enumerate(docs)
        )
        system_prompt = (
            "你是一个专业的客服助手。请仅基于以下参考文档回答用户问题。"
            "如果文档中没有相关信息,请明确告知用户你无法回答,不要编造内容。\n\n"
            f"参考文档:\n{context_text}"
        )

        # 3. 调用 LLM
        messages = [{"role": "system", "content": system_prompt}]
        # 只保留最近 5 轮对话,控制 Token 消耗
        messages.extend(context.history[-10:])

        llm_response = await self.llm.chat(messages=messages, max_tokens=512)

        return {
            "text": llm_response,
            "sources": [doc.get("source", "") for doc in docs],
        }

    async def _handle_clarify(
        self,
        intent_result: IntentResult,
        context: DialogContext,
    ) -> Dict[str, Any]:
        """处理模糊意图:追问关键信息"""
        # 根据意图类型生成澄清问题
        clarify_templates = {
            "refund": "请问您是想申请退款还是退换货?订单号是多少?",
            "delivery": "请问您想查询哪个订单的物流信息?",
            "default": "能否再详细描述一下您的问题?这样我能更准确地帮助您。",
        }
        template = clarify_templates.get(intent_result.intent, clarify_templates["default"])
        return {"text": template, "route": "clarify"}

3.2 质量检查器

@dataclass
class QAResult:
    passed: bool
    reason: str = ""
    score: float = 0.0


class QualityChecker:
    """回答质量检查:防止幻觉和不当内容

    设计考量:
    - 事实性检查:回答是否引用了检索到的文档
    - 安全性检查:是否包含敏感信息或不当言论
    - 完整性检查:是否完整回答了用户问题
    """

    # 敏感词列表(生产环境应从配置中心加载)
    SENSITIVE_PATTERNS = ["内部价格", "员工折扣", "竞争对手"]

    async def check(
        self,
        response: Dict[str, Any],
        user_message: str,
        context: DialogContext,
    ) -> QAResult:
        text = response.get("text", "")

        # 1. 安全性检查
        for pattern in self.SENSITIVE_PATTERNS:
            if pattern in text:
                return QAResult(passed=False, reason=f"包含敏感内容: {pattern}", score=0.0)

        # 2. 事实性检查:如果使用了 RAG,回答应引用文档来源
        sources = response.get("sources", [])
        if sources and not any(s in text for s in ["根据", "按照", "依据"]):
            return QAResult(
                passed=False,
                reason="RAG 回答未引用文档来源,可能存在幻觉",
                score=0.4,
            )

        # 3. 空回答检查
        if len(text.strip()) < 10:
            return QAResult(passed=False, reason="回答过短,可能未理解问题", score=0.2)

        return QAResult(passed=True, score=0.9)

四、边界分析与架构权衡

4.1 延迟分层

规则引擎的响应延迟在 50-100 毫秒,LLM+RAG 的延迟在 2-5 秒。这种数量级的差异意味着:用户在等待 LLM 回答时,必须看到"正在思考"的加载状态。更优的方案是流式输出——LLM 生成第一个 Token 后立即推送,将感知延迟从 2-5 秒降至 200-500 毫秒。

4.2 知识库的时效性

RAG 的回答质量高度依赖知识库的时效性。如果政策已更新但知识库未同步,模型会基于过时文档生成错误回答。解决方案是建立知识库版本管理机制,每次政策变更时触发增量索引更新,并在回答中标注文档版本号。

4.3 规则引擎与 LLM 的边界

规则引擎适合处理确定性高、流程标准的问题(如查订单状态、退换货流程)。LLM 适合处理需要理解语义、综合判断的问题(如"花瓶碎了谁负责")。但两者之间存在灰色地带——某些问题既可以用规则处理,也可以用 LLM 处理。此时应优先选择规则引擎,因为其延迟更低、结果更可控。

五、总结

混合对话引擎的核心价值在于:让规则引擎和 LLM 各司其职,规则引擎保障高频场景的响应速度和确定性,LLM 拓展长尾问题的覆盖范围。质量检查机制是最后一道防线,确保自动回答的准确性和安全性。

落地路线建议:第一步,梳理现有规则引擎的意图覆盖率和转人工率,识别长尾问题的分布;第二步,为 Top 20 长尾意图构建知识库,接入 RAG 引擎;第三步,实现三层路由和质量检查,灰度上线,对比转人工率的变化;第四步,基于用户反馈持续优化知识库和分类阈值。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐