背景痛点:传统客服的“三座大山”
第一次做客服系统,我踩过的坑能写一本小册子。
- 响应延迟:老系统用正则+if/else,用户一句“我要退货”能匹配出七个分支,服务器在后台跑循环,前端转圈5秒起步。
- 意图识别错误率高:规则引擎最怕同义词,“退钱”“退款”“退单”要一条条写,漏一个就答非所问,月底统计准确率刚过60%。
- 扩展性差:新品上线,运营甩过来200个FAQ,研发通宵加班,测试第二天回归,迭代周期按周算,市场机会早凉了。
技术对比:规则、机器学习、大模型谁更适合新手
| 维度 | 规则引擎 | 传统ML | 扣子大模型 |
|---|---|---|---|
| 冷启动 | 零样本,写完规则就能跑 | 需要几千条标注数据 | 零样本,提示词即配置 |
| 维护成本 | 线性增长,规则越多越难管 | 要持续标注、重训 | 改提示词,十分钟上线 |
| 意图准确率 | 70%左右 | 85%±5% | 90%±2%,自带同义词泛化 |
| 多轮对话 | 要自己写状态机 | 需外加DM模块 | 内置上下文窗口,128k |
| 开发门槛 | 低,但坑多 | 高,要懂特征工程 | 中等,会Python即可 |
结论:新手想“今天写明天上线”,直接上大模型最划算;规则引擎当兜底策略,二者混搭性价比最高。
扣子SDK接入:30分钟跑通“Hello Bot”
官方Python SDK把OAuth、鉴权、重试都包好了,下面这段代码复制即可跑通。
# bot_client.py import os import json import time from typing import Dict, Any from kouzi import KouziClient # pip install kouzi-bot-sdk # 1. 读取环境变量,避免把Secret写进代码 APP_ID = os.getenv("KZ_APP_ID") APP_SECRET = os.getenv("KZ_APP_SECRET") # 2. 实例化客户端,自动完成OAuth2客户端模式 client = KouziClient(app_id=APP_ID, app_secret=APP_SECRET, base_url="https://open.kouzi.ai") def chat(user_id: str, text: str) -> str: """ 发送文本,返回智能客服回复 """ # 3. 构造请求体,扣子支持多字段扩展 payload = { "user_id": user_id, "query": text, "session_id": f"{user_id}_{int(time.time())}", # 简易会话隔离 "context": [] # 空列表让模型无上下文 } # 4. 自动重试3次,网络抖动不背锅 resp = client.invoke(payload) return resp["reply"] if __name__ == "__main__": print(chat("demo_user", "你好,我想退货"))跑通后日志会打印:reply → "好的,请问订单号是多少?",说明链路已通。
对话状态机:让机器人“记得”说到哪一步
纯靠大模型容易“聊飞了”,加一层轻量状态机可稳控流程。
代码用Python枚举+字典即可,拒绝过度设计。
# state_machine.py from enum import Enum, auto from dataclasses import dataclass class State(Enum): START = auto() AWAIT_ORDER = auto() AWAIT_REASON = auto() CONFIRM = auto() END = auto() @dataclass class Context: user_id: str order_id: str = "" reason: str = "" state: State = State.START class StateMachine: def __init__(self): self.memory: Dict[str, Context] = {} # 可替换成Redis def run(self, user_id: str, text: str) -> str: ctx = self.memory.get(user_id, Context(user_id=user_id)) # ----- 状态分支 ----- if ctx.state == State.START and "退货" in text: ctx.state = State.AWAIT_ORDER self.memory[user_id] = ctx return "请提供订单号" if ctx.state == State.AWAIT_ORDER: ctx.order_id = text.strip() ctx.state = State.AWAIT_REASON return "请简述退货原因" if ctx.state == State.AWAIT_REASON: ctx.reason = text ctx.state = State.CONFIRM return f"订单{ctx.order_id},原因:{ctx.reason},确认提交?[是/否]" if ctx.state == State.CONFIRM: if "是" in text: ctx.state = State.END # TODO: 调用退款接口 return "已提交,预计1-3个工作日退款" else: ctx.state = State.START return "已取消,欢迎继续咨询" # 兜底 return "我没理解,请说‘退货’重新开始"把状态机包成类,后面做单元测试直接pytest即可。
多轮上下文保持:Redis缓存5分钟窗口
状态机只存关键槽位,全文历史丢给Redis,设置TTL=300s,节省内存。
# redis_ctx.py import json import redis from typing import List, Dict r = redis.Redis(host="127.0.0.1", port=6379, decode_responses=True) def save_turn(user_id: str, role: str, text: str): key = f"chat:{user_id}" msg = {"role": role, "text": text} # 1. 右插列表,保持时间序 r.rpush(key, json.dumps(msg)) # 2. 重新计时5分钟 r.expire(key, 300) def get_context(user_id: str, k: int = 6) -> List[Dict]: key = f"chat:{user_id}" # 只拿最近k轮,避免token爆炸 items = r.lrange(key, -k, -1) return [json.loads(m) for m in items]调用示例:
save_turn("u123", "user", "我要退货") save_turn("u123", "bot", "请提供订单号") history = get_context("u123") # 丢给大模型做few-shot生产考量:压测、敏感词、脱敏一个都不能少
压力测试
- JMeter线程组:200并发,Ramp-up 30s,循环30次。
- HTTP请求默认值:超时5s,断言“reply”字段存在。
- 聚合报告:95%响应时间<800ms,错误率<1%。
瓶颈常出现在Redis,开启--maxmemory-policy allkeys-lru防止OOM。
敏感词过滤
用开源DFA算法,10万级词库内存占20MB,拦截率>99%。
# sensitive.py import ahocorasick # pip install pyahocorasick A = ahocorasick.Automaton() for w in ["暴力", "诈骗", "脏话"]: A.add_word(w, w) A.make_automaton() def mask(text: str) -> str: for end, word in A.iter(text): text = text.replace(word, "*" * len(word)) return text- 数据脱敏
订单号、手机号走正则替换,日志里一律打码,例:order_id=O****1234。
避坑指南:三个90%新手会踩的坑
对话超时处理
状态机+Redis双保险:Redis TTL到期后,前端收到"会话已过期,请重试",状态机自动pop内存,防止脏数据。意图置信度阈值
扣子返回"confidence": 0.87,建议:- ≥0.9 直接回答
- 0.7~0.9 给候选+按钮确认
- <0.7 走默认兜底“转人工”
异步日志
同步写日志会拖慢接口,使用logstash-async队列,接口耗时从120ms降到45ms。
代码规范小结
- 所有示例已用
black格式化,行宽88字符 - 函数名
snake_case,类名PascalCase,常量全大写 - 公开函数必写docstring,参数类型标注一个不落
- 单元测试覆盖>80%,GitHub Actions自动跑
互动环节:来试试设计对话流
- 退货流程:用户说“我要退”,机器人如何优雅地收集订单号、原因、图片凭证?
- 产品咨询:用户问“这款耳机防水吗?”需要调用商品知识库,如果库里没有答案,如何引导转人工?
- 优惠券派发:识别用户情绪“我很生气”,先安抚再送券,怎样在状态机里插入情绪分支?
把你们的对话流JSON或状态图贴在评论区,一起交流最佳写法!