基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
AIHT
内容
精选全部 AI 动态热点榜AI 日报收藏
分类
模型行业编程产品技术观点开源
更多
Agent 接入
返回首页开发者导航
AIHT
精选 / 资讯详情
8.0
热点
AI SCORE
技术实践2026-10-26 12:26

RAG 检索调优:先检查分块与召回

dev.to · AI#RAG#文档分块#检索评估
Editor brief · 编辑速览

文章分析小块检索精度与大块上下文完整性的取舍,主张按文档结构切分并保留元数据。调参前先测量召回率,避免将检索遗漏误判为模型或提示词问题。

文章思维导图
Knowledge map
拖拽缩放
AI deep read

AI 深度解读

RAG 答错了,先别急着换模型或改提示词。作者提出的排查顺序是:先确认正确段落有没有进入检索结果,再检查它是否被切断、混进了无关内容,或者丢掉了标题和章节信息。答案没送到模型面前,后面的优化就很难奏效。

这篇最有用的结论不是某个最佳 chunk_size,而是一套可验证的做法:先按文档结构切分,再限制长度;给片段补上可检索的上下文;用真实问题测召回,最后才调整参数。

chunk_size 调的是精度与上下文,数字不能直接照搬

分块(chunking)决定了哪些内容会作为一个单元进入索引。小块主题集中,查询更容易匹配,也能减少送给模型的无关 token;但切得太碎,指代、条件和适用范围可能消失。大块保留了上下文,却容易把多个主题揉进同一个嵌入向量,稀释真正相关的内容。

原文引用了 Chroma 研究团队在 2024 年 7 月发布的评估:覆盖五个语料库,按 token 计算召回率、精确率和交并比(IoU),不同切分策略的召回率差异最高达到 9%。

使用 text-embedding-3-large、检索 5 个片段时,RecursiveCharacterTextSplitter 的两组结果很直观:

切分配置,单位为 token 召回率 精确率
200,无重叠 88.1% 7.0%
800,重叠 400 85.4% 1.5%

片段扩大后,召回没有提高,无关内容却明显增多。原文还指出,OpenAI file search 的 800-token 分块、400-token 重叠默认配置,在那次实验中的召回略低于平均值,其他指标得分最低。这只能说明该配置在那套评估里的表现,不能据此判定它适合或不适合所有业务。

调参前,先确认你比较的是 token 还是字符。

LangChain 的 chunk_size=1000 默认表示 1,000 个字符;只有传入基于 token 的 length_function,才按 token 计数。跨工具比较时,数字相同不代表输入规模相同。

重叠也要单独看。它把上一块末尾复制到下一块开头,能缓解边界切断信息的问题,代价是重复存储、重复检索和重复占用模型上下文。Chroma 的实验中,减少重叠改善了会惩罚重复 token 的 IoU;但对较小的 all-MiniLM-L6-v2,250-token 分块、125-token 重叠获得了 82.4% 的最佳召回,零重叠时降到 77.1%。

因此,这些数据支持的是「需要按模型和语料测量」,并不支持一律取消重叠。

先保住章节边界,再让片段带上身份

固定长度窗口不知道一句话在哪里结束,更不知道「退款政策」和「配送说明」属于两个主题。一个 500 字符窗口,可能切开表格,也可能把两个章节的段落拼在一起。

作者采用两阶段切分:先沿 Markdown 标题、HTML 分区、PDF 标题或代码函数等结构拆开,再对过长的章节施加长度限制。LangChain 的 MarkdownHeaderTextSplitter 会把指定标题写入元数据;LlamaIndex 提供 MarkdownNodeParser,处理代码则有按语言切分、支持 chunk_lines 和 max_chars 限制的 CodeSplitter。递归切分器优先尝试段落、行、词,也是在尽量保留自然边界。

边界完整还不够。「期限是 30 天」即使没有被截断,也缺少对象和适用范围。

原文引用 Anthropic 在 2024 年 9 月介绍的 Contextual Retrieval:用 Claude 为片段生成通常为 50-100 tokens 的专属上下文,在生成嵌入和建立 BM25 索引之前前置。其测试中,仅上下文嵌入就让 top-20 检索失败率相对下降 35%,从 5.7% 降到 3.7%。

作者给出的低成本方案是,把文档标题和章节路径一起送去生成嵌入,同时保存为元数据。前者帮助匹配,后者支持按产品、版本、租户过滤,以及追溯来源。两件事要分别落实:只把标题存在元数据里,并不会自动改变嵌入内容。

下面这段代码把结构切分、长度限制和标题上下文串在了一起,也提供了后续评估入口:

from langchain_text_splitters import MarkdownHeaderTextSplitter, RecursiveCharacterTextSplitter

