AI Agent客服系统实战:从0到1搭建智能客服

8次阅读
没有评论

AI Agent客服系统实战:从0到1搭建智能客服

一、开场:客服困境与AI破局

大家好,我是老金。

去年我们公司的客服团队是这样的:

  • 10个客服,每天处理2000+咨询
  • 重复问题占70%(查订单、问物流、问退换货)
  • 客服流动率高,培训成本大
  • 高峰期用户等待超过30分钟

老板说:”能不能用AI解决?”

我们花了2个月,从0到1搭建了智能客服系统。现在:

  • AI处理80%的重复咨询
  • 客服专注于复杂问题
  • 用户等待时间降到2分钟
  • 客服满意度从75%提升到92%

今天分享完整的搭建经验。

二、系统架构设计

整体架构

┌─────────────────────────────────────────────────────────┐
│                  智能客服系统架构                        │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  用户入口层                                             │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐                │
│  │ 网页客服 │  │ 微信公众号│  │ APP内嵌 │                │
│  └────┬────┘  └────┬────┘  └────┬────┘                │
│       │            │            │                       │
│       └────────────┼────────────┘                       │
│                    ↓                                    │
│  ┌─────────────────────────────────────────────────┐   │
│  │              对话管理层                          │   │
│  │  • 意图识别                                      │   │
│  │  • 槽位填充                                      │   │
│  │  • 对话状态管理                                  │   │
│  │  • 多轮对话                                      │   │
│  └────────────────────┬────────────────────────────┘   │
│                       ↓                                 │
│  ┌─────────────────────────────────────────────────┐   │
│  │              核心AI层                            │   │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐      │   │
│  │  │ LLM大脑  │  │ 知识库   │  │ 工具集   │      │   │
│  │  │(GPT-4)   │  │ (RAG)    │  │(API调用) │      │   │
│  │  └──────────┘  └──────────┘  └──────────┘      │   │
│  └────────────────────┬────────────────────────────┘   │
│                       ↓                                 │
│  ┌─────────────────────────────────────────────────┐   │
│  │              业务系统层                          │   │
│  │  订单系统 │ 物流系统 │ 用户系统 │ 通知系统      │   │
│  └─────────────────────────────────────────────────┘   │
│                                                         │
└─────────────────────────────────────────────────────────┘

核心模块

# 系统核心模块
class CustomerServiceAgent:
    """智能客服Agent"""

    def __init__(self, config: dict):
        # LLM核心
        self.llm = get_llm(config["model"])

        # 知识库
        self.knowledge_base = RAGSystem(
            embedder=config["embedder"],
            vector_store=config["vector_store"]
        )

        # 工具集
        self.tools = self._init_tools(config["tools"])

        # 对话管理
        self.dialog_manager = DialogManager()

        # 意图识别
        self.intent_classifier = IntentClassifier()

    def _init_tools(self, tool_configs):
        """初始化工具集"""
        tools = {}
        for config in tool_configs:
            if config["type"] == "order":
                tools["query_order"] = QueryOrderTool(config)
                tools["cancel_order"] = CancelOrderTool(config)
            elif config["type"] == "product":
                tools["search_product"] = SearchProductTool(config)
            elif config["type"] == "logistics":
                tools["query_logistics"] = QueryLogisticsTool(config)
        return tools

三、意图识别模块

意图分类设计

