在人工智能领域迅猛发展的今天,检索增强生成(RAG)系统已成为构建可靠且上下文感知应用的基石,这些应用由大型语言模型(LLM)驱动。RAG 系统通过检索外部知识库(如向量数据库)中的相关信息,来弥合 LLM 与外部知识之间的差距,从而提升模型的响应质量。然而,在实现高效 RAG 系统时,一个最关键却常常被忽略的步骤是“分块”(chunking)——即将大型文档分解成更小、更易消化的片段的过程。
为什么分块如此重要?LLM 的上下文窗口有限,通常限制了一次能处理的文本量。糟糕的分块策略可能导致上下文不完整、检索无关信息,甚至生成幻觉输出。相反,精心选择的分块策略能显著提升检索准确性、响应相关性和整体系统性能。事实上,分块对 RAG 效果的影响往往大于嵌入模型或向量存储的选择。
本文将探讨八种流行的分块策略,借鉴行业最佳实践。我们将涵盖每种策略的描述、优缺点、理想使用场景,以及适用的代码示例。最后,我们提供一个决策框架,帮助您为 RAG 系统选择合适的技术。
fromtypingimportList
importre
defword_splitter(source_text:str) ->List[str]:
source_text = re.sub("\s+"," ", source_text)
returnre.split("\s", source_text)
defget_chunks_fixed_size_with_overlap(text:str, chunk_size:int, overlap_fraction:float=0.2) ->List[str]:
text_words = word_splitter(text)
overlap_int =int(chunk_size * overlap_fraction)
chunks = []
foriinrange(0,len(text_words), chunk_size):
chunk_words = text_words[max(i - overlap_int,0): i + chunk_size]
chunk =" ".join(chunk_words)
chunks.append(chunk)
returnchunks
ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;display: table;padding: 0px 0.2em;margin: 4em auto 2em;color: rgb(255, 255, 255);background: rgb(15, 76, 129);">2. 递归分块ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">这种方法使用分隔符的层次结构(例如,双换行符分段落,然后单换行符分句子),递归地分割文本,直到块符合所需大小。优点:比固定大小更尊重文档结构,减少了不自然的断点。
缺点:仍基于规则,可能无法捕捉深层语义;编码稍复杂。
何时使用:适用于半结构化文本,如博客文章、新闻或研究论文,这些文本存在逻辑断点。
代码示例(Python):
fromtypingimportList
defrecursive_chunking(text:str, max_chunk_size:int=1000) ->List[str]:
iflen(text) <= max_chunk_size:
return[text.strip()]iftext.strip()else[]
separators = ["\n\n","\n",". "," "]
forseparatorinseparators:
ifseparatorintext:
parts = text.split(separator)
chunks = []
current_chunk =""
forpartinparts:
test_chunk = current_chunk + separator + partifcurrent_chunkelsepart
iflen(test_chunk) <= max_chunk_size:
current_chunk = test_chunk
else:
ifcurrent_chunk:
chunks.append(current_chunk.strip())
current_chunk = part
ifcurrent_chunk:
chunks.append(current_chunk.strip())
final_chunks = []
forchunkinchunks:
iflen(chunk) > max_chunk_size:
final_chunks.extend(recursive_chunking(chunk, max_chunk_size))
else:
final_chunks.append(chunk)
return[chunkforchunkinfinal_chunksifchunk]
return[text[i:i + max_chunk_size]foriinrange(0,len(text), max_chunk_size)]
内容感知分块基于自然内容单元(如句子、段落或章节)进行分割,使用分隔符或 NLP 工具。
优点:保持逻辑完整性,提升检索相关性。
缺点:依赖一致的格式;对结构不良的数据无效。
何时使用:适用于格式化文档,如报告、书籍或法律文本。
代码示例:使用 NLTK 库进行句子分割:
importnltk
nltk.download('punkt')
defsentence_chunking(text:str) ->List[str]:
sentences = nltk.sent_tokenize(text)
returnsentences # 如需进一步组合以达到所需大小
这种高级技术使用嵌入来分组语义相似的句子或段落,通常通过余弦相似度阈值实现。
优点:确保块在上下文中连贯,提升 RAG 准确性。
缺点:嵌入计算导致高计算成本。
何时使用:适用于密集的技术内容,如科学论文或代码库,其中含义优先于结构。
代码示例:使用 sentence-transformers:
fromsentence_transformersimportSentenceTransformer, util
model = SentenceTransformer('all-MiniLM-L6-v2')
defsemantic_chunking(sentences
ist[str], similarity_threshold:float=0.7) ->List[str]:
embeddings = model.encode(sentences)
chunks = []
current_chunk = [sentences[0]]
foriinrange(1,len(sentences)):
sim = util.cos_sim(embeddings[i-1], embeddings[i])
ifsim > similarity_threshold:
current_chunk.append(sentences[i])
else:
chunks.append(' '.join(current_chunk))
current_chunk = [sentences[i]]
chunks.append(' '.join(current_chunk))
returnchunks
利用 LLM 作为“代理”来智能确定块边界,基于提示进行操作。
优点:高度适应性且语义感知,无需固定规则。
缺点:因 LLM 调用而昂贵且缓慢;可能不一致。
何时使用:高价值场景,如自定义企业数据,精度至关重要。
代码示例:通过 OpenAI API 提示 LLM:
importopenai
defAgentic_chunking(text:str, max_chunk_size:int) ->List[str]:
response = openai.ChatCompletion.create(
model="gpt-4",
messages=[{"role":"user","content":f"将此文本分割成不超过{max_chunk_size}个字符的连贯块:{text}"}]
)
chunks = response.choices[0].message.content.split('\n\n') # 假设块由双换行符分隔
returnchunks
通过在文本上滑动窗口创建重叠块,确保连续上下文。
优点:捕捉边界间信息,减少信息丢失。
缺点:因冗余而增加存储和检索开销。
何时使用:叙事或序列数据,如故事、转录或时间序列日志。
代码示例:类似于固定大小,但步长可调:
defsliding_window_chunking(text:str, window_size:int, step:int) ->List[str]:
words = text.split()
chunks = []
foriinrange(0,len(words), step):
chunk =' '.join(words[i:i + window_size])
ifchunk:
chunks.append(chunk)
returnchunks
构建多级结构(例如,文档 > 章节 > 段落 > 句子),用于分层检索。
优点:启用灵活的多分辨率搜索。
缺点:设置和索引复杂。
何时使用:大型层次文档,如手册、维基或学术论文。
代码示例:表示为树状结构(简化):
# 使用 BeautifulSoup 等库解析 HTML 或自定义解析器处理层次
defhierarchical_chunking(document:dict) ->dict:
# 假设文档已解析成章节
hierarchy = {'sections': []}
forsectionindocument['sections']:
chunks = {'title': section['title'],'paragraphs': [pforpinsection['content'].split('\n\n')]}
hierarchy['sections'].append(chunks)
returnhierarchy
一种新颖方法,首先在长上下文中生成嵌入,然后在嵌入后派生块,以保留完整上下文。
优点:更好地处理长依赖;对某些模型成本效益高。
缺点:需要兼容的嵌入模型;不普遍适用。
何时使用:与长上下文 LLM 一起使用,或处理相互连接的叙事,如小说或法律合同。
代码示例:使用专用 API(例如 Cohere):
importcohere
co = cohere.Client('your-api-key')
deflate_chunking(text:str) ->List[str]:
# 嵌入长文本,然后基于嵌入簇进行分割
response = co.embed(texts=[text], model='embed-english-v3.0')
embeddings = response.embeddings[0]
# 使用聚类(例如 KMeans)定义块
# 简化:假设后处理分割
return[text] # 实际实现的占位符
选择合适的分块策略取决于几个因素:
总之,没有一种分块技术适用于所有场景——最佳选择取决于您的数据和用例。通过理解这些策略并应用决策框架,您可以优化 RAG 系统以实现卓越性能。
| 欢迎光临 链载Ai (https://www.lianzai.com/) | Powered by Discuz! X3.5 |