游乐游手机版
首页/AI教程/文章详情

RAG系列三:调对4个参数让系统从能用变好用

时间:2026-06-08 15:20
RAG系统通过调整块大小、重叠长度、召回数量和嵌入模型四个核心参数可显著提升答案质量。块大小设为答案长度1 5-2倍,重叠取块大小10%-20%,召回数依据查询复杂度设定,嵌入模型需匹配文档语言类型。合理配置这些参数能平衡检索精度与上下文完整性,避免答案碎片化或信息冗余。

为什么相同的代码逻辑,你的 RAG 系统回答总是不准确?

在前两篇文章中,我们成功搭建了一个可运行的 RAG Pipeline。然而,不少朋友很快发现:代码虽然能够跑通,但答案质量却极不稳定——有时精准得令人惊喜,有时文档里明明有现成答案,检索却一无所获;有时检索结果看似准确,大模型却长篇大论地答非所问。

RAG 系列(三):调对这 4 个参数,让你的 RAG 从「能用」变「好用」

问题的根源通常不在代码本身,而在于参数设置。

RAG 系统有 4 个核心参数,就像收音机上的四个调节旋钮:

  • Chunk Size(文本块大小):决定每个文本块的长度
  • Chunk Overlap(重叠长度):决定相邻文本块之间保留多少重叠内容
  • Top-K(召回数量):决定每次检索返回多少个文本块
  • Embedding Model(嵌入模型):决定文本如何转换为向量表示

这四个参数的组合配置,直接决定了系统“能否找到相关信息”以及“找到的信息是否足够支撑回答”。接下来,我们通过控制变量实验的方式,带你直观感受不同参数设置带来的效果差异。

参数一:Chunk Size —— 每个文本块应该切多长?

什么是 Chunk Size?

想象一下,你正在翻阅一本 500 页的技术手册。Chunk Size 就相当于你每次翻开的页数——是只看 1 页、5 页,还是直接看 50 页?

在 RAG 系统中,Chunk Size 指的是每个文本块的最大字符数(或 Token 数)。文档会被切分成若干块,每一块的长度都不能超过这个阈值。

为什么 Chunk Size 如此关键?

Chunk Size 直接影响以下两个核心指标:

Chunk Size检索精度上下文完整性通俗理解
太小(128)像查词典词条——精准但孤立无援
中等(512)像阅读一段话——有上下文又不至于太长
太大(2048)像浏览一整章——信息全面但噪音较多

切得太小会怎样?假设文档中写道:“系统使用 Redis 做缓存,默认过期时间是 3600 秒。如果超过这个时间,数据会被自动清理。”如果设置 Chunk Size=128,这句话可能被切成两块:“系统使用 Redis 做缓存,默认过期时间是 3600 秒。”和“如果超过这个时间,数据会被自动清理。”当你提问“Redis 缓存过期后会发生什么?”时,Retriever 可能只召回第一块,LLM 看到“3600 秒”却不知道后面还有“自动清理”——答案自然残缺不全。

切得太大呢?假设 Chunk Size=2048,一个块里塞了 5 个不相关的主题。当你询问某个具体问题时,这个块被召回后,LLM 的注意力会被无关内容稀释——就像在嘈杂的菜市场里想听清一个人说话,难度倍增。

如何选择合适的 Chunk Size?

虽然没有万能公式,但有一条经验法则值得参考:

Chunk Size ≈ 你期望的答案长度的 1.5 ~ 2 倍

文档类型推荐 Chunk Size理由
FAQ / 问答对256 ~ 384答案简短,精准匹配更为重要
技术文档 / API 手册512 ~ 768答案中等长度,需要一定上下文支撑
论文 / 书籍章节1024 ~ 1536论述性强,需要大段上下文来理解
法律合同 / 医疗记录768 ~ 1024专业术语密集,需要前后文推断

参数二:Chunk Overlap —— 相邻块之间应重叠多少?

什么是 Chunk Overlap?

还是那本技术手册。如果你每次看 5 页,Overlap 就相当于每次翻页时保留上一章的最后几页。例如 Overlap=1 表示:第一次看 1-5 页,第二次看 5-9 页(第 5 页会重复出现)。

为什么需要重叠设计?

如果没有重叠,关键信息很可能被“切在接缝处”:

块 A:"系统使用 Redis 做缓存,默认过期时间是 3600 秒。"块 B:"如果超过这个时间,数据会被自动清理。"

如果用户问“Redis 缓存过期后会发生什么?”,Embedding 模型可能认为块 B 与问题更相关(因为都提到了“过期后”),于是只召回块 B。但块 B 开头是“如果超过这个时间”——缺少块 A 的上下文,LLM 就不知道“这个时间”具体指什么。

如果设置 Overlap=50,块 B 开头会带上之前 50 个字符:

块 B(带重叠):"默认过期时间是 3600 秒。如果超过这个时间,数据会被自动清理。"

这样一来,即使只召回块 B,LLM 也能明白“这个时间 = 3600 秒”。

Overlap 应该设置多少?

一般建议设为 Chunk Size 的 10% ~ 20%:

Chunk Size推荐 Overlap说明
25625 ~ 50文本较短,少量重叠即可保住上下文
51250 ~ 100通用场景下的黄金比例
1024100 ~ 200长文本需要更多重叠来保障衔接

参数三:Top-K —— 每次召回多少个文本块?

什么是 Top-K?

Top-K 是 Retriever 每次返回的文本块数量。K=4 表示“返回最相关的 4 个块”,K=10 则表示“返回最相关的 10 个块”。

为什么 Top-K 很关键?

K 值太小容易漏掉信息,K 值太大则容易引入噪音。

场景 A:K=2,遗漏了关键信息

用户问:“怎么配置数据库连接池和日志级别?”这个问题涵盖两个主题。如果 K=2,Retriever 可能只返回“数据库连接池”相关的两块,完全没有涉及“日志级别”——LLM 只能回答一半内容。

场景 B:K=20,噪音淹没了有效答案

用户问:“默认超时时间是多少?”文档里明明有明确答案。但 K=20 召回了 20 个块,其中 19 个都在讲不相关的内容。LLM 的上下文窗口被无关信息占满,反而找不到那个简单的数字。

如何选择合适的 Top-K?

Top-K = 期望答案涉及的主题数 × 2 ~ 3

查询类型推荐 K理由
单点事实查询(“默认端口是多少?”)3 ~ 5答案集中,少而精更高效
多条件查询(“怎么配置 A 和 B?”)5 ~ 8可能涉及多个主题,需更大覆盖
综合概述(“总结第三章的内容”)8 ~ 12需要覆盖整章的多个要点

参数四:Embedding Model —— 谁来担任「语义翻译」?

Embedding 是 RAG 系统的「翻译官」

Embedding 模型的任务很简单:将文本转换为一串数字(向量)。语义相似的文本,其向量之间的距离就近;语义不相似的,距离就远。

Retriever 正是依赖这个原理——将用户问题转换为向量,然后在向量库中寻找距离最近的那些文本块。

不同模型的差异有多大?

差异相当显著。同一个问题,不同模型召回的结果可能截然不同。

模型擅长语言维度定位适合场景
text-embedding-3-small英文1536性价比高英文文档、预算敏感型项目
text-embedding-3-large英文3072精度优先英文文档、对精度要求极高
BAAI/bge-large-zh-v1.5中文1024中文领域最强中文文档、国内场景首选
BAAI/bge-m3多语言1024多语言通用中英混合、跨语言检索任务

一个真实的对比实验

我们使用同一份中文技术文档(《Automotive SPICE PAM v4.0》),同一个问题,对比 text-embedding-3-smallBAAI/bge-large-zh-v1.5 的召回效果:

问题:“什么是过程能力等级 1?”

模型第 1 召回结果第 2 召回结果评价
text-embedding-3-small第 12 页:关于项目管理的段落第 89 页:关于风险评估的段落❌ 均未涉及“过程能力等级”
BAAI/bge-large-zh-v1.5第 45 页:过程能力等级 1 的定义第 46 页:等级 1 的实践示例✅ 精准命中目标内容

原因很直观:OpenAI 的模型主要基于英文语料训练,对中文专业术语的理解能力,远不如 BGE 这类在中文语料上专门微调过的模型。

如何选择 Embedding 模型?

可以参考以下决策逻辑:

你的文档是什么语言?├─ 纯英文 → text-embedding-3-small(性价比最高)│ 或 text-embedding-3-large(精度最高)├─ 纯中文 → BAAI/bge-large-zh-v1.5(国内首选)│ 或 BAAI/bge-m3(如果有中英混合需求)└─ 中英混合 → BAAI/bge-m3(多语言支持最佳)

实战:控制变量实验

我们来做一组实验:使用同一份文档、同一个问题,只改变 Chunk Size,观察答案质量如何变化。

实验设计