class IntentClassifier:
    """意图识别器"""

    INTENTS = {
        "order_query": {
            "name": "订单查询",
            "keywords": ["订单", "查订单", "订单号", "我的订单"],
            "examples": [
                "帮我查一下订单ORD12345",
                "我的订单到哪了",
                "查订单状态"
            ],
            "slots": ["order_id"],
            "priority": 1
        },
        "order_cancel": {
            "name": "订单取消",
            "keywords": ["取消订单", "退订单", "不要了"],
            "examples": [
                "我要取消订单",
                "帮我退掉这个订单"
            ],
            "slots": ["order_id", "reason"],
            "priority": 2
        },
        "product_query": {
            "name": "商品咨询",
            "keywords": ["商品", "产品", "多少钱", "有货吗"],
            "examples": [
                "这款手机多少钱",
                "有红色吗",
                "这个产品怎么样"
            ],
            "slots": ["product_name", "attribute"],
            "priority": 1
        },
        "logistics_query": {
            "name": "物流查询",
            "keywords": ["快递", "物流", "发货", "到哪了"],
            "examples": [
                "我的快递到哪了",
                "什么时候发货",
                "物流信息"
            ],
            "slots": ["order_id"],
            "priority": 1
        },
        "return_request": {
            "name": "退换货",
            "keywords": ["退货", "换货", "退款", "质量"],
            "examples": [
                "我要退货",
                "这个质量有问题想换货"
            ],
            "slots": ["order_id", "reason", "type"],
            "priority": 2
        },
        "general_qa": {
            "name": "通用问答",
            "keywords": [],
            "examples": [],
            "slots": [],
            "priority": 10
        }
    }

    def __init__(self):
        self.keyword_matcher = KeywordMatcher()
        self.semantic_classifier = SemanticClassifier()

    async def classify(self, user_input: str, context: dict = None) -> dict:
        """分类意图"""
        # 1. 关键词快速匹配
        keyword_scores = self.keyword_matcher.match(
            user_input, 
            self.INTENTS
        )

        # 2. 语义分类
        semantic_scores = await self.semantic_classifier.classify(
            user_input,
            list(self.INTENTS.keys())
        )

        # 3. 融合得分
        final_scores = self._merge_scores(
            keyword_scores,
            semantic_scores
        )

        # 4. 选择最佳意图
        best_intent = max(final_scores.items(), key=lambda x: x[1])

        return {
            "intent": best_intent[0],
            "confidence": best_intent[1],
            "all_scores": final_scores
        }

    def _merge_scores(self, keyword_scores, semantic_scores, 
                      alpha=0.3):
        """融合关键词和语义得分"""
        merged = {}
        for intent in keyword_scores:
            merged[intent] = (
                alpha * keyword_scores[intent] + 
                (1 - alpha) * semantic_scores.get(intent, 0)
            )
        return merged

槽位填充

class SlotFiller:
    """槽位填充器"""

    SLOT_PATTERNS = {
        "order_id": {
            "pattern": r"ORDd{10}",
            "description": "订单号,格式ORD+10位数字"
        },
        "phone": {
            "pattern": r"1[3-9]d{9}",
            "description": "手机号"
        },
        "product_name": {
            "extract": "llm",  # 使用LLM提取
            "description": "商品名称"
        }
    }

    async def fill_slots(self, user_input: str, 
                        intent: str, 
                        required_slots: list,
                        context: dict = None) -> dict:
        """填充槽位"""
        filled = {}
        missing = []

        for slot in required_slots:
            if slot in self.SLOT_PATTERNS:
                slot_config = self.SLOT_PATTERNS[slot]

                if slot_config.get("extract") == "llm":
                    # 使用LLM提取
                    value = await self._llm_extract(
                        user_input, slot, slot_config["description"]
                    )
                else:
                    # 正则匹配
                    match = re.search(
                        slot_config["pattern"], 
                        user_input
                    )
                    value = match.group(0) if match else None

                if value:
                    filled[slot] = value
                else:
                    missing.append(slot)

        return {
            "filled": filled,
            "missing": missing,
            "need_ask": len(missing) > 0
        }

    async def _llm_extract(self, text: str, slot_name: str, 
                          description: str) -> str:
        """使用LLM提取槽位值"""
        prompt = f"""
        从以下文本中提取"{slot_name}"({description}):

        文本:{text}

        如果找不到,返回null。
        只返回提取的值,不要其他内容。
        """

        result = await llm.generate(prompt)
        return result.strip() if result.lower() != "null" else None

四、对话状态管理

对话上下文

class DialogContext:
    """对话上下文"""

    def __init__(self, session_id: str):
        self.session_id = session_id
        self.history = []
        self.current_intent = None
        self.slots = {}
        self.state = "idle"  # idle, collecting, executing, confirming
        self.user_info = {}
        self.created_at = datetime.now()

    def add_message(self, role: str, content: str):
        """添加消息"""
        self.history.append({
            "role": role,
            "content": content,
            "timestamp": datetime.now().isoformat()
        })

    def get_recent_history(self, n: int = 5):
        """获取最近n轮对话"""
        return self.history[-n*2:]  # 每轮包含user和assistant


