news 2026/4/24 7:25:48

【RAG 详解:让模型学会“查资料”】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【RAG 详解:让模型学会“查资料”】

【LangChain】本文主要是我在学习 LangChain 过程中的一些理解总结,偏入门和认知梳理。

    • 一、问题:模型如何获取“它不知道的信息”?
    • 二、RAG 是什么?
    • 三、RAG 的完整流程
    • 四、Embedding(向量化)
    • 五、向量数据库
    • 六、RAG 中的 Prompt
    • 七、用 LangChain 实现 RAG
      • 1. 加载文档
      • 2. 切分文本(按 Token 数切分,保持语义完整性)
      • 3. 定义嵌入模型
      • 4. 配置 Redis 向量存储
      • 5. 将文档存入向量库(离线阶段完成)
      • 6. 创建检索器
      • 7. 定义提示词模板
      • 8. 定义文档格式化函数
      • 9. 构建 RAG 链
      • 10. 结果演示
    • 八、RAG 的本质
    • 九、小结

上一篇: LangChain 核心组件梳理:从模块到流程

一、问题:模型如何获取“它不知道的信息”?

在上一篇中,我们已经了解了:

LangChain 可以将 AI 任务拆解为多个步骤,将组件链接起来并组织成完整流程。

但还有一个关键问题没有解决:

模型如何获取“它不知道的信息”?

比如:

  • 本地文档

  • 企业知识库

  • 实时数据


LLM的训练数据有截止日期,它无法回答关于你公司内部流程的问题,也无法告诉你今天发生了什么。
原生 LLM 就像一个知识渊博但“与世隔绝”的专家——它的知识停留在训练完成的那一刻。

举个例子:假设你问 ChatGPT:“我们公司今年的年假政策是什么?”它会礼貌地告诉你它不知道,或者更糟——编造一个听起来合理但完全错误的答案(这就是“幻觉”)。因为它从未见过你们公司的内部员工手册。

这就引出了本文的核心内容:

RAG(检索增强生成)


二、RAG 是什么?

RAG = 检索(Retrieval) + 生成(Generation)

核心流程:

用户提问 → 检索相关内容 → 作为上下文输入模型 → 生成答案

举个例子:想象你在写一篇论文。

纯 LLM 模式:你不查任何资料,全凭记忆写。写到后面可能记错了某个年份,或者漏掉了重要观点。

RAG 模式:你先去图书馆,找到几本最相关的参考书,把关键段落复印下来,摊在桌上。然后你一边看这些资料,一边写论文。这样写出来的内容准确、有据可查。


本质变化:

以前:
模型“凭记忆回答”——容易产生幻觉,信息陈旧

现在:
模型“查资料再回答”——基于实时、可信的参考资料

这就像给模型配备了一个专属图书管理员:每次提问时,管理员先去资料库里找到最相关的几页内容,递到模型面前,模型再据此作答。

三、RAG 的完整流程

RAG 通常分为两个阶段,理解这个流程是掌握 RAG 的关键。

  1. 离线数据准备阶段

    加载文档 → 切分文本 → 向量化 → 存入向量数据库

这个阶段的目标是:把私有知识库“翻译”成模型能检索的形式。


举个例子:假设你要把一本 300 页的《公司员工手册》变成 RAG 知识库。

  • 加载文档:把 PDF 文件读进来。

  • 切分文本:300 页太长,一次塞给模型它看不过来(上下文窗口有限)。所以要把它切成一段一段,比如每段约 200 个 Token,像把一本大书剪成一张张卡片。

  • 向量化:把每张“卡片”上的文字转换成一串数字(向量),让计算机能“理解”它的含义。

  • 存入向量数据库:把所有卡片和它们的数字指纹(向量)存进一个专门用来快速搜索的仓库。

  1. 在线检索阶段

    用户提问 → 问题向量化 → 相似度检索 → 获取相关内容 → 组装 Prompt → 生成答案

这个阶段的目标是:根据用户问题,找到最相关的资料,让模型据此回答。