def chunk_markdown(doc_title: str, markdown: str):
    # 第一阶段:按真实结构切分,将标题写入元数据
    by_section = MarkdownHeaderTextSplitter(
        headers_to_split_on=[("#", "h1"), ("##", "h2"), ("###", "h3")]
    ).split_text(markdown)

    # 第二阶段:仅在长章节内部限制长度,此处 chunk_size 的单位是字符
    sized = RecursiveCharacterTextSplitter(
        chunk_size=1200, chunk_overlap=100
    ).split_documents(by_section)

    for d in sized:
        section = " > ".join(d.metadata[k] for k in ("h1", "h2", "h3") if k in d.metadata)
        d.metadata.update({"doc_title": doc_title, "section": section})
        # 将这段文本用于嵌入:标题、章节路径与正文
        d.metadata["embed_text"] = f"{doc_title} | {section}\n{d.page_content}"
    return sized

def recall_at_k(eval_set, search, k=10):
    """eval_set: [(question, expected_section)], search(q, k) -> list of chunks."""
    hits = sum(
        any(c.metadata["section"] == expected for c in search(q, k))
        for q, expected in eval_set
    )
    return hits / len(eval_set)

要盯住 d.metadata["embed_text"] 这一行:它只是构造了待嵌入文本,实际索引流程仍需读取这个字段。chunk_size=1200 和 chunk_overlap=100 都是假设起点,不是推荐值。原文还说明,使用 split_documents 时,重叠发生在章节内部,不跨章节边界。

怎么证明切分改好了,而不是碰巧答对一次

作者建议收集 50-100 个真实问题,来源可以是日志、支持工单或团队提问,再为每个问题标注真正能回答它的章节。

随后测 recall@k:有多少问题的正确章节出现在前 k 个检索结果中。上面代码默认 k=10,只要结果中存在目标章节,就记为命中。

每次只改一个变量,例如长度、重叠或切分器,然后重建索引、重新测量。改动会影响索引里的嵌入,不能只修改配置就宣称优化完成。没有标注集时,原文提到可以让 LLM 基于语料生成评估问题,但仍优先推荐真实查询。

这里有两个边界。第一,Chroma 的 token 级召回与示例里的问题级章节命中率不是同一口径,不能直接横向比较。第二,检索命中只能证明证据被找到了,不能证明模型最终正确使用了证据。

原文的报销机器人也只是一个假设案例:固定 1,000 字符分块中的「30 天内提交」,补上 HR Handbook | Expenses > Deadlines 后更容易被识别。作者要求用 recall@10 验证,并未提供这次改造的真实前后数据。

我的判断

我赞同把切分和召回放到换模型之前排查。它把「回答感觉不对」变成了一个可定位的问题:正确证据究竟在哪一步丢了。

但「结构切分需要更少重叠」在文中主要是作者经验,没有给出他们团队的对照数据。它也依赖文档本身有可信的结构;标题缺失、章节过长,或答案横跨多个章节时,仍需要额外处理。标题前置是便宜的上下文补充,也不能直接视为 Anthropic 生成式上下文方案的等价替代。

示例评估还有一个工程细节:只比较 section 字符串,多份文档出现相同章节名时可能误判。正式评估应使用能唯一定位来源的标识。若一个问题需要多个章节才能回答,单次命中也不足以代表证据完整。

固定长度和递归切分依然适合作为基线。结构切分是否值得增加解析工作,应由自己的查询集回答;原文最可靠的主张,正是先测量再选择。

能拿来做什么

  • 为团队文档问答整理 50-100 个真实问题,标注答案章节,先记录当前配置的 recall@10,再开始修改切分方式。
  • 对 Markdown 技术文档先按标题拆分,再限制长章节;把标题与章节路径加入实际嵌入输入,并核对参数使用字符还是 token。
  • 在 Node 或其他检索服务的入库流程中保留来源、章节、产品和版本信息,让过滤与引用能够追溯到具体文档。
  • 如果正在做代码检索,可以把语言结构切分与现有递归切分做对照,每次只改变一个变量,用相同问题集判断是否值得替换。
Original source

本文由 AI 通读原文后重新编写,观点部分为站内判断,细节以原文为准,来源 dev.to · AI,原文版权归原作者所有。

阅读原文
上一篇
实测 Agent 生成视频:能渲染仍需审美验收
下一篇
用组合建模给AI探索测试划定空间

资讯速览

dev.to · AI · 2026-10-26 12:26

AI 评分 8.0 / 10

相关标签

#RAG#文档分块#检索评估

继续阅读

  • 给编程 Agent 一套抑制过度设计的规则
  • 实测 Agent 生成视频:能渲染仍需审美验收
  • 用组合建模给AI探索测试划定空间
  • 实测ElevenLabs免费API的计费与边界
  • RAG重排如何权衡精度、延迟与成本