上一篇我们给 bot 装了 Memory,它终于能记住"刚聊过什么"。但还有一类问题它照样答不上来:你公司的事、你电脑里的文档、你脑子里的业务知识——它全不知道。这一篇,我们用 RAG 让它"看懂"你的私有数据。

先看问题:Memory 也救不了"无知"

先复现一下:

print(chain_with_memory.invoke(
 {"input": "我们公司 Q3 的 OKR 是什么?"},
 config=config
).content)

输出大概率是:

抱歉,我没有相关信息。您能提供更多细节吗?

有 Memory 也没用——因为你从来没在对话里告诉过它 Q3 OKR 是什么。Memory 能解决的只是"刚才说了什么",但它解决不了"模型本来就不知道什么"。

这是两类完全不同的问题:

问题类型 Memory 能解决吗 例子
刚才说过什么 ✅ 能 “我叫什么?”
模型从没学过的知识 ❌ 不能 “公司 Q3 OKR 是什么?”

模型的训练数据截止在某个时间点,而且从不包含你公司的内部文档、你的笔记、你的产品手册。模型的知识 ≠ 你的业务知识,这个 gap 靠 Memory 填不了。

那怎么填?答案就是 RAG。

RAG 与非 RAG 对比

RAG 是什么(一句话讲清)

RAG = Retrieval-Augmented Generation(检索增强生成)

拆开来就三步:

  1. 用户提问

  2. 去你的"知识库"里找相关内容(Retrieval,检索)

  3. 把找到的内容 + 问题一起交给模型(Generation,生成)

就这么简单。模型自己不知道答案没关系,你帮它"开卷考试"就行。

最小可用版本:手动拼 RAG,理解本质

在用任何框架之前,先搞清楚底层发生了什么。

from langchain_openai import ChatOpenAI

llm = ChatOpenAI()

# 假装这是从你的知识库里找到的内容
context = """
公司 Q3 OKR:
1. **提升用户留存率 20%**

2. **推出 AI Agent 新产品**

3. **完成数据平台迁移**

"""

question = "我们公司 Q3 的 OKR 是什么?"

prompt = f"""
基于以下内容回答问题:

{context}

问题:{question}
"""

print(llm.invoke(prompt).content)

输出:

公司 Q3 的 OKR 有三项:
1. **提升用户留存率 20%**

2. **推出 AI Agent 新产品**

3. **完成数据平台迁移**

答出来了。

这就是 RAG 的本质——把"外部知识"塞进 prompt。模型不需要"学会"你的业务知识,你直接把相关内容喂给它,它就能基于这些内容来回答。

和上一篇 Memory 的套路一模一样:Memory 的本质是"把历史对话塞进 prompt",RAG 的本质是"把外部知识塞进 prompt"。底层都是同一件事:往 messages 里多塞点东西。

引入 Embedding:让机器能"查找"

手动拼 context 的问题很明显:如果你有 1000 页文档,你怎么知道该塞哪几段?

总不能把 1000 页全塞进 prompt 吧——token 不够,成本也扛不住。

答案是:先搜索,再塞进去。而搜索的方式,不是关键词匹配,是向量搜索(Embedding)。

什么是 Embedding(一句话版)

把一段文本变成一组数字(向量),语义越相似的文本,向量在空间里越靠近。

比如:

  • “AI 智能体” 和 “AI Agent” → 向量很近(意思一样)
  • “AI 智能体” 和 “今天天气不错” → 向量很远(毫无关系)

这样,用户问了一个问题,你把问题也变成向量,然后去文档向量里找最近的几个——那就是最相关的内容。

Embedding 语义向量示意

试一下

from langchain_openai import OpenAIEmbeddings

embeddings = OpenAIEmbeddings()

vector = embeddings.embed_query("AI 智能体是什么")
print(len(vector)) # 1536
print(vector[:5]) # [0.0012, -0.0283, 0.0156, ...]

