基于 LangChain + ChromaDB + 阿里云 DashScope 的检索增强生成(RAG)智能客服系统,面向服装/时尚零售场景,提供知识库管理、多轮对话问答和流式输出的完整解决方案。
本系统将服装领域的知识文档(尺码推荐、洗涤养护、颜色选择)向量化存入 ChromaDB,当用户提问时,通过语义检索召回最相关的文档片段,结合对话历史,由 Qwen3-Max 大模型生成基于参考资料的精准回复。
核心特性:
- 文档摄取:支持上传
.txt知识文档,自动分块、向量化、去重存储 - 语义检索:基于 ChromaDB + HNSW 索引的高效向量相似度搜索
- 上下文生成:将检索结果作为参考上下文注入 LLM,生成高质量的领域回答
- 多轮对话:基于文件的会话历史持久化,支持上下文连续的多轮问答
- 流式输出:前端实时流式展示 LLM 生成的回答,提升交互体验
- 两套应用:独立的知识库管理端 + 用户问答端,职责清晰
┌──────────────────────────────────────────────────────────────────────┐
│ RAGProject │
│ │
│ ┌──────────────────────────┐ ┌──────────────────────────────┐ │
│ │ app_file_uploader.py │ │ app_QA.py │ │
│ │ (Streamlit · 知识库管理) │ │ (Streamlit · 用户问答) │ │
│ │ │ │ │ │
│ │ 管理员上传 TXT 文档 ────►│ │ 用户输入问题 ────────────────►│ │
│ └───────────┬──────────────┘ └──────────────┬────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────────┐ ┌───────────────────────────────────┐ │
│ │ knowleage_base.py │ │ rag.py │ │
│ │ KnowledgeBaseService│ │ RagService │ │
│ │ │ │ │ │
│ │ 1. MD5 去重校验 │ │ 1. 构建 LCEL 链: │ │
│ │ 2. 递归文本分割 │ │ input → retrieve → prompt │ │
│ │ 3. 文本向量化 │ │ → ChatTongyi → output │ │
│ │ 4. 存入 ChromaDB │ │ 2. 包装消息历史 Runnable │ │
│ └───────────┬──────────┘ └──────────────┬────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────────┐ ┌───────────────────────────────────┐ │
│ │ vector_stores.py │ │ file_history_store.py │ │
│ │ Vector_StoreService │ │ FileChatMessageHistory │ │
│ │ │ │ │ │
│ │ ChromaDB 封装层 │ │ 按 session_id 读写 JSON 会话文件 │ │
│ │ · get_retriever() │ │ │ │
│ └───────────┬──────────┘ └──────────────┬────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────────┐ ┌───────────────────────────────────┐ │
│ │ chroma_db/ │ │ chat_history/ │ │
│ │ (ChromaDB 持久化) │ │ (会话历史 JSON 文件) │ │
│ │ · chroma.sqlite3 │ │ · user_001 │ │
│ │ · HNSW 索引文件 │ │ │ │
│ └──────────────────────┘ └───────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ config_data.py │ │
│ │ 所有可调参数集中管理: 分块大小、模型名称、路径、阈值等 │ │
│ └───────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
| 层级 | 技术 | 说明 |
|---|---|---|
| LLM | 阿里云 Tongyi Qwen3-Max | 基于参考资料生成专业回复 |
| Embedding | 阿里云 DashScope text-embedding-v4 | 文本向量化 |
| 向量数据库 | ChromaDB (HNSW 索引) | 文档向量存储与语义检索 |
| RAG 框架 | LangChain (LCEL) | 链式编排:检索 → 提示词 → 生成 |
| 前端 | Streamlit | 两个独立 Web 应用 |
| 会话持久化 | 文件系统 JSON | 按 session_id 存储对话历史 |
RAGProject/
│
├── config_data.py # 全局配置中心(路径、模型、阈值等参数)
├── rag.py # RAG 核心服务:构建 LCEL 链,编排检索与生成
├── vector_stores.py # ChromaDB 向量存储封装,提供 retriever
├── knowleage_base.py # 知识库服务:文档摄取、分块、去重、入库
├── file_history_store.py # 文件驱动的 LangChain 对话历史存储
│
├── app_QA.py # Streamlit 应用:用户智能问答界面
├── app_file_uploader.py # Streamlit 应用:知识库文档上传界面
│
├── data/ # 原始知识库文档
│ ├── 尺码推荐.txt # 身高体重 → 尺码对照表
│ ├── 洗涤养护.txt # 面料洗护保养指南(按季节/材质)
│ └── 颜色选择.txt # 肤色/场合/体型颜色搭配建议
│
├── chroma_db/ # ChromaDB 持久化向量存储(自动生成)
│ ├── chroma.sqlite3 # 向量元数据数据库
│ └── <uuid>/ # 向量集合数据目录
│ ├── data_level0.bin # 嵌入向量二进制数据
│ ├── header.bin # 索引头信息
│ ├── length.bin # 向量长度元数据
│ └── link_lists.bin # HNSW 图链接表
│
├── chat_history/ # 对话历史持久化(自动生成)
│ └── user_001 # session_id 对应的 JSON 消息文件
│
└── md5.txt # 已入库文档 MD5 记录(去重用)
用户上传 .txt 文件
│
▼
app_file_uploader.py ─── 读取文件内容,解码为 UTF-8 字符串
│
▼
KnowledgeBaseService.upload_by_str()
│
├── [1] 计算文本 MD5 → 与 md5.txt 比对去重
│ └── 已存在 → 返回 "[跳过]内容已经存在知识库里"
│
├── [2] 文本长度 > max_split_char_number (1000)?
│ ├── 是 → RecursiveCharacterTextSplitter 分割
│ │ chunk_size=1000, chunk_overlap=100
│ └── 否 → 保持原文不分割
│
├── [3] 为每个 chunk 附加元数据
│ {source, create_time, operator}
│
├── [4] Chroma.add_texts() → DashScope Embedding → 写入 chroma_db/
│
└── [5] 记录 MD5 到 md5.txt → 返回成功信息
用户输入问题
│
▼
RagService chain.invoke() / chain.stream()
│
├── [1] RunnablePassthrough ─── 保持原始 input 传递
│
├── [2] 并行分支: format_for_retriever(input)
│ │
│ ├── Vector_StoreService.get_retriever()
│ │ └── ChromaDB.similarity_search(query, k=2)
│ │ └── 返回 Top-2 最相关文档片段
│ │
│ └── format_document(docs)
│ └── 将文档片段拼接为上下文字符串
│
├── [3] format_for_prompt_template()
│ └── 组装 {input, context, history} 三要素
│
├── [4] ChatPromptTemplate
│ ├── System: "以我提供的已知参考资料为主,简洁和专业的回答..."
│ │ context: {检索到的文档片段}
│ ├── System: "并且我提供用户的对话历史记录如下:"
│ ├── Placeholder: {history} ← 从 FileChatMessageHistory 加载
│ └── Human: "请回答用户的提问:{input}"
│
├── [5] print_prompt() ─── 控制台输出完整提示词(调试用)
│
├── [6] ChatTongyi (qwen3-max) ─── 调用阿里云 DashScope 生成回复
│
└── [7] StrOutputParser ─── 提取纯文本输出
│
├── stream() 模式 → 逐 token 推送至 Streamlit 前端
└── FileChatMessageHistory.add_messages() → 持久化本轮对话
所有可调参数集中管理,避免硬编码分散:
| 参数 | 默认值 | 说明 |
|---|---|---|
md5_path |
"./md5.txt" |
已入库文档 MD5 记录文件路径 |
collection_name |
"rag" |
ChromaDB 集合名称 |
persist_directory |
"./chroma_db" |
ChromaDB 持久化目录 |
chunk_size |
1000 |
文本分块最大字符数 |
chunk_overlap |
100 |
相邻分块重叠字符数 |
separators |
[".","\\n\\n","\\n",".","。","?","?","!","!"," ",""] |
递归分割优先级分隔符 |
max_split_char_number |
1000 |
低于此长度的文本不分割 |
similarity_threshold |
2 |
检索返回 Top-K 文档数 |
embedding_model_name |
"text-embedding-v4" |
DashScope 嵌入模型 |
chat_model_name |
"qwen3-max" |
Tongyi 对话模型 |
session_config |
{"session_id": "user_001"} |
默认会话配置 |
封装 ChromaDB,对外提供统一的检索接口:
class Vector_StoreService:
def __init__(self, embedding):
# 使用 config 中的 collection_name 和 persist_directory
# 创建 Chroma 向量存储实例
def get_retriever(self):
# 返回 LangChain Retriever,k = config.similarity_threshold负责文档摄入的完整生命周期管理:
get_string_md5(input_str):计算字符串 MD5 摘要check_md5(md5_str)/save_md5(md5_str):去重判断与记录KnowledgeBaseService:upload_by_str(data, file_name):核心摄入方法,串联去重→分块→向量化→存储全流程
系统的核心编排模块,使用 LangChain LCEL 声明式构建处理链:
RunnableParallel({
input: RunnablePassthrough(),
context: Retriever 链
})
→ Prompt 格式化
→ ChatPromptTemplate
→ ChatTongyi (qwen3-max)
→ StrOutputParser
通过 RunnableWithMessageHistory 包装,自动从 FileChatMessageHistory 加载/保存对话历史。
实现 LangChain 的 BaseChatMessageHistory 接口,将每个 session 的对话历史序列化为 JSON 文件:
class FileChatMessageHistory(BaseChatMessageHistory):
# 文件路径: ./chat_history/{session_id}
def add_messages(self, messages):
# 追加消息并序列化为 JSON
@property
def messages(self):
# 读取 JSON 并反序列化为 BaseMessage 列表基于 Streamlit 的聊天式 UI:
- 初始化
RagService并缓存于st.session_state - 展示对话历史(含欢迎消息)
- 用户输入后通过
chain.stream()流式输出 AI 回复 - 自动持久化每轮对话
基于 Streamlit 的文档上传界面:
- 支持上传
.txt文件 - 显示文件名、MIME 类型、文件大小
- 调用
KnowledgeBaseService.upload_by_str()完成入库 - 展示处理结果(成功 / 已跳过)
- Python ≥ 3.10
- 阿里云 DashScope API Key(需开通 DashScope 服务)
本项目作为 lc-course 的子模块,依赖由父级 pyproject.toml 通过 uv 管理:
# 在项目根目录 (lc-course/)
pip install uv # 如未安装 uv
uv sync或手动安装核心依赖:
pip install langchain langchain-chroma langchain-community langchain-text-splitters
pip install chromadb dashscope streamlit python-dotenv在项目根目录 lc-course/ 下创建 .env 文件:
DASHSCOPE_API_KEY=your_dashscope_api_key_here在阿里云 DashScope 控制台获取 API Key。
启动知识库管理应用:
streamlit run app_file_uploader.py在浏览器中上传 data/ 目录下的 3 个知识文档(或自定义 .txt 文件):
尺码推荐.txt洗涤养护.txt颜色选择.txt
streamlit run app_QA.py在聊天框中输入问题(如 "我身高170cm,体重70kg,应该选什么尺码?"),系统将基于知识库返回专业回复。
所有可调参数位于 config_data.py,可根据业务需求调整:
| 调优场景 | 建议修改 |
|---|---|
| 提高检索精度 | 降低 similarity_threshold(返回更少但更相关的文档) |
| 扩大检索范围 | 提高 similarity_threshold(返回更多参考文档) |
| 调整分块粒度 | 修改 chunk_size 和 chunk_overlap |
| 切换模型 | 修改 embedding_model_name / chat_model_name 为其他 DashScope 支持的模型 |
| 多用户隔离 | 为不同用户设置不同的 session_config["session_id"] |
| 添加自定义分隔符 | 在 separators 列表中添加适合业务文本的分隔符 |
可以单独运行各模块进行调试:
# 测试向量检索
python vector_stores.py
# 测试知识库服务
python knowleage_base.py
# 测试 RAG 链(注意:需要先确保知识库有数据)
python rag.py- 准备业务相关的
.txt文档 - 通过
app_file_uploader.py上传,或直接将文件放入data/目录后调用KnowledgeBaseService.upload_by_str() - 系统自动完成分块、向量化和去重,无需重启问答服务
| 依赖 | 版本 | 用途 |
|---|---|---|
langchain |
≥1.2.18 | RAG 链编排框架 |
langchain-chroma |
≥1.1.0 | ChromaDB 向量存储集成 |
langchain-community |
≥0.4.1 | DashScope Embeddings、ChatTongyi 集成 |
langchain-text-splitters |
≥1.1.2 | RecursiveCharacterTextSplitter 文本分割 |
chromadb |
≥1.5.9 | 向量数据库(HNSW 索引) |
dashscope |
≥1.25.18 | 阿里云 DashScope SDK(嵌入 + 对话模型) |
streamlit |
≥1.57.0 | Web UI 框架 |
python-dotenv |
≥1.2.2 | 环境变量管理 |
- 两应用分离:
app_file_uploader.py(管理端)和app_QA.py(用户端)是两个独立的 Streamlit 应用,可分别部署和授权。 - 配置统一:所有参数集中在
config_data.py,各模块通过 import 引用,避免配置分散。 - 去重机制:知识库摄入时通过 MD5 摘要去重,防止同一文档重复入库。
- LLM 接地:系统提示词明确要求"以我提供的已知参考资料为主",确保回复不会偏离知识库内容。
- 流式交互:使用 LangChain 的
.stream()方法 + Streamlit 的write_stream()实现打字机效果的实时输出。