大模型应用开发(十八)_向量检索
6. Embedding
Embedding(嵌入向量) 是一种把文字、图像、音频等“非数值信息”转换成 可计算的数字向量 的技术。
在自然语言处理中,它最常见的用途是把 句子、段落或单词 转换成一串浮点数列表(通常是几百或几千维的向量),以便计算机能理解和比较它们之间的语义关系。
举个例子
假设有三句话:
- “我喜欢苹果”
- “我爱吃水果”
- “我今天去跑步了”
模型会将它们转换成如下向量(示意):
[0.12, -0.34, 0.56, ...] # 我喜欢苹果
[0.10, -0.32, 0.60, ...] # 我爱吃水果
[-0.45, 0.22, 0.11, ...] # 我今天去跑步了
前两条的向量在空间上“更接近”,说明它们语义相似(都与“吃水果”相关),
而第三条则比较远,语义差别大。
Embedding 常见用途
| 场景 | 说明 |
|---|---|
| 🔎 文本相似度 | 判断两句话或文档的语义是否接近(比如检索问答) |
| 📚 向量数据库 | 将大量文本嵌入向量后存入数据库,用于 RAG 检索增强生成 |
| 🧠 聚类与分类 | 用向量表示样本,再进行聚类或分类 |
| 🗣️ 语义搜索 | 用户输入一句话,系统找出语义最相近的文本 |
| 🤖 推荐系统 | 计算物品或用户的向量相似度进行推荐 |
文本向量化工具代码实例
通过环境变量动态选择 OpenAI 或国产免费 API(如硅基流动),将文本转换为机器可理解的数字向量,以便进行后续的语义搜索或文档匹配。
注:该代码需要去https://cloud.siliconflow.cn/注意账号获取密钥,这次用他的向量模型,免费的。
.env配置如下
DEEPSEEK_API_KEY = "yourkey"
DEEPSEEK_BASE_URL = "<https://api.deepseek.com>"
OPENAI_API_KEY = "yourkey"
OPENAI_BASE_URL = "<https://api.aigc369.com/v1>"
# 向量嵌入用 硅基流动 (免费)
EMBEDDING_API_KEY=yourkey
EMBEDDING_BASE_URL=https://api.siliconflow.cn/v1
EMBEDDING_MODEL=BAAI/bge-m3

具体代码如下
import os
from dotenv import load_dotenv
from langchain_openai import OpenAIEmbeddings
# 1. 加载环境变量
load_dotenv()
# 2. 逻辑开关:True 用 OpenAI 官方,False 用硅基流动(或其他兼容接口)
use_openai = os.getenv("USE_OPENAI", "false").lower() == "true"
if use_openai:
config = {
"api_key": os.getenv("OPENAI_API_KEY"),
"base_url": os.getenv("OPENAI_BASE_URL"), # 默认是 <https://api.openai.com/v1>
"model": "text-embedding-3-large"
}
else:
# 硅基流动配置 (免费使用 BAAI 系列模型)
config = {
"api_key": os.getenv("EMBEDDING_API_KEY"),
"base_url": os.getenv("EMBEDDING_BASE_URL"), # 例如 <https://api.siliconflow.cn/v1>
"model": "BAAI/bge-m3" # 推荐模型,中文支持极好
}
# 3. 初始化嵌入模型
embeddings = OpenAIEmbeddings(
model=config["model"],
openai_api_key=config["api_key"],
openai_api_base=config["base_url"]
)
# 4. 要嵌入的文本
texts = ["狗", "猫", "你好", "你好啊", "你叫什么名字?", "我叫王大锤", "很高兴认识你,大锤"]
# 5. 计算并输出
try:
results = embeddings.embed_documents(texts)
print(f"--- 当前模型: {config['model']} ---")
print(f"向量条数: {len(results)}")
print(f"维度: {len(results[0])}")
print(f"前 5 个数值: {results[0][:5]}")
except Exception as e:
print(f"运行失败,请检查 API Key 或 Base URL。错误信息: {e}")
6.1 检索增强生成系统
代码实例
它先利用向量数据库从本地文档中“捞出”相关线索,再交给 DeepSeek 模型进行逻辑筛选和总结,从而确保即使检索结果存在细微偏差,也能给出精准、不被干扰的最终答案。
注:这里由于向量模型质量不好,采用了模型纠偏,把检索结果给了模型让他来帮我选择。

