语义分块用于RAG:构建网络数据管道
Advanced Data Extraction Specialist
TL;DR:
- 语义分块通过意义的变化将文本分组。 它需要一个定义的分割方法,一个嵌入模型,以及一个决定块结束位置的规则。
- 干净的源文本是检索质量的一部分。 导航菜单和重复的横幅可能在检索器看到之前扭曲块。
- 每个块应保留其源和位置。 追溯使得检索到的段落对引用和诊断有用。
- 语义分割器需要一个基准比较。 额外的嵌入工作并不保证比更简单的分割策略得到更好的答案。
- 可以免费开始。 使用Scrapeless账户积分评估上游收集步骤,针对一小组已批准的文档。
介绍:检索在向量存储之前开始
检索器只能返回其索引中存储的段落。在 检索增强生成 中,检索到的证据是生成输入的一部分。如果一个段落包含一半的解释,或者将政策声明与不相关的导航文本结合在一起,则即使相似性评分看起来很强,答案生成器也会接收到不完整的上下文。
语义分块改变这些段落的开始和结束位置。分割器不像只在固定长度处切割,而是比较相邻句子的表示,并在其意义足够分歧时创建边界。大小限制仍然重要,因为一个连贯的话题可能比下游模型可以接受的要长。
本文描述了一个以Scrapeless作为获取层、透明的Python分割器作为转换层的网络到RAG管道。它补充了使用语言模型的结构化提取:提取生成任务字段,而分块为后续检索准备段落。
语义分块的工作原理
语义分块使用意义的表示来选择文本边界。一个常见的实现是将文档分割成句子,嵌入这些句子,比较相邻的向量,并在变化足够大的地方开始一个新组。
句子嵌入方法 将句子文本映射为可以比较相似性的向量。相似性得分的意义依赖于模型和输入,因此数值边界设置是一个需要评估的参数,而不是普遍的主题变化定义。
| 策略 | 边界规则 | 有用的起始点 | 主要权衡 |
|---|---|---|---|
| 固定大小 | 配置的长度 | 一个简单的基线 | 可能会切割通过解释 |
| 结构感知 | 标题和段落换行 | 组织良好的参考页面 | 依赖于干净的文档结构 |
| 语义 | 嵌入相似性的变化 | 有主题变化的文本 | 增加模型计算和调优 |
| 混合 | 结构、语义检查和限制 | 混合文档集合 | 需要更多的配置进行评估 |
标题、表格、代码块和列表需要特殊处理。句子分割器可能通过将值与其列标签分离来损坏表格。在预处理过程中保留这些单位,或将它们路由到结构感知路径。
管道一瞥
该管道将捕获的网页文档转换为保留其来源的索引段落。其阶段为获取 → 文本规范化 → 句子分割 → 嵌入 → 块形成 → 检索评估。
Scrapeless Web Unlocker 处理目标请求。您的应用清理返回的内容,通过所选模型生成嵌入,并存储结果段落。网页获取本身并不创建向量索引或选择正确的分块策略。
在转换之前保存原始响应。保留请求的URL、任何已建立的规范URL、收集时间、页面标题和规范文本的哈希。规范URL必须来自源证据;不要通过删除路径段来制造它。
前提条件
您需要一组批准的公共源页面、用于获取的Scrapeless API密钥、用于处理的Python,以及可以本地运行或通过您选择的提供商运行的嵌入模型。提供商或模型决定了嵌入设置和访问要求。
本教程的分割器消耗一个 sentences.json 文件,包含 source_url 字符串、有序的 sentences 列表,以及包含每个句子的一个数值向量的 embeddings 列表。它不假设特定的托管模型或向量维度。在一次运行中必须对每个句子使用相同模型。
获取经过身份验证的页面和嵌入生成是此示例中尚未执行的端到端前提条件。分割器可以独立检查,但本地算法检查无法确定检索质量或成功的模型集成。
阶段 1:捕获和清理源文档
有用的文档应以预期的页面内容为起点,而不是挑战页面、登录表单或导航外壳。通过Web 解锁请求接口获取页面,并在提取文本之前验证其预期内容是否存在。
对于每个接受的页面,去除脚本、样式、重复的导航和不相关的推广块。保留标题和段落边界。如果页面是动态呈现的,请使用文档化的渲染配置,并检查返回的内容是否包含相关文本。
分别存储原始和标准化内容。这使得能够诊断一个不良检索段落是来自收集、清理、分割还是嵌入模型。源数据和派生数据之间的来源关系在这里非常有用:该块来自标准化文档的特定版本。
不要默默地将一个页面的主体与另一个页面的标题或发布日期结合起来。如果某个字段不可用,请保持其未设置。页面收集时间和源出版时间是不同的元数据字段。
阶段 2:分割句子并生成一致的嵌入
句子分割生成语义分割器将要比较的有序文本单元。使用适当语言的分割器,并检查其对缩写、小数点、标题和项目符号列表的处理。
避免声称基本句号分割适用于每个页面。包含缩写或代码示例的段落可能会分解成误导性的片段。对于短参考页面,标题和段落边界可能已经提供足够的结构,无需语义传递。
为每个句子生成一个嵌入,并保留准确的顺序。记录模型标识符、标准化设置和源文本哈希与向量一起。计算相似性之前,拒绝缺失的向量、不一致的维度或非有限值。
段落级上下文窗口可以改善某些句子的表示,但这会改变实验。当比较分割规则时,请固定该设置,以便嵌入输入的变化不会错误归因于边界算法。
使用 Scrapeless 开始抓取
使用 Scrapeless 提升您的网络抓取和自动化工作流程!
今天注册并获得5美元的免费信用——无需信用卡。立即在Scrapeless 控制面板领取您的免费信用。
阶段 3:创建有界的语义块
下面的分割器在相邻句子相似度低于配置阈值或字符预算将被超过时开始一个新块。将其保存为chunk_sentences.py。
注意:此脚本需要一个真实的
sentences.json文件,该文件是从您捕获的文本和嵌入模型生成的。嵌入生成仍然是一个前提;该示例不包括虚构的向量集或声称测量检索改进。
python
import json
import math
import os
from pathlib import Path
source = json.loads(Path("sentences.json").read_text())
sentences = source["sentences"]
vectors = source["embeddings"]
threshold = float(os.environ["SIMILARITY_THRESHOLD"])
max_chars = int(os.environ["MAX_CHUNK_CHARS"])
if not -1 <= threshold <= 1 or max_chars <= 0:
raise ValueError("Invalid chunking configuration")
if not sentences or len(sentences) != len(vectors):
raise ValueError("Each sentence needs one embedding")
dimension = len(vectors[0])
if not dimension:
raise ValueError("Embeddings must not be empty")
for text, vector in zip(sentences, vectors):
if not isinstance(text, str) or not text.strip():
raise ValueError("Sentence text must be nonempty")
if len(text) > max_chars:
raise ValueError("Segment oversized sentences before chunking")
if len(vector) != dimension or not all(math.isfinite(v) for v in vector):
raise ValueError("Invalid embedding dimension or value")
if sum(v * v for v in vector) == 0:
raise ValueError("A zero vector has no cosine direction")
def cosine(a, b):
numerator = sum(x * y for x, y in zip(a, b))
denominator = math.sqrt(sum(x*x for x in a) * sum(y*y for y in b))
return max(-1.0, min(1.0, numerator / denominator))
chunks, start, current = [], 0, []
for index, sentence in enumerate(sentences):
topic_change = index > 0 and cosine(vectors[index-1], vectors[index]) < threshold
too_long = len(" ".join(current + [sentence])) > max_chars
if current and (topic_change or too_long):
chunks.append({"start_sentence": start, "end_sentence": index,
"text": " ".join(current)})
current, start = [], index
current.append(sentence)
chunks.append({"start_sentence": start, "end_sentence": len(sentences),
"text": " ".join(current)})
records = [dict(chunk, source_url=source["source_url"], chunk_index=index)
for index, chunk in enumerate(chunks)]
Path("chunks.json").write_text(json.dumps(records, ensure_ascii=False, indent=2))
print(json.dumps({"chunk_count": len(records)}))
句子区间使用包含的开始和排除的结束。该约定使得能够准确重建哪些输入句子生成了一个块。空输入和无效向量显式失败,而不是生成一个看似成功的空索引。
这里的大小限制以字符计数。它不是令牌限制。如果您的嵌入或生成系统有令牌预算,请在发送文本之前使用该系统的令牌化器来测量令牌。该代码拒绝超大句子,以便上游分割器能够有意识地处理它。
此实现经过精心设计:它比较相邻的句子向量。它并没有在文档远端实施聚类、适应性百分位阈值或训练的边界分类器。这些是需要自己评估的独立方法。
阶段 4:索引段落而不丢失其来源
索引应保留足够的元数据,以便将检索到的块重新连接到其源文档。存储块文本、源 URL、文本哈希、块索引、句子范围、收集时间和嵌入模型标识符。
从最终块文本创建块嵌入,如果这是您的检索器所使用的表示。平均句子嵌入是一种不同的设计选择;不要假设它会产生与嵌入组装段落相同的检索排名。
当源文档发生变化时,保持旧版本和新版本的可区分性。在更改基础文本的同时重用相同的块标识符可能导致存储的引用模糊不清。基于标准化源哈希的版本键有助于区分观察结果。
向量存储只是一个可能的索引。小型评估可以在内存中检索段落。从最简单的安排开始,这样您就可以检查针对每个问题返回的文本。
阶段 5:比较检索,而不仅仅是块外观
语义块划分应该根据应用程序需要回答的问题进行评估。视觉上整齐的一组段落并不能证明检索器会返回正确的证据。
构建一个小型问题集,并在原始文档中标记支持段落。运行固定大小的基线、结构感知基线和语义拆分器,对照相同的标准化内容。保持嵌入模型、检索设置和答案评估不变。
| 测量 | 它回答的问题 |
|---|---|
| 支持段落检索 | 是否检索到必要的证据? |
| 无关的检索文本 | 浪费了多少上下文? |
| 答案支持 | 生成的答案是否遵循证据? |
| 处理时间 | 划分和嵌入的运营成本是多少? |
| 输入量 | 每个模型步骤到达了多少文本? |
关于 语义块划分的计算权衡 的研究支持将该方法视为一种经验选择。额外的工作并不总是能在每个任务和数据集上产生改进。
按阶段检查失败。在原始捕获中缺失证据是获取问题。断开的段落是清理问题。一个相关的段落在无关段落之下排名,可能指向嵌入或检索配置。这些问题需要不同的调整。
通过 Scrapeless 定价 分别跟踪获取组件。嵌入和索引成本属于下游堆栈;它们不应该作为 Web 解锁器使用报告。
结论:选择能检索到更好证据的拆分器
当文件内的意义以更简单的边界无法捕获的方式发生变化时,语义块划分是有用的。实用的工作流程是保留干净的源文本,生成一致的嵌入,执行段落限制,并将检索与简单的替代方案进行比较。
在整个过程中保持源元数据附加。检索到的段落在应用程序能够显示其来源和代表哪个文档版本时变得更加有用。
准备构建您的网络数据管道了吗?
加入开发者团队,在 Discord 和 Telegram 上工作,进行网络数据收集。
创建一个 Scrapeless 账户,将工作流程调整到您自己批准的数据源。
常见问题
问:语义块划分总是优于固定大小的块划分吗?
不。它的好处取决于文档、嵌入模型、检索设置和任务。在选择之前在共享评估集上比较策略。
问:Scrapeless 在这个工作流程中生成嵌入吗?
不。Scrapeless 提供上游网络访问步骤。您选择的嵌入模型和索引处理下游表示和检索。
问:表格和代码块应如何划分?
保留它们的结构或通过专用结构感知拆分器处理。仅凭句子边界可能会将值与标题分离,或破坏代码上下文。
问:如果获取的页面包含挑战或不同的 HTML,该怎么办?
拒绝不符合源合同的内容,并检查获取或解析阶段。语义相似性无法修复不正确的源文档。
问:收集需要单独的代理吗?
使用您所选择的 Web 解锁器请求的路由选项。不要根据在收集后运行的块划分算法推断代理要求。
问:管道可以在没有 AI 代理的情况下运行吗?
可以。应用程序可以获取文档并在没有自主代理的情况下运行基于嵌入的块划分。语义比较仍然需要嵌入模型。
问:应对并行源收集应用什么限制?
从有限集合开始,例如每个主机不超过三个工作者,并遵守更严格的目标和账户限制。嵌入批处理有单独的模型特定限制。
问:任何公共页面都可以添加到RAG索引中吗?
仅仅是公共可用性并不自动为每个集合或重用建立权限。在摄取之前,请审查源条款、适用权利以及计划的数据处理。
在Scrapeless,我们仅访问公开可用的数据,并严格遵循适用的法律、法规和网站隐私政策。本博客中的内容仅供演示之用,不涉及任何非法或侵权活动。我们对使用本博客或第三方链接中的信息不做任何保证,并免除所有责任。在进行任何抓取活动之前,请咨询您的法律顾问,并审查目标网站的服务条款或获取必要的许可。



