Deep Read

Naive RAG 讲解

RAG,全称 Retrieval-Augmented Generation(检索增强生成),是过去两年大模型应用落地里最重要的工程方案之一。 如果把大模型比作一个"很聪明但记忆固定的学生",那么 RAG 做的事情就是:先去外部资料里找答案,再带着资料回答问题。

Naive RAG,可以理解为最基础、最原始、也是最容易落地的一代 RAG 方案。它的结构并不复杂,但已经足够解决很多实际问题,比如企业知识库问答、内部文档检索、客服机器人、私有数据接入大模型等。


一、为什么需要 RAG

大语言模型能力很强,但存在三类核心局限。

1. 时效性有限

模型知识来自训练数据,而训练数据有截止时间。最新政策、财报、内部 SOP、个人知识库文档——这些如果没被放进训练数据,模型就无法直接回答。

2. 知识覆盖不完整

即使训练了海量语料,也不可能覆盖所有垂直领域和每家企业的内部资料。医疗、法律、金融等高门槛领域,以及内部文档、合同、产品手册、私有接口说明——模型可能"懂一点",但不够深、不够准。

3. 幻觉问题

大模型在不知道答案时,仍可能生成一个看起来很像答案的内容——语气自信,内容却可能是错的。其根本原因是生成模型在概率分布下会"补全最像答案的文本"。

image.png

当问题依赖最新知识、外部知识、私有知识时,仅靠模型参数往往不够。


二、什么是 RAG:微调 vs RAG

面对"知识更新慢"和"容易幻觉"的问题,常见有两条路:微调(Fine-Tuning)RAG

微调

在通用大模型基础上用特定领域数据继续训练,让模型更适应某类任务或领域。适合解决语气风格统一、输出格式稳定、特定任务能力增强等问题。

image.png

但也存在明显代价:数据准备成本高、训练和迭代成本高、知识更新不灵活,不适合频繁变动的知识库场景。

RAG

RAG 不把新知识塞进模型参数,而是给模型外挂一个"可检索知识库":先从外部知识库找相关内容,拼进提示词,再让 LLM 基于上下文生成答案。

image.png

RAG 拆成三个词理解:

  • Retrieval(检索):用户提问后,去外部知识源(企业文档、产品手册、FAQ、论文等)找到相关片段。在 Naive RAG 中,常见的做法是把文档切分后向量化,存入向量数据库,再通过向量检索匹配。
  • Augmented(增强):增强的对象不是模型,而是 Prompt。把检索到的相关片段和用户问题拼在一起,让模型在"带资料"的前提下回答。
  • Generation(生成):把增强后的提示词喂给 LLM,输出答案。

本质一句话:RAG = LLM + 外部知识检索,也就是"让模型先翻书,再回答"。

image.png

image.png

相比微调,RAG 的优点是成本更低、上线更快、知识更新更方便,更适合企业文档和私有数据接入。在多数知识库问答场景里,RAG 往往是第一选择。


三、Naive RAG 的核心流程

Naive RAG 虽然叫"Naive",但流程已很完整。工程上分为两个阶段:离线阶段在线阶段。后续所有进阶 RAG 的变化,本质上都是在这两个阶段上做增强(索引更好、检索更准、生成更稳)。

1. 离线阶段:构建知识库

在系统正式服务之前,把原始文档加工成"可检索"的知识库。

a. 数据清洗

原始文档常有 HTML 标签、页眉页脚、空白字符、重复内容、敏感信息等噪音。清洗的目标是明确:哪些内容值得进知识库,哪些应该被丢掉。企业场景还可能涉及权限对齐、归属关系梳理、业务字段补全等。

b. 文档分块

RAG 不会把整篇文档直接塞给模型或向量库,而是先拆成小块。原因有三:

  • 模型上下文窗口和向量模型输入长度都有限
  • 整篇文档过长会稀释语义,噪音太多会影响检索精度
  • 很多问题只对应文档中的一小段,分块越合理,命中正确答案的概率越高

分块的目标不是机械切开,而是切成既足够短、又尽量保留语义完整的小单元

c. 向量化

每个文本块通过 Embedding(向量)模型转成向量,让"语义相近"的内容在向量空间里靠得更近,使文本可以进行相似度计算。

d. 存储

向量及其原始文本、元数据存入向量数据库。向量数据库的核心价值是高效检索:提供相似度搜索、Top-K 返回、元数据过滤和高并发能力。

2. 在线阶段:回答用户问题

流程:用户提问 → 问题向量化 → 向量检索 → 拼接提示词 → LLM 生成答案。

检索结果的质量直接决定最终回答质量。

image.png


四、Top-K 与 Top-P

Top-K

把检索结果按相似度排序后,取前 K 条(如 Top-3 取最相关的 3 条)。这是最直观、最常用的做法。

