摘要:今天来搞一个每个电商项目都需要的东西——AI 智能客服。用 RAG 架构把商品 FAQ 和订单数据灌进向量库,对接 GLM-4.7-Flash(永久免费 200K)做对话生成,再配上多轮对话记忆和人工兜底。一套完整的 7×24 小时不下班的客服系统,成本约等于零。代码全部可运行,直接集成到 Django 项目里。
人工客服,太贵了
做电商的朋友都知道,客服是最吃人力的一环。
算一笔账:一个客服月薪 5000-8000,三班倒至少 3 个人。一年下来光人力成本就 20 万+。而且人总会犯错——回复慢了、态度差了、信息搞混了,都是投诉的导火索。
更头疼的是,凌晨 2 点有人问"你们怎么退换货",没人回。第二天用户可能就不买了。
订单系统稳了,商品系统也跑起来了。下一步就是让 AI 把客服这活儿接过来。
今天我们就搭一个完整的 AI 客服系统:
- 意图识别:先判断用户想干嘛(查订单?问 FAQ?还是想骂人?)
- 知识检索:从 FAQ 知识库里找到最相关的回答
- LLM 生成:让 AI 基于检索结果组织语言回答
- 人工兜底:搞不定的转人工,不让 AI 瞎说
整个系统的成本?≈ ¥0。GLM-4.7-Flash 永久免费额度 200K tokens,日常咨询量完全够用。
RAG 是什么
为什么需要 RAG
你可能会问:直接把 FAQ 塞给大模型不就行了?
问题在于:
- 大模型不知道你的商品信息。它不知道商城支持微信支付、不知道退换货政策是 7 天无理由。
- Prompt 有长度限制。把 100 条 FAQ 全塞进去,token 爆了。
- 回答不可控。模型可能"幻觉"——编造一个不存在的优惠政策。
RAG(Retrieval-Augmented Generation,检索增强生成)就是为了解决这三个问题。
RAG 的工作流程
画成图就是四步:
用户提问:"你们怎么退换货?" │ ▼ ┌─────────────────┐ │ ① 向量化查询 │ 把用户问题编码成向量(768 维) └────────┬────────┘ │ ▼ ┌─────────────────┐ │ ② 语义检索 │ 在知识库中找最相似的 3 条 FAQ │ (余弦相似度) │ → 命中:"怎么退换货?"(相似度 0.92) └────────┬────────┘ │ ▼ ┌─────────────────┐ │ ③ 拼接 Prompt │ 系统人设 + 检索到的 FAQ + 用户问题 └────────┬────────┘ │ ▼ ┌─────────────────┐ │ ④ LLM 生成回答 │ GLM-4.7-Flash 基于参考资料生成自然语言回答 └─────────────────┘ │ ▼ "收到商品 7 天内可申请无理由退换货~ 操作步骤:我的订单 → 申请售后 → 选择退换原因 → 提交"一句话总结 RAG:先查资料,再回答问题。就像考试翻书——你不一定要记住所有知识点,但你要知道去哪翻。
意图识别:客服的"前门"
不是所有问题都要走 RAG。用户说"转人工",你不需要检索知识库;用户说"查订单",你需要的是查数据库,不是查 FAQ。
所以我们加一层意图识别:
用户输入 │ ├─ "转人工" ──────→ 转人工客服(兜底) ├─ "查订单" ──────→ 查订单数据库(业务逻辑) ├─ "快递到哪了" ──→ 查物流接口(业务逻辑) └─ 其他问题 ─────→ RAG 检索 + LLM 生成简单场景用关键词匹配就够了,不需要额外训练分类模型。
动手实战
环境准备
pipinstallzhipuai sentence-transformers numpy三个核心依赖:
| 库 | 作用 |
|---|---|
zhipuai | 调用 GLM-4.7-Flash(永久免费) |
sentence-transformers | 中文文本向量化 |
numpy | 向量计算(余弦相似度) |
对接 GLM-4.7-Flash
GLM-4.7-Flash 是智谱 AI 的轻量模型,永久免费额度 200K tokens。客服场景的回答通常就 100-200 字,200K 额度能覆盖大量日常咨询。
fromzhipuaiimportZhipuAI client=ZhipuAI(api_key="your-api-key-here")defcall_glm4_flash(messages:list,temperature:float=0.3)->str:""" 调用 GLM-4.7-Flash 生成回答 客服场景 temperature 建议 0.1~0.3(低温度 = 回答更稳定) """response=client.chat.completions.create(model="glm-4-flash",# 永久免费模型messages=messages,temperature=temperature,max_tokens=1024,# 客服回答不需要太长)returnresponse.choices[0].message.content为什么 temperature 设 0.3?客服回答要的是"准确",不是"创意"。温度太高,模型可能会"发挥想象力"编造优惠政策,那就闯祸了。
构建知识库
知识库是 RAG 的"大脑"。把商城的 FAQ 整理成结构化数据:
FAQ_KNOWLEDGE_BASE=[{"id":"faq_001","question":"你们支持哪些支付方式?","answer":"商城支持微信支付、支付宝、银行卡三种支付方式。","category":"支付",},{"id":"faq_002","question":"发货到全国要多久?","answer":"普通订单 3-5 个工作日送达,偏远地区 5-7 个工作日。支持顺丰加急。","category":"物流",},{"id":"faq_003","question":"怎么退换货?","answer":"收到商品 7 天内可申请无理由退换货。我的订单 → 申请售后 → 选择退换原因 → 提交。","category":"售后",},# ... 更多 FAQ]实际项目中,这些 FAQ 从哪来?三个来源:
- 人工整理:客服团队把高频问题整理成文档
- 历史工单挖掘:从过去的客服聊天记录中提取高频问答对
- AI 生成:用大模型根据商品信息自动生成 FAQ(后面会讲)
向量检索引擎
这是 RAG 的核心组件。把每条 FAQ 转成向量,查询时算余弦相似度:
fromsentence_transformersimportSentenceTransformerimportnumpyasnp# 加载中文语义向量模型embedding_model=SentenceTransformer("shibing624/text2vec-base-chinese")classVectorStore:"""轻量级向量存储——把知识库变成可检索的向量空间"""def__init__(self):self.documents=[]# 原始文档self.vectors=None# 向量矩阵defadd_documents(self,docs:list):"""把 FAQ 批量编码为向量"""self.documents=docs texts=[f"{doc['question']}{doc['answer']}"fordocindocs]self.vectors=embedding_model.encode(texts,normalize_embeddings=True)defsearch(self,query:str,top_k:int=3)->list:"""语义检索:找到和用户问题最相关的 FAQ"""query_vector=embedding_model.encode([query],normalize_embeddings=True)# 归一化后,点积 = 余弦相似度similarities=(self.vectors @ query_vector.T).flatten()top_indices=similarities.argsort()[::-1][:top_k]results=[]foridxintop_indices:doc=self.documents[idx].copy()doc["score"]=float(similarities[idx])results.append(doc)returnresults为什么用
shibing624/text2vec-base-chinese?这是专门针对中文优化的轻量向量模型,只有 400MB,本地就能跑。"退换货"和"退货流程"能被识别为相似含义。
检索效果示例:
用户问:"东西坏了怎么办?" → 检索结果: 1. [售后] 商品质量有问题怎么办?(相似度 0.87)✅ 2. [售后] 怎么退换货?(相似度 0.81)✅ 3. [物流] 发货到全国要多久?(相似度 0.34)❌ 过滤"东西坏了"和"商品质量有问题"虽然用词完全不同,但语义相似度很高。这就是向量检索比关键词匹配强的地方。
意图识别
# 意图规则表:关键词 → 意图类型INTENT_RULES={"转人工":"transfer_human","人工客服":"transfer_human","我要投诉":"transfer_human","查订单":"query_order","我的订单":"query_order","快递到哪了":"query_logistics",}defidentify_intent(user_message:str)->dict:"""关键词匹配意图——简单但有效"""forkeyword,intentinINTENT_RULES.items():ifkeywordinuser_message:return{"intent":intent,"need_rag":False}return{"intent":"general_qa","need_rag":True}关键词匹配能覆盖 80% 的场景。剩下 20% 的边界情况(用户说"我不想要了"想退货但没说关键词),RAG 也能兜住。
多轮对话 + 上下文记忆
用户不会一句话把问题说清楚。典型的对话是多轮的:
用户:帮我查一下最近的订单 AI:为您查询到 2 笔订单... 用户:第二笔什么时候发货? ← 这里的"第二笔"需要上下文 AI:第二笔订单(MYD20260102002)目前待支付,支付后 24 小时内发货~实现多轮对话的关键是对话历史管理:
classConversationMemory:"""多轮对话记忆管理"""def__init__(self):self.sessions={}# session_id → 对话历史defget_system_prompt(self)->dict:"""客服人设 Prompt"""return{"role":"system","content":("你是商城的智能客服小梅,请遵守以下规则:\n""1. 语气亲切友好,像朋友聊天\n""2. 回答简洁准确,控制在 200 字以内\n""3. 只回答购物相关问题\n""4. 不确定的问题诚实说,不要编造\n""5. 退款/投诉等敏感操作建议联系人工客服"),}defadd_message(self,session_id:str,role:str,content:str):"""添加消息到对话历史(最多保留最近 10 轮 = 20 条)"""self.sessions.setdefault(session_id,[]).append({"role":role,"content":content})iflen(self.sessions[session_id])>20:self.sessions[session_id]=self.sessions[session_id][-20:]为什么要截断历史?一方面控制 token 用量,另一方面太久远的对话对当前问题没帮助。保留最近 10 轮足够。
组装完整的客服系统
把所有模块串起来(核心框架,完整实现见配套代码):
classMeiYaDaChatbot:"""AI 客服——完整系统"""def__init__(self):self.vector_store=VectorStore()self.memory=ConversationMemory()self.vector_store.add_documents(FAQ_KNOWLEDGE_BASE)defchat(self,session_id:str,user_message:str)->str:"""处理一次用户消息"""# ① 记录用户消息self.memory.add_message(session_id,"user",user_message)# ② 意图识别intent=identify_intent(user_message)["intent"]# ③ 根据意图走不同分支ifintent=="transfer_human":reply=self._handle_transfer_human()elifintent=="query_order":reply=self._handle_order_query(user_message)elifintent=="query_logistics":reply=self._handle_logistics_query(user_message)else:reply=self._handle_rag_query(session_id,user_message)# ④ 记录 AI 回复self.memory.add_message(session_id,"assistant",reply)returnreplydef_handle_rag_query(self,session_id:str,user_message:str)->str:"""RAG 核心:检索 → 拼接 → 生成"""# ① 检索知识库 top-3,过滤低相似度(score > 0.3)# ② 拼接【分类】问题+答案为上下文# ③ 组装消息:系统人设 + 参考资料 + 最近 6 条对话历史# ④ call_glm4_flash(messages, temperature=0.3) 生成回答...Django 集成
在 Django 项目中暴露成 API 接口:
fromrest_framework.decoratorsimportapi_view,permission_classesfromrest_framework.permissionsimportIsAuthenticatedfromrest_framework.responseimportResponse chatbot=MeiYaDaChatbot()@api_view(['POST'])@permission_classes([IsAuthenticated])defchat_view(request):"""AI 客服对话接口 POST /api/chat/"""user_message=request.data.get('message','').strip()session_id=f"user_{request.user.id}"reply=chatbot.chat(session_id,user_message)returnResponse({'reply':reply,'session_id':session_id})前端只需要一个输入框 + 一个消息列表,往/api/chat/POST 消息就行。
AI 加速:让 AI 帮你建知识库
手动写 FAQ 太累了。我们可以用 AI 来批量生成:
用 GLM-4.7-Flash 自动生成 FAQ
defgenerate_faq_from_product(product_info:str)->list:""" 根据商品信息,让 AI 自动生成 FAQ 输入:商品描述文本 输出:FAQ 列表 [{"question": ..., "answer": ...}, ...] """prompt=f""" 根据以下商品信息,生成 5 个用户最可能问的问题和回答。 格式要求:每行一个 QA,用 "Q: 问题\nA: 回答" 的格式。 商品信息:{product_info}"""response=call_glm4_flash([{"role":"user","content":prompt}],temperature=0.5)# 解析 AI 返回的 QA 对,逐行匹配 "Q:" / "A:" 前缀...returnfaq_list# 使用示例:把商品描述丢进去,5 条 FAQ 自动生成,直接灌入知识库new_faqs=generate_faq_from_product("梅雅达自营蓝牙耳机 Pro Max\n- 主动降噪 40dB\n- 续航 36 小时\n- 价格 ¥399")这个方法的核心思路:让 AI 站在用户的角度想问题。用户看到"续航 36 小时"会问什么?“充电要多久?”“能用几天?”——这些问题人工客服天天在回答,AI 一扫商品描述就能批量生成。
优化客服人设 Prompt
客服的回答质量 80% 取决于 Prompt 写得好不好:
# ❌ 差的 Prompt"你是一个客服,请回答用户的问题。"# ✅ 好的 PromptGOOD_SYSTEM_PROMPT=""" 你是商城的智能客服小梅~ 【性格】 - 亲切友好,像朋友聊天 - 适当使用 emoji 和"~"让对话更温暖 【规则】 - 回答简洁,控制在 200 字以内 - 只回答购物相关的问题 - 不确定的问题诚实说"这个我需要确认一下" - 退款/投诉建议联系人工客服 【参考资料】 如果有提供参考资料,优先基于参考资料回答,不要编造资料里没有的信息。 """关键区别:好的 Prompt 有人设、有边界、有兜底策略。AI 不会乱说话,也不会"死机"——遇到不会的就引导到人工。
学完来练练手
- 🟢 入门:给知识库加上"会员等级"相关的 FAQ(银卡/金卡/钻石卡不同权益)
- 🟡 进阶:把 VectorStore 改成持久化存储(用
pickle或numpy.save把向量矩阵存到磁盘,启动时直接加载,避免每次重启都重新跑 embedding 模型) - 🔴 挑战:接入真实的订单数据库,让"查订单"功能从 Django ORM 查询真实数据,而不是返回 mock 数据
配套文件day15_chatbot.py完整实现可参考学习。GLM-4.7-Flash 文档;sentence-transformers 文档。
本系列为梅雅达编程笔记原创,首发于 CSDN。有问题?评论区见 🍃