文章目录
-
- 1.项目目的
- 2.整体流程
- [3.Embedding 模型选择详解](#3.Embedding 模型选择详解)
-
- [3.1.什么是 Embedding 模型?](#3.1.什么是 Embedding 模型?)
- 3.2.为什么选这个模型?
- [3.3.其他常见的中文 Embedding 模型](#3.3.其他常见的中文 Embedding 模型)
- 3.4.选型的一般原则
- [4.LLM 模型选择](#4.LLM 模型选择)
- 5.向量数据库选择
- 6.区分一些概念
- 7.效果图
- 8.完整代码
1.项目目的
RAG(Retrieval-Augmented Generation,检索增强生成) 系统。核心思路是:不直接把问题丢给 AI,而是先从本地文档中检索出相关内容,再把资料+问题一起发给 AI,让 AI 基于真实资料回答,避免瞎编。
2.整体流程
读取 docs.txt → 按句号切分成句子片段 → SentenceTransformer 将每个句子转成向量(数字列表) → 向量存入 ChromaDB(向量数据库) → 用户提问 → 同样转成向量 → ChromaDB 用向量相似度搜索,找回最相关的 k 个句子 → 把找到的句子 + 用户问题 拼成提示词 → 发给 Kimi AI → 返回回答
3.Embedding 模型选择详解
这部分在代码第 49 行:
embed_model = SentenceTransformer("shibing624/text2vec-base-chinese")
3.1.什么是 Embedding 模型?
把"文字的意思"压缩成一组数字(向量)。意思相近的文字,它们在向量空间里的距离也近:
"今天天气很好" → [0.12, 0.34, 0.56, ...] "今天天气不错" → [0.13, 0.33, 0.55, ...] ← 距离近 "苹果多少钱" → [0.71, 0.22, 0.88, ...] ← 距离远
3.2.为什么选这个模型?
代码注释里提到了另一个备选:
# embed_model = SentenceTransformer("BAAI/bge-small-zh")
两个都是中文 Embedding 模型,对比:
| 模型 | 参数 | 向量维度 | 特点 |
|---|---|---|---|
shibing624/text2vec-base-chinese |
~110M | 768 | 中文语义匹配好,入门首选 |
BAAI/bge-small-zh |
~33M | 512 | 更小更快,但精度略低 |
项目最终用了 text2vec-base-chinese,它在中文句子相似度上表现更好,但代价是向量维度更高(768 vs 512),占用更多存储空间,检索也稍慢一点。
3.3.其他常见的中文 Embedding 模型
| 模型 | 维度 | 适用场景 | 说明 |
|---|---|---|---|
shibing624/text2vec-base-chinese |
768 | 通用中文 | 平衡了效果和速度,本项目选用 |
BAAI/bge-large-zh |
1024 | 高精度 | 维度高,效果最好但也最慢 |
BAAI/bge-small-zh |
512 | 快速检索 | 轻量,适合大规模文档 |
moka-ai/m3e-base |
768 | 通用中文 | 另一个热门的国产模型 |
tencent-ailab/embedding-en |
768 | 英文专用 | 腾讯出品,只适合英文 |
3.4.选型的一般原则
- 中文用中文专用模型(不能用 GPT 的 text-embedding-ada-002 吗?可以,但要调 API 花钱)
- 维度越高精度越好,但检索越慢、占空间越大
- 模型大小 vs 速度的权衡 :
bge-small最快,text2vec-base中等,bge-large最慢 - 本地 vs API 模型:本项目的模型跑在本地(免费),也可以用 OpenAI/Kimi 的在线 Embedding API(付费但省部署)
4.LLM 模型选择
本项目选的是 Kimi(moonshot-v1-8k):
model="moonshot-v1-8k"
8k 指上下文窗口 8000 token(约 6000 个汉字)。Kimi 有更大的 moonshot-v1-32k(32000 token)。对 RAG 来说 8k 够用,因为资料已经由检索筛选过了,不需要把整篇文档塞进去。
如果想换别的 LLM,只需改 api_key、base_url 和 model 名称,代码结构不变。支持的选项:
| LLM | base_url | model 名 |
|---|---|---|
| Kimi | https://api.moonshot.cn/v1 |
moonshot-v1-8k |
| OpenAI | https://api.openai.com/v1 |
gpt-4o-mini |
| DeepSeek | https://api.deepseek.com |
deepseek-chat |
| 通义千问 | https://dashscope.aliyuncs.com/compatible-mode/v1 |
qwen-plus |
5.向量数据库选择
本项目用 ChromaDB (本地持久化 PersistentClient)。
常见向量数据库对比:
| 数据库 | 部署方式 | 适用场景 |
|---|---|---|
| ChromaDB | 嵌入式/本地 | 原型、小项目、个人使用(本项目选的) |
| FAISS | Python 库 | 高性能、大规模向量搜索 |
| Milvus | 服务端 | 生产级、分布式 |
| Qdrant | 服务端 | 生产级、Rust 实现 |
| PGVector | PostgreSQL 插件 | 已有 Postgres 的项目 |
ChromaDB 的特点是零配置 ,PersistentClient(path="./chroma_db") 一句代码就创建好,数据存在本地文件夹,适合学习和原型开发。
6.区分一些概念
一句话区分:
| 概念 | 一句话 |
|---|---|
| HuggingFace | 模型商店,下载模型的地方 |
| SentenceTransformer | 加载 Embedding 模型的 Python 工具包 |
| Embedding 模型 | 把文字转成向量的模型,负责"理解语义" |
| LLM 模型 | 读懂资料并生成回答的模型,负责"写作文" |
| 向量数据库 | 存向量并支持相似度搜索的数据库,负责"快速查找" |
流程就是:Embedding 模型把文档变向量 → 向量数据库存起来 → 提问时转向量去搜 → LLM 根据搜到的结果写回答。HuggingFace 只是下载模型的地方,SentenceTransformer 是加载模型的工具。
7.效果图
外部资料docx.txt为
python
Python是由Guido van Rossum于1991年首次发布的一种高级编程语言。Python的设计哲学强调代码的可读性和简洁性,使用缩进来表示代码块。Python是一种解释型语言,这意味着代码可以在不编译的情况下直接运行。Python支持多种编程范式,包括面向对象编程、函数式编程和过程式编程。
Python的标准库非常丰富,被称为"内置电池"(Batteries Included),涵盖了文件操作、网络通信、数据处理等常见功能。Python还拥有庞大的第三方库生态系统,通过pip包管理器可以轻松安装。常用的第三方库包括用于数值计算的NumPy、数据处理的Pandas、机器学习的Scikit-learn和深度学习的PyTorch。
Python的应用领域非常广泛。在Web开发方面,有Django和Flask等流行框架。在数据科学和人工智能领域,Python已经成为最主流的语言。在自动化运维和脚本编写方面,Python也因其简洁易用而广受欢迎。此外,Python在科学计算、金融分析和教育领域也有大量应用。
Python在数据科学领域的地位尤其突出。Pandas库提供了DataFrame数据结构,使得数据处理变得简单高效。Matplotlib和Seaborn等库可以生成高质量的统计图表。Scikit-learn提供了统一的机器学习API,让分类、回归、聚类等任务变得标准化。这些工具的组合使Python成为数据科学家的首选语言。
Python的版本演进也非常活跃。Python 2于2000年发布,2020年正式停止维护。Python 3于2008年发布,是目前主要使用的版本。最新的Python 3.12引入了许多新特性,包括更清晰的错误信息、性能优化和新的语言特性。Python社区采用PEP(Python Enhancement Proposal)流程来讨论和决定语言的变化。
效果图如下

由于实现代码中没有加入上下文,因此每次对话都是独立的,没有对话记忆,如下图。如果需要对话记忆功能,需要把对话历史也发给向量数据库。

8.完整代码
python
import chromadb
import os
# HuggingFace 镜像(国内网络加速)
os.environ["HF_ENDPOINT"] = "https://hf-mirror.com"
from sentence_transformers import SentenceTransformer
from openai import OpenAI
from chromadb import Collection as ChromaCollection
# =========================
# 1. KIMI API 配置
# =========================
KIMI_API_KEY = "sk-xxxxxxxxxxxx"
KIMI_BASE_URL = "https://api.moonshot.cn/v1" # 例如 https://api.moonshot.cn/v1
# client = OpenAI(
# api_key=os.environ.get("OPENAI_API_KEY"),
# base_url=os.environ.get("OPENAI_BASE_URL"),
# )
client = OpenAI(
api_key=KIMI_API_KEY,
base_url=KIMI_BASE_URL
)
# =========================
# 2. 读取文档
# =========================
BASE_DIR = os.path.dirname(__file__) # 获取当前脚本所在目录,包含rag.py 和 docs.txt
DOC_PATH = os.path.join(BASE_DIR, "docs.txt")
with open(DOC_PATH, "r", encoding="utf-8") as f:
text = f.read()
# 文本切分:split() 按句号切分,并去除空白;strip() 去除前后空白;if c.strip() 过滤掉空字符串
chunks = [c.strip() for c in text.split("。") if c.strip()]
# =========================
# 3. Embedding模型(本地,在 main 中延迟加载)
# =========================
embed_model = None
def embed(texts): # 输入是一个文本列表List<Str>,输出是一个向量列表List<List<Float>>
global embed_model
if embed_model is None:
print("正在加载 Embedding 模型...")
embed_model = SentenceTransformer("shibing624/text2vec-base-chinese")
return embed_model.encode(texts).tolist() # encode() 返回 numpy 数组,tolist() 转换为 Python 列表,方便存储到 ChromaDB 中
# =========================
# 4. 初始化Chroma
# =========================
client_db = None
collection: ChromaCollection | None = None
def init_chroma():
global client_db, collection
print("正在初始化 ChromaDB...")
client_db = chromadb.PersistentClient(path="./chroma_db")
collection = client_db.get_or_create_collection(
name="kimi_rag"
)
# =========================
# 5. 初始化向量库(只执行一次)
# =========================
def init_db():
if collection.count() > 0:
return
embeddings = embed(chunks)
# chunks 和 embeddings 的长度应该一致,每个 chunk 对应一个 embedding 向量
for i, chunk in enumerate(chunks):
collection.add(
ids=[str(i)], # ChromaDB 需要每个文档一个唯一ID,这里用字符串形式的索引作为ID
documents=[chunk],
embeddings=[embeddings[i]]
)
# =========================
# 6. 检索
# =========================
def retrieve(q, k=3):
q_vec = embed([q])[0] # embed() 返回一个列表,里面是一个向量,所以取 [0] 获取这个向量
# 进行向量检索,返回最相似的 k 个文档
results = collection.query(
query_embeddings=[q_vec],#支持批量查询[q_vec1, q_vec2, ...]
n_results=k
) # query() 返回一个字典,包含 "ids", "documents", "embeddings" 等字段,这里我们只需要 "documents"
return results["documents"][0] # results["documents"] 是一个列表,里面是一个列表(因为我们只查询了一个向量),所以取 [0] 获取这个文档列表,即最相似的 k 个文档
# =========================
# 7. 调用Kimi LLM
# =========================
def ask_llm(prompt):
response = client.chat.completions.create(
model="moonshot-v1-8k", # Kimi模型
messages=[
{"role": "user", "content": prompt}
],
temperature=0.3 # 控制生成文本的随机程度,0.3 表示较为保守,适合需要准确回答的场景
)
return response.choices[0].message.content
# =========================
# 8. RAG主流程
# =========================
def ask(query):
context = retrieve(query)
prompt = f"""
你是一个企业知识助手,请严格根据资料回答问题,不允许编造。
【资料】
{chr(10).join(context)}
【问题】
{query}
"""
return ask_llm(prompt)
# =========================
# 9. 主程序
# =========================
if __name__ == "__main__":
# 初始化 ChromaDB(必须在 init_db 之前)
init_chroma()
print("开始初始化数据库")
init_db()
print("数据库初始化完成")
print("RAG系统已启动(Chroma + Kimi)")
while True:
q = input("\n请输入问题:")
print("\n回答:\n", ask(q))