"""RAG 参数控制变量实验固定:文档、问题、Embedding 模型、Top-K、LLM变量:Chunk Size"""import osfrom pathlib import Pathfrom langchain_text_splitters import RecursiveCharacterTextSplitterfrom langchain_chroma import Chromafrom langchain_community.document_loaders import PyPDFLoaderfrom langchain_core.prompts import ChatPromptTemplatefrom langchain_core.output_parsers import StrOutputParserfrom langchain_core.runnables import RunnablePassthroughfrom langchain_openai import ChatOpenAI, OpenAIEmbeddings# 加载文档doc = PyPDFLoader("./data/Automotive-SPICE-PAM-v40.pdf").load()# Embedding(固定)embeddings = OpenAIEmbeddings(model="BAAI/bge-large-zh-v1.5",api_key=os.getenv("EMBEDDING_API_KEY"),base_url="https://api.siliconflow.cn/v1",chunk_size=32,)# LLM(固定)llm = ChatOpenAI(model="glm-4-flash",api_key=os.getenv("LLM_API_KEY"),base_url="https://open.bigmodel.cn/api/paas/v4",temperature=0,)# 测试不同 Chunk Sizedef test_chunk_size(chunk_size, overlap):print(f"n{'='*50}")print(f"Chunk Size={chunk_size}, Overlap={overlap}")print(f"{'='*50}")# 切分splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size,chunk_overlap=overlap,length_function=len,)chunks = splitter.split_documents(doc)print(f"生成 {len(chunks)} 个块")# 建向量库persist_dir = f"./chroma_db_{chunk_size}"if os.path.exists(persist_dir):import shutilshutil.rmtree(persist_dir)vector_store = Chroma.from_documents(documents=chunks,embedding=embeddings,persist_directory=persist_dir,)# 构建 RAG Chain(LCEL 方式)retriever = vector_store.as_retriever(search_kwargs={"k": 4})prompt = ChatPromptTemplate.from_messages([("system", "根据参考内容回答。参考:n{context}"),("human", "{question}")])rag_chain = ({"context": retriever | (lambda docs: "nn".join(d.page_content for d in docs)), "question": RunnablePassthrough()}| prompt | llm | StrOutputParser())# 提问question = "什么是过程能力等级 1?"answer = rag_chain.invoke(question)print(f"n答案:{answer[:200]}...")# 打印召回的来源sources = retriever.invoke(question)print(f"n召回 {len(sources)} 个来源:")for i, s in enumerate(sources[:3], 1):print(f"[{i}] 第{s.metadata.get('page', '?')}页: {s.page_content[:80]}...")# 跑三组实验test_chunk_size(chunk_size=128, overlap=20)test_chunk_size(chunk_size=512, overlap=50)test_chunk_size(chunk_size=1024, overlap=100)

预期结果

Chunk Size块数召回质量典型现象
128很多(约 4000)精度高但上下文断裂召回的块中包含“过程能力等级”关键词,但前后文不足,LLM 回答零散不连贯
512中等(约 1000)最佳平衡点召回的块包含完整的定义与示例,LLM 回答连贯且准确
1024较少(约 500)上下文完整但精度偏低召回的块中包含大量无关内容(如其他等级的描述),LLM 回答冗长拖沓

最容易踩的 5 个坑

坑 1:Chunk Size 按 Token 数设置,但 length_function 却用了字符数

# ❌ 错误:你以为 chunk_size=512 代表 512 个 Tokensplitter = RecursiveCharacterTextSplitter(chunk_size=512)# 实际上默认 length_function=len 是按字符数计算的!# 512 字符 ≈ 256 Token(中文),导致块比你预想的小一半

解决办法:如果希望按 Token 数切分,需要显式指定 tokenizer:

import tiktokendef token_length(text):return len(tiktoken.encoding_for_model("gpt-4").encode(text))splitter = RecursiveCharacterTextSplitter(chunk_size=512,length_function=token_length,# ✅ 按 Token 数计算)

坑 2:Overlap 设置过大,导致向量库中 30% 都是重复内容

Overlap 并非免费资源。每个重叠字符都需要进行一次 Embedding 计算,并在向量库中占用一份存储空间。Overlap=100、Chunk Size=200 意味着 50% 的存储都是冗余的。

解决办法:Overlap 控制在 Chunk Size 的 10%~15%,最高不要超过 20%。

坑 3:更换 Embedding 模型后,没有清空旧的向量库

# ❌ 错误:昨天用 BGE 建了索引,今天换成 OpenAI,直接复用同一个 chroma_db/vector_store = Chroma.from_documents(documents=chunks, embedding=new_embeddings)# 结果:查询时使用的向量与索引时的向量来自不同模型,完全无法匹配

解决办法:更换 Embedding 模型时,务必删除旧向量库并重新建立索引:

if os.path.exists(persist_directory):shutil.rmtree(persist_directory)# ✅ 清空旧数据

坑 4:Top-K 固定写死,没有根据问题复杂度动态调整

所有问题都使用 K=4,但“默认端口是多少?”(简单事实查询)和“总结第三章的所有要点”(综合概述)所需的信息量完全不同。

解决办法:简单问题用 K=34,复杂问题用 K=810。更进阶的做法是让 LLM 先判断问题复杂度,再动态决定 K 值(后续文章会详细介绍)。

坑 5:没有监控“零召回”(Zero Retrieval)的情况

有时候 Retriever 召回了 0 个相关块(例如用户问了一个文档中完全不涉及的话题),但你浑然不知,LLM 只能凭记忆胡乱编造。

解决办法:为检索结果添加阈值过滤——如果最相似块的相似度分数低于某个阈值(如 0.6),直接告知用户“文档中未找到相关信息”,而不是将不相关的块塞给 LLM:

# 在检索后加一层过滤docs = retriever.invoke(question)if not docs or max_similarity < 0.6:return "抱歉,根据现有文档无法回答这个问题。"

参数选择速查表

将以上内容浓缩为一张速查表,方便你贴在显示器旁随时参考:

参数小白默认值什么时候调大什么时候调小
Chunk Size512答案需要大段上下文(书籍/论文)答案很短(FAQ/配置项)
Chunk Overlap50(≈10%)句子经常跨页或跨段文档结构清晰,边界明确
Top-K4问题涉及多个主题问题很具体,答案唯一
EmbeddingBGE(中文)/ OpenAI(英文)中文专业文档场景英文通用文档场景

小结

本文详细讲解了 RAG 系统最核心的 4 个参数:

  1. Chunk Size:决定每个文本块的长度。默认推荐 512,短答案场景用 256,长论述场景用 1024。
  2. Chunk Overlap:决定相邻块之间的重叠量。默认设为 Chunk Size 的 10%,确保跨块信息不被切断。
  3. Top-K:决定召回块的数量。默认 4,复杂问题增大到 8,简单问题减小到 3。
  4. Embedding Model:中文场景选 BGE,英文场景选 OpenAI,切换时记得清空向量库并重建索引。

通过控制变量实验我们证实:参数并非越大越好,也并非越小越好,关键在于找到适合你文档类型和查询模式的最佳平衡点。

参考资料

  • LangChain Text Splitters 文档 —— 官方分块策略详解
  • BGE Embedding 模型 GitHub —— 中文 Embedding 最佳实践
  • MTEB Leaderboard —— Embedding 模型权威排行榜
  • ChromaDB 距离度量说明 —— 余弦相似度 vs 欧氏距离
来源:https://juejin.cn/post/7634438325378859050
上一篇本地大模型部署:Ollama+Qwen2.5在Win11上的配置步骤详解 下一篇我是如何剖析Claude Code整体架构与启动流程
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Kimi App手机电脑联动下载安装及浏览器兼容教程
AI教程 · 2026-06-09

Kimi App手机电脑联动下载安装及浏览器兼容教程

本文介绍了Kimi智能助手从手机端到电脑端的下载与安装方法,重点阐述了不同平台(包括iOS、Android、Windows、macOS)的获取途径。同时,详细说明了如何通过浏览器直接访问网页版,并针对主流浏览器的兼容性进行了分析,旨在帮助用户根据自身设备选择最便捷、稳定的使用方式。

HeyGen稳定安装步骤:先配置创意团队环境再注册开通
AI教程 · 2026-06-09

HeyGen稳定安装步骤:先配置创意团队环境再注册开通

HeyGen的稳定安装与高效使用,关键在于前期团队环境的统一规划与后期账号流程的顺畅完成。团队需明确设计规范、素材管理及权限分工,为工具运行打下基础。随后,通过官方渠道完成注册、验证及订阅开通,确保服务稳定。最后进行基础功能测试与团队培训,即可快速投入实际创作流程。

Mochi 1从零搭建本地服务与工作流导入指南
AI教程 · 2026-06-09

Mochi 1从零搭建本地服务与工作流导入指南

本文介绍了在成功完成Mochi1本地服务的基础搭建后,如何继续处理工作流导入这一关键后续步骤。内容涵盖工作流文件准备、导入操作的具体流程、常见问题的排查与解决,以及导入后的配置优化与测试验证,旨在帮助用户将预设的自动化流程顺利集成到本地环境中,确保工具发挥完整效能。

InvokeAI Linux用户安装配置与节点处理指南
AI教程 · 2026-06-09

InvokeAI Linux用户安装配置与节点处理指南

本文详细介绍了在Linux系统上安装和配置InvokeAI的完整流程。内容涵盖从环境准备、依赖安装到模型下载与加载的关键步骤,并重点解析了核心组件“处理节点”的安装与使用方法。指南旨在帮助用户顺利完成部署,并理解其工作流程,以便更好地利用这一AI图像生成工具进行创作。

Dify保姆级部署指南:服务安装与模型接入下载
AI教程 · 2026-06-09

Dify保姆级部署指南:服务安装与模型接入下载

本文详细介绍了开源AI应用开发平台Dify的部署流程。内容涵盖从服务器环境准备、Docker安装、Dify核心服务启动,到如何接入OpenAI、Azure等云端大模型API,以及如何配置Ollama等本地模型。最后,还提供了使用ModelScope社区下载特定模型文件并集成到本地环境中的具体操作方法,旨在帮助用户快速搭建属于自己的AI应用开发与测试平台。