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

RAG 答不准,先检查文本切块边界

dev.to · AI#RAG#文本切块#调试
Editor brief · 编辑速览

文章用 API 密钥轮换问答说明:切块可能拆散主题与关键事实,即使文档已入库也难以正确检索。提供小脚本检查切割位置,帮助在调整嵌入、重排和提示词前定位问题。

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

AI 深度解读

文档明明写着「旧 API key 在轮换后还能使用 24 小时」,RAG 助手却答不出来,问题可能出在切块:标题留在上一块,时效落进下一块,还混入撤销密钥的说明。信息进了索引,却没有以完整、可理解的形式被检索。

作者用一段 Python 脚本,把固定长度切块和按标题切块摆在一起检查。它值得读的地方是把排查顺序落到了实处:先确认模型拿到的材料是否完整,再考虑 embeddings、重排器和提示词。

为什么答案在索引里,助手还是找不到

检索增强生成(RAG)通常先把文档切成块,为每块生成向量,再根据问题检索相关内容。后续环节处理的都是这些块。原始页面写得再清楚,也不能保证某个被召回的片段保留了完整答案。

作者归纳了三类边界问题:

  • 固定字符数可能截断句子,甚至把一个单词拆开。
  • 操作说明和所属标题分离,读者不知道操作针对哪个对象。
  • 块太小,缺少信息;块太大,混入多个主题,匹配不够集中。

这些问题通常不会触发程序报错。流水线照常运行,质量却悄悄下降。

作者构造了一页虚拟文档,包含 API keys 下的三个小节:Rotate a key、Revoke a key 和 Limits。其中,轮换后的旧 key 可继续使用 24 小时;撤销立即生效且不可恢复;每个项目最多有 5 个活跃 key,轮换不算新增。

这些数字是演示文档里的设定,不是某款产品的真实规则。示例要验证的是:切块之后,问题的主题和回答所需的事实还在不在一起。

答案进入索引,不等于回答问题所需的上下文进入了同一个块。

用一个脚本,看清切口落在哪里

下面的脚本把同一份 Markdown 分别按固定长度和标题切分,再打印风险标记与首尾文本。它只使用 Python 标准库,适合检查切块结果。

import re
import sys

def fixed_size(text, size):
    """Cut every `size` characters, no overlap."""
    return [text[i:i + size] for i in range(0, len(text), size)]

def by_heading(text):
    """Split on markdown headings; keep the heading path with each chunk."""
    chunks, path, body = [], {}, []

    def flush():
        content = " ".join(body).strip()
        if content:
            heading = " > ".join(path[k] for k in sorted(path))
            chunks.append(f"[{heading}] {content}")
        body.clear()

    for line in text.splitlines():
        m = re.match(r"^(#+)\s+(.*)", line)
        if m:
            flush()
            level = len(m.group(1))
            path = {k: v for k, v in path.items() if k < level}
            path[level] = m.group(2)
        elif line.strip():
            body.append(line.strip())
    flush()
    return chunks

def flags(chunk, is_last):
    text = chunk.strip()
    out = []
    if not text.startswith(("#", "[")):
        out.append("NO HEADING")
    if not is_last and not text.endswith((".", "!", "?", ":")):
        out.append("CUT MID-SENTENCE")
    if len(text.split()) < 8:
        out.append("TOO SMALL")
    return out

def report(name, chunks):
    print(f"== {name}: {len(chunks)} chunks")
    for i, c in enumerate(chunks, 1):
        f = flags(c, i == len(chunks))
        one_line = " ".join(c.split())
        print(f"C{i} ({len(c.split())} words) {f or ['ok']}")
        print(f"   starts: {one_line[:38]!r}")
        print(f"   ends:   {one_line[-38:]!r}")

text = open(sys.argv[1]).read()
report("fixed-size, 120 chars", fixed_size(text, 120))
print()
report("by heading", by_heading(text))

重点看 chunks.append(f"[{heading}] {content}"):标题路径被直接放进每块文本,帮助片段独立表达所属主题。固定长度方案则每 120 个字符切一次,没有重叠。

作者使用 Python 3.13.5,执行 python3 chunk_check.py sample.md。固定长度方案生成 4 块,词数依次为 25、24、22、16;前三块都被标记为 CUT MID-SENTENCE,后三块都被标记为 NO HEADING。

关键事实「24 hours」落在 C2,开头却是 y keeps working,后半部分又转向撤销操作。C3 混合撤销说明和额度标题,C4 从 ve up to 5 active keys 开始。原本清晰的页面,变成了需要跨块拼接才能读懂的材料。