具体代码
import os
from dotenv import load_dotenv
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.docstore.document import Document
from langchain_community.vectorstores import FAISS
from langchain.prompts import ChatPromptTemplate
# 1. 加载环境变量
load_dotenv()
# --- 配置:向量模型使用硅基流动,对话模型使用 DeepSeek ---
# 向量配置
emb_config = {
"api_key": os.getenv("EMBEDDING_API_KEY"),
"base_url": os.getenv("EMBEDDING_BASE_URL"),
"model": "BAAI/bge-m3"
}
# 对话配置
llm_config = {
"api_key": os.getenv("DEEPSEEK_API_KEY"),
"base_url": os.getenv("DEEPSEEK_BASE_URL"),
"model": "deepseek-chat"
}
# 2. 初始化模型
embeddings = OpenAIEmbeddings(
model=emb_config["model"],
openai_api_key=emb_config["api_key"],
openai_api_base=emb_config["base_url"]
)
llm = ChatOpenAI(
model=llm_config["model"],
openai_api_key=llm_config["api_key"],
openai_api_base=llm_config["base_url"]
)
# 3. 原始文本库
texts = [
"我的个人爱好是去看电影。",
"《带我飞往月球》是我最喜欢的歌曲之一。",
"凯尔特人队是我最喜欢的球队。",
"这是一篇关于波士顿凯尔特人的文件资料。",
"篮球是一项伟大的竞技活动。",
"波士顿凯尔特人队以20分的优势赢得了比赛",
"《艾尔登之环》是过去15年最好的游戏之一。",
"拉里·伯德是一位标志性的NBA球员。"
]
documents = [Document(page_content=text) for text in texts]
db = FAISS.from_documents(documents, embeddings)
# 4. 核心 RAG 流程
def rag_answer(query):
print(f"\\n[用户提问]: {query}")
# 第一步:检索(为了确保搜到,我们取前 3 名)
# BGE 模型建议加上指令前缀
processed_query = f"为这个句子生成表示以用于检索相关文章:{query}"
retrieved_docs = db.similarity_search(processed_query, k=3)
# 打印一下捞上来的鱼
print(f"[检索到的线索]: {[d.page_content for d in retrieved_docs]}")
# 第二步:构造 Prompt 将线索喂给 DeepSeek
context = "\\n".join([f"- {d.page_content}" for d in retrieved_docs])
prompt = ChatPromptTemplate.from_template("""
你是一个严谨的助手。请仅基于以下提供的【参考资料】来回答用户的问题。
如果资料中没有相关信息,请直接回答“不知道”。
【参考资料】:
{context}
【用户问题】:
{question}
""")
# 第三步:LLM 生成答案
chain = prompt | llm
response = chain.invoke({"context": context, "question": query})
print(f"[DeepSeek 回答]: {response.content}")
print("-" * 60)
# 5. 运行测试
if __name__ == "__main__":
rag_answer("关于我的爱好你知道些什么?")
rag_answer("我最喜欢的歌曲是什么?")
6.2 重写问题(Rephrase)
rephrase_prompt = hub.pull("langchain-ai/chat-langchain-rephrase")
是 LangChain Hub 的用法。它的作用是:
从官方或社区的「提示词模板仓库(LangChain Hub)」中下载一个标准 Prompt 模板。
具体解释
LangChain Hub 是一个集中存放常用 Prompt 的平台,类似于“Prompt 代码仓库”。
上面那行代码:
rephrase_prompt = hub.pull("langchain-ai/chat-langchain-rephrase")
等价于做了这件事:
从 langchain-ai 的仓库里拉取一个名为 "chat-langchain-rephrase" 的提示模板,用于 重写用户输入的问题。
1.为什么要重写问题?
在 RAG 对话系统 中,我们希望模型能结合上下文理解用户提问。
举个例子:
用户:RAG之父是谁?
(上文问过“中国大模型RAG之父是谁”)
如果你直接把这个问题送去检索,Retriever 不知道“RAG之父”指谁。
所以我们要用一个 Prompt 让模型改写:
把用户的问题改写成包含上下文的独立问题:
"RAG之父是谁?" ➜ "中国大模型RAG之父是谁?"
这就是 chat-langchain-rephrase Prompt 的功能。
代码实例