继续上面的例子:现在有员工问:“年假怎么申请?”

  • 系统把“年假怎么申请?”也转换成一串数字(向量)。

  • 在向量仓库里找哪些卡片上的向量和这个问题向量最“接近”。

  • 找到最相似的那几张卡片——上面写的恰好就是年假政策的内容。

  • 把这些卡片内容和问题一起打包,告诉模型:“嘿,参考这些资料来回答用户的问题。”

  • 模型看完资料,给出准确答案:“您可以在 OA 系统中提交年假申请,每年享有 10 天年假……”


我们可以理解为:

“先建索引,再查询”——和搜索引擎的原理异曲同工。

下面这张图清晰地展示了完整流程:

四、Embedding(向量化)

1.什么是Embedding?

Embedding 的核心思想是:把人类语言的“语义”转换为计算机能计算的“数字”。

举个例子:

“我喜欢猫” → [0.12, 0.98, -0.34, …]
这个数字列表就是向量,它的维度通常是固定的(比如 OpenAI 的 text-embedding-3-large 模型生成 3072 维向量)

一个形象的理解方式:想象你要向一个外星人描述各种动物的特征。外星人不认识文字,只懂数字。于是你发明了一套编码系统:

“毛茸茸” → +0.8

“会喵喵叫” → +0.9

“喜欢抓老鼠” → +0.7

“汪汪叫” → -0.8

那么“猫”的描述可能就接近 [0.8, 0.9, 0.7, -0.8],而“狗”的描述则是 [0.7, -0.9, -0.5, 0.9]。

Embedding 模型做的事情更复杂、更精确,但原理是一样的——用数字来代表含义。

2.为什么需要?
->用于计算语义相似度

在向量空间中,含义相近的文本,其向量也彼此接近。我们可以用余弦相似度来度量两个向量的“方向一致性”——方向越一致,语义越相似。

实验一下:

“猫是一种可爱的宠物” → 向量 A

“狗是人类忠实的朋友” → 向量 B

“今天天气真好” → 向量 C

计算相似度你会发现:A 和 B 的相似度较高(都是关于宠物的),而它们和 C 的相似度都很低。这就是向量检索的基础。

理解
把“语义相似”变成“向量接近”

这使得计算机可以用数学方法完成“找意思相近的内容”这种原本只有人类才能做的事。

五、向量数据库

1.作用

存储向量,并支持高效的相似度搜索。

一篇文档转换成一个 1536 维的向量,一百万篇文档就是约 1.5GB 的纯向量数据。如何在海量向量中快速找到与查询最相似的几条?这就是向量数据库的专长。


传统数据库 vs 向量数据库
用一个找电影的例子来理解:

传统数据库(如 MySQL):

你输入:SELECT * FROM movies WHERE title LIKE '%爱情%'

它返回:所有标题里包含“爱情”两个字的电影。

问题来了:《泰坦尼克号》标题里没有“爱情”,但它确实是一部爱情片,传统数据库找不到它。


向量数据库:

你把《泰坦尼克号》的剧情简介转换成向量存进去。

你输入:“推荐一些感人的爱情电影” → 转换成向量

向量数据库计算发现,《泰坦尼克号》的剧情向量和你的查询向量方向很接近,于是把它推荐给你——哪怕标题里压根没有“爱情”二字
将两者比较:

对比维度传统数据库向量数据库
匹配方式关键词精确匹配语义相似匹配
查询示例WHERE name = '苹果'“一种红色的水果”也能找到“苹果”相关文档
语义理解能力不懂“含义”,仅做字面匹配理解“语义”,可基于上下文和意图检索
典型工具 / 集成MySQL、PostgreSQL 等关系型数据库Chroma、Redis(RediSearch)、Pinecone、Milvus 等(LangChain 均支持集成)

我们可以理解为:

向量数据库是一个“按语义搜索”的数据库

六、RAG 中的 Prompt

1.问题:

模型本身并不知道:

哪部分是用户的问题

哪部分是我们提供的参考资料

应该基于什么来回答

所以我们需要用 Prompt 明确告诉模型


错误的 Prompt 写法(没有区分角色):

