1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
| from typing import List
import re
from sentence_transformers import SentenceTransformer
class TextChunker:
"""文本分块器"""
def __init__(self, method: str = "recursive"):
self.method = method
self.embedding_model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
def chunk(self, text: str, **kwargs) -> List[str]:
"""分块"""
if self.method == "fixed_size":
return self.fixed_size_chunk(text, **kwargs)
elif self.method == "recursive":
return self.recursive_chunk(text, **kwargs)
elif self.method == "semantic":
return self.semantic_chunk(text, **kwargs)
else:
raise ValueError(f"Unknown chunking method: {self.method}")
def fixed_size_chunk(
self,
text: str,
chunk_size: int = 500,
overlap: int = 50
) -> List[str]:
"""固定大小分块"""
chunks = []
start = 0
text_length = len(text)
while start < text_length:
end = start + chunk_size
chunk = text[start:end]
chunks.append(chunk)
# 移动到下一个块(考虑重叠)
start = end - overlap
return chunks
def recursive_chunk(
self,
text: str,
separators: List[str] = None,
chunk_size: int = 1000,
overlap: int = 100
) -> List[str]:
"""递归分块 - 按分隔符智能分块"""
if separators is None:
separators = ["\n\n", "\n", "。", "!", "?", ".", "!", "?", " ", ""]
# 尝试按分隔符分割
for separator in separators:
if separator in text:
parts = text.split(separator)
chunks = []
current_chunk = ""
for part in parts:
# 如果添加这个部分会超过限制
if len(current_chunk) + len(part) + len(separator) > chunk_size:
if current_chunk:
chunks.append(current_chunk)
# 如果单个部分本身就太长,递归处理
if len(part) > chunk_size:
sub_chunks = self.recursive_chunk(
part,
separators[separators.index(separator) + 1:],
chunk_size,
overlap
)
chunks.extend(sub_chunks)
current_chunk = ""
else:
current_chunk = part + separator
else:
current_chunk += part + separator
# 添加最后一个块
if current_chunk:
chunks.append(current_chunk)
return chunks
# 如果没有找到分隔符,使用固定大小分块
return self.fixed_size_chunk(text, chunk_size, overlap)
def semantic_chunk(
self,
text: str,
max_similarity: float = 0.7
) -> List[str]:
"""语义分块 - 基于语义相似度"""
# 首先按句子分割
sentences = re.split(r'([。!?.!?])', text)
sentences = [
s1 + s2
for s1, s2 in zip(sentences[::2], sentences[1::2])
if s1.strip()
]
if not sentences:
return [text]
chunks = []
current_chunk = [sentences[0]]
for sentence in sentences[1:]:
# 计算与当前块最后一个句子的相似度
last_sentence = current_chunk[-1]
similarity = self.compute_similarity(last_sentence, sentence)
# 如果相似度高,合并到当前块
if similarity < max_similarity:
current_chunk.append(sentence)
else:
# 否则开始新块
chunks.append("".join(current_chunk))
current_chunk = [sentence]
# 添加最后一个块
if current_chunk:
chunks.append("".join(current_chunk))
return chunks
def compute_similarity(self, text1: str, text2: str) -> float:
"""计算语义相似度"""
emb1 = self.embedding_model.encode(text1)
emb2 = self.embedding_model.encode(text2)
# 余弦相似度
import numpy as np
return np.dot(emb1, emb2) / (np.linalg.norm(emb1) * np.linalg.norm(emb2))
|