news 2026/1/31 2:06:49

从0到1构建智能客服agent:基于LLM的实战架构与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从0到1构建智能客服agent:基于LLM的实战架构与避坑指南


从0到1构建智能客服agent:基于LLM的实战架构与避坑指南

背景痛点:规则引擎的“三座大山”

去年我们团队接手某电商售后系统时,老代码里躺着 1.3 万条正则规则,维护人已经离职,留下一句话:“改一条规则,全站回归 3 天”。
痛点总结如下:

  1. 冷启动成本高:新场景需要写正则、画流程图、再写单元测试,平均 2 人/周。
  2. 泛化能力差:用户一句“我买的白色 42 码鞋子能换成黑色 43 吗?”需要 7 条规则才能覆盖颜色和尺码的排列组合。
  3. 上下文断裂:传统槽位填充只能记住上一轮,用户中途问“运费谁出”再回来,状态机直接懵圈。

于是老板拍板:用 LLM 重构,目标 200 并发、P99<800 ms、意图准确率 ≥92%。

技术选型:Rasa vs Dialogflow vs LLM 自研

维度Rasa 3.xDialogflow CXLLM + 自研框架
中文预训练需自己训谷歌通用模型可接自研/开源 13B
私有部署
多轮状态有限可视化画布代码级灵活
知识更新重启服务后台上传热加载向量库
成本(月)4 核 8 G ≈ 1 k0.006 美元/轮GPU 推理 ≈ 1.2 k

结论:ToB 场景数据不能出机房,LLM 方案胜出。
架构图如下:

核心链路:网关 → 敏感词异步过滤 → 对话状态机(LangChain)→ 向量检索(FAISS)→ LLM → 后处理 → 超时重试 → 回包。

核心实现

1. 用 LangChain 搭状态机

LangChain 的ConversationBufferWindowMemory默认把全历史扔给 LLM,200 轮后 token 爆炸。我们重写了一个CompressedMemory

from typing import List, Dict from langchain.schema import BaseMemory class CompressedMemory(BaseMemory): """滑动窗口 + 摘要压缩,token 控制在 1k 以内""" def __init__(self, max_token: int = 1024): self.max_token = max_token self.history: List[Dict[str, str]] = [] def save_context(self, inputs: Dict[str, str], outputs: Dict[str, str]) -> None: self.history.append({"in": inputs.get("query"), "out": outputs.get("reply")}) self._compress() def _compress(self) -> None: while self._token_len() > self.max_token: # 弹出最早一轮,保留最近 3 轮 self.history.pop(0) def _token_len(self) -> int: return sum(len(m["in"]) + len(m["out"]) for m in self.history) def load_memory_variables(self, inputs: Dict[str, str]) -> Dict[str, str]: return {"history": "\n".join([f"User:{m['in']}\nBot:{m['out']}" for m in self.history[-5:]])}

CompressedMemory塞进LLMChain,状态机就瘦身成功。

2. 基于 FAISS 的知识库语义检索

知识库格式:Markdown,每段≤512 字,先拆块再向量化。

import faiss import numpy as np from sentence_transformers import SentenceTransformer class FaissIndex: def __init__(self, model_name: str = "shibing624/text2vec-base-chinese"): self.encoder = SentenceTransformer(model_name) self.index = faiss.IndexFlatIP(768) # 余弦相似度 self.text_map = [] def add_docs(self, docs: List[str]) -> None: embeddings = self.encoder.encode(docs, normalize_embeddings=True) self.index.add(np.array(embeddings, dtype=np.float32)) self.text_map.extend(docs) def search(self, query: str, topk: int = 3) -> List[str]: q = self.encoder.encode([query], normalize_embeddings=True) scores, idx = self.index.search(np.array(q, dtype=np.float32), topk) return [self.text_map[i] for i in idx[0] if i != -1]

实测 4 核 8 G,10 万条向量,平均检索 18 ms,QPS 400 无压力。

3. 对话历史压缩算法与性能对比

方案平均 token/轮意图准确率P99 延迟
全历史3.2 k94.1 %1.3 s
滑动窗口 5 轮0.9 k93.8 %0.7 s
摘要压缩0.6 k92.5 %0.6 s

权衡后选“滑动窗口 5 轮”,准确率掉 0.3 %,延迟降一半。

生产考量

1. 超时重试机制

LLM 推理偶尔 5 s 才回包,不能让前端空等。用tenacity包两层重试:

from tenacity import retry, stop_after_attempt, wait_random_exponential @retry(wait=wait_random_exponential(multiplier=1, max=10), stop=stop_after_attempt(3)) def llm_generate(prompt: str) -> str: resp = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=[{"role": "user", "content": prompt}], timeout=8 ) return resp.choices[0].message.content