class DialogManager:
    """对话管理器"""

    def __init__(self):
        self.sessions = {}  # session_id -> DialogContext

    def get_or_create_session(self, session_id: str) -> DialogContext:
        """获取或创建会话"""
        if session_id not in self.sessions:
            self.sessions[session_id] = DialogContext(session_id)
        return self.sessions[session_id]

    async def process(self, session_id: str, user_input: str) -> dict:
        """处理用户输入"""
        ctx = self.get_or_create_session(session_id)

        # 添加用户消息
        ctx.add_message("user", user_input)

        # 状态机处理
        if ctx.state == "idle":
            return await self._handle_idle(ctx, user_input)
        elif ctx.state == "collecting":
            return await self._handle_collecting(ctx, user_input)
        elif ctx.state == "confirming":
            return await self._handle_confirming(ctx, user_input)
        else:
            return await self._handle_idle(ctx, user_input)

    async def _handle_idle(self, ctx: DialogContext, user_input: str):
        """处理空闲状态"""
        # 识别意图
        intent_result = await intent_classifier.classify(
            user_input, 
            {"history": ctx.history}
        )

        ctx.current_intent = intent_result["intent"]
        intent_config = IntentClassifier.INTENTS[ctx.current_intent]

        # 填充槽位
        slot_result = await slot_filler.fill_slots(
            user_input,
            ctx.current_intent,
            intent_config["slots"],
            {"history": ctx.history}
        )

        ctx.slots.update(slot_result["filled"])

        if slot_result["need_ask"]:
            # 需要收集更多信息
            ctx.state = "collecting"
            question = self._generate_question(
                ctx.current_intent,
                slot_result["missing"][0]
            )
            return {"type": "ask", "content": question}
        else:
            # 信息完整,执行操作
            return await self._execute_intent(ctx)

    async def _handle_collecting(self, ctx: DialogContext, user_input: str):
        """处理信息收集状态"""
        intent_config = IntentClassifier.INTENTS[ctx.current_intent]
        missing_slots = [
            s for s in intent_config["slots"] 
            if s not in ctx.slots
        ]

        # 尝试填充缺失槽位
        slot_result = await slot_filler.fill_slots(
            user_input,
            ctx.current_intent,
            missing_slots,
            {"history": ctx.history}
        )

        ctx.slots.update(slot_result["filled"])

        still_missing = [
            s for s in intent_config["slots"] 
            if s not in ctx.slots
        ]

        if still_missing:
            question = self._generate_question(
                ctx.current_intent,
                still_missing[0]
            )
            return {"type": "ask", "content": question}
        else:
            return await self._execute_intent(ctx)

    def _generate_question(self, intent: str, missing_slot: str) -> str:
        """生成追问"""
        questions = {
            "order_id": "请问您的订单号是多少?",
            "phone": "请问您的手机号是多少?",
            "reason": "请问取消/退货的原因是什么?",
            "product_name": "请问您想咨询哪个商品?"
        }
        return questions.get(missing_slot, f"请问{missing_slot}是多少?")

五、工具集成

订单查询工具

class QueryOrderTool:
    """订单查询工具"""

    def __init__(self, config: dict):
        self.api_base = config["api_base"]
        self.api_key = config["api_key"]

    async def execute(self, order_id: str, phone: str = None) -> dict:
        """执行查询"""
        # 调用订单API
        async with aiohttp.ClientSession() as session:
            params = {"order_id": order_id}
            if phone:
                params["phone"] = phone

            async with session.get(
                f"{self.api_base}/orders/{order_id}",
                headers={"Authorization": f"Bearer {self.api_key}"},
                params=params
            ) as response:
                if response.status == 200:
                    order = await response.json()
                    return self._format_result(order)
                elif response.status == 404:
                    return {"success": False, "message": "订单不存在"}
                else:
                    return {"success": False, "message": "查询失败,请稍后再试"}

    def _format_result(self, order: dict) -> dict:
        """格式化查询结果"""
        status_map = {
            "pending": "待付款",
            "paid": "已付款",
            "shipped": "已发货",
            "delivered": "已签收",
            "cancelled": "已取消"
        }

        return {
            "success": True,
            "order_id": order["order_id"],
            "status": status_map.get(order["status"], order["status"]),
            "items": [
                {"name": item["name"], "quantity": item["quantity"]}
                for item in order["items"]
            ],
            "total": order["total"],
            "tracking_number": order.get("tracking_number"),
            "tracking_url": order.get("tracking_url"),
            "message": f"订单{order['order_id']}状态:{status_map.get(order['status'])}"
        }

