使用langchain实现五种分块策略:语义分块、父文档分块、递归分块、特殊格式、固定长度分块

article/2025/8/7 11:28:24

文章目录

    • 分块策略详解
      • 1. 固定长度拆分(简单粗暴)
      • 2. 递归字符拆分(智能切割)
      • 3. 特殊格式拆分(定向打击)
        • Markdown分块
      • 4. 语义分割(更智能切割)
        • 基于Embedding的语义分块
        • 基于模型的端到端语义分块
      • 5. 父文档检索(分层切割)
        • 方式一:返回完整文档
        • 方式二:分层块结构

分块策略详解

文档拆分策略有多种,每种策略都有其自身的优势。

但是粗略来说我们1~5的分块策略是由差到好的,具体可以根据项目多方面测试下

在这里插入图片描述

1. 固定长度拆分(简单粗暴)

最直观的策略是根据固定长度进行拆分。

就像用刀切面包一样,每100个字符就切一刀,不管切到哪里。

from langchain_text_splitters import CharacterTextSplitter
text_splitter = CharacterTextSplitter(chunk_size=100, chunk_overlap=0, separator="")document="""One of the most important things I didn't understand about the world when I was a child is the degree to which the returns for performance are superlinear.Teachers and coaches implicitly told us the returns were linear. "You get out," I heard a thousand times, "what you put in." They meant well, but this is rarely true. If your product is only half as good as your competitor's, you don't get half as many customers. You get no customers, and you go out of business.It's obviously true that the returns for performance are superlinear in business. Some think this is a flaw of capitalism, and that if we changed the rules it would stop being true. But superlinear returns for performance are a feature of the world, not an artifact of rules we've invented. We see the same pattern in fame, power, military victories, knowledge, and even benefit to humanity. In all of these, the rich get richer. [1]You can't understand the world without understanding the concept of superlinear returns. And if you're ambitious you definitely should, because this will be the wave you surf on.It may seem as if there are a lot of different situations with superlinear returns, but as far as I can tell they reduce to two fundamental causes: exponential growth and thresholds.The most obvious case of superlinear returns is when you're working on something that grows exponentially. For example, growing bacterial cultures. When they grow at all, they grow exponentially. But they're tricky to grow. Which means the difference in outcome between someone who's adept at it and someone who's not is very great.Startups can also grow exponentially, and we see the same pattern there. Some manage to achieve high growth rates. Most don't. And as a result you get qualitatively different outcomes: the companies with high growth rates tend to become immensely valuable, while the ones with lower growth rates may not even survive.Y Combinator encourages founders to focus on growth rate rather than absolute numbers. It prevents them from being discouraged early on, when the absolute numbers are still low. It also helps them decide what to focus on: you can use growth rate as a compass to tell you how to evolve the company. But the main advantage is that by focusing on growth rate you tend to get something that grows exponentially.YC doesn't explicitly tell founders that with growth rate "you get out what you put in," but it's not far from the truth. And if growth rate were proportional to performance, then the reward for performance p over time t would be proportional to pt.Even after decades of thinking about this, I find that sentence startling."""
texts = text_splitter.split_text(document)
total_characters = len(document)
num_chunks = len(texts)
average_chunk_size = total_characters/len(texts)print(f"Total Characters: {total_characters}")
print(f"Number of chunks: {num_chunks}")
print(f"Average chunk size: {average_chunk_size:.1f}")
print(f"\nFirst few chunks:")
for i, chunk in enumerate(texts[:3]):print(f"Chunk {i+1} ({len(chunk)} chars): {chunk[:100]}...")

控制台会输出:
在这里插入图片描述

以上结果我们可以在https://chunkviz.up.railway.app/ 看到,如下图:
在这里插入图片描述

一般实战中我们会设置约10%-20%做重叠窗口,这样可以使得模型分块之后可以具备连续上下文逻辑。

2. 递归字符拆分(智能切割)

最直观的策略是根据固定长度进行拆分。这种简单而有效的方法可以确保每个块不超过指定的大小限制。

就像用刀切面包一样,每100个字符就切一刀,不管切到哪里。

1. 首先尝试按最大单位切割(段落)

# 就像先按整个萝卜来分
段落1 = "这是第一段内容..."
段落2 = "这是第二段内容..." 
段落3 = "这是第三段内容..."

2. 如果段落太大,就切成句子

# 萝卜太大了,按句子切
句子1 = "这是第一个句子。"
句子2 = "这是第二个句子。"
句子3 = "这是第三个句子。"

3. 如果句子还是太大,就切成单词

# 句子还是太长,按单词切
单词1 = "这是"
单词2 = "第一个"
单词3 = "很长的"
单词4 = "句子"

4. 最后实在没办法,才按字符切