超时阈值 8 s,最多 3 次,失败返回兜底话术“人工客服稍后联系您”。

2. 敏感词过滤异步化

把敏感词检测拆成独立服务,用asyncio并行:

import aiohttp, asyncio async def sensitive_check(text: str) -> bool: async with aiohttp.ClientSession() as session: async with session.post("http://internal-filter/sensitive", json={"q": text}) as resp: result = await resp.json() return result.get("hit", False)

主流程里await asyncio.wait_for(sensitive_check(query), timeout=0.1),超时就当通过,后续离线审计再处理,保证主链路 P99 不受拖累。

避坑指南

1. 避免 LLM 幻觉的 prompt 技巧

  • 先检索后回答:prompt 里加“仅使用以下上下文回答,若找不到请说‘暂无相关信息’”。
  • 给示例:Few-shot 3 例,把“不知道”的样本也写进去,让模型学会拒绝。
  • 温度 0.1 起步,别迷信 temperature=0,实测 0 反而产出重复废话。

2. 对话上下文窗口滑动实现

上文已给CompressedMemory,记得把系统提示(如“你是客服助手”)固定在最前,不参与滑动,否则模型会“失忆”自己是谁。

3. 知识库更新时的版本兼容

  • 向量库带版本号:kb_v20240618.index,新库先灰度 5 % 流量,观察 30 min 无异常再全量。
  • 兼容老会话:用户已开聊的 session 仍指向旧索引,新 session 才用新索引,避免上下文跳变。

性能数据小结

测试环境:4 核 8 G / 100 并发 / 单卡 A10

  • QPS:峰值 230
  • P99:780 ms
  • 意图准确率:93.8 %
  • 幻觉率:2.1 %(人工抽检 500 轮)

开放问题

如何设计降级策略应对 API 限流?
当 LLM 供应商突然返回 429 时,我们除了“抱歉请稍后再试”,还能不能:

  • 本地 6B 小模型接力?
  • 把请求拆成异步工单,后续短信回复?
  • 直接给 FAQ 链接让用户自助?

欢迎留言聊聊你的实战做法。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/31 2:06:47

中国科学技术大学LaTeX论文模板快速上手与格式规范指南

中国科学技术大学LaTeX论文模板快速上手与格式规范指南 【免费下载链接】ustcthesis LaTeX template for USTC thesis 项目地址: https://gitcode.com/gh_mirrors/us/ustcthesis 在学术论文写作中&#xff0c;格式排版往往占据大量时间却难以达到学校规范要求。中国科学…

作者头像 李华
网站建设 2026/1/31 2:06:14

构建高效中文聊天机器人:从架构设计到性能优化实战

构建高效中文聊天机器人&#xff1a;从架构设计到性能优化实战 1.中文场景下的“慢”与“不准” 做中文 Chatbot&#xff0c;第一步就被分词卡住。同样一句“南京市长江大桥”&#xff0c;jieba 可能切成“南京/市长/江大桥”&#xff0c;而用户想问的却是“南京市/长江大桥”…

作者头像 李华
网站建设 2026/1/31 2:06:09

ChatGLM-6B镜像结构详解:/ChatGLM-Service/app.py核心逻辑逐行解读

ChatGLM-6B镜像结构详解&#xff1a;/ChatGLM-Service/app.py核心逻辑逐行解读 1. 镜像定位与服务本质 ChatGLM-6B 智能对话服务不是简单的模型调用封装&#xff0c;而是一套面向工程落地的轻量级推理服务闭环。它把一个62亿参数的双语大模型&#xff0c;转化成你本地终端上可…

作者头像 李华
网站建设 2026/1/31 2:06:06

老旧电脑性能重生:tiny11builder系统精简工具新手优化指南

老旧电脑性能重生&#xff1a;tiny11builder系统精简工具新手优化指南 【免费下载链接】tiny11builder Scripts to build a trimmed-down Windows 11 image. 项目地址: https://gitcode.com/GitHub_Trending/ti/tiny11builder 你是否曾遇到这样的窘境&#xff1a;打开文…

作者头像 李华
网站建设 2026/1/31 2:06:03

用YOLOv12官版镜像5分钟跑通COCO数据集

用YOLOv12官版镜像5分钟跑通COCO数据集 在目标检测工程实践中&#xff0c;最让人抓狂的不是调参失败&#xff0c;而是卡在环境搭建和数据准备环节——明明想验证一个新模型&#xff0c;却要在CUDA版本、Flash Attention编译、COCO数据集下载解压、路径配置之间反复折腾两小时。…

作者头像 李华