我用LangChain重构智能客服的实战经验:日均处理10万条QA的踩坑总结
我用LangChain重构智能客服的实战经验:日均处理10万条QA的踩坑总结
上个月接了个电商客户的急活,他们的客服团队每天要回答成千上万条关于物流、退换货规则的重复问题。老板不想继续堆人力,让我用 AI 做个能自动回复的智能客服机器人。说实话,这个项目看起来挺简单,不就是调个 API 吗?结果我花了两周时间,才把那个基于 LangChain 的 RAG(检索增强生成)Agent 真正跑顺。今天就把这中间踩过的坑和最终落地的方案记录下来,给想尝试同类项目的兄弟避避雷。
方案选型与架构落地
一开始我确实飘了,觉得直接上 Vector Store 加 LLM 链就行。但客户的数据量不小,光 FAQ 就有好几万条,而且还在持续更新。试了一圈发现,如果不做优化,响应速度根本达不到生产要求。
当时我有两个方案摆在面前:
方案 A:把所有文档一次性向量化,存进 Milvus 或 Pinecone 这类专门的向量数据库,查询时实时检索。
方案 B:使用 LangChain 内置的内存式存储(如 FAISS),配合定期批量更新机制,部署在本地服务器。
我最终选了方案 A。原因很现实:客户对可用性要求极高,方案 B 在数据量增大后检索延迟会显著上升,而且本地维护成本不低。虽然方案 A 需要引入额外的云服务组件,但它能更好地支撑高并发。
我们在技术栈上选了 Python 3.10 + LangChain 0.1+ + OpenAI GPT-4o-mini(性价比不错)。下面是核心 Agent 的搭建代码,不算复杂,但有个细节必须注意:
```python
from langchain.agents import create_openai_functions_agent, AgentExecutor
from langchain_community.utilities import SQLDatabase
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
初始化 LLM,这里用了国内可访问的接口兼容方案
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
定义工具列表,这里只演示一个简单的检索工具
tools = [search_knowledge_base]
构建提示词模板,这是让 Agent "聪明" 的关键
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个专业的客服助手,请根据以下知识库内容回答用户问题。如果不知道,请礼貌告知并建议联系人工客服。"),
("user", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
])
创建 Agent
agent = create_openai_functions_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
```
这里的 verbose=True 在生产环境最好关掉,不然日志量会把磁盘撑爆。我当时没注意,第一天上线测试,光日志文件就占了 20GB,坑死了。
检索优化与调试实录
代码跑通只是第一步,真正头疼的是检索效果。刚开始的时候,用户问“我的快递为什么还没到?”,机器人经常答非所问,或者引用了过期的物流政策。
我排查了半天,发现是向量相似度阈值设得太宽了。默认的 0.5 阈值太松,导致很多无关文档也被召回。我调整了检索策略,引入了 Hybrid Search(混合搜索):先用关键词 BM25 过滤掉明显不相关的文档,再用向量检索做语义匹配,最后把两个结果合并去重。
另外,我还做了一个重要的工程决策:对 FAQ 文档进行分段处理。原始文档有的长达几千字,直接切片会导致上下文丢失。我把文档拆分成 500 字左右的段落,并为每个段落添加了元数据标签(如“物流”、“售后”、“价格”)。这样在检索时,可以先按标签过滤,再算向量相似度,准确率提升了大约 30%。
以下是检索链的核心逻辑:
```python
from langchain_community.vectorstores import Chroma
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
文本拆分
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = text_splitter.split_documents(documents)
向量化并存入 Chroma(开发阶段用这个,上线后换 Milvus)
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(chunks, embeddings)
组合检索器
retriever = vectorstore.as_retriever(
search_type="similarity_score_threshold",
search_kwargs={"k": 5, "score_threshold": 0.75}
)
```
有一个很有意思的现象是,当用户的问题非常模糊时,比如只发了一个“?”或者“你好”,Agent 会陷入死循环或者胡乱引用文档。我在 prompt 里加了一条强约束:“如果用户输入不包含具体问题,请直接回复‘您好,请问有什么可以帮助您的?’”。这一行改动,解决了 80% 的无效交互。
说实话,当时我觉得这样就行,结果上线后被质检部门打回来,说有些专业术语解释得不够准确。我又加了一个“术语修正表”,在检索前先对实体词进行标准化替换。这个小改动让整个系统的专业度上了一个档次。
这个项目让我深刻体会到,AI Agent 落地不是调个包就完事,真正的难点在于数据质量和检索策略的微调。如果你也在做类似的项目,记得预留足够的时间给数据清洗和 prompt 迭代。
本文基于实际项目经验整理,欢迎在评论区交流技术问题。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)