Top-P

不是固定取多少条,而是按累计相似度阈值来取。例如相似度依次为 0.9、0.5、0.4、0.3、0.2,累计阈值设为 2,则累加至 0.9+0.5+0.4+0.3=2.1,取前 4 条。更灵活,但业务场景中 Top-K 仍更常用,因为更稳定、更容易调试。


五、常见分块策略

分块是 Naive RAG 最关键的环节之一——分块做差了,向量化、检索、生成都会一起变差。

1. 固定长度分块

按固定字符数或 Token 数切分。

  • 优点:实现简单、速度快、存储和检索效率高
  • 缺点:容易把完整语义硬切断,上下文衔接可能丢失
def split_by_fixed_char_count(text, count):  
text = ("自然语言处理(NLP),作为计算机科学、人工智能与语言学的交融之地,致力于赋予计算机解析和处理人类语言的能力。"  
        "在这个领域,机器学习发挥着至关重要的作用。利用多样的算法,机器得以分析、领会乃至创造我们所理解的语言。"  
        "从机器翻译到情感分析,从自动摘要到实体识别,NLP的应用已遍布各个领域。随着深度学习技术的飞速进步,"  
        "NLP的精确度与效能均实现了巨大飞跃。如今,部分尖端的NLP系统甚至能够处理复杂的语言理解任务,"  
        "如问答系统、语音识别和对话系统等。NLP的研究推进不仅优化了人机交流,也对提升机器的自主性和智能水平起到了关键作用。")  
# 假设我们按照每100个字符来切分文本  
chunks = split_by_fixed_char_count(text, 100)  
  
for i, chunk in enumerate(chunks):  
    print(f"块{i} - 长度{len(chunk)} - 内容: {chunk}")

2. 固定长度 + 重叠窗口

每个块长度固定,相邻块之间保留部分重叠内容(overlap),以保住跨块语义连续性。生产环境里 overlap 常设为块大小的 10%~20%

  • 优点:比纯固定分块更稳,能减少语义断裂
  • 缺点:数据冗余增加、存储成本变高,也可能带入更多噪音
# chunk_size表示块的字符个数,stride为步长,滑动窗口的大小为chunk_size-stride  
# overlapping window 重叠窗口  
  
def sliding_window_chunks(text, count, stride):  
    # 列表推导式  
    return [text[i: i+count] for i in range(0, len(text), count - stride)]  
  
  
text = ("自然语言处理(NLP),作为计算机科学、人工智能与语言学的交融之地,致力于赋予计算机解析和处理人类语言的能力。"  
        "在这个领域,机器学习发挥着至关重要的作用。利用多样的算法,机器得以分析、领会乃至创造我们所理解的语言。"  
        "从机器翻译到情感分析,从自动摘要到实体识别,NLP的应用已遍布各个领域。随着深度学习技术的飞速进步,"  
        "NLP的精确度与效能均实现了巨大飞跃。如今,部分尖端的NLP系统甚至能够处理复杂的语言理解任务,"  
        "如问答系统、语音识别和对话系统等。NLP的研究推进不仅优化了人机交流,也对提升机器的自主性和智能水平起到了关键作用。")  
  
  
chunks = sliding_window_chunks(text, 100, 20)  
  
for i, chunk in enumerate(chunks):  
    print(f"块 {i} - 长度{len(chunk)},内容: {chunk}")

3. 按句子分块

尤其适合中文场景——一句话往往已是较完整的语义单元。

  • 优点:语义自然,阅读和解释性更好
  • 缺点:块可能太短、容易缺失上下文,对复杂答案不够友好
# 正则表达式  
import re  
  
text = ("自然语言处理(NLP),作为计算机科学、人工智能与语言学的交融之地,致力于赋予计算机解析和处理人类语言的能力。"  
        "在这个领域,机器学习发挥着至关重要的作用。利用多样的算法,机器得以分析、领会乃至创造我们所理解的语言。"  
        "从机器翻译到情感分析,从自动摘要到实体识别,NLP的应用已遍布各个领域。随着深度学习技术的飞速进步,"  
        "NLP的精确度与效能均实现了巨大飞跃!如今,部分尖端的NLP系统甚至能够处理复杂的语言理解任务,"  
        "如问答系统、语音识别和对话系统等。NLP的研究推进不仅优化了人机交流,也对提升机器的自主性和智能水平起到了关键作用。")  
'''  
re.split(r'[。?!]', text) 是使用正则表达式对字符串 text 进行分割的操作。  
re.split(pattern, string):这是 Python 的 re 模块中的一个函数,用于根据正则表达式 pattern 将字符串 string 分割成多个部分。  
r'[。?!]':这是一个正则表达式模式,用于匹配中文句子结束的标点符号。  
re.split 函数会返回一个列表,其中包含分割后的子字符串和匹配到的标点符号  
'''  
sentences = re.split(r'[。?!]', text)  
print(sentences)  
print('-' * 100)  
  
