基于 RAG 架构实现本地 AI 智能客服系统
1. 背景
随着大语言模型(LLM)的快速发展,AI 客服、企业知识库问答、智能助手等应用逐渐落地。
传统客服系统通常依赖:
- 固定关键词匹配
- FAQ 页面
- 人工维护规则
存在以下问题:
- 用户表达方式多样,关键词匹配效果差
- 知识更新需要修改代码
- 无法理解上下文语义
- 面对大量业务文档维护成本高
例如用户询问:
“钱怎么还没有退回来?”
传统系统可能无法匹配:
“退款多久到账?”
而基于 RAG 的系统可以通过语义理解找到相关知识。
2. 什么是 RAG?
RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合:
- 文档检索
- 向量数据库
- 大语言模型
的 AI 应用架构。
简单流程:
用户问题
↓
问题向量化
↓
向量数据库检索
↓
获取相关知识
↓
拼接 Prompt
↓
LLM生成回答
核心思想:
不重新训练模型,而是给模型提供外部知识,让模型根据资料回答。
3. 系统整体架构
本项目实现一个本地运行的 AI 客服系统。
整体架构:
用户
|
|
RAG应用程序
|
--------------------
| |
Chroma向量库 Ollama
| |
知识文档 Qwen模型
|
txt / markdown文档
技术组件:
| 模块 | 技术 |
|---|---|
| 开发语言 | Python |
| AI框架 | LangChain |
| 大语言模型 | Qwen2 |
| 模型运行 | Ollama |
| Embedding模型 | nomic-embed-text |
| 向量数据库 | Chroma |
| 知识文件 | TXT文档 |
4. RAG工作流程
4.1 文档加载
首先准备客服知识库:
目录:
docs/
└── 电商客服.txt
示例内容:
Q: 如何联系客服?
A:
用户可以通过在线客服、客服电话或提交工单方式联系客服人员。
Q: 如何申请退款?
A:
进入我的订单,选择商品点击申请退款。
程序读取:
DirectoryLoader(
docs_dir,
glob="**/*.txt"
)
加载所有 txt 文档。
4.2 文档切割
由于大模型输入长度有限,需要将长文本切分。
例如:
原文:
退款规则:
用户申请退款后,需要审核。
审核通过后1-7天到账。
切割:
chunk1:
退款规则:
用户申请退款后,需要审核。
chunk2:
审核通过后1-7天到账。
代码:
RecursiveCharacterTextSplitter(
chunk_size=300,
chunk_overlap=100
)
参数说明:
| 参数 | 作用 |
|---|---|
| chunk_size | 每个文本块大小 |
| chunk_overlap | 文本块之间重叠内容 |
5. Embedding向量化
计算机无法直接理解:
如何退款?
需要转换成向量:
[0.213,
0.532,
0.891,
...]
这个过程叫:
Embedding
本项目使用:
nomic-embed-text
代码:
embeddings = OllamaEmbeddings(
model="nomic-embed-text"
)
6. 构建向量数据库
文本:
退款多久到账?
转换:
向量:
[0.23,0.54,0.88]
保存:
Chroma
----------------------
向量 文本
----------------------
xxx 退款规则
xxx 商品规则
xxx 订单规则
----------------------
代码:
Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db"
)
7. 用户问题检索
用户输入:
退货怎么办?
首先:
问题向量化:
退货怎么办?
↓
[0.22,0.55,0.87]
然后 Chroma 搜索相似内容:
返回:
支持七天无理由退货
退货需要提交售后申请
8. Prompt增强
普通模型:
用户:
怎么退款?
RAG:
变成:
你是客服助手。
参考资料:
进入订单选择退款申请。
用户问题:
怎么退款?
请根据资料回答。
这样模型不会依赖自己的记忆,而是参考企业知识库。
9. 模型生成答案
最终交给 Qwen:
参考资料:
进入我的订单,
点击申请退款。
问题:
怎么退款?
生成:
您好,
您可以进入我的订单,
选择对应商品并点击申请退款。
10. 核心代码实现
初始化模型
llm = ChatOllama(
model="qwen2:1.5b",
temperature=0
)
创建检索器
retriever = vectorstore.as_retriever(
search_kwargs={
"k":3
}
)
表示:
每次查询返回最相关的 3 个文本块。
构建 RAG Chain
rag_chain = (
{
"context": retriever | format_docs,
"question": RunnablePassthrough()
}
| prompt
| llm
| StrOutputParser()
)
执行流程:
问题
↓
Retriever
↓
Context
↓
Prompt
↓
LLM
↓
Answer
11. 为什么选择 RAG 而不是训练模型?
很多人认为:
企业知识应该重新训练模型。
实际上大多数场景不需要。
原因:
训练模型:
成本高
周期长
需要GPU
更新困难
RAG:
修改文档
↓
重新生成向量
↓
立即生效
例如:
退款规则改变:
以前:
7天到账
修改:
3天到账
重新建立知识库即可。
12. 当前系统存在的问题
目前版本属于基础 RAG:
还有优化空间。
1. 检索优化
目前:
向量搜索
可以增加:
- BM25关键词搜索
- 混合检索
- rerank模型
2. 对话记忆
当前:
问题1
↓
回答
问题2
↓
不知道问题1
可以加入:
Redis保存历史消息:
conversation_id
message list
实现多轮对话。
3. Agent能力
未来可以让 AI 调用业务接口:
例如:
用户:
查询我的订单
Agent:
调用:
订单服务API
返回:
订单正在运输中
13. 后续架构升级方向
最终企业级 AI 客服:
用户
|
API Gateway
|
AI Agent
|
--------------------
| |
RAG Tools
| |
知识库 订单API
支付API
商品API
|
LLM
能力:
- 知识问答
- 查询订单
- 自动退款
- 商品推荐
- 多轮聊天
14. 总结
本文实现了一个基于:
LangChain
+
Ollama
+
Chroma
+
Qwen
+
Embedding模型
的本地 RAG 智能客服系统。
核心流程:
文档
↓
文本切割
↓
Embedding
↓
向量数据库
↓
语义检索
↓
Prompt增强
↓
LLM生成
RAG 并不是训练一个新的模型,而是通过外部知识增强大模型能力。
未来可以继续扩展:
RAG评估
↓
Agent
↓
Tool Calling
↓
Memory
↓
生产部署
最终构建企业级 AI 应用。
15.RAG.PY(DEMO)
本地需先安装 Ollama
ollama pull qwen2:1.5b # 对话模型(轻量,适合本地)
ollama pull nomic-embed-text # 嵌入模型(用于向量化)
pip install langchain langchain-ollama langchain-community chromadb # python依赖
#rag.py
import os
from langchain_ollama import ChatOllama, OllamaEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_community.document_loaders import TextLoader, DirectoryLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
# ==================== 配置 ====================
# 这里配置文档目录、向量数据库目录、对话模型和嵌入模型
DOCS_DIR = "./docs" # 你的文档目录
CHROMA_DIR = "./chroma_db" # 向量数据库存储路径
MODEL_NAME = "qwen2:1.5b" # 对话模型
EMBED_MODEL = "nomic-embed-text" # 嵌入模型
# ==================== 第一步:加载并分割文档 ====================
def load_and_split_docs(docs_dir):
"""从目录加载所有 .txt 文件并切分为小块。"""
loader = DirectoryLoader(
docs_dir,
glob="**/*.txt",
loader_cls=TextLoader,
loader_kwargs={"encoding": "utf-8"},
)
documents = loader.load()
print(f"📄 加载了 {len(documents)} 个文档")
# 使用递归分割器将文档切成小块,提升检索质量
splitter = RecursiveCharacterTextSplitter(
chunk_size=300, # 每块最多 300 字符
chunk_overlap=100, # 相邻块重叠 100 字符,保证语义连续
separators=["\n\n", "\n", "。", "!", "?", " "],
)
chunks = splitter.split_documents(documents)
print(f"✂️ 分割成 {len(chunks)} 个文本块")
return chunks
# ==================== 第二步:向量化并存入 Chroma ====================
def build_vectorstore(chunks):
"""使用嵌入模型将文本块向量化,并创建 Chroma 向量数据库。"""
embeddings = OllamaEmbeddings(
model=EMBED_MODEL
)
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory=CHROMA_DIR,
)
print("✅ 重新创建向量数据库")
return vectorstore
# ==================== 第三步:构建 RAG 问答链 ====================
def build_rag_chain(vectorstore):
"""构建检索增强生成(RAG)问答链。"""
llm = ChatOllama(
model=MODEL_NAME,
temperature=0
)
retriever = vectorstore.as_retriever(
search_kwargs={
"k": 3
}
)
# 定义对话模板,回答须基于检索到的知识库内容
prompt = ChatPromptTemplate.from_template("""
你是一个电商客服助手。
必须严格根据下面知识库回答。
如果知识库没有答案,回复:
根据现有资料,我无法回答这个问题。
知识库:
{context}
用户问题:
{question}
回答:
""")
# 辅助函数:将检索到的文档拼接为字符串
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
# 使用 LCEL(LangChain 表达式语言)组装链路
# context 从检索器获取后由 format_docs 拼接成字符串,question 直接传入 prompt
rag_chain = (
{
"context": retriever | format_docs,
"question": RunnablePassthrough(),
}
| prompt
| llm
| StrOutputParser()
)
return rag_chain
# ==================== 第四步:运行交互式问答 ====================
if __name__ == "__main__":
# 1. 加载文档
chunks = load_and_split_docs(DOCS_DIR)
# 2. 构建向量库
vectorstore = build_vectorstore(chunks)
# 3. 构建 RAG 链
rag_chain = build_rag_chain(vectorstore)
# 4. 交互式问答
print("\n" + "=" * 50)
print("🤖 RAG 知识库问答系统已启动(输入 exit 退出)")
print("=" * 50)
while True:
query = input("\n你:").strip()
if query.lower() in ["exit", "quit", "q"]:
print("再见!")
break
if not query:
continue
response = rag_chain.invoke(query)
print(f"\nAI:{response}")
示例展示:

更多推荐


所有评论(0)