按标题切分后得到 3 块,分别有 35、29、23 个词,全部显示 ok。轮换标题和 24 小时的事实终于在一起。

不过,ok 只表示通过这几个简单检查。NO HEADING 检查开头是否为 # 或 [;CUT MID-SENTENCE 检查结尾是否为 . ! ? :,并豁免最后一块;TOO SMALL 检查是否少于 8 个词。首尾各展示 38 个字符,方便人工定位。

脚本没有检查「过大」,也不会真正理解句子。中文标点和不依赖空格分词的文本,都需要另行适配。

标题要带上,长章节还得继续拆

作者没有主张把所有文档都改成按标题切块。一个章节可能长到不适合整块处理,另一些文档根本没有标题。

更实用的做法是保留结构,再处理长度:每块附带所属标题或标题路径;长章节优先按段落拆分;只有单个段落仍然过长时,才退回硬性长度切分。

标题路径的作用,是补回片段失去的定位信息。例如「点击 Rotate,然后确认」单独出现时不够明确,加上 [API keys > Rotate a key],才知道操作对象是什么。原文借 Anthropic 的 Contextual Retrieval 说明这类上下文缺失,但没有在脚本中实现那套方案。

检查也不该只跑一次。作者建议抽取 10 到 20 份代表性文档,包含长表格、FAQ、更新日志、PDF 转文本等麻烦样本。先统计标记,再阅读被标记的内容,判断问题普遍存在,还是集中在少数文档。

每次只改一个切块设置,并重新运行检索测试。可以用小规模 recall@k 检查:对已有问题,前 k 个检索结果是否找回需要的材料。

原文还介绍了浏览器里的 RAG Chunk Visualizer & Cost Calculator,可显示彩色块、token 估算和质量标记,并称文本留在本机。另有 6 页的 RAG Quick-Start 和 46 页的完整指南。这些是补充资源介绍,不是切块策略有效性的实验依据。

我的判断

我赞成把切块检查放在排查前面。它成本低,能直接暴露「主题和事实分家」的问题,也能避免把输入材料缺损误判成模型能力不足。

但这篇的证据范围很窄:作者实际展示的是一份自造文档上的切分结果,没有真实问题集上的召回率对照,也没有生成答案的准确率数据。按标题切分在这个例子里更完整,不能据此认定它在一般团队的知识库里效果更好。

固定长度、带重叠窗口的切分,以及结构优先再限制长度,都是可以比较的方案。原文只测试了无重叠的固定长度方案,没有给出这些替代做法之间的对照数据。策略建议仍属于经验之谈。

这段 Markdown 解析也只是草图。它逐行识别 #,没有处理围栏代码块等完整语法情形;正文中的空行会被合并,结构信息也可能损失。对于包含代码示例的技术文档,不能直接把它当成生产解析器。

风险标记负责找疑点,检索测试才负责判断改动有没有帮助。

此外,「事实应与主题同块」是很好的默认目标,但原文没有讨论相邻块扩展、父子块检索等补充上下文的办法。如果已有流水线会补回上下文,还需要结合实际送给模型的材料判断问题。

能拿来做什么

  • 给文档助手排查错答时,先打印实际召回块的首尾和标题路径,确认用户问的对象与关键事实是否一起出现,再决定是否调整提示词。
  • 从技术文档库抽取 10 到 20 份样本,加入代码块、FAQ、表格和 PDF 转文本,运行检查并人工读异常片段;处理中文前,先调整标点和长度规则。
  • 调整切块时先保留标题路径,长章节按段落继续拆分,并保留一组真实问题做 recall@k 对照,每次只改变一个设置。
  • 在 Node 或 Python 的入库流水线中加入同类检查报告,持续观察句中截断和极短片段,让文档更新引入的边界问题更容易被发现。
Original source

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

阅读原文
上一篇
强模型做审计,低成本模型执行计划
下一篇
Agent 能读验证码,审批就可能失效

资讯速览

dev.to · AI · 2026-11-12 16:39

AI 评分 8.0 / 10

相关标签

#RAG#文本切块#调试

继续阅读

  • Codex 安全扫描开放 CLI 与 SDK
  • 强模型做审计,低成本模型执行计划
  • Agent 能读验证码,审批就可能失效
  • AI 清理进程为何会误关 Mac 应用
  • 接口返回成功,还需验证用户能否看到