工具调用管理

class ToolExecutor:
    """工具执行器"""

    def __init__(self, tools: dict):
        self.tools = tools
        self.execution_log = []

    async def execute(self, tool_name: str, params: dict) -> dict:
        """执行工具"""
        if tool_name not in self.tools:
            return {
                "success": False,
                "error": f"Unknown tool: {tool_name}"
            }

        tool = self.tools[tool_name]

        start_time = time.time()
        try:
            result = await tool.execute(**params)
            success = result.get("success", True)
        except Exception as e:
            result = {"success": False, "error": str(e)}
            success = False

        latency = time.time() - start_time

        # 记录执行日志
        self.execution_log.append({
            "tool": tool_name,
            "params": params,
            "result": result,
            "success": success,
            "latency": latency,
            "timestamp": datetime.now().isoformat()
        })

        return result

六、知识库建设

知识库内容

# 客服知识库内容结构
KNOWLEDGE_BASE_STRUCTURE = {
    "products": {
        "description": "产品信息",
        "items": [
            {
                "id": "prod_001",
                "name": "智能手表Pro",
                "category": "智能穿戴",
                "price": 1999,
                "features": ["心率监测", "睡眠追踪", "NFC支付"],
                "faq": [
                    "续航多久?续航约7天",
                    "防水吗?支持5ATM防水"
                ]
            }
        ]
    },
    "policies": {
        "description": "政策规则",
        "items": [
            {
                "id": "policy_001",
                "title": "退换货政策",
                "content": "签收后7天内支持无理由退货,30天内质量问题可换货...",
                "keywords": ["退货", "换货", "退款"]
            }
        ]
    },
    "procedures": {
        "description": "操作流程",
        "items": [
            {
                "id": "proc_001",
                "title": "如何申请退货",
                "steps": [
                    "登录账户,进入我的订单",
                    "找到需要退货的订单,点击申请退货",
                    "选择退货原因,提交申请",
                    "等待审核(1-2个工作日)",
                    "审核通过后,寄回商品"
                ]
            }
        ]
    }
}

知识库检索

class KnowledgeRetriever:
    """知识库检索器"""

    def __init__(self, vector_store, embedder):
        self.vector_store = vector_store
        self.embedder = embedder

    async def retrieve(self, query: str, top_k: int = 3) -> list:
        """检索相关知识"""
        # 向量化查询
        query_vector = await self.embedder.embed(query)

        # 检索
        results = await self.vector_store.search(
            query_vector,
            top_k=top_k,
            filter={"category": "customer_service"}
        )

        return results

    async def augment_prompt(self, query: str, prompt: str) -> str:
        """使用知识增强提示词"""
        # 检索相关知识
        relevant_docs = await self.retrieve(query)

        if not relevant_docs:
            return prompt

        # 构建上下文
        context = "nn".join([
            f"【{doc['title']}】n{doc['content']}"
            for doc in relevant_docs
        ])

        return f"""
参考以下知识库内容回答用户问题:

{context}

---

{prompt}
        """

七、人机协作机制

转人工判断

class HumanTransferDecider:
    """转人工判断器"""

    TRANSFER_CONDITIONS = {
        "sentiment_negative": {
            "trigger": "用户情绪负面",
            "check": lambda ctx: ctx.sentiment_score  5 and not ctx.resolved
        },
        "user_request": {
            "trigger": "用户主动要求",
            "check": lambda ctx: "人工" in ctx.last_user_input
        },
        "policy_violation": {
            "trigger": "涉及政策敏感问题",
            "check": lambda ctx: any(kw in ctx.last_user_input 
                                    for kw in ["投诉", "举报", "法律"])
        }
    }

    def should_transfer(self, context: DialogContext) -> tuple:
        """判断是否需要转人工"""
        for condition_name, condition in self.TRANSFER_CONDITIONS.items():
            if condition["check"](context):
                return True, condition["trigger"]

        return False, None