一段文本变成了 1536 个数字。你不需要理解每个数字的含义,只要知道:两段文本的向量越接近,它们的语义就越相似。搜索引擎用关键词匹配,向量搜索用语义匹配——后者聪明得多。

构建你的第一个"知识库"(Vector Store)

有了 Embedding,下一步就是把你的文档全部向量化,存进一个向量数据库,随时可以搜。

Step 1:准备文档

docs = [
 "LangChain 是一个用于构建 LLM 应用的框架。",
 "RAG 可以让模型使用外部知识回答问题。",
 "Memory 用于管理对话历史,让 bot 记住上下文。",
 "Embedding 把文本变成向量,用于语义搜索。",
 "FAISS 是 Meta 开源的向量检索库,速度很快。",
]

实际项目里这会是你的文档、PDF、网页内容等。这里先用几条短文本演示。

Step 2:创建向量库

from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS

embeddings = OpenAIEmbeddings()
vectorstore = FAISS.from_texts(docs, embedding=embeddings)

一行代码,LangChain 帮你做了三件事:

  1. 把每条文本调 Embedding API 变成向量

  2. 把向量存进 FAISS 索引

  3. 返回一个可以搜索的 vectorstore 对象

Step 3:检索

retriever = vectorstore.as_retriever()

results = retriever.invoke("什么是 RAG?")

for r in results:
 print(r.page_content)

输出:

RAG 可以让模型使用外部知识回答问题。
LangChain 是一个用于构建 LLM 应用的框架。
Embedding 把文本变成向量,用于语义搜索。
Memory 用于管理对话历史,让 bot 记住上下文。

注意:你搜的是"什么是 RAG?",但它不是在做关键词匹配(文档里没有"什么是"这几个字),而是在做语义匹配——找到了内容最相关的几条文档。

这就是 Retrieval 的部分。接下来只要把检索结果塞进 prompt,就完成了完整的 RAG。

把 Retrieval + LLM 串起来(真正的 RAG)

定义 Prompt 模板

from langchain_core.prompts import ChatPromptTemplate

prompt = ChatPromptTemplate.from_template("""
基于以下上下文回答问题。如果上下文中没有相关信息,请直接说"我没有找到相关信息"。

上下文:
{context}

问题:{question}
""")

构建 RAG Chain(核心代码)

from langchain_core.runnables import RunnablePassthrough

rag_chain = (
 {
 "context": retriever,
 "question": RunnablePassthrough(),
 }
 | prompt
 | llm
)

这三行代码是 RAG 的核心。用的是 LangChain 的 LCEL(LangChain Expression Language)语法:

  • retriever 负责接收用户输入、检索相关文档
  • RunnablePassthrough() 把用户输入原样传递给 question
  • prompt 把 context 和 question 拼成完整提示词
  • llm 生成最终回答

调用

print(rag_chain.invoke("什么是 RAG?").content)

输出:

RAG(检索增强生成)是一种让模型使用外部知识来回答问题的技术。

跑通了。从"文档入库"到"问答输出",完整的 RAG pipeline 就这几行代码。

你刚刚做了什么?(拆解流程)

每次调用 rag_chain.invoke("xxx") 时,背后发生了四步:

用户输入 "什么是 RAG?"
 → retriever 把问题向量化,去 FAISS 里搜最相关的文档
 → 把文档内容填入 prompt 的 {context},问题填入 {question}
 → 拼好的 prompt 发给 LLM
 → LLM 基于上下文生成回答

本质上和你在第三节手动拼 context 是同一件事——只是现在 retriever 自动帮你"找"context 了。

RAG Pipeline 流程图

和 Memory 对比

到现在我们学了两个核心能力,放一起看看:

能力 解决的问题 往 prompt 里塞了什么
Memory 记住"刚说过什么" 历史对话消息
RAG 获取"本来不知道什么" 外部知识库的内容