# 实在没办法了,按字符切
字符1 = "这"
字符2 = "是"
字符3 = "一"
字符4 = "个"
from langchain_text_splitters import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=0)document="""One of the most important things I didn't understand about the world when I was a child is the degree to which the returns for performance are superlinear.Teachers and coaches implicitly told us the returns were linear. "You get out," I heard a thousand times, "what you put in." They meant well, but this is rarely true. If your product is only half as good as your competitor's, you don't get half as many customers. You get no customers, and you go out of business.It's obviously true that the returns for performance are superlinear in business. Some think this is a flaw of capitalism, and that if we changed the rules it would stop being true. But superlinear returns for performance are a feature of the world, not an artifact of rules we've invented. We see the same pattern in fame, power, military victories, knowledge, and even benefit to humanity. In all of these, the rich get richer. [1]You can't understand the world without understanding the concept of superlinear returns. And if you're ambitious you definitely should, because this will be the wave you surf on.It may seem as if there are a lot of different situations with superlinear returns, but as far as I can tell they reduce to two fundamental causes: exponential growth and thresholds.The most obvious case of superlinear returns is when you're working on something that grows exponentially. For example, growing bacterial cultures. When they grow at all, they grow exponentially. But they're tricky to grow. Which means the difference in outcome between someone who's adept at it and someone who's not is very great.Startups can also grow exponentially, and we see the same pattern there. Some manage to achieve high growth rates. Most don't. And as a result you get qualitatively different outcomes: the companies with high growth rates tend to become immensely valuable, while the ones with lower growth rates may not even survive.Y Combinator encourages founders to focus on growth rate rather than absolute numbers. It prevents them from being discouraged early on, when the absolute numbers are still low. It also helps them decide what to focus on: you can use growth rate as a compass to tell you how to evolve the company. But the main advantage is that by focusing on growth rate you tend to get something that grows exponentially.YC doesn't explicitly tell founders that with growth rate "you get out what you put in," but it's not far from the truth. And if growth rate were proportional to performance, then the reward for performance p over time t would be proportional to pt.Even after decades of thinking about this, I find that sentence startling."""texts = text_splitter.split_text(document)
total_characters = len(document)
num_chunks = len(texts)
average_chunk_size = total_characters/len(texts)print(f"Total Characters: {total_characters}")
print(f"Number of chunks: {num_chunks}")
print(f"Average chunk size: {average_chunk_size:.1f}")
print(f"\nFirst few chunks:")
for i, chunk in enumerate(texts[:3]):print(f"Chunk {i+1} ({len(chunk)}字符): {chunk}")

控制台会输出:
在这里插入图片描述

以上结果我们可以在https://chunkviz.up.railway.app/ 看到,如下图:
在这里插入图片描述

这个网站开发者将空格也视为了分割块,可忽略它的span数计算总数与我们不一样(它的bug):
在这里插入图片描述