class HumanAgentQueue:
    """人工客服队列"""

    def __init__(self):
        self.queue = asyncio.Queue()
        self.agents = {}  # agent_id -> status

    async def add_to_queue(self, session_id: str, context: DialogContext,
                          reason: str):
        """加入人工队列"""
        await self.queue.put({
            "session_id": session_id,
            "context": context,
            "reason": reason,
            "timestamp": datetime.now()
        })

        # 通知用户
        return {
            "type": "transfer",
            "message": f"正在为您转接人工客服,预计等待{self._estimate_wait()}分钟..."
        }

    def _estimate_wait(self) -> int:
        """预估等待时间"""
        queue_length = self.queue.qsize()
        available_agents = sum(
            1 for status in self.agents.values() 
            if status == "available"
        )

        if available_agents == 0:
            return queue_length * 5  # 假设每个5分钟

        return max(1, queue_length // available_agents * 3)

八、效果评估

核心指标

class CustomerServiceMetrics:
    """客服系统指标"""

    def __init__(self):
        self.metrics = {
            "total_conversations": 0,
            "ai_resolved": 0,
            "human_resolved": 0,
            "avg_response_time": [],
            "user_satisfaction": [],
            "intent_accuracy": []
        }

    def record_conversation(self, result: dict):
        """记录对话结果"""
        self.metrics["total_conversations"] += 1

        if result["resolved_by"] == "ai":
            self.metrics["ai_resolved"] += 1
        else:
            self.metrics["human_resolved"] += 1

        self.metrics["avg_response_time"].append(result["response_time"])

        if "satisfaction" in result:
            self.metrics["user_satisfaction"].append(result["satisfaction"])

    def get_report(self) -> dict:
        """生成报告"""
        total = self.metrics["total_conversations"]

        return {
            "total_conversations": total,
            "ai_resolution_rate": self.metrics["ai_resolved"] / max(1, total),
            "human_transfer_rate": self.metrics["human_resolved"] / max(1, total),
            "avg_response_time": sum(self.metrics["avg_response_time"]) / max(1, len(self.metrics["avg_response_time"])),
            "avg_satisfaction": sum(self.metrics["user_satisfaction"]) / max(1, len(self.metrics["user_satisfaction"]))
        }

九、部署与优化

部署架构

# kubernetes部署配置
apiVersion: apps/v1
kind: Deployment
metadata:
  name: customer-service-agent
spec:
  replicas: 3
  selector:
    matchLabels:
      app: customer-service-agent
  template:
    spec:
      containers:
      - name: agent
        image: customer-service-agent:v1.0
        ports:
        - containerPort: 8080
        env:
        - name: LLM_API_KEY
          valueFrom:
            secretKeyRef:
              name: llm-secrets
              key: api-key
        resources:
          requests:
            memory: "512Mi"
            cpu: "500m"
          limits:
            memory: "1Gi"
            cpu: "1000m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 30

持续优化

优化项 方法 频率
意图识别准确率 分析误判案例,补充训练数据 每周
知识库更新 新增FAQ,更新产品信息 每日
响应速度 优化缓存策略,减少API调用 持续
用户满意度 收集反馈,改进对话策略 每周

十、总结

实施清单

阶段 任务 时间
需求分析 梳理业务场景、FAQ清单 1周
架构设计 系统架构、接口设计 3天
核心开发 意图识别、对话管理、工具集成 3周
知识库建设 收集、整理、向量化 2周
测试优化 功能测试、效果调优 1周
灰度上线 小流量验证,逐步放量 1周

关键成功因素

  1. 知识库质量:FAQ要全、答案要准
  2. 意图识别:覆盖主要场景,边界清晰
  3. 人机协作:知道什么时候该转人工
  4. 持续优化:根据数据反馈不断改进

下期预告

明天聊聊AI Agent代码助手——让AI帮你写代码、改Bug!


往期回顾

正文完
 0
技术老金
版权声明:本站原创文章,由 技术老金 于2026-03-31发表,共计13270字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)