【摘要】本文用 DeepSeek + LangChain + Chroma 从零搭建一个能回答"自己公司/团队内部资料"的智能问答机器人。核心就三步:文档切片 → 向量化 → 检索生成。附带完整可运行代码和中文分块的 5 个避坑要点。


一、为什么要用 RAG?

很多刚接触的同学会问:大模型这么强,为什么还要搞 RAG(Retrieval-Augmented Generation,检索增强生成)?

因为大模型有两个硬伤:

  1. 知识陈旧:训练数据有截止时间,公司上个月新改的制度它根本不知道。
  2. 容易幻觉:遇到不懂的内容会一本正经地"编"。

RAG 的思路很朴素:先把你自己的资料切块存进向量库,提问时先检索最相关的片段,再连同问题一起喂给大模型,让它"照着资料回答"。资料是真实的,答案自然靠谱,幻觉也少很多。

这也是 2025 年 RAG 相关内容同比增长 300%、成为最大技术黑马的原因。


二、整体架构

原始文档 (MD/TXT/PDF)
   │ ① 切块 (Split)
   ▼
文本片段 (Chunks)
   │ ② Embedding 向量化
   ▼
向量数据库 (Chroma)  ◄───────┐
                              │ ③ 相似度检索(提问时)
用户提问 ────────────────────┘
   │ ④ 组装 Prompt
   ▼
DeepSeek 大模型 ──► 基于资料的答案

三、环境准备

pip install langchain langchain-community chromadb sentence-transformers openai

DeepSeek 提供 OpenAI 兼容接口,直接用 openai 库即可,无需额外安装专用包。


四、完整源码

1. 接入 DeepSeek

import os
from langchain_openai import ChatOpenAI

os.environ["DEEPSEEK_API_KEY"] = "sk-你的key"   # 在 https://platform.deepseek.com 申请

llm = ChatOpenAI(
    model="deepseek-chat",
    api_key=os.environ["DEEPSEEK_API_KEY"],
    base_url="https://api.deepseek.com/v1",
    temperature=0.1,   # 问答场景温度调低,避免自由发挥
)

2. 加载文档并切块

from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 加载 ./docs 目录下所有 md 文档
loader = DirectoryLoader("./docs", glob="*.md", loader_cls=TextLoader)
documents = loader.load()

# 切块:中文建议 chunk_size 略大、overlap 略高
splitter = RecursiveCharacterTextSplitter(
    chunk_size=512,        # 每块约 512 字
    chunk_overlap=80,      # 相邻块重叠 80 字,防止关键信息被切裂
    separators=["\n\n", "\n", "。", "!", "?", ";", ",", " ", ""],  # 按中文标点优先切
)
chunks = splitter.split_documents(documents)
print(f"共切出 {len(chunks)} 个文本块")

3. 向量化并存入 Chroma

from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import Chroma

# 本地中文 embedding 模型,免费、无需联网
embeddings = HuggingFaceEmbeddings(model_name="shibing624/text2vec-base-chinese")

vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    persist_directory="./chroma_db",
)

4. 组装检索问答链

from langchain.chains import RetrievalQA

retriever = vectorstore.as_retriever(search_kwargs={"k": 4})  # 取最相关的 4 块

qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    retriever=retriever,
    return_source_documents=True,   # 返回来源,便于核对
)

question = "报销流程是什么?"
result = qa_chain.invoke({"query": question})

print("回答:", result["result"])
print("\n--- 依据的来源片段 ---")
for i, doc in enumerate(result["source_documents"][:3]):
    print(f"[{i+1}] {doc.page_content[:120]}...")

运行效果示例:

回答: 按公司制度,报销需填写报销单并附发票,直属主管审批后,财务在
      3 个工作日内复核打款;单笔超过 5000 元需 CTO 二次审批。
--- 依据的来源片段 ---
[1] 报销管理制度:……5000 元以上需 CTO 审批……
[2] 财务指引:……报销单需在当月月底前提交……

五、中文 RAG 最常见的 5 个坑

# 现象 解法
1 分块漏信息 一句话被切成两半,检索不到 chunk_overlap 调到 60~100 字
2 按英文标点切 中文长句被粗暴切断 separators 加上 。!?;,
3 默认 embedding 不适配中文 检索结果文不对题 text2vec / bge 等中文模型
4 温度太高 答案开始"自由发挥" 问答场景 temperature=0.1 以下
5 不返回来源 出错时无法排查 打开 return_source_documents=True

六、进阶方向

  • 多轮对话:换 ConversationalRetrievalChain 保留历史
  • PDF 文档:加载器换 PyMuPDFLoader
  • 数据量大:把 Chroma 换成 Faiss / Milvus
  • 对外服务:用 FastAPI 包一层 HTTP 接口

结语

RAG 不复杂,核心就是"切好块 + 中文向量化 + 检索生成"三步。难点不在大模型,而在中文分块和检索质量。把上面 5 个坑避开,你就能交付一个真正能用的内部问答机器人。

如果你在搭建过程中遇到问题,欢迎在评论区留言交流。


本文代码在 Python 3.11 + LangChain 0.2 环境验证。API Key 请使用自己的,切勿泄露到公开仓库。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