检索增强生成 (RAG)
索引
- 加载: 首先我们需要加载我们的数据。这是通过 文档加载器 完成的。
- 分割: 文本分割器 将大型
文档分割成较小的块。这对于索引数据和将其传递给模型都很有用,因为大型块更难搜索,并且无法适应模型的有限上下文窗口。 - 存储: 我们需要一个地方来存储和索引我们的分割,以便后续可以进行搜索。这通常使用 向量存储 和 嵌入模型 来完成。
检索与生成
在检索增强生成(RAG)流程里,“索引”这一步的核心任务,是把已经切分好的文本块变成“可语义检索”的形式,主要由**向量存储(Vector Store)和嵌入模型(Embedding Model)**共同完成:
# langchain_demo.py
from langchain_community.document_loaders import TextLoader
from langchain_community.embeddings import DashScopeEmbeddings, dashscope
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
import dashscope
from openai import OpenAI
# 1. 加载本地文件
loader = TextLoader("article.txt", encoding="utf-8")
docs = loader.load()
# 2. 切分
# splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=50)
splitter = RecursiveCharacterTextSplitter(
separators=["\n\n"], # 优先双空行,再单空行
chunk_size=10, # 远大于段落长度,保证不再切
chunk_overlap=0 # 不需要重叠
)
chunks = splitter.split_text(docs[0].page_content)
for chunk in chunks:
print("-------chunk-----------")
print(chunk)
# 3. 嵌入 + 向量存储(本地文件夹 ./chroma_db)
embeddings = DashScopeEmbeddings(
model="text-embedding-v4", # 效果最佳,支持维度压缩
dashscope_api_key="sk-3ad75cfac********473b2a" # 也可 export DASHSCOPE_API_KEY=sk-xx
)
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db" # 持久化
)
# 4. 查询
query = "文章里提到了哪些关于盘古的概念?"
retrieved = vectorstore.similarity_search(query, k=3)
for doc in retrieved:
print("---- 相关块 ----")
print(doc.page_content, "\n")
嵌入模型:把文字变成“坐标”
-
作用:将任意文本(文档块或用户查询)映射成一条固定维度的稠密向量(embedding)。语义相近的文本,在向量空间里距离更近。
-
选型举例:
-
免费轻量:BAAI/bge-small-en-v1.5,384 维,512 token,足够一般场景。
-
高性能:OpenAI text-embedding-3-large,3072 维,8191 token,MTEB 排名更高。
-
-
注意:一旦更换模型,必须重新索引,否则查询向量与文档向量不在同一语义空间,检索会失效。
了解Embedding
机器学习 / 自然语言处理(NLP)中的 Embedding
-
定义:Embedding 是将高维离散数据(如单词、句子、用户ID、商品ID等)映射到低维连续向量空间的技术。
-
目的:让机器能“理解”文本、图像或类别数据的语义或相似性。
-
例子:
-
Word Embedding(词嵌入):如 Word2Vec、GloVe、FastText。
-
Sentence Embedding(句嵌入):如 BERT、Sentence-BERT。
-
Item Embedding(商品嵌入):推荐系统中将用户或商品表示为向量。
-
例句:
“BERT 生成的 embedding 能很好地捕捉‘国王’和‘女王’之间的语义关系。”
举例:Word Embedding(词嵌入)
Word Embedding(词嵌入)就是把每个单词变成一串固定长度的数字(一个向量),而且含义越接近的单词,向量在几何上也越接近。
这样做的好处是:计算机本来只能处理数字,现在却能“看懂”单词之间的语义、语法关系。Word Embedding 就是把“单词”变成“向量”,让机器能用数学方法理解语言。
为什么要 Embedding?
传统方法(如 one-hot 编码)把每个词表示成“独热”向量:
| 词 | 向量(维度=词表大小) |
| 猫 | [1, 0, 0, …, 0] |
| 狗 | [0, 1, 0, …, 0] |
| 苹果 | [0, 0, 1, …, 0] |
问题:
-
维度灾难(词表多大,向量就多长)。
-
所有词两两正交,无法体现“猫”和“狗”比“猫”和“苹果”更相似。
Word Embedding 解决思路:把每个词压缩到一个低维实数向量(通常 50~300 维),并且让相似词的向量靠得更近。
怎么得到这些向量?
经典模型/方法:
| 模型 | 核心思想(极简版) |
| Word2Vec | 用上下文预测中心词(Skip-Gram)或用中心词预测上下文(CBOW)。 |
| GloVe | 统计“共现矩阵”+矩阵分解,让全局统计信息与局部窗口信息结合。 |
| FastText | 把单词拆成字符 n-gram,能更好地处理生僻词、拼写变体。 |
训练完成后,每个词对应一个向量。
例(二维投影后):
king ──► [0.2, 0.8]
queen ──► [0.25, 0.75]
man ──► [0.5, 0.5]
woman ──► [0.52, 0.48]
你会发现:
king − man + woman ≈ queen
这就是著名的语义类比任务。
拿到向量后能干嘛?
-
近义词查找:输入“happy”,返回“joyful、cheerful、delighted”。
-
文本分类:用词向量平均或加权后喂给分类器。
-
机器翻译、问答、搜索、推荐……几乎所有 NLP 任务都能受益。
小结一句话
Word Embedding 就是把“单词”变成“低维、稠密、带语义信息的向量”,让机器可以计算“猫”(cat)和“狗”(dog)的距离,比“猫”和“香蕉”(banana)更近。
# 2. index_and_query.py
from sentence_transformers import SentenceTransformer
import chromadb, os, textwrap
# -------------------- 参数 --------------------
FILE_PATH = "article.txt" # 待索引的文本
CHUNK_SIZE = 100 # 每块大约字符数
COLLECTION = "demo"
EMBED_MODEL = "all-MiniLM-L6-v2" # 384 维开源模型,大小 80 MB
# -------------------- 加载 & 切分 --------------------
with open(FILE_PATH, encoding="utf8") as f:
text = f.read()
# chunks = textwrap.wrap(text, CHUNK_SIZE) # 简单等长切分
import re
chunks = re.split(r'\n{2,}', text.strip()) # 空行切分
for a in chunks:
print("------------")
print(a)
# -------------------- 嵌入模型 --------------------
embedder = SentenceTransformer(EMBED_MODEL)
# -------------------- 向量存储 --------------------
client = chromadb.PersistentClient(path="./chroma_db") # 本地文件夹持久化
collection = client.get_or_create_collection(name=COLLECTION)
# 如果集合里已有数据,先清空(演示用)
if collection.count() > 0:
client.delete_collection(COLLECTION)
collection = client.get_or_create_collection(name=COLLECTION)
# 向量化 + 写入
embeddings = embedder.encode(chunks, show_progress_bar=True).tolist()
collection.add(
documents=chunks,
embeddings=embeddings,
ids=[f"id_{i}" for i in range(len(chunks))]
)
# -------------------- 查询 --------------------
query = "文章里哪些地方提到了尼尔斯·玻尔?"
query_emb = embedder.encode([query]).tolist()
print(query_emb)
hits = collection.query(query_embeddings=query_emb, n_results=3)
for doc in hits["documents"][0]:
print("---- 相关块 ----")
print(doc, "\n")
将大文件进行切分为小段(chunk),embedding 模型也有 token 上限,不切块就会“装不下”或“漏掉重点”。
向量模型里的 token 就是“它自己切出来的最小语义单元”,可以是一个字、一个词,也可能是子词(sub-word)甚至单个 Unicode 字节——取决于它当初训练时用的分词器(tokenizer)。为什么要有 token,文本是变长序列,模型只能吃固定维度的数字矩阵。先把字符串 → 离散编号序列(token ids)→ 再嵌入成连续向量,才能送进神经网络。
embedding 模型 = 把“高维、离散”的文字 / 图像 / 声音 → 压缩成“低维、连续”向量的函数;
核心目标:让“语义相似”的东西在向量空间里靠得近,“语义不同”离得远。
常见开源 embedding 模型
| 名称 | 维数 | 语言 | 特点 |
| all-MiniLM-L6-v2 | 384 | 英 | 轻量,80 MB,通用基线 |
| paraphrase-multilingual-MiniLM-L12-v2 | 384 | 多 | 支持中文 |
| bge-base-zh-v1.5 | 768 | 中 | 中文 SOTA,需指令前缀 |
| text2vec-base-chinese | 768 | 中 | 早期中文明星 |
| Alibaba text-embedding-v4 | 1024(可调) | 多 | 商业、维度可压缩 |
embedding 模型用法
维度是384,384 个浮点数