底层逻辑完全一样:往 prompt 里补信息,让模型"看到"更多东西。

升级一下:让检索更准

默认的 retriever 能用,但不够精细。几个常用调参:

1. Top-K 控制:只要最相关的几条

retriever = vectorstore.as_retriever(search_kwargs={"k": 2})

默认返回 4 条文档,改成 2 条可以减少噪音,也省 token。k 设多少取决于你的场景——文档越长、内容越杂,k 可以调大;文档短而精,k 小一点就够。

2. 相似度搜索 vs MMR

默认用的是相似度搜索(找最像的 top-k)。还有一种叫 MMR(最大边际相关性):

retriever = vectorstore.as_retriever(
 search_type="mmr",
 search_kwargs={"k": 3}
)

MMR 在保证相关性的同时,会刻意让结果多样化——避免找回来的 3 条文档说的是同一件事。适合文档有大量重复内容的场景。

3. 为什么要把文档切块?(chunk,埋个伏笔)

你可能注意到,我们的示例文档都是短句子。但真实世界的文档——一份 PDF 可能有 50 页,一个网页可能有上万字。

直接把整篇文档变成一个向量?不行,太粗糙了,搜索精度会很差。

实际做法是:先把文档切成小块(chunk),每块单独向量化。搜索时匹配到的是最相关的那几个 chunk,而不是整篇文档。

chunk 怎么切、切多大,直接影响 RAG 的效果。这是下一篇的重点内容,这里先知道有这回事就行。

Memory + RAG 合体(关键一节)

Memory 让 bot 记住对话,RAG 让 bot 读懂文档。两个合在一起,才是一个真正可用的 AI 应用。

from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory

store = {}

def get_session_history(session_id: str) -> ChatMessageHistory:
 if session_id not in store:
 store[session_id] = ChatMessageHistory()
 return store[session_id]

rag_chain_with_memory = RunnableWithMessageHistory(
 rag_chain,
 get_session_history,
 input_messages_key="question",
 history_messages_key="history",
)

现在你的 bot 同时具备两种能力:

config = {"configurable": {"session_id": "user_1"}}

# 第一轮:用 RAG 回答知识库里的问题
print(rag_chain_with_memory.invoke(
 "什么是 RAG?",
 config=config
).content)

# 第二轮:用 Memory 记住上一轮的对话
print(rag_chain_with_memory.invoke(
 "你刚才说的那个技术,能用在客服场景吗?",
 config=config
).content)

第二个问题里的"那个技术"指的是 RAG——模型能理解,因为 Memory 保留了上一轮的对话。而第一个问题的回答依赖 RAG 从知识库里检索到的内容。

Memory + RAG = 真正可用的 AI 应用基础设施。 后面要学的 Agent、Tool Use 等高级能力,都建立在这个基础之上。

Memory 与 RAG 协同示意

现实世界怎么用

RAG 不是玩具,它已经是很多 AI 产品的核心架构。三个最常见的场景:

公司知识库问答 —— 把内部文档(产品手册、流程规范、FAQ)灌进向量库,员工直接用自然语言提问。不用再翻 Confluence 翻半天。

技术文档助手 —— 把 API 文档、SDK 文档向量化,开发者问"怎么用 XXX 接口",直接给出代码示例和参数说明。比搜索引擎精准得多。

客服机器人 —— 把商品信息、退换货政策、常见问题库接入 RAG。用户问"我的订单能退吗",bot 基于实际政策回答,不会瞎编。

这三个场景的底层架构完全一样:文档 → chunk → embedding → 向量库 → retriever → LLM。区别只在于数据源和 prompt 设计。

RAG 的局限(很关键)

RAG 好用,但不是万能的。几个你马上会遇到的问题:

检索不准 → 答案就错。 如果 retriever 找回来的文档和问题不相关,模型只能基于错误的 context 瞎答。垃圾进,垃圾出。

