索引

  1. 加载: 首先我们需要加载我们的数据。这是通过 文档加载器 完成的。
  2. 分割文本分割器 将大型 文档 分割成较小的块。这对于索引数据和将其传递给模型都很有用,因为大型块更难搜索,并且无法适应模型的有限上下文窗口。
  3. 存储: 我们需要一个地方来存储和索引我们的分割,以便后续可以进行搜索。这通常使用 向量存储 和 嵌入模型 来完成。

检索与生成

  1. 检索: 根据用户输入,从存储中使用 检索器 检索相关的分割。
  2. 生成聊天模型 / 大型语言模型 使用包含问题和检索数据的提示生成答案。

在检索增强生成(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=300chunk_overlap=50 这两个参数决定“怎么把长文本切成小份”:

  1. chunk_size=300
    每个文本块 最多允许 300 个 token(或字符,取决于长度函数,默认是 token)。
    只要累积到 300 就立即切一刀,保证后续传给模型的单块不会超过上下文限制。

  2. 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

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