for i, chunk in enumerate(sentences):  
    print(f"块 {i + 1} - 长度{len(chunk)},内容: {chunk}")

4. 递归分块

LangChain 中常见的方案(如 RecursiveCharacterTextSplitter)。核心思想:优先按更自然的分隔符切分,如果块仍太大就继续按更细粒度分隔符再切,最后尽量合并成接近目标长度的块。相比机械固定切分,更适合真实文档。

  
# 利用langchain的RecursiveCharacterTextSplitter  
from langchain_text_splitters import RecursiveCharacterTextSplitter  
  
text = ("自然语言处理(NLP),作为计算机科学、人工智能与语言学的交融之地,致力于赋予计算机解析和处理人类语言的能力。"  
        "在这个领域,机器学习发挥着至关重要的作用。利用多样的算法,机器得以分析、领会乃至创造我们所理解的语言。"  
        "从机器翻译到情感分析,从自动摘要到实体识别,NLP的应用已遍布各个领域。随着深度学习技术的飞速进步,"  
        "NLP的精确度与效能均实现了巨大飞跃。如今,部分尖端的NLP系统甚至能够处理复杂的语言理解任务,"  
        "如问答系统、语音识别和对话系统等。NLP的研究推进不仅优化了人机交流,也对提升机器的自主性和智能水平起到了关键作用。")  
  
splitter = RecursiveCharacterTextSplitter(  
    chunk_size=20,  # 分割长度  
    chunk_overlap=5,  # 重叠长度 /重叠窗口大小  
   # separators=["\n\n", "\n", " ", ""],  
    separators = ["\n\n", "\n", "。", ","] )  
  
chunks = splitter.split_text(text)  
  
for i, chunk in enumerate(chunks):  
    print(f"块 {i + 1} - 长度{len(chunk)},内容: {chunk}")  
  
  
'''  
推荐使用:RecursiveCharacterTextSplitter + Overlap  
注意参数:  
   - chunk_size: 一般300~500 tokens(或字符)  
   - chunk_overlap: 一般为chunk_size的5%-20%(防止上下文断裂)  
   - separators: 从高到低设置合适的分隔符,例如:["\n\n", "\n", ".", " "]  
  
注意事项:  
  - 避免切片太小:小于 100 tokens 的 chunk 容易导致语义不完整。  
  - 避免切片太大:超过模型最大上下文, 会导致无法嵌入。  
  - 动态调整参数:针对不同类型文档调整 chunk_size 和 overlap。  
'''

六、优点与局限

优点

  • 成本低、上线快:不需要训练模型,适合快速验证业务
  • 知识更新方便:文档更新后重新索引即可,无需重新训练
  • 易于接入私有数据:企业内部资料、产品手册、客服文档可直接接入
  • 可解释性更强:回答来自哪些检索片段可以展示给用户,更容易追溯依据

局限

  • 检索未必精准:向量相似不等于业务相关,有时检索到的是"像"而不是"对"
  • 分块质量高度影响效果:块太大噪音多,块太小上下文不够
  • 缺少复杂推理:擅长"找到相关资料",但跨文档整合、多跳检索等复杂推理场景容易吃力
  • 噪声干扰:检索结果带噪音时,模型可能被误导生成不准确的答案

因此后来出现了 Hybrid Search、Re-ranking、Query Rewrite、Multi-hop RAG、Graph RAG、Agentic RAG 等进阶方案,本质都是在修补 Naive RAG 这些短板。


七、总结

Naive RAG 的本质,是把外部知识切碎、编码、存储,在用户提问时取回最相关的碎片,交给大模型组织成答案。

它不是让模型"学会了新知识",而是让模型在回答时"临时查到了新知识"。这个差别非常关键:

  • 微调:把知识写进脑子里
  • RAG:给模型配一个随时能翻的资料库

它的核心流程是:清洗分块 → 向量化 → 存入向量库 → 用户提问时检索 → 拼入 Prompt → LLM 生成答案。它对所有 RAG 系统定义了一个今天仍然成立的基本范式:先检索,再生成

对于绝大多数企业知识库、私有问答、文档助手场景来说,Naive RAG 往往就是第一步,也是最值得先跑通的一步。

Naive RAG的核心步骤

1. 索引化(离线阶段):

文档 -> 分块 -> 向量化 -> 存储

2. 检索:

用户提问 -> 向量化 -> 检索数据库 -> 得到 TOP-K 个相关文档块

3. 增强生成:

增强提示词(原始问题 + 相关文档块) -> LLM -> 生成答案