从 RAG 公式与工作流讲到向量/嵌入与余弦相似度,覆盖 LangChain Models、Prompt、Chain、Memory、文档分割与 Chroma 检索,并给出完整 RAG 开发步骤总结。
本文承接「提示词工程」笔记,面向要在业务里用 外部知识库 给大模型「补课」、减少幻觉并接入 私有/实时数据 的读者。内容包含 RAG 定义与标准流程 、向量与嵌入 、LangChain 组件(Models / Prompt / Chain / Memory / Loader / 向量库) 及 完整 RAG 开发步骤 。读完后能够复述离线/在线两条链路,并理解链式调用
prompt | model | parser与向量检索在整体中的位置。
⚡ 快速参考
- 适用场景:领域知识不在预训练权重里、需要基于文档回答、要降低幻觉并控制数据边界(内网文档、手册、工单等)。
- 核心结论 :RAG = 检索技术 + LLM 提示 ;标准路径为 Indexing(切分→嵌入→入库)→ Retrieval(query 转向量→Top-K)→ Generation(上下文+问题→生成) ;LangChain 负责 组件编排(不造模型)。
- 最短步骤 :准备文档 → 加载与 chunk 分割 → Embedding 写入向量库 → 用户提问 相似度检索 → Prompt 融合检索片段 → Chat/LLM 生成 →(可选)流式与 会话记忆。
- 常用命令/代码 :环境安装与各组件示例见 第三节 ;向量检索典型调用:
vector_store.similarity_search("问题", k=4)(笔记原文)。 - 避坑提醒 :嵌入 维度越高语义越细但开销越大 ;chunk_size / overlap 直接影响召回与上下文长度;临时记忆 重启即丢,持久化需自行按 session 落盘。
📚 学习目标
- 掌握 RAG 离线/在线流程 与 Indexing---Retrieval---Generation 三阶段对应关系。
- 能独立完成 DashScope / Ollama 三类模型调用 、PromptTemplate / ChatPromptTemplate 基础写法,以及 Chroma + DashScopeEmbeddings 的入库与检索代码骨架。
- 能说出
invoke与stream的差异 、余弦相似度在检索中的作用 ,以及 链|组合数据流 的面试级表述。
一、基础概念
1.1 什么是 RAG
RAG(Retrieval-Augmented Generation)检索增强生成公式:RAG = 检索技术 + LLM 提示
解决大模型 4 大问题:
- 知识过时(无实时数据)
- 领域知识不足(无私有数据)
- 幻觉(编造答案)
- 数据安全性
1.2RAG 核心价值
- 用外部文档 / 私有知识库给模型 "补课"
- 回答基于事实,减少幻觉
- 无需重新训练模型,成本低、更新快
- 支持实时数据接入
1.3 RAG 标准工作流程
RAG 分两条线:
离线流程(构建知识库)
- 文档加载 → 文本分割 → 向量化 → 存入向量数据库
在线流程(用户提问)
- 用户问题向量化 → 向量检索 → 提示词融合 → 模型生成答案
三阶段标准流程:
- 索引 Indexing:文档切分 chunk → 嵌入向量 → 存入向量库
- 检索 Retrieval:用户 query 转向量 → 相似度匹配 Top-K
- 生成 Generation:检索内容 + 问题 → 输入模型 → 精准回答
1.4 向量与嵌入基础
向量是什么
- 向量 = 文本的数学身份证:把文字语义转为固定长度数字序列,让计算机能计算相似度。
向量嵌入
- 使用文本嵌入模型(如 text-embedding-v1)生成向量。维度越高,语义越精准,但性能开销越大。常用:1536 维
余弦相似度
- 判断两个向量方向是否相近:
- 夹角越小 → 越相似
- 只看方向,不看长度
- 公式:余弦相似度 = 点积 / (模长A × 模长B)
1.5 LangChain 是什么
LangChain 是什么
- LLM 应用开发框架,不造模型,只做组件编排。把模型、提示词、记忆、检索、文档、链、智能体全部打通。
LangChain 核心功能(原始笔记)
- Prompts:提示词工程
- Models:模型调用
- History:对话记忆
- Indexes:文档 / 向量库管理
- Chains:执行链路
- Agent:智能体
1.6 LangChain Models:三类模型
- LLMs:文本生成
- Chat Models:多轮对话
- Embeddings Models:文本转向量
1.7 核心术语对比
| 术语 | 含义 | 典型用途 | 常见误区 |
|---|---|---|---|
| Indexing | 文档切块、嵌入、入库 | 构建可检索知识库 | 切块过大导致噪声多;过小丢语义 |
| Retrieval | query 与向量库相似度匹配 | 取 Top-K 片段作上下文 | k 过大稀释主题;过小漏证据 |
| Generation | 将检索片段与问题拼入提示再生成 | 最终回答用户 | 不把检索结果写入提示仍易幻觉 |
| Embedding | 文本→稠密向量 | 检索、聚类、去重 | 混用不同嵌入模型导致向量空间不一致 |
| Chain(链) | 用 ` | ` 串联组件 | `prompt |
二、原理详解
2.1 为什么用 RAG 而不是只堆提示词(归纳)
原始笔记从四个痛点出发:时效、私域、幻觉、安全 。RAG 通过 可更新的外部知识 让生成锚定在检索到的证据上,成本低于重训,适合文档迭代频繁的场景。
2.2 离线 vs 在线:数据流(对应原始笔记两条线)
text
离线:文档 -> Loader -> Split -> Embed -> VectorStore 持久化
在线:用户问题 -> Embed -> similarity_search(Top-K) -> Prompt 融合 -> LLM -> 回答
2.3 余弦相似度为何常用
高维向量下,方向一致往往比绝对长度更能表达「语义相近」。余弦相似度对向量长度缩放相对不敏感,适合文案级别语义检索(笔记:只看方向,不看长度)。
2.4 LangChain 「链」的本质
什么是链
- 用
|把组件串起来,上一个输出 = 下一个输入。
text
chain = prompt | model | parser
2.5 RAG 检索流程复述
- 加载文档 → 分割 → 向量化 → 入库
- 用户问题 → 向量化 → 相似度检索
- 检索结果 + 问题 → 提示词 → 模型回答
三、完整实战代码
3.1 环境安装
bash
pip install langchain langchain-community langchain-ollama langchain-chroma dashscope chromadb bs4 jq
3.2 阿里云通义千问调用
python
# LLM 模型
from langchain_community.llms.tongyi import Tongyi
llm = Tongyi(model="qwen-max")
res = llm.invoke("讲个笑话")
# 聊天模型
from langchain_community.chat_models.tongyi import ChatTongyi
chat = ChatTongyi(model="qwen3-max")
3.3 Ollama 本地模型
python
from langchain_ollama import OllamaLLM
model = OllamaLLM(model="qwen3:4b")
3.4 嵌入模型
python
# 阿里云
from langchain_community.embeddings import DashScopeEmbeddings
embed = DashScopeEmbeddings()
# Ollama
from langchain_ollama import OllamaEmbeddings
embed = OllamaEmbeddings(model="qwen3-embedding")
3.5 两种调用方式
.invoke():一次性返回.stream():流式输出
3.6 LangChain 提示词模板
通用模板 PromptTemplate
python
from langchain_core.prompts import PromptTemplate
prompt = PromptTemplate.from_template("给{lastname}家{gender}起名")
少样本模板 FewShotPromptTemplate
python
from langchain_core.prompts import FewShotPromptTemplate
参数:
- examples:示例列表
- example_prompt:示例模板
- prefix:前缀说明
- suffix:后缀问题
- input_variables:变量
聊天模板 ChatPromptTemplate
python
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_messages([
("system", "你是诗人"),
("human", "{question}")
])
支持动态历史:MessagesPlaceholder("history")
3.7 LangChain Chain 链式调用
必须会的解析器(原始笔记)
- StrOutputParser:AIMessage → 字符串
- JsonOutputParser:AIMessage → JSON 字典
python
chain = prompt | model | parser
自定义函数入链
python
from langchain_core.runnables import RunnableLambda
chain = prompt | model | RunnableLambda(lambda x: {"name": x.content})
或直接写函数:
python
chain = prompt | model | (lambda x: {"name": x.content})
3.8 LangChain 记忆 Memory
临时记忆(内存)
InMemoryChatMessageHistory:重启程序丢失。
长期记忆(文件持久化)
- 自定义
FileChatMessageHistory - 按
session_id存文件 - 重启程序仍可读取历史
带记忆的链
python
from langchain_core.runnables.history import RunnableWithMessageHistory
chain = RunnableWithMessageHistory(
base_chain,
get_session_history,
input_messages_key="input",
history_messages_key="chat_history"
)
3.9 文档加载与分割
文档加载器 Document Loader
统一返回 Document 对象:
- page_content:内容
- metadata:元信息
常用 Loader(原始笔记):
- CSVLoader:加载 CSV
- JSONLoader:加载 JSON(依赖 jq)
- PyPDFLoader:加载 PDF(依赖 pypdf)
- TextLoader:加载文本
文档分割(关键)
RecursiveCharacterTextSplitter(官方推荐)
python
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n","\n","。","!","?"," "]
)
3.10 向量数据库与检索
向量库类型
- InMemoryVectorStore:内存版
- Chroma:本地轻量级向量库
基本使用
python
from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings
vector_store = Chroma(
embedding_function=DashScopeEmbeddings(),
persist_directory="./chroma_db"
)
# 添加
vector_store.add_documents(docs)
# 检索
vector_store.similarity_search("问题", k=4)
四、开发避坑总结
4.1 典型问题清单
-
检索命中差或答非所问
原因 :chunk_size/chunk_overlap与文档结构不匹配;分隔符未覆盖实际标点习惯(笔记强调分割关键)。
解决 :按文档类型调参;优先RecursiveCharacterTextSplitter并从日志中抽查若干 chunk 语义完整性。 -
同一库多次试验结果漂移
原因 :嵌入模型更换或混用(笔记:维度与模型差异带来向量空间不一致)。
解决 :同一业务固定同一Embeddings实现;重建索引后再对比指标。 -
多轮对话「失忆」或上下文爆炸
原因 :仅用内存历史或未限制 history 长度(笔记:InMemoryChatMessageHistory重启丢失;长期需FileChatMessageHistory+ session)。
解决:明确 session 边界;对历史做裁剪/摘要并与 RAG 检索片段分工(事实来自库,偏好来自对话)。
4.2 最佳实践
- 检索侧:固定 Top-K 策略并记录每次检索片段,便于回归与风控审计。
- 生成侧:提示中要求引用或摘录检索内容,与笔记「绑定参考文本」思路一致。
- 工程侧:persist_directory 与嵌入模型版本写入配置或元数据,避免「同一目录混模型」。
五、面试考点
5.1 高频问题
-
Q1:用一句话解释 RAG。
A :检索增强生成 ,先用检索从向量库取相关片段,再把片段与问题交给 LLM 生成,公式上即 检索 + 提示 + 生成。 -
Q2:Indexing、Retrieval、Generation 各做什么?
A:Indexing 把文档切块并嵌入入库;Retrieval 把 query 转向量并做相似度 Top-K;Generation 把检索内容与问题融合后由模型输出答案。 -
Q3:LangChain 里
prompt | model | parser表达什么?
A :链式组合 runnable:前一组件输出作为后一组件输入;parser负责把消息解析成字符串或 JSON(笔记 StrOutputParser / JsonOutputParser)。
5.2 进阶追问
- 余弦相似度为何「只看方向」仍适合语义检索? → 高维语义向量往往更强调方向一致性;对长度缩放相对稳健。
- LLM、Chat Model、Embeddings 三类分工? → 生成/对话/转向量三类接口,RAG 至少用到 Embeddings + Chat 或 LLM。
本文为MY_TRUCK原创实战学习笔记,持续更新Java后端与AI应用领域干货,问题欢迎评论区交流。