chunk 切得不好 → 信息丢失。 切得太小,一段完整的意思被拆成两半;切得太大,搜索精度下降。chunk 策略直接决定 RAG 的天花板。

token 成本。 每次把检索到的文档塞进 prompt,都在消耗 token。文档越多、k 越大,成本越高。得在"回答质量"和"成本"之间找平衡。

模型会"过度依赖"context。 有时候检索到的内容和问题沾点边但不完全对,模型可能硬往上靠,给出一个"看起来对但其实答非所问"的回答。

这些问题都有解法,但需要更精细的工程手段——这就是下一篇的内容。

下一步去哪

你现在的 RAG 已经能跑了,但还很"粗糙"。真实项目里要解决的问题还有很多:

  • 文档切分(Text Splitter):怎么切 chunk 效果最好?按段落?按句子?按 token 数?
  • 更好的检索策略:混合检索、重排序(Reranking)、父子文档检索
  • 向量数据库选型:FAISS 适合 Demo,生产环境用 Chroma、Milvus 还是 Pinecone?
  • 多轮检索:用户追问时,怎么结合对话历史重新检索?

Memory 让 bot 有了"短期记忆",RAG 让 bot 有了"知识储备"。下一篇我们把 RAG 打磨得更精细,让检索真正靠谱起来。


下一篇:RAG 进阶——从 chunk 策略到检索优化,让你的知识库问答从"能用"变"好用"


本文对应的 GitHub 源码地址如下,复制到浏览器打开即可:

https://github.com/ailifetime/agentLearn/tree/main/lessons/lesson-03-rag

这里给大家精心整理了一份全面的AI大模型学习资源包括:AI大模型全套学习路线图(从入门到实战)、精品AI大模型学习书籍手册、视频教程、实战学习、面试题等,资料免费分享

👇👇扫码免费领取全部内容👇👇
在这里插入图片描述

1. 成长路线图&学习规划

要学习一门新的技术,作为新手一定要先学习成长路线图方向不对,努力白费

这里,我们为新手和想要进一步提升的专业人士准备了一份详细的学习成长路线图和规划。可以说是最科学最系统的学习成长路线。
在这里插入图片描述

2. 大模型经典PDF书籍

书籍和学习文档资料是学习大模型过程中必不可少的,我们精选了一系列深入探讨大模型技术的书籍和学习文档,它们由领域内的顶尖专家撰写,内容全面、深入、详尽,为你学习大模型提供坚实的理论基础(书籍含电子版PDF)

在这里插入图片描述

3. 大模型视频教程

对于很多自学或者没有基础的同学来说,书籍这些纯文字类的学习教材会觉得比较晦涩难以理解,因此,我们提供了丰富的大模型视频教程,以动态、形象的方式展示技术概念,帮助你更快、更轻松地掌握核心知识

在这里插入图片描述

4. 2026行业报告

行业分析主要包括对不同行业的现状、趋势、问题、机会等进行系统地调研和评估,以了解哪些行业更适合引入大模型的技术和应用,以及在哪些方面可以发挥大模型的优势。

5. 大模型项目实战

学以致用 ,当你的理论知识积累到一定程度,就需要通过项目实战,在实际操作中检验和巩固你所学到的知识,同时为你找工作和职业发展打下坚实的基础。

在这里插入图片描述

6. 大模型面试题

面试不仅是技术的较量,更需要充分的准备。

在你已经掌握了大模型技术之后,就需要开始准备面试,我们将提供精心整理的大模型面试题库,涵盖当前面试中可能遇到的各种技术问题,让你在面试中游刃有余。

在这里插入图片描述

7. 资料领取:全套内容免费抱走,学 AI 不用再找第二份

不管你是 0 基础想入门 AI 大模型,还是有基础想冲刺大厂、了解行业趋势,这份资料都能满足你!
现在只需按照提示操作,就能免费领取:

👇👇扫码免费领取全部内容👇👇
在这里插入图片描述

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