把两段文字分别变成 384 维向量后,算余弦相似度即可得到 0~1 的语义接近程度。
在 LangChain 的 RecursiveCharacterTextSplitter 里,chunk_size=300 和 chunk_overlap=50 这两个参数决定“怎么把长文本切成小份”:
-
chunk_size=300
每个文本块 最多允许 300 个 token(或字符,取决于长度函数,默认是 token)。
只要累积到 300 就立即切一刀,保证后续传给模型的单块不会超过上下文限制。 -
chunk_overlap=50
相邻两块之间 保留 50 个 token 的重复内容。
作用类似“滑动窗口”,防止因硬切导致句子/语义被截断,检索时丢掉关键信息。
形象比喻:
把一条长绳子剪成小段,每段最长 30 cm,但每刀都往回多留 5 cm 再下剪,这样段与段之间就有 5 cm 重叠,不会突然断在关键位置。
举例
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
# 1. 任选轻量级模型
model = SentenceTransformer("all-MiniLM-L6-v2")
# 2. 待比较文本
sentences = [
"我喜歡自然語言處理",
"我愛 NLP",
"今天天氣真棒"
]
# 3. 一次编码 → 3 条 384 维向量
embs = model.encode(sentences)
# 4. 两两算余弦相似度
sim = cosine_similarity(embs)
# 5. 打印结果
import pandas as pd
df = pd.DataFrame(sim, index=sentences, columns=sentences)
print(df.round(3))
我喜歡自然語言處理 我愛 NLP 今天天氣真棒
我喜歡自然語言處理 1.000 0.517 0.379
我愛 NLP 0.517 1.000 0.269
今天天氣真棒 0.379 0.269 1.000
更多推荐



所有评论(0)