news 2026/4/23 17:42:10

从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/4/23 16:14:03

SVGAPlayer-Web-Lite:轻量级动画播放器技术指南

SVGAPlayer-Web-Lite&#xff1a;轻量级动画播放器技术指南 【免费下载链接】SVGAPlayer-Web-Lite 项目地址: https://gitcode.com/gh_mirrors/sv/SVGAPlayer-Web-Lite SVGAPlayer-Web-Lite 是一款专为移动端 Web 设计的轻量级动画播放器&#xff0c;通过现代 Web 技术…

作者头像 李华
网站建设 2026/4/16 16:07:46

施工组织设计毕业设计中的效率瓶颈与自动化优化实践

施工组织设计毕业设计中的效率瓶颈与自动化优化实践 面向土木工程/工程管理高年级学生&#xff0c;一份“施工组织设计”往往占毕业设计 60 % 以上工作量。传统手工排程、Excel 拉横道图、Project 拖甘特图&#xff0c;看似轻车熟路&#xff0c;却常把最后两周逼成“熬夜画图大…

作者头像 李华
网站建设 2026/4/23 12:48:16

ChatGLM-6B精彩对话案例集:技术文档解读、代码生成、创意写作展示

ChatGLM-6B精彩对话案例集&#xff1a;技术文档解读、代码生成、创意写作展示 1. 这不是“又一个聊天机器人”&#xff0c;而是一个能真正帮上忙的智能协作者 你有没有过这样的时刻&#xff1a;面对一份密密麻麻的技术文档&#xff0c;逐字阅读像在爬山&#xff1b;写一段Pyt…

作者头像 李华
网站建设 2026/4/23 12:23:41

DeerFlow新手指南:控制台UI与Web UI双模式使用

DeerFlow新手指南&#xff1a;控制台UI与Web UI双模式使用 1. 什么是DeerFlow&#xff1f;你的个人深度研究助理 你有没有过这样的经历&#xff1a;想快速了解一个前沿技术&#xff0c;却要在十几个网页间反复跳转、复制粘贴、整理逻辑&#xff1b;想分析某个行业的最新动态&…

作者头像 李华
网站建设 2026/4/23 11:31:06

夸克网盘自动管理工具:从繁琐操作到高效管理的效率提升指南

夸克网盘自动管理工具&#xff1a;从繁琐操作到高效管理的效率提升指南 【免费下载链接】quark-auto-save 夸克网盘签到、自动转存、命名整理、发推送提醒和刷新媒体库一条龙 项目地址: https://gitcode.com/gh_mirrors/qu/quark-auto-save 每天手动签到领空间太麻烦&am…

作者头像 李华
网站建设 2026/4/23 12:18:41

动手试了Qwen3-1.7B:LangChain集成效果超出预期

动手试了Qwen3-1.7B&#xff1a;LangChain集成效果超出预期 最近在本地快速验证一个轻量级大模型的工程可用性&#xff0c;选中了刚开源不久的Qwen3-1.7B——它不像动辄几十GB的大块头&#xff0c;显存占用低、启动快、响应灵敏&#xff0c;更重要的是&#xff0c;它对标准LLM…

作者头像 李华