资讯动态

RAG 分块策略实测:固定长度、递归切分与语义切分如何选择,TaoToken 统一 Key 接入配置骨架

发布时间:2026/9/27 20:39:22 来源:尧图企业网站定制
1. 为什么你的 RAG 总是答非所问分块策略才是第一道质量闸门RAG 分块策略决定了检索召回的上限固定长度、递归切分与语义切分三种方案在证据完整性、上下文噪声和 Token 成本上差异巨大。如果你正在搭企业知识库、做文档问答或者调了半天 Prompt 发现回答还是不稳定那大概率问题不在模型而在切分。我见过太多项目把 Chunk Size 当成一个拍脑袋的经验参数——切 500 字、重叠 50 字然后就开始换模型、改 Prompt、加 Rerank唯独不回头看“证据到底有没有落在正确的片段里”。分块策略至少影响五个维度检索召回正确证据能否进前 K、证据完整性前置条件和例外情况有没有被切断、噪声比例无关文本占了多少上下文、生成成本每次注入的 Token 数、更新成本改一段要不要重算一堆向量。一份制度文档里“仅在 E102 连续出现三次时”这个条件如果被切到了下一个 Chunk模型就只能看到“必须先停止服务”回答自然缺胳膊少腿。这篇会带你走完一条可复现的实测路径先固定实验变量再分别跑固定长度、递归切分、语义切分三种策略用 RecallK、MRR、引用准确率和成本四个指标做对比最后给出一套可直接复制的config.toml与settings.json配置骨架并通过 TaoToken 统一 Key 接入 AI 工具完成向量化和生成验证。适合已经跑通 RAG 基础链路、想系统优化检索质量的数据工程和算法同学。2. TaoToken 前置统一 Key 与 API 通道准备在开始分块实验之前先把模型调用通道固定下来。分块策略对比要求 Embedding 模型和生成模型在整个实验过程中保持一致否则分数变化你根本分不清是切分带来的还是模型换了的。TaoToken 在这里的作用是提供一个统一的 Key 和 API 入口让你在 Python 脚本、向量化服务、生成服务之间复用同一套凭证不用每个工具单独配一遍。你需要准备的东西很简单一个 TaoToken 账号一个 API Key以及确认你要用的 Embedding 模型和生成模型名称。API 基础地址是https://taotoken.net/api所有请求走这个入口。如果你还没建 Key可以到控制台创建创建和管理 API Keyhttps://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite接入文档含各语言 SDK 示例https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite注意Embedding 模型和生成模型建议分开配置 Key 用途标签方便后续按实验轮次统计成本。同一轮实验内不要更换模型版本。如果你后续要做长期编码或 Agent 类任务可以了解 Coding Plan如果只是想先验证模型对话效果可以直接用模型对话页面试几条问题模型对话体验https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewriteCoding Plan 详情https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite3. 可复制配置config.toml 与 settings.json 骨架下面这套配置骨架把分块参数、Embedding 参数、检索参数和 TaoToken 接入信息集中管理方便你在不同策略之间切换时只改一个字段。先建目录结构mkdir -p rag-chunk-lab/{config,data,scripts,results} cd rag-chunk-labconfig/config.toml内容如下重点是[chunk]段三种策略通过strategy字段切换[taotoken] base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY embedding_model your-embedding-model chat_model your-chat-model timeout_seconds 60 [chunk] # 可选值: fixed | recursive | semantic strategy recursive max_chars 800 overlap_chars 80 semantic_threshold 0.45 semantic_max_chars 900 [retrieval] top_k 5 distance cosine [eval] question_set data/questions.jsonl gold_labels data/gold_labels.jsonl output_dir resultsconfig/settings.json用来放运行时覆盖项和实验元信息方便记录 Git Commit 和模型版本{ experiment_id: chunk-exp-001, embedding_dim: 1536, normalize: true, record_git_commit: true, strategies: [fixed, recursive, semantic], top_k_list: [3, 5, 10], notes: 同一批文档、同一问题集仅切换 chunk.strategy }环境变量里设置 Key不要写进配置文件export TAOTOKEN_API_KEY你的Key提示embedding_dim要和实际模型输出维度一致本文以 1536 维为例。维度不匹配会导致向量库写入失败或检索结果异常。4. 三种切分策略的实现与验证请求4.1 固定长度切分按字符数截断并保留固定重叠实现最简单适合快速建立基线def fixed_split(text: str, size: int 500, overlap: int 80) - list[str]: if size overlap: raise ValueError(size 必须大于 overlap) chunks, start [], 0 while start len(text): end min(len(text), start size) value text[start:end].strip() if value: chunks.append(value) if end len(text): break start end - overlap return chunks固定切分适合日志、连续转写文本这类结构很弱的内容。但在制度文档里要小心一个条款的“例外情况”被切到下一个 Chunk 后模型很容易只看到主规则而忽略例外。4.2 递归结构切分优先按标题、段落、列表、句子等分隔符切分只有当单元超过最大长度时才继续拆。分隔符顺序是关键把句号放在标题之前会让结构信息先丢失SEPARATORS [\n## , \n### , \n\n, \n, 。, , ] def recursive_split(text: str, max_chars: int 800) - list[str]: text text.strip() if len(text) max_chars: return [text] if text else [] for separator in SEPARATORS: parts text.split(separator) if len(parts) 1: continue result, current [], for part in parts: candidate f{current}{separator}{part} if current else part if len(candidate) max_chars: current candidate else: result.extend(recursive_split(current, max_chars)) current part result.extend(recursive_split(current, max_chars)) return result return [text[:max_chars], *recursive_split(text[max_chars:], max_chars)]递归切分通常是通用文档的首选基线它保留了标题层级和段落边界同时用最大长度兜底。4.3 语义切分先按句子切分再计算相邻句子的语义相似度相似度明显下降时聚合为一个语义单元def semantic_split(sentences, embeddings, threshold0.45): groups, current [], [] for index, sentence in enumerate(sentences): current.append(sentence) if index len(sentences) - 1: break similarity cosine(embeddings[index], embeddings[index 1]) if similarity threshold: groups.append(.join(current).strip()) current [] if current: groups.append(.join(current).strip()) return groups语义阈值不能凭感觉定。阈值过低多个主题会被粘在一起阈值过高Chunk 数量暴涨检索噪声和索引成本都会上升。实际使用时还要叠加最大 Token 限制。4.4 通过 TaoToken 发起向量化与生成请求用统一 Key 调用 Embedding 接口把切好的 Chunk 批量向量化import os, requests BASE https://taotoken.net/api HEADERS { Authorization: fBearer {os.environ[TAOTOKEN_API_KEY]}, Content-Type: application/json, } def embed(texts: list[str], model: str) - list[list[float]]: resp requests.post( f{BASE}/embeddings, headersHEADERS, json{model: model, input: texts}, timeout60, ) resp.raise_for_status() return [item[embedding] for item in resp.json()[data]]生成阶段同样走统一通道把 Top-K 召回的 Chunk 拼进上下文def answer(question: str, contexts: list[str], model: str) - str: prompt 仅根据以下资料回答资料不足时明确说明。\n\n prompt \n---\n.join(contexts) prompt f\n\n问题{question} resp requests.post( f{BASE}/chat/completions, headersHEADERS, json{ model: model, messages: [{role: user, content: prompt}], temperature: 0, }, timeout60, ) resp.raise_for_status() return resp.json()[choices][0][message][content]5. 验证请求与成功结果指标计算与效果对比跑完三种策略后用同一问题集计算 RecallK、MRR、引用准确率和成本。问题集要覆盖四类原文复述、跨段推理、带条件的故障处理、知识库外问题。每条问题标注正确证据 Chunk{ id: q-017, question: E102连续出现三次后重启服务前还需要做什么, gold_chunk_ids: [doc-8-p18-c03, doc-8-p18-c04], answer_points: [检查采集链路, 确认影响范围, 再重启采集服务], is_answerable: true }RecallK 计算正确证据是否被召回def recall_at_k(gold: set, retrieved: list, k: int) - float: top set(retrieved[:k]) return len(gold top) / len(gold) if gold else 0.0MRR 更重视正确证据是否靠前def reciprocal_rank(gold: set, retrieved: list) - float: for rank, cid in enumerate(retrieved, start1): if cid in gold: return 1.0 / rank return 0.0下面是一份实验记录格式示例数值用于说明记录方式你需要替换成自己数据集的实际结果策略Chunk 数Recall5MRR平均输入 TokenP95 检索延迟固定 500/8012400.780.66312082ms递归 8009800.840.73268075ms语义 最大 90011300.860.76281096ms从这组数据不能直接得出“语义切分永远最好”。它只说明在当前数据集上语义切分可能提高证据命中但索引和检索成本也更高。还要看知识库外问题的拒答率、引用准确率和不同文档类型的分组结果。如果制度文档 Recall 高但引用准确率低可能是 Chunk 包含了多个相互冲突的条款如果故障手册 Recall 低可能是“现象—原因—处理步骤”被拆成了三个不相邻的片段。验证成功的标志是同一文档重复导入 Chunk 数保持不变改一个段落只更新受影响的向量标题、页码、章节版本能在回答中正确引用知识库外问题会拒答而不是返回相似但错误的内容。6. 本篇常见错排查报错一size must be greater than overlap。固定切分里size overlap会直接抛错。检查config.toml里max_chars和overlap_chars的关系重叠一般取最大长度的 10% 到 15%。报错二向量维度不匹配。写入向量库时报dimension mismatch通常是settings.json里的embedding_dim和实际模型输出不一致。先用一条文本调 Embedding 接口打印len(embedding)确认。报错三递归切分返回空列表。如果文本全是分隔符或空白recursive_split可能返回空。在入口处加if not text.strip(): return []并在入库前过滤空 Chunk。报错四语义切分 Chunk 数量暴涨。阈值设太高比如 0.7会把每个句子都切成独立单元。先把阈值降到 0.4 到 0.5 之间试再叠加semantic_max_chars兜底。报错五401 鉴权失败。检查TAOTOKEN_API_KEY环境变量是否导出成功请求头是否是Bearer前缀。用curl快速验证curl -s https://taotoken.net/api/models \ -H Authorization: Bearer $TAOTOKEN_API_KEY | head -c 300报错六检索延迟随 Chunk 数线性上升。Chunk 数增加十倍时 P95 延迟如果超出可接受范围先检查向量索引类型和是否做了归一化。必要时对检索结果做 Rerank 而不是无限增大 Top-K。排障和接入相关问题优先查接入文档和 API Key 管理页API Keys 管理https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite7. 混合策略与上线前验证清单实际工程中最稳妥的方案通常不是三选一而是分层处理先解析标题、表格、列表、代码块和页码以章节或业务小节作为一级 Chunk一级 Chunk 超过 Token 上限时用递归切分对长段落或无结构文本用语义切分为每个子 Chunk 保存父章节标题和来源位置检索子 Chunk回答时回填父章节摘要。这样既保留文档结构又避免单个片段过长。父子 Chunk 要注意权限继承子 Chunk 不能因为回填父章节而跨越原有权限边界。上线前逐条核对同一文档重复导入 Chunk 数是否稳定改一个段落是否只更新受影响的向量标题、页码、章节和版本信息能否正确引用知识库外问题是否拒答不同租户是否得到不同证据集合Embedding 模型升级后旧向量是否与新向量混用Chunk 数增加十倍时 P95 延迟是否仍可接受评测集是否和调参数据隔离。分块策略没有脱离业务的万能答案。固定切分适合建立基线递归切分适合大多数结构化文档语义切分适合主题变化明显且对证据完整性要求高的内容。建议先用递归结构切分跑通全链路再建立小型评测集最后针对失败样本选择性引入语义切分。先测量再优化比盲目更换模型更省时间和成本。如果你要长期跑编码或 Agent 类任务可以进一步了解 Coding Plan 的额度方案如果只是想快速验证某条问题在统一通道下的回答效果直接到模型对话页面试几条模型对话https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewriteCoding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite官网入口https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