具体代码
import os
from dotenv import load_dotenv
from langchain_community.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import FAISS
from langchain import hub
from langchain.chains import create_history_aware_retriever, create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain.prompts import ChatPromptTemplate
# 1. 加载环境变量
load_dotenv()
# --- 配置:DeepSeek 对话 + 硅基流动向量 ---
emb_config = {
"api_key": os.getenv("EMBEDDING_API_KEY"),
"base_url": os.getenv("EMBEDDING_BASE_URL"),
"model": "BAAI/bge-m3"
}
llm_config = {
"api_key": os.getenv("DEEPSEEK_API_KEY"),
"base_url": os.getenv("DEEPSEEK_BASE_URL"),
"model": "deepseek-chat"
}
# 2. 加载与分块
loader = TextLoader(r"D:\\python object\\DeepSeek\\LangChain\\RAG\\demo.txt", encoding='utf-8')
docs = loader.load()
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=40)
texts = text_splitter.split_documents(docs)
print(f"✅ 文档加载完成,共分割 {len(texts)} 段")
# 3. 初始化 Embedding (兼容 OpenAI 格式的硅基流动)
embeddings = OpenAIEmbeddings(
model=emb_config["model"],
openai_api_key=emb_config["api_key"],
openai_api_base=emb_config["base_url"]
)
db = FAISS.from_documents(texts, embeddings)
# 4. 检索器 (k=3 表示检索最相关的3个片段)
retriever = db.as_retriever(search_kwargs={"k": 3})
# 5. 初始化 DeepSeek 作为 LLM
llm = ChatOpenAI(
model=llm_config["model"],
openai_api_key=llm_config["api_key"],
openai_api_base=llm_config["base_url"]
)
# 6. 加载重写问题提示模板 (从历史对话中提炼独立问题)
rephrase_prompt = hub.pull("langchain-ai/chat-langchain-rephrase")
# 7. 创建具备“历史感知”的检索器
# 注意:这里由 DeepSeek 负责将“他毕业于哪?”改写为“XX(人名)毕业于哪?”
history_aware_retriever = create_history_aware_retriever(
llm=llm,
retriever=retriever,
prompt=rephrase_prompt
)
# 8. 回答用 Prompt 模板
qa_prompt = ChatPromptTemplate.from_template("""
你是一个专业的知识问答助手。请基于以下【参考资料】回答用户问题。
如果你在资料中找不到答案,请诚实回答不知道,不要胡乱猜测。
【参考资料】:
{context}
【当前对话历史】:
{chat_history}
【用户问题】:
{input}
""")
# 9. 构建文档处理链
document_chain = create_stuff_documents_chain(llm, qa_prompt)
# 10. 构建最终 RAG 对话链
rag_chain = create_retrieval_chain(history_aware_retriever, document_chain)
# 11. 对话循环测试
chat_history = []
def ask(question, chat_history):
# 调用对话链
response = rag_chain.invoke({
"input": question,
"chat_history": chat_history
})
answer = response.get("answer", "")
context_docs = response.get("context", [])
print(f"\\n🧠 问题:{question}")
print(f"💬 答案:{answer}")
# 输出引用来源
if context_docs:
print("📚 参考片段:")
for i, doc in enumerate(context_docs, 1):
print(f" [{i}] {doc.page_content.strip()[:100]}...")
# 这一步至关重要:手动更新对话历史到列表中
chat_history.append({"role": "user", "content": question})
chat_history.append({"role": "assistant", "content": answer})
if __name__ == "__main__":
ask("中国大模型RAG之父是谁?", chat_history)
ask("他毕业于哪所学校?", chat_history)
6.3 chain_type
在 LangChain 中,chain_type 是构建文档问答(RAG)时的关键参数之一。它决定了 检索到的文档如何与 LLM 结合使用。下面是一张完整的对比表格,帮你清楚理解常见的 chain_type 类型及其差异
1.LangChain 中常见的 chain_type 类型对比表
| chain_type 名称 | 核心逻辑 | 工作方式 | 适用场景 | 优点 | 缺点 | 是否支持多文档 |
|---|---|---|---|---|---|---|
stuff |
直接“塞入”所有检索结果 | 将所有文档拼接成一个大字符串,一次性喂给 LLM | 小规模文档、短上下文 | 简单高效、实现最容易 | 超出上下文窗口容易截断;无法处理长文档 | ✅ 是 |
map_reduce |
分段总结再汇总 | “map”阶段每个文档单独生成回答;“reduce”阶段合并结果 | 长文本、需要汇总总结的任务 | 可处理超长内容,节省上下文 | 结构复杂、速度慢 | ✅ 是 |
refine |
逐步优化答案 | 先用第1篇文档生成初稿,之后每篇文档依次优化答案 | 文档之间互相关联、需要累积推理 | 输出更完整连贯 | 耗时长,对Prompt设计要求高 | ✅ 是 |
map_rerank |
并行生成并评分 | 每个文档单独回答并附带分数,最后选出最高分答案 | 需要最可靠答案、检索结果差异大 | 精确、可控性高 | 计算量大、慢 | ✅ 是 |
self_query |
模型自生成检索条件 | 模型先生成检索查询再检索文档 | 智能RAG(自动选择检索内容) | 语义匹配强 | 依赖模型能力 | ✅ 是 |
contextual_compression |
压缩冗余上下文 | 先用LLM压缩冗余信息,再进入问答链 | 冗长文档或重复内容较多 | 降低token消耗 | 需要额外LLM调用 | ✅ 是 |
retrieval_qa |
通用封装 | 自动将检索器与问答链拼接 | 快速构建RAG原型 | 一行构建、简单易用 | 细节控制少 | ✅ 是 |
2.举个例子:
from langchain.chains import RetrievalQA
qa_chain = RetrievalQA.from_chain_type(
llm=ChatOpenAI(),
retriever=my_retriever,
chain_type="stuff" # 👈 可改为 map_reduce、refine、map_rerank 等
)
| 示例 | 说明 |
|---|---|
stuff |
把检索到的3段文字拼接成一个输入直接问LLM |
map_reduce |
每段文字分别回答后再总结最终答案 |
refine |
先看第1段答一遍,然后依次用后面几段改进答案 |
map_rerank |
每段文字答一遍并评分,取分数最高的那个答案 |
3.推荐选择建议:
| 使用场景 | 推荐 chain_type | 理由 |
|---|---|---|
| 简单问答、短文档 | stuff |
最快、最稳 |
| 文档很长 | map_reduce |
能处理超长文本 |
| 逻辑性强、答案需完善 | refine |
输出连贯、逐步优化 |
| 检索质量参差不齐 | map_rerank |
通过评分自动筛选最优答案 |
6.4 RAGFromPDF
运行代码:
python -m streamlit run "D:\\python object\\DeepSeek\\LangChain\\RAG\\RAGFromPDF\\main.py"

解析PDF项目代码,如有需求请私信。
更多推荐

所有评论(0)