该方法是dify的默认方法。我们可以修改分割符从而快速简单达到效果,比如
separators 分隔符设置
默认的separators 是"\n\n"(即回车)。比如:

  • 中文文档:应增加中文标点符号(如"。“、”,")作为分隔符
  • Markdown文档:可使用标题标记(#、##)、代码块标记(```)等作为分隔符

3. 特殊格式拆分(定向打击)

某些文档具有固有的结构,例如 HTML、Markdown 或 JSON 文件。
这里我就先介绍下Markdown ,其他我遇到了再跟大家补充。

Markdown分块

MarkdownHeaderTextSplitter是对RecursiveCharacterTextSplitter用了多个不同符号使得其能适应格式。
包含了以下符号作为分隔符:

  • #######

  • ````lang` - 三个反引号开始的代码块

  • ~~~lang - 三个波浪号开始的代码块

  • *** - 星号分割线(3个或更多)

  • --- - 短横线分割线(3个或更多)

  • ___ - 下划线分割线(3个或更多)

4. 语义分割(更智能切割)

递归文本分块基于预定义规则工作,虽然简单高效,但可能无法准确捕捉语义变化。
基于语义的分块策略则直接分析文本内容,根据语义相似度判断分块位置。

基于Embedding的语义分块

但从概念上讲,该方法是在文本含义发生显著变化时对文本进行拆分。例如,我们可以使用滑动窗口方法生成嵌入向量,并比较嵌入以发现显著差异:

1. 句子分割 首先将整个文本按句子分割(默认按句号、问号、感叹号分割)。

2. 创建语义窗口 为了获得更稳定的语义表示,不是单独分析每个句子,而是创建"滑动窗口"。默认情况下,每个窗口包含3个句子:当前句子加上前后各一个句子。这样做可以减少单个短句造成的噪音。

3. 生成嵌入向量 对每个滑动窗口的组合句子生成嵌入向量,这些向量能够捕获文本的语义含义。

4. 计算语义距离 计算相邻窗口之间的余弦距离。余弦距离越大,说明两个窗口的语义差异越大。

5. 确定断点阈值 系统提供4种方法来确定什么程度的语义距离算作"断点":

  • 百分位数法:找出距离最大的前5%作为断点
  • 标准差法:超过平均值加3倍标准差的距离作为断点
  • 四分位数法:基于四分位距离的统计方法
  • 梯度法:分析距离变化的梯度来找断点

6. 执行分割 在语义距离超过阈值的位置进行分割,形成最终的语义块。

from langchain_experimental.text_splitter import SemanticChunker
from langchain_community.embeddings import HuggingFaceBgeEmbeddings
import os
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'model_name = "BAAI/bge-small-en"
model_kwargs = {"device": "cpu"}
encode_kwargs = {"normalize_embeddings": True}
hf = HuggingFaceBgeEmbeddings(model_name=model_name, model_kwargs=model_kwargs, encode_kwargs=encode_kwargs
)text_splitter = SemanticChunker(hf)
document="""One of the most important things I didn't understand about the world when I was a child is the degree to which the returns for performance are superlinear.Teachers and coaches implicitly told us the returns were linear. "You get out," I heard a thousand times, "what you put in." They meant well, but this is rarely true. If your product is only half as good as your competitor's, you don't get half as many customers. You get no customers, and you go out of business.It's obviously true that the returns for performance are superlinear in business. Some think this is a flaw of capitalism, and that if we changed the rules it would stop being true. But superlinear returns for performance are a feature of the world, not an artifact of rules we've invented. We see the same pattern in fame, power, military victories, knowledge, and even benefit to humanity. In all of these, the rich get richer. [1]You can't understand the world without understanding the concept of superlinear returns. And if you're ambitious you definitely should, because this will be the wave you surf on.It may seem as if there are a lot of different situations with superlinear returns, but as far as I can tell they reduce to two fundamental causes: exponential growth and thresholds.The most obvious case of superlinear returns is when you're working on something that grows exponentially. For example, growing bacterial cultures. When they grow at all, they grow exponentially. But they're tricky to grow. Which means the difference in outcome between someone who's adept at it and someone who's not is very great.Startups can also grow exponentially, and we see the same pattern there. Some manage to achieve high growth rates. Most don't. And as a result you get qualitatively different outcomes: the companies with high growth rates tend to become immensely valuable, while the ones with lower growth rates may not even survive.Y Combinator encourages founders to focus on growth rate rather than absolute numbers. It prevents them from being discouraged early on, when the absolute numbers are still low. It also helps them decide what to focus on: you can use growth rate as a compass to tell you how to evolve the company. But the main advantage is that by focusing on growth rate you tend to get something that grows exponentially.YC doesn't explicitly tell founders that with growth rate "you get out what you put in," but it's not far from the truth. And if growth rate were proportional to performance, then the reward for performance p over time t would be proportional to pt.Even after decades of thinking about this, I find that sentence startling."""
texts = text_splitter.create_documents([document])
print(texts[0].page_content)
print(len(texts))total_characters = len(document)
num_chunks = len(texts)
average_chunk_size = total_characters/len(texts)print(f"Total Characters: {total_characters}")
print(f"Number of chunks: {num_chunks}")
print(f"Average chunk size: {average_chunk_size:.1f}")
print(f"\nFirst few chunks:")
for i, chunk in enumerate(texts[:3]):print(f"Chunk {i+1}: {chunk}\n")

在这里插入图片描述

由于文本是英文的,可能我们还是不太能直观看明白Embedding模型做了什么,由此我又写了以下代码,这个代码将会从语义分割的实现到绘制图(工程上用上面那份代码即可,这份代码只是做解释)

from langchain_experimental.text_splitter import SemanticChunker
from langchain_community.embeddings import HuggingFaceBgeEmbeddings
import os
import re
import numpy as np
import matplotlib.pyplot as plt
from langchain_community.utils.math import cosine_similarity# 设置中文字体支持
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False# 设置环境
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'# 初始化嵌入模型
model_name = "BAAI/bge-small-en"
model_kwargs = {"device": "cpu"}
encode_kwargs = {"normalize_embeddings": True}
hf = HuggingFaceBgeEmbeddings(model_name=model_name,model_kwargs=model_kwargs,encode_kwargs=encode_kwargs
)# 你的文档内容
document = """One of the most important things I didn't understand about the world when I was a child is the degree to which the returns for performance are superlinear.Teachers and coaches implicitly told us the returns were linear. "You get out," I heard a thousand times, "what you put in." They meant well, but this is rarely true. If your product is only half as good as your competitor's, you don't get half as many customers. You get no customers, and you go out of business.It's obviously true that the returns for performance are superlinear in business. Some think this is a flaw of capitalism, and that if we changed the rules it would stop being true. But superlinear returns for performance are a feature of the world, not an artifact of rules we've invented. We see the same pattern in fame, power, military victories, knowledge, and even benefit to humanity. In all of these, the rich get richer. [1]You can't understand the world without understanding the concept of superlinear returns. And if you're ambitious you definitely should, because this will be the wave you surf on.It may seem as if there are a lot of different situations with superlinear returns, but as far as I can tell they reduce to two fundamental causes: exponential growth and thresholds.The most obvious case of superlinear returns is when you're working on something that grows exponentially. For example, growing bacterial cultures. When they grow at all, they grow exponentially. But they're tricky to grow. Which means the difference in outcome between someone who's adept at it and someone who's not is very great.Startups can also grow exponentially, and we see the same pattern there. Some manage to achieve high growth rates. Most don't. And as a result you get qualitatively different outcomes: the companies with high growth rates tend to become immensely valuable, while the ones with lower growth rates may not even survive.Y Combinator encourages founders to focus on growth rate rather than absolute numbers. It prevents them from being discouraged early on, when the absolute numbers are still low. It also helps them decide what to focus on: you can use growth rate as a compass to tell you how to evolve the company. But the main advantage is that by focusing on growth rate you tend to get something that grows exponentially.YC doesn't explicitly tell founders that with growth rate "you get out what you put in," but it's not far from the truth. And if growth rate were proportional to performance, then the reward for performance p over time t would be proportional to pt.Even after decades of thinking about this, I find that sentence startling."""def combine_sentences(sentences, buffer_size=1):"""组合句子,创建滑动窗口"""for i in range(len(sentences)):combined_sentence = ""# 添加前面的句子for j in range(i - buffer_size, i):if j >= 0:combined_sentence += sentences[j]["sentence"] + " "# 添加当前句子combined_sentence += sentences[i]["sentence"]# 添加后面的句子for j in range(i + 1, i + 1 + buffer_size):if j < len(sentences):combined_sentence += " " + sentences[j]["sentence"]sentences[i]["combined_sentence"] = combined_sentencereturn sentencesdef calculate_cosine_distances(sentences):"""计算余弦距离"""distances = []for i in range(len(sentences) - 1):embedding_current = sentences[i]["combined_sentence_embedding"]embedding_next = sentences[i + 1]["combined_sentence_embedding"]# 计算余弦相似度similarity = cosine_similarity([embedding_current], [embedding_next])[0][0]# 转换为余弦距离distance = 1 - similaritydistances.append(distance)sentences[i]["distance_to_next"] = distancereturn distances, sentences# 手动实现语义距离计算
def calculate_semantic_distances(text, embeddings_model, buffer_size=1):"""计算文本的语义距离"""# 按句子分割sentence_split_regex = r"(?<=[.?!])\s+"single_sentences_list = re.split(sentence_split_regex, text)single_sentences_list = [s.strip() for s in single_sentences_list if s.strip()]print(f"分割出 {len(single_sentences_list)} 个句子:")for i, sentence in enumerate(single_sentences_list):print(f"{i + 1}. {sentence[:100]}...")print()# 创建句子字典sentences = [{"sentence": x, "index": i} for i, x in enumerate(single_sentences_list)]# 组合句子(滑动窗口)sentences = combine_sentences(sentences, buffer_size)# 获取嵌入向量print("生成嵌入向量...")embeddings = embeddings_model.embed_documents([x["combined_sentence"] for x in sentences])for i, sentence in enumerate(sentences):sentence["combined_sentence_embedding"] = embeddings[i]# 计算距离distances, sentences = calculate_cosine_distances(sentences)return distances, sentences, single_sentences_list# 计算语义距离
print("开始计算语义距离...")
distances, sentences_with_embeddings, original_sentences = calculate_semantic_distances(document, hf)print(f"计算出 {len(distances)} 个距离值")
print("距离值:", [f"{d:.3f}" for d in distances])
print()# 绘制图表
plt.figure(figsize=(15, 8))# 绘制距离线
plt.plot(distances, linewidth=2, marker='o', markersize=4)# 设置y轴上限
y_upper_bound = max(distances) * 1.2
plt.ylim(0, y_upper_bound)
plt.xlim(0, len(distances))# 计算阈值
breakpoint_percentile_threshold = 95
breakpoint_distance_threshold = np.percentile(distances, breakpoint_percentile_threshold)print(f"95%分位数阈值: {breakpoint_distance_threshold:.3f}")# 绘制阈值线
plt.axhline(y=breakpoint_distance_threshold, color='r', linestyle='-', linewidth=2,label=f'95%阈值: {breakpoint_distance_threshold:.3f}')# 计算超过阈值的距离数量
num_distances_above_threshold = len([x for x in distances if x > breakpoint_distance_threshold])
num_chunks = num_distances_above_threshold + 1print(f"超过阈值的距离数量: {num_distances_above_threshold}")
print(f"最终块数: {num_chunks}")# 显示块数量
plt.text(x=(len(distances) * 0.01), y=y_upper_bound * 0.9, s=f"{num_chunks} 个分块",fontsize=14, bbox=dict(boxstyle="round,pad=0.3", facecolor="yellow", alpha=0.7))# 获取超过阈值的索引
indices_above_thresh = [i for i, x in enumerate(distances) if x > breakpoint_distance_threshold]
print(f"断点位置: {indices_above_thresh}")# 为每个块着色和标注
colors = ['lightblue', 'lightgreen', 'lightcoral', 'lightyellow', 'lightpink', 'lightgray', 'lightcyan']# 处理第一个块
if indices_above_thresh:# 第一个块:从开始到第一个断点plt.axvspan(0, indices_above_thresh[0], facecolor=colors[0], alpha=0.3)plt.text(x=indices_above_thresh[0] / 2,y=breakpoint_distance_threshold + y_upper_bound / 20,s=f"块 1", horizontalalignment='center', fontsize=10,bbox=dict(boxstyle="round,pad=0.2", facecolor=colors[0], alpha=0.8))# 中间的块for i in range(1, len(indices_above_thresh)):start_idx = indices_above_thresh[i - 1]end_idx = indices_above_thresh[i]plt.axvspan(start_idx, end_idx, facecolor=colors[i % len(colors)], alpha=0.3)plt.text(x=(start_idx + end_idx) / 2,y=breakpoint_distance_threshold + y_upper_bound / 20,s=f"块 {i + 1}", horizontalalignment='center', fontsize=10,bbox=dict(boxstyle="round,pad=0.2", facecolor=colors[i % len(colors)], alpha=0.8))# 最后一个块:从最后一个断点到结束last_breakpoint = indices_above_thresh[-1]plt.axvspan(last_breakpoint, len(distances),facecolor=colors[len(indices_above_thresh) % len(colors)], alpha=0.3)plt.text(x=(last_breakpoint + len(distances)) / 2,y=breakpoint_distance_threshold + y_upper_bound / 20,s=f"块 {len(indices_above_thresh) + 1}", horizontalalignment='center', fontsize=10,bbox=dict(boxstyle="round,pad=0.2",facecolor=colors[len(indices_above_thresh) % len(colors)], alpha=0.8))
else:# 如果没有断点,整个文档就是一块plt.axvspan(0, len(distances), facecolor=colors[0], alpha=0.3)plt.text(x=len(distances) / 2,y=y_upper_bound / 2,s="块 1", horizontalalignment='center', fontsize=12,bbox=dict(boxstyle="round,pad=0.3", facecolor=colors[0], alpha=0.8))# 标记超过阈值的点
for i, distance in enumerate(distances):if distance > breakpoint_distance_threshold:plt.plot(i, distance, 'ro', markersize=8, markerfacecolor='red', markeredgecolor='darkred', markeredgewidth=2)plt.annotate(f'断点\n{distance:.3f}',xy=(i, distance),xytext=(i, distance + y_upper_bound / 10),arrowprops=dict(arrowstyle='->', color='red', lw=1.5),ha='center', fontsize=9,bbox=dict(boxstyle="round,pad=0.2", facecolor="red", alpha=0.7, edgecolor="darkred"))plt.title("超线性回报文章的语义距离分析", fontsize=16, fontweight='bold')
plt.xlabel("句子位置索引", fontsize=12)
plt.ylabel("连续句子间的余弦距离", fontsize=12)
plt.grid(True, alpha=0.3)
plt.legend()# 添加距离统计信息
stats_text = f"""距离统计:
最小值: {min(distances):.3f}
最大值: {max(distances):.3f}
平均值: {np.mean(distances):.3f}
标准差: {np.std(distances):.3f}"""plt.text(x=len(distances) * 0.7, y=y_upper_bound * 0.7, s=stats_text,fontsize=10, bbox=dict(boxstyle="round,pad=0.5", facecolor="white", alpha=0.8, edgecolor="gray"))plt.tight_layout()
plt.show()# 使用SemanticChunker进行实际分割验证
print("\n" + "=" * 50)
print("使用SemanticChunker验证结果:")
text_splitter = SemanticChunker(hf)
chunks = text_splitter.split_text(document)print(f"SemanticChunker分割结果: {len(chunks)} 个块")
for i, chunk in enumerate(chunks):print(f"\n块 {i + 1} ({len(chunk)} 字符):")print(chunk[:200] + "..." if len(chunk) > 200 else chunk)print(f"\n总字符数: {len(document)}")
print(f"平均块大小: {len(document) / len(chunks):.1f} 字符")

控制台输出为如下。

  1. 句子分割 ✅
    首先按标点符号(句号、问号、感叹号)拆分文本,得到了29个独立句子。

  2. 创建滑动窗口 (buffer_size=1) ✅
    为每个句子添加上下文,构建包含前后邻句的组合文本:

    • 句子1:只包含句子1+句子2(没有前句)
    • 句子2:包含句子1+句子2+句子3
    • 句子3:包含句子2+句子3+句子4
    • 句子29:只包含句子28+句子29(没有后句)
  3. 生成嵌入向量
    使用BGE模型为29个组合句子生成768维的向量表示,每个向量捕获了句子及其上下文的语义信息。

  4. 计算余弦距离
    计算相邻句子间的语义距离,得到28个距离值(两两之间计算29句就会变为28个距离):

    • 距离值范围:0.011 到 0.096
    • 距离越大表示语义跳跃越明显
  5. 确定分块阈值
    使用95%分位数作为分块阈值:

    • 计算所有距离的95%分位数
    • 超过这个阈值的位置被认为是语义断点
  6. 识别分块点
    找出距离值超过阈值的位置,这些就是文本的语义转折点,用于分割不同的主题块。

  7. 可视化展示
    生成图表显示:

    • 蓝色曲线:28个位置的语义距离变化
    • 红色水平线:95%分位数阈值
    • 彩色区域:不同的语义块
    • 红色圆点:标记的断点位置
开始计算语义距离...
分割出 29 个句子:
1. One of the most important things I didn't understand about the world when I was a child is the degre...
2. Teachers and coaches implicitly told us the returns were linear....
3. "You get out," I heard a thousand times, "what you put in." They meant well, but this is rarely true...
4. If your product is only half as good as your competitor's, you don't get half as many customers....
5. You get no customers, and you go out of business....
6. It's obviously true that the returns for performance are superlinear in business....
7. Some think this is a flaw of capitalism, and that if we changed the rules it would stop being true....
8. But superlinear returns for performance are a feature of the world, not an artifact of rules we've i...
9. We see the same pattern in fame, power, military victories, knowledge, and even benefit to humanity....
10. In all of these, the rich get richer....
11. [1]You can't understand the world without understanding the concept of superlinear returns....
12. And if you're ambitious you definitely should, because this will be the wave you surf on....
13. It may seem as if there are a lot of different situations with superlinear returns, but as far as I ...
14. The most obvious case of superlinear returns is when you're working on something that grows exponent...
15. For example, growing bacterial cultures....
16. When they grow at all, they grow exponentially....
17. But they're tricky to grow....
18. Which means the difference in outcome between someone who's adept at it and someone who's not is ver...
19. Startups can also grow exponentially, and we see the same pattern there....
20. Some manage to achieve high growth rates....
21. Most don't....
22. And as a result you get qualitatively different outcomes: the companies with high growth rates tend ...
23. Y Combinator encourages founders to focus on growth rate rather than absolute numbers....
24. It prevents them from being discouraged early on, when the absolute numbers are still low....
25. It also helps them decide what to focus on: you can use growth rate as a compass to tell you how to ...
26. But the main advantage is that by focusing on growth rate you tend to get something that grows expon...
27. YC doesn't explicitly tell founders that with growth rate "you get out what you put in," but it's no...
28. And if growth rate were proportional to performance, then the reward for performance p over time t w...
29. Even after decades of thinking about this, I find that sentence startling....生成嵌入向量...
计算出 28 个距离值
距离值: ['0.020', '0.048', '0.048', '0.058', '0.047', '0.036', '0.036', '0.033', '0.030', '0.068', '0.059', '0.020', '0.042', '0.022', '0.083', '0.059', '0.063', '0.019', '0.035', '0.096', '0.067', '0.011', '0.028', '0.064', '0.042', '0.045', '0.014', '0.048']95%分位数阈值: 0.077
超过阈值的距离数量: 2
最终块数: 3
断点位置: [14, 19]==================================================
使用SemanticChunker验证结果:
SemanticChunker分割结果: 3 个块块 1 (1414 字符):
One of the most important things I didn't understand about the world when I was a child is the degree to which the returns for performance are superlinear. Teachers and coaches implicitly told us the ...块 2 (299 字符):
When they grow at all, they grow exponentially. But they're tricky to grow. Which means the difference in outcome between someone who's adept at it and someone who's not is very great. Startups can al...块 3 (935 字符):
Most don't. And as a result you get qualitatively different outcomes: the companies with high growth rates tend to become immensely valuable, while the ones with lower growth rates may not even surviv...总字符数: 2658
平均块大小: 886.0 字符

在这里插入图片描述

基于模型的端到端语义分块

更先进的方法是使用专门训练的神经网络模型,直接判断每个句子是否应作为分块点。这种方法无需手动设置阈值,而是由模型端到端地完成分块决策。

以阿里达摩院开发的语义分块模型为例,其工作流程为:

  1. 将文本窗口中的N个句子输入模型
  2. 模型为每个句子生成表示向量
  3. 通过二分类层直接判断每个句子是否为分块点
    在这里插入图片描述
from modelscope.pipelines import pipeline# 加载语义分块模型
semantic_segmentation = pipeline('text-semantic-segmentation',model='damo/nlp_bert_semantic-segmentation_chinese-base'
)# 进行分块
result = semantic_segmentation(long_text)
segments = result['text']

这种方法的优势在于模型已经学习了语义变化的复杂模式,无需手动调整参数。但需注意模型的泛化能力,应在特定领域文档上进行验证。

5. 父文档检索(分层切割)

父文档检索器的核心思想其实很简单:用小块来检索,用大块来回答。这样做的好处显而易见:我们获得了子块级别的检索精度,同时保持了父块级别的上下文丰富度。

具体的工作流程是这样的:

  1. 分层分块:首先将原始文档分成较大的父块,然后将每个父块进一步分割成更小的子块。若是不分块即为全文,若是分块即分层。
  2. 索引子块:对这些小的子块进行向量化并建立索引
  3. 检索时的魔法:当用户提问时,系统会基于子块的嵌入进行相似度搜索,但返回的不是子块本身,而是包含该子块的父块

下图展示了分层的父文档构建过程:
在这里插入图片描述

举个例子,假设用户问"腾讯的广告收入是多少",系统可能会匹配到一个专门讨论广告收入数字的小块,但返回的是包含完整财务分析上下文的大块,这样LLM就能基于更全面的信息来回答问题。涉及到向量检索,我们通常是采用向量数据库存储从而实现检索(后续代码可以体现)。

dify中2025年集成了父子检索,且将其作为推荐分块,如下图:
在这里插入图片描述

父文档检索器有两种常见的实现方式:

方式一:返回完整文档

如果你的原始文档本身就不是特别长(比如产品说明书、新闻文章等),可以直接将完整文档作为"父块"。这种情况下:

  • 子块:文档的段落或句子
  • 父块:完整的文档

这种方式特别适合处理大量相对短小的文档集合,比如FAQ数据库、产品目录等。

from langchain_community.embeddings import HuggingFaceBgeEmbeddings
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
from langchain.vectorstores import Chroma
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.documents import Document
import osos.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'model_name = "BAAI/bge-small-en"
model_kwargs = {"device": "cpu"}
encode_kwargs = {"normalize_embeddings": True}
bge_embeddings = HuggingFaceBgeEmbeddings(model_name=model_name, model_kwargs=model_kwargs, encode_kwargs=encode_kwargs
)document="""One of the most important things I didn't understand about the world when I was a child is the degree to which the returns for performance are superlinear.Teachers and coaches implicitly told us the returns were linear. "You get out," I heard a thousand times, "what you put in." They meant well, but this is rarely true. If your product is only half as good as your competitor's, you don't get half as many customers. You get no customers, and you go out of business.It's obviously true that the returns for performance are superlinear in business. Some think this is a flaw of capitalism, and that if we changed the rules it would stop being true. But superlinear returns for performance are a feature of the world, not an artifact of rules we've invented. We see the same pattern in fame, power, military victories, knowledge, and even benefit to humanity. In all of these, the rich get richer. [1]You can't understand the world without understanding the concept of superlinear returns. And if you're ambitious you definitely should, because this will be the wave you surf on.It may seem as if there are a lot of different situations with superlinear returns, but as far as I can tell they reduce to two fundamental causes: exponential growth and thresholds.The most obvious case of superlinear returns is when you're working on something that grows exponentially. For example, growing bacterial cultures. When they grow at all, they grow exponentially. But they're tricky to grow. Which means the difference in outcome between someone who's adept at it and someone who's not is very great.Startups can also grow exponentially, and we see the same pattern there. Some manage to achieve high growth rates. Most don't. And as a result you get qualitatively different outcomes: the companies with high growth rates tend to become immensely valuable, while the ones with lower growth rates may not even survive.Y Combinator encourages founders to focus on growth rate rather than absolute numbers. It prevents them from being discouraged early on, when the absolute numbers are still low. It also helps them decide what to focus on: you can use growth rate as a compass to tell you how to evolve the company. But the main advantage is that by focusing on growth rate you tend to get something that grows exponentially.YC doesn't explicitly tell founders that with growth rate "you get out what you put in," but it's not far from the truth. And if growth rate were proportional to performance, then the reward for performance p over time t would be proportional to pt.Even after decades of thinking about this, I find that sentence startling."""child_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=0)
docs = child_splitter.create_documents([document])vectorstore = Chroma(collection_name="documents", embedding_function=bge_embeddings
)store = InMemoryStore()
retriever = ParentDocumentRetriever(vectorstore=vectorstore,docstore=store,child_splitter=child_splitter
)retriever.add_documents([Document(page_content=document)], ids=None)# 返回小文档检索,问题:什么原因造成了超线性回报?
sub_docs = vectorstore.similarity_search("What causes superlinear returns?")# 返回大文档检索
retrieved_docs = retriever.get_relevant_documents("What causes superlinear returns?")
print(sub_docs[0].page_content)
print('='*40)
print(retrieved_docs[0].page_content)

控制台的输出:

在这里插入图片描述

方式二:分层块结构

对于长文档,我们需要建立真正的分层结构:

  • 原始文档 → 父块(比如400字符)→ 子块(比如100字符)

这种方式更适合处理长报告、学术论文、技术文档等。
这部分代码与方式一很类似:

from langchain_community.embeddings import HuggingFaceBgeEmbeddings
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
from langchain.vectorstores import Chroma
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.documents import Document
import osos.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'model_name = "BAAI/bge-small-en"
model_kwargs = {"device": "cpu"}
encode_kwargs = {"normalize_embeddings": True}
bge_embeddings = HuggingFaceBgeEmbeddings(model_name=model_name, model_kwargs=model_kwargs, encode_kwargs=encode_kwargs
)document="""One of the most important things I didn't understand about the world when I was a child is the degree to which the returns for performance are superlinear.Teachers and coaches implicitly told us the returns were linear. "You get out," I heard a thousand times, "what you put in." They meant well, but this is rarely true. If your product is only half as good as your competitor's, you don't get half as many customers. You get no customers, and you go out of business.It's obviously true that the returns for performance are superlinear in business. Some think this is a flaw of capitalism, and that if we changed the rules it would stop being true. But superlinear returns for performance are a feature of the world, not an artifact of rules we've invented. We see the same pattern in fame, power, military victories, knowledge, and even benefit to humanity. In all of these, the rich get richer. [1]You can't understand the world without understanding the concept of superlinear returns. And if you're ambitious you definitely should, because this will be the wave you surf on.It may seem as if there are a lot of different situations with superlinear returns, but as far as I can tell they reduce to two fundamental causes: exponential growth and thresholds.The most obvious case of superlinear returns is when you're working on something that grows exponentially. For example, growing bacterial cultures. When they grow at all, they grow exponentially. But they're tricky to grow. Which means the difference in outcome between someone who's adept at it and someone who's not is very great.Startups can also grow exponentially, and we see the same pattern there. Some manage to achieve high growth rates. Most don't. And as a result you get qualitatively different outcomes: the companies with high growth rates tend to become immensely valuable, while the ones with lower growth rates may not even survive.Y Combinator encourages founders to focus on growth rate rather than absolute numbers. It prevents them from being discouraged early on, when the absolute numbers are still low. It also helps them decide what to focus on: you can use growth rate as a compass to tell you how to evolve the company. But the main advantage is that by focusing on growth rate you tend to get something that grows exponentially.YC doesn't explicitly tell founders that with growth rate "you get out what you put in," but it's not far from the truth. And if growth rate were proportional to performance, then the reward for performance p over time t would be proportional to pt.Even after decades of thinking about this, I find that sentence startling."""child_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=0)
docs = child_splitter.create_documents([document])# add:增加了parent_splitter这句
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=400)
vectorstore = Chroma(collection_name="documents", embedding_function=bge_embeddings
)store = InMemoryStore()
retriever = ParentDocumentRetriever(vectorstore=vectorstore,docstore=store,child_splitter=child_splitter,# addparent_splitter=parent_splitter
)retriever.add_documents([Document(page_content=document)], ids=None)# 返回小文档检索,问题:什么原因造成了超线性回报?
sub_docs = vectorstore.similarity_search("What causes superlinear returns?")# 返回大文档检索
retrieved_docs = retriever.get_relevant_documents("What causes superlinear returns?")
print(sub_docs[0].page_content)
print('='*40)
print(retrieved_docs[0].page_content)

在这里插入图片描述


http://www.hkcw.cn/article/cyCjogFtjX.shtml

相关文章

(七)【Linux进程的创建、终止和等待】

1 进程创建 1.1 在谈fork函数 #include <unistd.h> // 需要的头文件// 返回值&#xff1a;子进程中返回0&#xff0c;父进程返回子进程id&#xff0c;出错返回-1调用fork函数后&#xff0c;内核做了下面的工作&#xff1a; 创建了一个子进程的PCB结构体、并拷贝一份相…

EMO2:基于末端执行器引导的音频驱动虚拟形象视频生成

今天带来EMO2&#xff08;全称End-Effector Guided Audio-Driven Avatar Video Generation&#xff09;是阿里巴巴智能计算研究院研发的创新型音频驱动视频生成技术。该技术通过结合音频输入和静态人像照片&#xff0c;生成高度逼真且富有表现力的动态视频内容&#xff0c;值得…

Baklib知识中台加速企业服务智能化实践

知识中台架构体系构建 Baklib 通过构建多层级架构体系实现知识中台的底层支撑&#xff0c;其核心包含数据采集层、知识加工层、服务输出层及智能应用层。在数据采集端&#xff0c;系统支持对接CRM、ERP等业务系统&#xff0c;结合NLP技术实现非结构化数据的自动抽取&#xff1…

GpuGeek 618大促引爆AI开发新体验

随着生成式AI技术迅猛发展&#xff0c;高效可靠的算力资源已成为企业和开发者突破创新瓶颈的战略支点。根据赛迪顾问最新发布的《2025中国AI Infra平台市场发展研究报告》显示&#xff0c;2025年中国生成式人工智能企业应用市场规模将达到629.0亿元&#xff0c;作为AI企业级应用…

Linux线程同步实战:多线程程序的同步与调度

个人主页&#xff1a;chian-ocean 文章专栏-Linux Linux线程同步实战&#xff1a;多线程程序的同步与调度 个人主页&#xff1a;chian-ocean文章专栏-Linux 前言&#xff1a;为什么要实现线程同步线程饥饿&#xff08;Thread Starvation&#xff09;示例&#xff1a;抢票问题 …

任务22:创建示例Django项目,展示ECharts图形示例

任务描述 知识点&#xff1a; DjangoECharts 重 点&#xff1a; DjangoECharts 内 容&#xff1a; 创建Django项目掌握ECharts绘制图形通过官网ECharts示例&#xff0c;完成Django项目&#xff0c;并通过配置项进行修改图形 任务指导 1、创建web_test的Django项目 2…

深度学习入门Day1--Python基础

一、基础语法 1.变量 python是“动态类型语言”的编程语言。用户无需明确指出x的类型是int。 x10 #初始化 print(x) #输出x x100 #赋值 print(x) print(type(x))#输出x的类型<class int>2.算术计算 >>>4*5 >20 >>>3**3#**表示乘方&#xff08;3…

九坤:熵最小化加速LLM收敛

&#x1f4d6;标题&#xff1a;One-shot Entropy Minimization &#x1f310;来源&#xff1a;arXiv, 2505.20282 &#x1f31f;摘要 我们训练了 13,440 个大型语言模型&#xff0c;发现熵最小化只需要一个未标记的数据和 10 步优化&#xff0c;以实现比使用数千个数据获得的…

微服务面试(分布式事务、注册中心、远程调用、服务保护)

1.分布式事务 分布式事务&#xff0c;就是指不是在单个服务或单个数据库架构下&#xff0c;产生的事务&#xff0c;例如&#xff1a; 跨数据源的分布式事务跨服务的分布式事务综合情况 我们之前解决分布式事务问题是直接使用Seata框架的AT模式&#xff0c;但是解决分布式事务…

儿童节快乐,聊聊数字的规律和同余原理

某年的6月1日是星期日。那么&#xff0c;同一年的6月30日是星期几&#xff1f; 星期是7天一个循环。所以说&#xff0c;这一天是星期几&#xff0c;7天之后同样也是星期几。而6月30日是在6月1日的29天之后&#xff1a;29 7 4 ... 1用29除以7&#xff0c;可以得出余数为1。而…

视觉分析明火检测助力山东化工厂火情防控

视觉分析技术赋能化工厂火情防控&#xff1a;从山东事故看明火与烟雾检测的应用价值 一、背景&#xff1a;山东化工事故中的火情防控痛点 近期&#xff0c;山东高密友道化学有限公司、淄博润兴化工科技有限公司等企业接连发生爆炸事故&#xff0c;暴露出传统火情防控手段的局…

javaEE->多线程:定时器

一. 定时器 约定一个时间&#xff0c;时间到了&#xff0c;执行某个代码逻辑&#xff08;进行网络通信时常见&#xff09; 客户端给服务器发送请求 之后就需要等待 服务器的响应&#xff0c;客户端不可能无限的等&#xff0c;需要一个最大的期限。这里“等待的最大时间”可以用…

HTML表单

1. 什么是表单 表单常用格式 文本框 密码框 单选按钮 复选框 列表框 按钮 多行文本域 文件域 邮箱 网址 数字 滑块 搜索框 2. 表单的高级应用 隐藏域&#xff08;⭐&#xff09; 隐藏域在网页中会经常被使用&#xff0c;比如我们登录了以后需要持续使用我们的登录信息&#xff…

STM32F407寄存器操作(ADC非连续扫描模式)

1.前言 书接上回&#xff0c;在看手册的时候我突然发现手册上还描述了另一种ADC扫描模式&#xff0c;即非连续扫描模式&#xff0c;想着连续扫描模式都已经探索过了&#xff0c;那就顺手把非非连续模式研究一下吧。 2.理论 我们先看看手册&#xff0c;这里我就以规则通道举例…

老年照护实训室建设方案设计:基础照护与专业护理实训

老年照护实训室的建设是提升老年照护人才培养质量的关键环节&#xff0c;其方案设计需精准对接基础照护与专业护理的实训需求&#xff0c;为学习者构建理论与实践深度融合的教学场景。点击获取实训室建设方案 一、建设背景与目标 &#xff08;一&#xff09;建设背景 随着人…

C语言 — 文件

目录 1.流1.1 流的概念1.2 常见的的流 2.文件的打开和关闭2.1 fopen函数2.2 fclose函数2.3 文件的打开和关闭 3.文件的输入输出函数3.1 fputc函数3.2 fgetc函数3.3 feof函数和ferror函数3.4 fputs函数3.5 fgets函数3.6 fwrite函数3.7 fread函数3.8 fprintf函数3.9 fscanf函数 4…

13. springCloud AlibabaSeata处理分布式事务

目录 一、分布式事务面试题 1.多个数据库之间如何处理分布式事务&#xff1f; 2.若拿出如下场景&#xff0c;阁下将如何应对? 3.阿里巴巴的Seata-AT模式如何做到对业务的无侵入? 4.对于分布式事务问题&#xff0c;你知道的解决方案有哪些?请你谈谈? 二、分布式事务问题…

java多线程与JUC

进程线程 进程&#xff1a;进程是操作系统分配资源的基本单位。在电脑中&#xff0c;一个软件就是一个进程 线程&#xff1a;线程是CPU调度的基本单位&#xff0c;是进程内的执行单元。相当于一个软件中的不同功能 多线程程序的特点&#xff1a;程序可以同时去做多件事&#…

GCC 下载安装

下载 官网&#xff1a;GCC, the GNU Compiler Collection- GNU Project Cygwin linux 环境 MinGW 在 Windows 上提供 GNU 开发工具比 Cygwin 更轻量&#xff08;不模拟完整的 POSIX 环境&#xff09;选择&#xff1a;binaries选择mingw-w64

GpuGeek如何成为AI基础设施市场的中坚力量

AI时代&#xff0c;算力基础设施已成为支撑技术创新和产业升级的关键要素。作为国内专注服务算法工程师群体的智算平台&#xff0c;GpuGeek通过持续创新的服务模式、精准的市场定位和系统化的生态建设&#xff0c;正快速成长为AI基础设施领域的中坚力量。本文将深入分析GpuGeek…