数据库表怎么设计的?
(后面跟着一大堆检索出来的文档内容)
模型会困惑:“我是应该直接回答数据库设计的问题,还是总结这些资料?”结果可能是它忽略了资料,凭自己的知识回答,完全丧失了 RAG 的意义。


正确的 Prompt 写法:

根据以下检索到的上下文片段来回答问题。
如果你不知道答案,就说你不知道。
最多只用三句话,回答要简明扼要。

问题:{question}
上下文:{context}

答案:{anwer}


这样模型就清楚了用户的需求:

角色(Role):我是一个基于资料回答的助手

输入(Intput):问题是 X,参考资料是 Y

输出要求( Limit):简明扼要,不超过三句,不知道就说不知道

  1. 本质:

让模型“基于资料回答”,而不是自己发挥。

这种 Prompt 设计是 RAG 效果的关键——既要让模型充分参考上下文,又要约束它不胡编乱造。

七、用 LangChain 实现 RAG

我们用 LangChain 的组件来完整实现一个 RAG 流程。

步骤:

加载文档 → 切分文本 → 向量化存储 → 创建检索器 → 构建 RAG 链 → 生成答案

我们可以看段实现 RAG的完整代码:

fromlangchain_openaiimportOpenAIEmbeddings,ChatOpenAIfromlangchain_redisimportRedisConfig,RedisVectorStorefromlangchain_core.output_parsersimportStrOutputParserfromlangchain_core.promptsimportChatPromptTemplatefromlangchain_core.runnablesimportRunnablePassthroughfromlangchain_community.document_loadersimportUnstructuredMarkdownLoaderfromlangchain_text_splittersimportCharacterTextSplitter

1. 加载文档

#UnstructuredMarkdownLoader 专门用于MD文档#PDF文档可用 PyPDFLoaderloader=UnstructuredMarkdownLoader("./knowledge.md")data=loader.load()

2. 切分文本(按 Token 数切分,保持语义完整性)

# ⽣成分割器text_splitter=CharacterTextSplitter.from_tiktoken_encoder(encoding_name="cl100k_base",#cl100k_base 是一种默认的编码格式chunk_size=200,#块大小chunk_overlap=50#块与块之间的重叠长度)documents=text_splitter.split_documents(data)

3. 定义嵌入模型

我们可以直接在LangChain官网搜索我们需要的嵌入模型以及如何安装和使用(需要魔法)

#这里使用OpenAi的text-embedding-3-largeembeddings=OpenAIEmbeddings(model="text-embedding-3-large")

4. 配置 Redis 向量存储

# Redis向量存储相关配置config=RedisConfig(index_name="my_knowledge_base",redis_url="redis://localhost:6666",)vector_store=RedisVectorStore(embeddings,config=config)

5. 将文档存入向量库(离线阶段完成)

vector_store.add_documents(documents)

6. 创建检索器

#通过调⽤向量数据库的as_retriever ⽅法,将向量存储⽤作检索器retriever=vector_store.as_retriever(search_kwargs={"k":4})

7. 定义提示词模板

# 提示词模板prompt=ChatPromptTemplate.from_messages([("human","""根据以下上下文回答问题。如果你不知道答案,就说不知道。 问题:{question} 上下文:{context} 答案:""")])

8. 定义文档格式化函数

#将检索到的文档转化成文本传递给提示词模板defformat_docs(docs):return"\n\n".join(doc.page_contentfordocindocs)

9. 构建 RAG 链

#自己尝试可以使用国产的千问,配置好apikey#model =ChatTongyi(model="qwen-turbo")model=ChatOpenAI(model="gpt-4o-mini")chain=(# 检索器+format_docs 分支1# question 分支2:RunnablePassthrough()在链中透传数据{"context":retriever|format_docs,"question":RunnablePassthrough()}|prompt|model|StrOutputParser())

10. 结果演示

forchunkinchain.stream("数据库表怎么设计的?"):print(chunk,end="",flush=True)

运行效果示例:
假设你的 knowledge.md 文档中有这样一段内容:

数据库表设计遵循第三范式,主要分为用户表、订单表、商品表。用户表包含用户ID、姓名、注册时间等字段。订单表通过用户ID与用户表关联,商品表通过商品ID与订单表关联。 当你运行代码并提问“数据库表怎么设计的?”时,系统会: 检索出上述这段最相关的内容 将其作为上下文喂给 LLM 模型据此生成答案,比如: 数据库设计采用第三范式,主要包含用户表(存储用户基本信息)、 订单表(记录订单数据,通过用户ID关联用户表)和商品表通过商品ID关联订单表)。

八、RAG 的本质

RAG = 模型能力 + 外部知识

我们也可以理解为:

RAG 并没有提升模型“智商”,它只是:让模型“看资料再回答”


举个例子,期末考试分开闭卷:

纯 LLM:一个闭卷考试的学生,只能靠记忆答题。

RAG:一个开卷考试的学生,可以翻书找答案。

显然,对于需要精确、实时、私有知识的问题,开卷考试的成绩会好得多


九、小结

从上文我们可以了解到RAG的总流程:

问题 → 检索 → 上下文 → 生成

这是一个“最小可运行 RAG Demo”,实际生产中还需要考虑缓存、召回优化、重排序等问题。

还有RAG 的优势:

  • 减少幻觉:答案有据可查,不再凭空编造

  • 支持私有数据:企业文档、个人笔记都可以成为知识源

  • 提高准确性:基于实时、相关的信息作答


LangChain 为 RAG 提供了完整的组件支持:

组件 作用
Document Loaders ——>加载多种格式的文档
Text Splitters——>将长文档切分为语义完整的块
Embeddings——>将文本转换为向量
Vector Stores——>存储向量并支持相似度检索
Retrievers——>统一的检索接口

今天的分享就到这里,下章再会~

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 21:28:12

博图ARRAY转BOOL指令,你OUT参数长度设对了吗?附仿真验证全流程

博图ARRAY转BOOL指令:OUT参数长度设计的陷阱与实战验证 第一次在产线调试时遇到ARRAY转BOOL指令的数据丢失问题,我盯着PLC监控界面反复确认了三次——明明输入数据完整,输出却总少了最后几位。直到深夜排查才发现是OUT参数长度设置不足。这个…

作者头像 李华
网站建设 2026/4/23 18:40:19

ComfyUI动画制作终极指南:5个MTB Nodes免费开源技巧快速上手

ComfyUI动画制作终极指南:5个MTB Nodes免费开源技巧快速上手 【免费下载链接】comfy_mtb Animation oriented nodes pack for ComfyUI 项目地址: https://gitcode.com/gh_mirrors/co/comfy_mtb 想要在ComfyUI中轻松制作专业级动画效果吗?MTB Node…

作者头像 李华
网站建设 2026/4/24 2:21:50

信息学奥赛迷宫寻路:从广搜模板到OpenJudge实战解析

1. 迷宫寻路:信息学奥赛的经典挑战 迷宫问题在信息学奥赛中就像数学考试里的必考题,几乎每年都会以不同形式出现。我第一次参加NOI集训时,教练就说过:"不会解迷宫题,就像战士不会用枪。"这话虽然夸张&#x…

作者头像 李华
网站建设 2026/4/20 20:22:28

从原理到实践:NeRF神经辐射场如何革新3D重建

1. NeRF技术为什么能颠覆传统3D重建 第一次看到NeRF生成的3D场景时,我整个人都惊呆了——就像魔术师从空帽子里变出活兔子一样,它竟然能从几十张普通照片中还原出逼真的三维世界。这完全打破了我对3D重建的认知,要知道传统方法需要专业设备扫…

作者头像 李华
网站建设 2026/4/18 19:21:40

CoppeliaSim中基于Lua脚本的多关节机械臂轨迹规划与运动控制详解

1. CoppeliaSim与Lua脚本基础 如果你正在研究机器人仿真,CoppeliaSim(原名V-REP)绝对是个绕不开的工具。这个强大的机器人仿真平台内置了Lua脚本支持,让用户能够通过编写简单的脚本控制复杂的机器人系统。我刚开始接触时也觉得有…

作者头像 李华