偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

RAG調(diào)優(yōu)進階:21種切塊策略,不光有代碼,更有超詳細(xì)場景、優(yōu)缺點分析! 原創(chuàng) 精華

發(fā)布于 2025-8-25 08:50
瀏覽
0收藏

RAG調(diào)優(yōu)進階:21種切塊策略,不光有代碼,更有超詳細(xì)場景、優(yōu)缺點分析!-AI.x社區(qū)

在深入每一種切塊策略之前,需要先把基礎(chǔ)工具和模擬數(shù)據(jù)準(zhǔn)備好。

import re
import nltk
from nltk.tokenize import sent_tokenize, word_tokenize
from transformers import AutoTokenizer
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np

# 確保已經(jīng)下載了nltk的punkt分詞器
try:
    nltk.data.find('tokenizers/punkt')
except nltk.downloader.DownloadError:
    nltk.download('punkt')

# 模擬一些文本數(shù)據(jù)
sample_text_long = """
RAG(Retrieval-Augmented Generation)是一種結(jié)合了檢索和生成能力的AI技術(shù)。它的核心思想是,當(dāng)大語言模型(LLM)需要回答問題時,它不再是憑空生成答案,而是首先從一個龐大的知識庫中檢索出最相關(guān)的上下文信息。

這些檢索到的信息隨后會被作為輸入的一部分,提供給LLM,LLM再基于這些“額外”的知識來生成最終的回答。這種方式能夠顯著減少模型“胡說八道”(幻覺)的現(xiàn)象,并使得模型能夠回答特定領(lǐng)域的問題,因為它的知識不再僅僅局限于訓(xùn)練時的數(shù)據(jù)。

在實際應(yīng)用中,RAG系統(tǒng)涉及到多個關(guān)鍵組件。首先是**數(shù)據(jù)攝取和預(yù)處理**,這通常包括將原始文檔(如PDF、網(wǎng)頁、數(shù)據(jù)庫記錄等)清洗、解析,并轉(zhuǎn)換為適合存儲和檢索的格式。接著是**切塊(Chunking)**,這是RAG流程中至關(guān)重要的一步。切塊的目的是將長文本分割成較小的、易于管理和檢索的片段。

切塊策略的選擇直接影響檢索的質(zhì)量。如果切塊過大,可能包含太多無關(guān)信息,增加LLM處理的負(fù)擔(dān);如果切塊過小,則可能丟失上下文,導(dǎo)致LLM無法理解完整語義。

然后是**嵌入(Embedding)**,將文本切塊轉(zhuǎn)換為高維向量,這些向量能夠捕捉文本的語義信息。接著是**向量數(shù)據(jù)庫(Vector Database)**,用于高效存儲和檢索這些嵌入向量。當(dāng)用戶提出問題時,**檢索器(Retriever)**會根據(jù)用戶問題的嵌入向量,在向量數(shù)據(jù)庫中查找最相似的文本切塊。

最后,**生成器(Generator)**,也就是LLM,會結(jié)合用戶的原始問題和檢索到的相關(guān)上下文信息,生成最終的答案。整個RAG流程的優(yōu)化是一個迭代的過程,需要不斷地調(diào)整各個環(huán)節(jié),才能達(dá)到最佳效果。
"""

sample_text_structured = """
# 第一章 引言
## 1.1 RAG的魅力
RAG技術(shù)有效解決了大模型幻覺問題。

## 1.2 本文目的
本文將深入探討RAG中的切塊策略。

# 第二章 切塊基礎(chǔ)
## 2.1 樸素切塊
這是一種簡單的切塊方法。

## 2.2 固定大小切塊
我們將會看到固定大小切塊的實現(xiàn)。

---
**Note:** 本文檔旨在提供RAG切塊的全面指南。
"""

sample_text_with_tables = """
這是一段關(guān)于公司業(yè)績的文本。

| Month | Sales | Profit |
|---|---|---|
| Jan | 100 | 20 |
| Feb | 120 | 25 |
| Mar | 110 | 22 |

以上是第一季度的財務(wù)數(shù)據(jù)。

下面是團隊成員信息:

| Name | Role |
|---|---|
| Alice | Engineer |
| Bob | Designer |
"""

sample_text_mixed_format = """
這是一個段落。它包含一些重要的信息,需要被完整保留。

1.  這是一個列表項。
2.  這是另一個列表項。
    * 子項1
    * 子項2

## 重要提示
請注意以下表格數(shù)據(jù):

| Item | Quantity | Price |
|---|---|---|
| Apple | 10 | 1.0 |
| Banana | 5 | 0.5 |
"""


一、基礎(chǔ)篇:簡單粗暴,但有時特好用!

這些方法操作起來賊簡單,對付特定數(shù)據(jù)效果還真不賴。

1. 樸素切塊(Naive Chunking):

  • 場景使用:當(dāng)你的文本天然就以行為單位組織,并且每一行都承載一個相對完整、獨立的想法時,這種方法特別高效。比如,會議紀(jì)要、聊天記錄、產(chǎn)品FAQ列表、項目待辦事項、帶有明確換行符的筆記等等。
  • 優(yōu)點:

     a.實現(xiàn)簡單: 幾乎是所有切塊方法中最容易實現(xiàn)的,一行代碼搞定。

     b.速度快: 處理大量文本時效率極高,不會引入復(fù)雜計算。

     c.語義清晰(特定場景下): 如果文本就是按行劃分語義,那么切出來的塊語義很純粹。

  • 缺點:

     a.上下文丟失風(fēng)險: 如果一句話或一個完整想法跨越多行,這種方法會直接將其“腰斬”,導(dǎo)致重要的上下文信息丟失。

     b.塊大小不均: 每行長度不一,導(dǎo)致切出來的塊大小差異大,可能超出LLM的token限制或過短導(dǎo)致信息不足。

     c.不適用于連續(xù)文本: 對于小說、論文、博客等段落式的連續(xù)文本,效果通常很差。

def naive_chunking(text):
    """
    按行分割文本。
    """
    return text.split('\n')

print("--- 1. 樸素切塊 ---")
chunks_naive = naive_chunking(sample_text_long)
for i, chunk in enumerate(chunks_naive[:5]): # 只打印前5個示例
    print(f"Chunk {i+1}: '{chunk.strip()}'")
print("...")

RAG調(diào)優(yōu)進階:21種切塊策略,不光有代碼,更有超詳細(xì)場景、優(yōu)缺點分析!-AI.x社區(qū)

2. 固定大小切塊(Fixed-size/Fixed Window Chunking):

  • 場景使用:面對“一鍋粥”式的原始、混亂文本數(shù)據(jù)時,比如從PDF中OCR(光學(xué)字符識別)出來的、沒有標(biāo)點符號或格式的文本,或者大型的日志文件、數(shù)據(jù)流。當(dāng)你對文本結(jié)構(gòu)一無所知,又需要快速將數(shù)據(jù)拆分成固定大小的片段以適應(yīng)模型輸入時,這是最直接的選擇。
  • 優(yōu)點:

     a.簡單且快速: 實現(xiàn)起來也很簡單,切割效率高。

     b.易于管理: 每個塊的大小固定,便于批量處理和模型輸入管理,尤其是在LLM有嚴(yán)格token限制時。

     c.兜底策略: 在其他結(jié)構(gòu)化切塊方法都失效時,可以作為一種普適的兜底方案。

  • 缺點:

     a.上下文被截斷: 最大的缺點是它會毫不留情地在任何位置切開文本,常常會把句子、段落甚至完整的想法截斷,導(dǎo)致語義不完整或上下文信息流失嚴(yán)重。

     b.信息冗余: 在處理結(jié)構(gòu)化文本時,一個塊可能包含多余的信息,或與下一個塊的內(nèi)容高度重疊。

def fixed_size_chunking(text, chunk_size, overlap=0):
    """
    按固定大小切塊,可選擇重疊。
    chunk_size: 每個切塊的字符數(shù)
    overlap: 重疊的字符數(shù)
    """
    chunks = []
    text_len = len(text)
    # 注意:這里 i 的步長是 chunk_size - overlap,確保重疊
    for i in range(0, text_len, chunk_size - overlap):
        chunk = text[i:i + chunk_size]
        chunks.append(chunk)
        if i + chunk_size >= text_len: # 防止最后一個切塊不完整,但又跳過重疊部分
            break
    return chunks

print("\n--- 2. 固定大小切塊 ---")
chunks_fixed = fixed_size_chunking(sample_text_long, chunk_size=200, overlap=0)
for i, chunk in enumerate(chunks_fixed[:3]):
    print(f"Chunk {i+1}: '{chunk.strip()}'")
print("...")

RAG調(diào)優(yōu)進階:21種切塊策略,不光有代碼,更有超詳細(xì)場景、優(yōu)缺點分析!-AI.x社區(qū)

3. 滑動窗口切塊(Sliding Window Chunking):

  • 場景使用:當(dāng)你的文本內(nèi)容上下文關(guān)聯(lián)緊密、信息連續(xù)性強時,比如小說、敘事性報告、詳細(xì)的技術(shù)文檔、自由流動的隨筆等。它能有效緩解固定大小切塊中上下文被截斷的問題,尤其適用于LLM需要更廣闊語境才能準(zhǔn)確理解和生成回答的場景。
  • 優(yōu)點:

     a.保持上下文: 通過塊之間的重疊,能有效保留跨塊的上下文信息,降低LLM理解時出現(xiàn)“斷層”的風(fēng)險。

     b.提高檢索精度: 檢索時,即使查詢命中一個重疊部分,也能帶回包含更完整上下文的塊。

     c.適用于無結(jié)構(gòu)文本: 對沒有明確標(biāo)題、段落分隔的文本也能較好地處理。

  • 缺點:

     a.冗余度增加: 重疊部分會增加存儲和處理的冗余,導(dǎo)致向量數(shù)據(jù)庫更大,嵌入和檢索成本增加。

     b.計算開銷: 更多的塊意味著更多的嵌入計算和檢索操作。

     c.參數(shù)調(diào)優(yōu):??chunk_size??? 和 ??overlap?? 的比例需要根據(jù)實際數(shù)據(jù)和LLM的特性進行仔細(xì)調(diào)優(yōu),否則可能效果不佳。

def sliding_window_chunking(text, chunk_size, overlap):
    """
    滑動窗口切塊,每個切塊與前一個重疊。
    chunk_size: 每個切塊的字符數(shù)
    overlap: 重疊的字符數(shù)
    """
    chunks = []
    text_len = len(text)
    start = 0
    while start < text_len:
        end = min(start + chunk_size, text_len)
        chunk = text[start:end]
        chunks.append(chunk)
        if end == text_len:
            break
        start += (chunk_size - overlap)
    return chunks

print("\n--- 3. 滑動窗口切塊 ---")
chunks_sliding = sliding_window_chunking(sample_text_long, chunk_size=200, overlap=50)
for i, chunk in enumerate(chunks_sliding[:3]):
    print(f"Chunk {i+1}: '{chunk.strip()}'")
print("...")

RAG調(diào)優(yōu)進階:21種切塊策略,不光有代碼,更有超詳細(xì)場景、優(yōu)缺點分析!-AI.x社區(qū)

4. 基于句子切塊(Sentence-based Chunking):

  • 場景使用:最適合語法結(jié)構(gòu)完整、句子獨立承載完整語義的文本,如新聞報道、博客文章、產(chǎn)品說明書、法律條文、論文摘要、結(jié)構(gòu)化的文檔或純文本數(shù)據(jù)。它可以作為更復(fù)雜切塊策略的“第一步”,得到粒度最小的語義單元。
  • 優(yōu)點:

     a.語義完整性高: 每個切塊都是一個完整的句子,通常能保證最小的語義單元不被破壞。

     b.粒度精細(xì): 提供了最細(xì)粒度的信息,便于后續(xù)的重排、過濾或更復(fù)雜的組合操作。

     c.易于理解: LLM處理完整句子時,理解成本更低。

  • 缺點:

     a.上下文不足: 單個句子可能缺乏足夠的上下文來完全理解其含義,尤其是在上下文分散于多個句子的復(fù)雜概念中。

     b.數(shù)量龐大: 對于長文檔,句子切塊會生成大量小塊,增加存儲和檢索的負(fù)擔(dān)。

     c.標(biāo)點依賴: 嚴(yán)重依賴文本中的標(biāo)點符號來識別句子邊界,如果文本質(zhì)量差(如OCR錯誤、缺乏標(biāo)點),效果會大打折扣。

def sentence_based_chunking(text):
    """
    使用nltk進行句子級別的切塊。
    """
    return sent_tokenize(text)

print("\n--- 4. 基于句子切塊 ---")
chunks_sentence = sentence_based_chunking(sample_text_long)
for i, chunk in enumerate(chunks_sentence[:5]):
    print(f"Chunk {i+1}: '{chunk.strip()}'")
print("...")

RAG調(diào)優(yōu)進階:21種切塊策略,不光有代碼,更有超詳細(xì)場景、優(yōu)缺點分析!-AI.x社區(qū)

5. 基于段落切塊(Paragraph-based Chunking):

  • 場景使用:適用于傳統(tǒng)意義上以段落為單位組織內(nèi)容的文檔,如博客文章、報告、論文、書籍章節(jié)等。當(dāng)你想在保持一定上下文量的同時,又能根據(jù)文章的自然邏輯進行分割時,這是非常好的選擇。
  • 優(yōu)點:

     a.上下文適中: 比句子切塊能提供更多的上下文,通常一個段落能表達(dá)一個完整的想法或論點。

     b.結(jié)構(gòu)清晰: 尊重文檔原有的段落結(jié)構(gòu),切塊結(jié)果更符合人類閱讀習(xí)慣。

     c.實現(xiàn)簡單: 通常通過雙換行符(??\n\n??)或簡單的文本解析就能實現(xiàn)。

  • 缺點:

     a.段落長度不一: 不同段落的長度差異可能很大,有些段落可能過長超出LLM的token限制,有些可能過短信息量不足。

     b.上下文跨段落: 如果一個邏輯概念跨越多個段落,可能會被切斷。

     c.格式依賴: 依賴于文本中正確的段落分隔符,如果原始文本格式混亂,效果會受影響。

def paragraph_based_chunking(text):
    """
    按雙換行符分割文本(通常表示段落)。
    """
    # 使用正則表達(dá)式匹配一個或多個換行符,并移除空字符串
    paragraphs = [p.strip() for p in re.split(r'\n{2,}', text) if p.strip()]
    return paragraphs

print("\n--- 5. 基于段落切塊 ---")
chunks_paragraph = paragraph_based_chunking(sample_text_long)
for i, chunk in enumerate(chunks_paragraph[:3]):
    print(f"Chunk {i+1}: '{chunk.strip()}'")
print("...")

RAG調(diào)優(yōu)進階:21種切塊策略,不光有代碼,更有超詳細(xì)場景、優(yōu)缺點分析!-AI.x社區(qū)

6. 基于頁面切塊(Page-based Chunking):

  • 場景使用:主要針對具有明確分頁結(jié)構(gòu)的文檔,如PDF文檔、掃描的紙質(zhì)文檔、幻燈片(PPT)、書籍等。當(dāng)你的檢索結(jié)果需要引用到頁碼,或者文檔的布局(如圖片、表格分布)與頁面的邏輯高度相關(guān)時,這種方法就顯得尤為重要。
  • 優(yōu)點:

     a.保留原始布局信息: 每個切塊對應(yīng)一個物理頁面,能完整保留該頁面的所有信息和布局,方便在原始文檔中定位。

     b.易于引用: 直接關(guān)聯(lián)頁碼,便于用戶或LLM引用原始出處。

     c.簡化處理: 對于已分頁的文檔,省去了復(fù)雜的語義分析。

  • 缺點:

     a.上下文被截斷: 如果一個概念、句子或表格跨越頁面,則會被無情截斷,導(dǎo)致語義不完整。

     b.塊大小差異大: 不同頁面的內(nèi)容量差異可能很大,導(dǎo)致切塊大小不均,影響LLM處理效率。

     c.依賴于文檔格式: 必須是已經(jīng)分頁的文檔才能使用,對于純文本或無分頁概念的文檔不適用。

def page_based_chunking(text, page_delimiter="---PAGE_BREAK---"):
    """
    模擬頁面切塊,假設(shè)文本中存在頁面分隔符。
    實際應(yīng)用中需要從PDF等文件讀取。
    """
    # 模擬一個多頁文本
    multi_page_text = (
        "這是第一頁的內(nèi)容。\n一些重要的信息在這里。\n" +
        page_delimiter + "\n" +
        "這是第二頁的內(nèi)容。\n繼續(xù)重要的討論。\n" +
        page_delimiter + "\n" +
        "第三頁是總結(jié)。\n全文到此結(jié)束。"
    )
    return [p.strip() for p in multi_page_text.split(page_delimiter) if p.strip()]

print("\n--- 6. 基于頁面切塊 ---")
chunks_page = page_based_chunking(sample_text_long) # 使用模擬文本
for i, chunk in enumerate(chunks_page):
    print(f"Page {i+1}:\n'{chunk}'")

RAG調(diào)優(yōu)進階:21種切塊策略,不光有代碼,更有超詳細(xì)場景、優(yōu)缺點分析!-AI.x社區(qū)

7. 結(jié)構(gòu)化切塊(Structured Chunking):

  • 場景使用:當(dāng)你處理的是具有明確內(nèi)部結(jié)構(gòu)的數(shù)據(jù)時,如日志文件(按日志條目)、JSON數(shù)據(jù)(按字段)、XML/HTML文檔(按標(biāo)簽)、Markdown文檔(按標(biāo)題或特定元素)、CSV文件(按行或特定列)。這種方法能確保切塊結(jié)果嚴(yán)格遵循數(shù)據(jù)本身的邏輯和層級。
  • 優(yōu)點:

     a.語義完整性強: 每個切塊都對應(yīng)數(shù)據(jù)中的一個邏輯單元,語義上高度完整和聚焦。

     b.準(zhǔn)確性高: 不依賴模糊的文本特征,而是基于確定的結(jié)構(gòu)規(guī)則,切塊準(zhǔn)確率高。

     c.便于信息抽取: 切塊后可以直接提取結(jié)構(gòu)化信息,方便后續(xù)的知識圖譜構(gòu)建或特定字段檢索。

  • 缺點:

     a.依賴于結(jié)構(gòu): 如果數(shù)據(jù)結(jié)構(gòu)不一致或有錯誤,切塊會失敗。

     b.解析復(fù)雜性: 需要針對不同結(jié)構(gòu)編寫特定的解析邏輯,增加了實現(xiàn)的復(fù)雜性。

     c.通用性差: 每種結(jié)構(gòu)需要一套獨立的切塊規(guī)則,無法通用。

def structured_chunking(text):
    """
    根據(jù)Markdown標(biāo)題結(jié)構(gòu)進行切塊。
    """
    chunks = []
    # 匹配Markdown標(biāo)題,同時捕獲標(biāo)題和其后的內(nèi)容
    # 注意:這里會把每個標(biāo)題下的內(nèi)容切成一個塊
    sections = re.split(r'^(#+ .*)$', text, flags=re.MULTILINE)
    
    current_heading = ""
    current_content = []
    
    # 第一個元素通常是空字符串或標(biāo)題之前的內(nèi)容
    if sections[0].strip():
        chunks.append(sections[0].strip())

    for i in range(1, len(sections)):
        part = sections[i].strip()
        if part.startswith('#'): # 這是一個標(biāo)題
            if current_content: # 如果有之前收集的內(nèi)容,先作為一個塊
                chunks.append("\n".join(current_content).strip())
                current_content = [] # 重置
            current_heading = part
            current_content.append(current_heading) # 將標(biāo)題也包含在塊內(nèi)
        else: # 這是一個標(biāo)題下的內(nèi)容
            current_content.append(part)
    
    if current_content: # 添加最后一個塊
        chunks.append("\n".join(current_content).strip())

    return [chunk for chunk in chunks if chunk] # 過濾空塊

print("\n--- 7. 結(jié)構(gòu)化切塊 ---")
chunks_structured = structured_chunking(sample_text_structured)
for i, chunk in enumerate(chunks_structured):
    print(f"Chunk {i+1}:\n'{chunk}'")

RAG調(diào)優(yōu)進階:21種切塊策略,不光有代碼,更有超詳細(xì)場景、優(yōu)缺點分析!-AI.x社區(qū)

8. 基于文檔結(jié)構(gòu)切塊(Document-Based Chunking):

  • 場景使用:適用于具有清晰的章節(jié)、小節(jié)和標(biāo)題層級的文檔,例如技術(shù)手冊、教科書、研究論文、長篇報告、企業(yè)知識庫文檔。當(dāng)你希望用戶能夠根據(jù)文檔的自然邏輯結(jié)構(gòu)進行檢索,或者LLM需要理解某個特定章節(jié)的完整語境時,這種方法是首選。它也是實現(xiàn)分層切塊(Hierarchical Chunking)的基礎(chǔ)。
  • 優(yōu)點:

     a.高度貼合文檔原意: 切塊結(jié)果與文檔的邏輯結(jié)構(gòu)保持一致,非常自然。

     b.上下文豐富: 每個塊通常包含一個完整的章節(jié)或小節(jié)內(nèi)容,提供足夠的上下文。

     c.便于導(dǎo)航和理解: 用戶和LLM都能清晰地知道信息所屬的章節(jié)位置。

  • 缺點:

     a.依賴文檔格式: 需要文檔有明確的標(biāo)題或章節(jié)標(biāo)記,對于非結(jié)構(gòu)化文本無效。

     b.解析復(fù)雜: 需要更智能的解析器來識別不同級別的標(biāo)題和其對應(yīng)的內(nèi)容。

     c.塊大小不均: 不同章節(jié)的長度可能差異巨大,導(dǎo)致一些塊過大。

def document_based_chunking(text):
    """
    基于文檔的自然結(jié)構(gòu)(如Markdown的章節(jié)和子章節(jié))進行切塊。
    這里我們將捕獲頂級標(biāo)題下的所有內(nèi)容作為一個塊,直到下一個同級或更高級的標(biāo)題。
    """
    chunks = []
    lines = text.split('\n')
    current_chunk_lines = []
    
    for line in lines:
        if line.startswith('#'): # 匹配任何級別的標(biāo)題
            if current_chunk_lines: # 如果當(dāng)前塊有內(nèi)容,就結(jié)束并添加
                chunks.append("\n".join(current_chunk_lines).strip())
                current_chunk_lines = []
            current_chunk_lines.append(line) # 將標(biāo)題作為新塊的開始
        else:
            current_chunk_lines.append(line)
            
    if current_chunk_lines: # 添加最后一個塊
        chunks.append("\n".join(current_chunk_lines).strip())
        
    return [chunk for chunk in chunks if chunk]

print("\n--- 8. 基于文檔結(jié)構(gòu)切塊 ---")
chunks_doc_struct = document_based_chunking(sample_text_structured)
for i, chunk in enumerate(chunks_doc_struct):
    print(f"Chunk {i+1}:\n'{chunk}'")

RAG調(diào)優(yōu)進階:21種切塊策略,不光有代碼,更有超詳細(xì)場景、優(yōu)缺點分析!-AI.x社區(qū)

二、進階篇:聰明切分,解決復(fù)雜問題!

這些方法需要更多的策略和算法支持,能應(yīng)對更復(fù)雜的場景。

9. 基于關(guān)鍵詞切塊(Keyword-based Chunking):

  • 場景使用:當(dāng)文檔沒有明確的標(biāo)題結(jié)構(gòu),但特定的關(guān)鍵詞或短語總是標(biāo)志著新主題或重要信息的開始時,這種方法非常有效。比如,法律合同中的“WHEREAS”、“THEREFORE”,醫(yī)療記錄中的“Diagnosis:”、“Treatment:”,或者產(chǎn)品說明書中的“Warning:”、“Troubleshooting:”。
  • 優(yōu)點:

     a.聚焦特定信息: 能有效地將包含特定關(guān)鍵詞的重要信息切分出來。

     b.規(guī)則靈活: 可以根據(jù)業(yè)務(wù)需求自定義關(guān)鍵詞列表。

     c.適用于半結(jié)構(gòu)化文本: 對缺乏嚴(yán)格結(jié)構(gòu),但有固定標(biāo)記的文本很有用。

  • 缺點:

      a.關(guān)鍵詞依賴: 嚴(yán)重依賴預(yù)定義的關(guān)鍵詞,如果關(guān)鍵詞選擇不當(dāng)或缺失,切塊效果會很差。

      b.上下文丟失: 關(guān)鍵詞可能出現(xiàn)在句子中間,切塊時可能導(dǎo)致句子被截斷。

      c.人工成本: 確定有效的關(guān)鍵詞列表可能需要人工分析和迭代。

def keyword_based_chunking(text, keywords):
    """
    在指定關(guān)鍵詞處進行切塊。
    """
    chunks = []
    # 構(gòu)建正則表達(dá)式,匹配所有關(guān)鍵詞并保留關(guān)鍵詞本身
    # 使用非捕獲組 (?:...) 結(jié)合 | 運算符
    pattern = '|'.join(re.escape(k) for k in keywords)
    parts = re.split(f'({pattern})', text) # 使用捕獲組保留分隔符

    current_chunk = ""
    for part in parts:
        if part.strip() in keywords: # 如果當(dāng)前部分是關(guān)鍵詞
            if current_chunk.strip(): # 將之前的累積作為新塊
                chunks.append(current_chunk.strip())
            current_chunk = part # 關(guān)鍵詞作為新塊的開始
        else:
            current_chunk += part
    
    if current_chunk.strip(): # 添加最后一個塊
        chunks.append(current_chunk.strip())
        
    return [chunk for chunk in chunks if chunk] # 過濾空塊

print("\n--- 9. 基于關(guān)鍵詞切塊 ---")
keywords_for_chunking = ["Note:", "首先是", "接著是"]
chunks_keyword = keyword_based_chunking(sample_text_long + sample_text_structured, keywords_for_chunking)
for i, chunk in enumerate(chunks_keyword):
    print(f"Chunk {i+1}:\n'{chunk}'")

RAG調(diào)優(yōu)進階:21種切塊策略,不光有代碼,更有超詳細(xì)場景、優(yōu)缺點分析!-AI.x社區(qū)

10. 基于實體切塊(Entity-based Chunking):

  • 場景使用:適用于文檔中特定實體(人名、地名、公司、產(chǎn)品等)是核心信息的場景,如新聞文章(圍繞特定人物或事件)、法律合同(圍繞當(dāng)事人)、醫(yī)學(xué)報告(圍繞患者、疾病)、電影劇本(圍繞角色)。當(dāng)你希望檢索結(jié)果能聚焦于某個實體及其相關(guān)描述時,這種方法能提供高度相關(guān)的上下文。
  • 優(yōu)點:

      a.高度聚焦: 每個切塊都圍繞一個或一組實體,保證了信息的強相關(guān)性。

      b.提升檢索精度: 用戶查詢某個實體時,能精準(zhǔn)召回所有與該實體相關(guān)的描述。

      c.知識圖譜構(gòu)建: 為后續(xù)構(gòu)建知識圖譜提供了結(jié)構(gòu)化的基礎(chǔ)。

  • 缺點:

       a.依賴NER模型: 需要高質(zhì)量的命名實體識別(NER)模型,模型性能直接影響切塊效果。

       b.計算開銷大: NER處理本身有計算成本,且切塊邏輯可能更復(fù)雜。

       c.通用性受限: 對于沒有明顯實體的文本,效果不佳。

# pip install spacy
# python -m spacy download en_core_web_sm
import spacy

try:
    nlp = spacy.load("en_core_web_sm")
except OSError:
    print("Downloading spacy model 'en_core_web_sm'...")
    spacy.cli.download("en_core_web_sm")
    nlp = spacy.load("en_core_web_sm")

def entity_based_chunking(text):
    """
    使用NER模型識別實體,并圍繞實體聚合文本。
    這里為了簡化,我們找到實體所在句子,并以句子為單位聚合。
    """
    doc = nlp(text)
    entities = {} # {entity_text: [sentences containing this entity]}

    for sent in doc.sents:
        found_entities_in_sent = False
        for ent in sent.ents:
            if ent.label_ in ["PERSON", "ORG", "GPE", "PRODUCT"]: # 關(guān)注人、組織、地理、產(chǎn)品等實體
                if ent.text notin entities:
                    entities[ent.text] = []
                entities[ent.text].append(sent.text.strip())
                found_entities_in_sent = True
        ifnot found_entities_in_sent: # 如果句子沒有實體,作為獨立塊或添加到“無實體”塊
            if"NO_ENTITY"notin entities:
                entities["NO_ENTITY"] = []
            entities["NO_ENTITY"].append(sent.text.strip())
            
    # 將字典轉(zhuǎn)換為列表,每個實體或無實體組一個塊
    chunks = []
    for entity, sents in entities.items():
        chunk_content = f"Related to {entity}:\n" + "\n".join(list(set(sents))) # 使用set去重
        chunks.append(chunk_content)
    
    return chunks

print("\n--- 10. 基于實體切塊 ---")
sample_ner_text = "Apple公司發(fā)布了新的iPhone 15。Tim Cook在發(fā)布會上強調(diào)了其強大的A17芯片。用戶可以在紐約的Apple Store購買。"
chunks_entity = entity_based_chunking(sample_ner_text)
for i, chunk in enumerate(chunks_entity):
    print(f"Chunk {i+1}:\n'{chunk}'")

RAG調(diào)優(yōu)進階:21種切塊策略,不光有代碼,更有超詳細(xì)場景、優(yōu)缺點分析!-AI.x社區(qū)

11. 基于Token切塊(Token-based Chunking):

  • 場景使用:主要用于需要精確控制LLM輸入token數(shù)量的場景,比如LLM有嚴(yán)格的上下文窗口限制(token limit),或者你希望最大化單個token的使用效率。它通常作為其他切塊方法(如句子切塊)的補充或后處理步驟,以確保最終的塊大小符合LLM要求,同時避免語義被完全破壞。
  • 優(yōu)點:

     a.精確控制塊大?。?/strong> 能夠嚴(yán)格控制每個塊的token數(shù)量,避免超出LLM的輸入限制。

     b.適用于非結(jié)構(gòu)化文本: 對于沒有明確語義結(jié)構(gòu)(如標(biāo)題、段落)的文本,可以作為一種有效的切塊方式。

     c.與LLM兼容性好: 直接以LLM理解的token為單位進行切塊,減少了LLM處理時的額外計算。

  • 缺點:

     a.語義完整性風(fēng)險: 和固定大小切塊類似,可能在token級別直接截斷句子或單詞,導(dǎo)致語義不完整。

     b.需要與語義策略結(jié)合: 單獨使用時容易丟失上下文,通常需要與句子切塊等語義方法結(jié)合,先按語義切小段,再對過長的段落進行token切分。

     c.依賴分詞器: 切塊結(jié)果依賴于所選分詞器的行為,不同分詞器結(jié)果可能不同。

# pip install transformers
def token_based_chunking(text, tokenizer_name="bert-base-uncased", max_tokens=128):
    """
    使用分詞器按token數(shù)量進行切塊。
    """
    tokenizer = AutoTokenizer.from_pretrained(tokenizer_name)
    tokens = tokenizer.encode(text, add_special_tokens=False) # 不添加特殊token
    
    chunks = []
    for i in range(0, len(tokens), max_tokens):
        chunk_tokens = tokens[i:i + max_tokens]
        chunk_text = tokenizer.decode(chunk_tokens)
        chunks.append(chunk_text)
    return chunks

print("\n--- 11. 基于Token切塊 ---")
chunks_token = token_based_chunking(sample_text_long, max_tokens=60)
for i, chunk in enumerate(chunks_token[:3]):
    print(f"Chunk {i+1}:\n'{chunk}'")
print("...")

12. 基于主題切塊(Topic-based Chunking):

  • 場景使用:當(dāng)你的文檔涵蓋多個主題,且主題之間有清晰的界限(但可能沒有明確的標(biāo)題或關(guān)鍵詞標(biāo)記),或者你希望每個切塊都能高度聚焦于一個單一主題時。比如,一個關(guān)于科技趨勢的報告可能同時討論AI、區(qū)塊鏈和元宇宙,用主題切塊可以確保每個塊只包含一個主題的內(nèi)容。
  • 優(yōu)點:

     a.高語義相關(guān)性: 每個切塊都包含一個或少數(shù)幾個緊密相關(guān)的主題,檢索命中后能提供高度聚焦的信息。

     b.應(yīng)對復(fù)雜文檔: 能夠處理主題交織、沒有明確結(jié)構(gòu)的長文檔。

     c.提升檢索質(zhì)量: 減少了切塊中的無關(guān)信息,提高了召回的精確性。

  • 缺點:

     a.實現(xiàn)復(fù)雜: 需要主題模型(如LDA、NMF)或高級聚類算法,計算成本較高。

     b.主題邊界模糊: 在主題過渡平滑的文檔中,確定清晰的主題邊界可能很困難。

     c.參數(shù)調(diào)優(yōu): 聚類算法的參數(shù)(如主題數(shù)量、相似度閾值)需要仔細(xì)調(diào)優(yōu)。

# pip install scikit-learn
def topic_based_chunking(text, min_sentences_per_topic=3):
    """
    通過句子相似度模擬主題切塊。
    將相似的句子聚類成一個主題塊。
    """
    sentences = sent_tokenize(text)
    if len(sentences) < min_sentences_per_topic:
        return [text] # 句子太少,無法有效分主題

    # 使用TF-IDF向量化句子
    vectorizer = TfidfVectorizer().fit(sentences)
    sentence_vectors = vectorizer.transform(sentences)

    chunks = []
    current_topic_sentences = [sentences[0]]
    
    for i in range(1, len(sentences)):
        # 計算當(dāng)前句子與當(dāng)前主題塊中所有句子的平均相似度
        current_sentence_vector = sentence_vectors[i]
        
        # 將當(dāng)前主題塊的句子向量合并
        current_topic_vectors = vectorizer.transform(current_topic_sentences)
        avg_similarity = np.mean(cosine_similarity(current_sentence_vector, current_topic_vectors))

        # 如果相似度低于某個閾值,或者當(dāng)前主題塊句子太多,就認(rèn)為主題切換
        # 這里閾值和數(shù)量都是啟發(fā)式的,實際應(yīng)用中需調(diào)優(yōu)
        if avg_similarity < 0.3or len(current_topic_sentences) >= 5: # 假設(shè)相似度低于0.3或句子多于5句視為新主題
            chunks.append(" ".join(current_topic_sentences))
            current_topic_sentences = [sentences[i]]
        else:
            current_topic_sentences.append(sentences[i])
            
    if current_topic_sentences:
        chunks.append(" ".join(current_topic_sentences))
        
    return chunks

print("\n--- 12. 基于主題切塊 ---")
chunks_topic = topic_based_chunking(sample_text_long)
for i, chunk in enumerate(chunks_topic):
    print(f"Chunk {i+1}:\n'{chunk}'")

RAG調(diào)優(yōu)進階:21種切塊策略,不光有代碼,更有超詳細(xì)場景、優(yōu)缺點分析!-AI.x社區(qū)

13. 表格感知切塊(Table-aware Chunking):

  • 場景使用:當(dāng)你的文檔中包含重要的表格數(shù)據(jù),并且你希望這些表格能夠作為一個完整的語義單元被處理和檢索時。這在財務(wù)報表、產(chǎn)品規(guī)格、統(tǒng)計數(shù)據(jù)、研究數(shù)據(jù)等文檔中尤為常見。通過將表格獨立切塊,LLM能更好地理解表格的結(jié)構(gòu)和內(nèi)容。
  • 優(yōu)點:

     a.保留表格結(jié)構(gòu)和完整性: 確保表格作為一個整體,不會被中間切斷,方便LLM理解其數(shù)據(jù)關(guān)系。

     b.提升表格數(shù)據(jù)檢索: 用戶查詢表格內(nèi)容時,能精確召回整個表格。

     c.有利于LLM處理: LLM對結(jié)構(gòu)化表格數(shù)據(jù)(如Markdown或JSON格式)的處理能力通常優(yōu)于純文本。

  • 缺點:

      a.解析復(fù)雜: 需要強大的表格解析能力,尤其對于非標(biāo)準(zhǔn)格式的表格或圖像中的表格(需要OCR+表格檢測)。

      b.上下文丟失: 表格周圍的文本上下文可能與表格內(nèi)容緊密相關(guān),但如果表格被單獨切塊,這種關(guān)聯(lián)可能會被削弱。

      c.依賴于格式: 僅適用于能夠識別出表格結(jié)構(gòu)的文檔。

def table_aware_chunking(text):
    """
    識別并單獨切塊表格。將表格內(nèi)容轉(zhuǎn)換為Markdown格式。
    """
    chunks = []
    # 匹配Markdown表格的正則表達(dá)式
    # 捕獲表格內(nèi)容,包括表頭、分隔線和行
    table_pattern = re.compile(r'(\|.*\|\n\|[-: ]+\|\n(?:\|.*\|\n?)+)', re.MULTILINE)
    
    last_end = 0
    for match in table_pattern.finditer(text):
        # 添加表格前的內(nèi)容
        pre_table_text = text[last_end:match.start()].strip()
        if pre_table_text:
            chunks.append(pre_table_text)
        
        # 添加表格本身
        chunks.append(match.group(0).strip())
        last_end = match.end()
        
    # 添加最后一個表格后的內(nèi)容
    post_table_text = text[last_end:].strip()
    if post_table_text:
        chunks.append(post_table_text)
        
    return [chunk for chunk in chunks if chunk]

print("\n--- 13. 表格感知切塊 ---")
chunks_table_aware = table_aware_chunking(sample_text_with_tables)
for i, chunk in enumerate(chunks_table_aware):
    print(f"Chunk {i+1}:\n'{chunk}'")

RAG調(diào)優(yōu)進階:21種切塊策略,不光有代碼,更有超詳細(xì)場景、優(yōu)缺點分析!-AI.x社區(qū)

14. 內(nèi)容感知切塊(Content-aware Chunking):

  • 場景使用:適用于包含多種內(nèi)容類型和結(jié)構(gòu)的復(fù)雜文檔,如網(wǎng)頁文章(包含段落、列表、圖片、嵌入視頻)、學(xué)術(shù)論文(包含正文、圖表、公式、參考文獻)、商業(yè)報告(包含文字、表格、圖示)。它是一種“智能”的切塊方法,能根據(jù)不同內(nèi)容的特點采用最合適的分割策略。
  • 優(yōu)點:

     a.語義完整性強: 能夠根據(jù)內(nèi)容類型靈活調(diào)整,最大程度地保持語義完整性,例如段落不被切斷,表格保持完整。

     b.通用性廣: 能夠處理混合格式的復(fù)雜文檔,適應(yīng)性強。

     c.提升檢索質(zhì)量: 每個切塊的內(nèi)容更加聚焦和完整,提高了檢索的準(zhǔn)確性和LLM的理解能力。

  • 缺點:

     a.實現(xiàn)復(fù)雜: 需要一套復(fù)雜的規(guī)則引擎來識別和區(qū)分不同類型的內(nèi)容,并應(yīng)用相應(yīng)的切塊邏輯。

     b.性能開銷: 解析和識別內(nèi)容類型可能增加處理時間。

     c.規(guī)則維護: 隨著文檔格式的變化,可能需要不斷更新和維護切塊規(guī)則。

def content_aware_chunking(text):
    """
    根據(jù)內(nèi)容類型(段落、列表、表格、標(biāo)題等)應(yīng)用不同的切塊規(guī)則。
    這是一個結(jié)合了多種策略的示例。
    """
    chunks = []
    lines = text.split('\n')
    current_chunk_lines = []
    in_table = False
    
    for line in lines:
        stripped_line = line.strip()

        # 檢查是否是表格行(簡單的啟發(fā)式判斷)
        if stripped_line.startswith('|') and'|'in stripped_line[1:]:
            ifnot in_table: # 如果剛進入表格,先結(jié)束前一個非表格塊
                if current_chunk_lines:
                    chunks.append("\n".join(current_chunk_lines).strip())
                    current_chunk_lines = []
                in_table = True
            current_chunk_lines.append(line)
        elif in_table: # 如果在表格中,但當(dāng)前行不是表格行,則表格結(jié)束
            if current_chunk_lines: # 添加完整的表格塊
                chunks.append("\n".join(current_chunk_lines).strip())
                current_chunk_lines = []
            in_table = False
            current_chunk_lines.append(line) # 當(dāng)前行作為新塊的開始
        elif stripped_line.startswith('#'): # 匹配標(biāo)題
            if current_chunk_lines: # 如果有內(nèi)容,結(jié)束前一個塊
                chunks.append("\n".join(current_chunk_lines).strip())
                current_chunk_lines = []
            current_chunk_lines.append(line) # 標(biāo)題作為新塊的開始
        elifnot stripped_line and current_chunk_lines: # 空行作為段落分隔符
            if current_chunk_lines[-1].strip() != "": # 避免連續(xù)空行導(dǎo)致空塊
                chunks.append("\n".join(current_chunk_lines).strip())
                current_chunk_lines = []
        else: # 普通文本行
            current_chunk_lines.append(line)
            
    if current_chunk_lines: # 添加最后一個塊
        chunks.append("\n".join(current_chunk_lines).strip())
        
    return [chunk for chunk in chunks if chunk['content']]

print("\n--- 14. 內(nèi)容感知切塊 ---")
chunks_content_aware = content_aware_chunking(sample_text_mixed_format)
for i, chunk in enumerate(chunks_content_aware):
    print(f"Chunk {i+1}:\n'{chunk}'")

15. 上下文切塊(Contextual Chunking):

  • 場景使用:當(dāng)你的知識庫內(nèi)容復(fù)雜、主題關(guān)聯(lián)性強,且LLM的上下文窗口足夠大,能夠容納額外注入的上下文信息時。這對于金融報告、法律合同、技術(shù)規(guī)范等需要深入理解文本背后邏輯和關(guān)聯(lián)性的場景非常有用。LLM可以生成關(guān)于某個切塊的摘要、主題標(biāo)簽或與相關(guān)切塊的鏈接,從而豐富每個塊的信息。
  • 優(yōu)點:

     a.提升理解深度: 通過LLM添加額外上下文,增強了每個切塊的語義豐富性,幫助下游LLM更好地理解和推理。

     b.降低幻覺: LLM對檢索到的信息理解更全面,減少了生成錯誤答案的風(fēng)險。

     c.靈活適應(yīng): LLM可以根據(jù)具體需求生成不同類型的上下文信息。

  • 缺點:

     a.成本高昂: 需要調(diào)用LLM進行額外處理,會增加API調(diào)用成本和計算延遲。

     b.token消耗: 添加額外上下文會增加每個切塊的token數(shù)量,可能更快達(dá)到LLM的上下文限制。

     c.LLM依賴: 效果嚴(yán)重依賴LLM的生成能力和對知識庫的理解程度。

def mock_llm_add_context(chunk_text, knowledge_base_overview):
    """
    模擬LLM為每個切塊添加相關(guān)上下文。
    在實際中,這需要調(diào)用一個真正的LLM。
    """
    if"RAG"in chunk_text and"檢索"in chunk_text:
        returnf"Context: This chunk details the core retrieval mechanism of RAG and its purpose related to knowledge bases. ---\n{chunk_text}"
    elif"切塊"in chunk_text and"影響"in chunk_text:
        returnf"Context: This chunk elaborates on the criticality of chunking strategies and their impact on LLM performance and context. ---\n{chunk_text}"
    else:
        # 模擬一個通用上下文
        returnf"Context: This text fragment discusses general AI concepts or system components. ---\n{chunk_text}"

def contextual_chunking(text, base_chunking_strategy=paragraph_based_chunking, knowledge_base_overview="Overview of AI and RAG systems."):
    """
    先進行基礎(chǔ)切塊,然后用LLM為每個切塊添加上下文。
    """
    base_chunks = base_chunking_strategy(text, **{}) # 確??梢詡魅肟兆值?    contextualized_chunks = [mock_llm_add_context(chunk, knowledge_base_overview) for chunk in base_chunks]
    return contextualized_chunks

print("\n--- 15. 上下文切塊 ---")
chunks_contextual = contextual_chunking(sample_text_long)
for i, chunk in enumerate(chunks_contextual[:3]):
    print(f"Chunk {i+1}:\n'{chunk}'")
print("...")

RAG調(diào)優(yōu)進階:21種切塊策略,不光有代碼,更有超詳細(xì)場景、優(yōu)缺點分析!-AI.x社區(qū)

16. 語義切塊(Semantic Chunking):

  • 場景使用:當(dāng)你的文檔主題連貫但缺乏明確結(jié)構(gòu),或者不同主題的句子交織在一起時,如訪談記錄、會議紀(jì)要的自由轉(zhuǎn)錄、長篇小說中人物情感的起伏、對某個復(fù)雜概念的多角度闡述。這種方法通過識別句子或段落的語義相似性,將真正“談?wù)撏患隆钡膬?nèi)容聚合在一起。
  • 優(yōu)點:

     a.高語義純度: 確保每個切塊中的內(nèi)容在語義上高度相關(guān),減少無關(guān)信息的干擾。

     b.應(yīng)對無結(jié)構(gòu)文本: 在沒有明確結(jié)構(gòu)的情況下,也能找到自然的語義邊界。

     c.提升檢索質(zhì)量: 用戶查詢某個概念時,能召回所有語義上相關(guān)的片段,即使它們在原文中不相鄰。

  • 缺點:

     a.實現(xiàn)復(fù)雜: 需要使用句子嵌入模型(如Sentence Transformers),并進行向量計算和聚類分析。

     b.計算開銷: 嵌入生成和相似度計算會增加處理時間。

     c.閾值敏感: 相似度閾值的設(shè)置非常關(guān)鍵,過高可能導(dǎo)致塊過小,過低可能導(dǎo)致塊過大并包含多個主題。

     d.模型依賴: 效果取決于所選嵌入模型的語義理解能力。

# pip install sentence-transformers # 實際應(yīng)用會用這個
from sentence_transformers import SentenceTransformer

# 加載一個預(yù)訓(xùn)練的句子嵌入模型 (首次運行可能需要下載)
try:
    embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
except Exception:
    print("Failed to load sentence-transformers model. Please ensure you have internet or download it manually.")
    # 提供一個備用/跳過策略
    embedding_model = None

def semantic_chunking(text, model=embedding_model, similarity_threshold=0.7):
    """
    先嵌入所有句子,然后根據(jù)相似度聚合。
    """
    if model isNone:
        print("Embedding model not loaded, skipping embedding chunking demo.")
        return [text] # 返回原始文本或進行其他默認(rèn)切塊

    sentences = sent_tokenize(text)
    if len(sentences) <= 1:
        return sentences

    sentence_embeddings = model.encode(sentences)
    
    chunks = []
    current_chunk_sentences = [sentences[0]]

    for i in range(1, len(sentences)):
        # 計算當(dāng)前句子與前一個句子嵌入的余弦相似度
        similarity = cosine_similarity([sentence_embeddings[i]], [sentence_embeddings[i-1]])[0][0]
        
        if similarity < similarity_threshold:
            # 如果相似度低,則認(rèn)為語義不連續(xù),結(jié)束當(dāng)前塊
            chunks.append(" ".join(current_chunk_sentences))
            current_chunk_sentences = [sentences[i]]
        else:
            current_chunk_sentences.append(sentences[i])
            
    if current_chunk_sentences: # 添加最后一個塊
        chunks.append(" ".join(current_chunk_sentences))
        
    return chunks

print("\n--- 16. 語義切塊 ---")
if embedding_model:
    chunks_semantic = semantic_chunking(sample_text_long, similarity_threshold=0.5) # 調(diào)整閾值以觀察不同效果
    for i, chunk in enumerate(chunks_semantic[:3]):
        print(f"Chunk {i+1}:\n'{chunk}'")
    print("...")
else:
    print("跳過嵌入切塊演示,因為SentenceTransformer模型未加載。")

17. 遞歸切塊(Recursive Chunking):

  • 場景使用:對于長度不確定、結(jié)構(gòu)不規(guī)則的文本,如采訪記錄、自由形式的寫作、用戶評論、非結(jié)構(gòu)化文檔等。當(dāng)你想確保每個切塊都滿足LLM的最大token限制,同時盡可能保持語義完整性時,遞歸切塊是一個非常強大的通用解決方案。它會優(yōu)先使用大的語義分隔符,如果仍超出限制,則嘗試更小的分隔符,直至滿足要求。
  • 優(yōu)點:

    a.靈活性高: 能夠處理各種長度和結(jié)構(gòu)的文本,適應(yīng)性強。

    b.平衡完整性與粒度: 優(yōu)先保留較大的語義單元(如段落),在必要時才進一步細(xì)分到句子或單詞,盡量減少上下文破壞。

    c.通用性強: 適合作為大多數(shù)RAG系統(tǒng)的通用切塊策略。

  • 缺點:

    a.實現(xiàn)略復(fù)雜: 相較于簡單切塊,邏輯更復(fù)雜,需要定義分隔符優(yōu)先級。

    b.分隔符依賴: 分隔符的選擇和順序會影響切塊質(zhì)量,需要一定的經(jīng)驗和實驗。

    c.可能仍然截斷: 在極端情況下,如果所有分隔符都用完仍無法滿足長度要求,最終可能還是會強制截斷文本。

def recursive_chunking(text, separators, max_chunk_size_char=500):
    """
    遞歸切塊,嘗試不同的分隔符,直到塊大小符合要求。
    separators: 分隔符列表,從大到小排列 (如 ['\n\n', '\n', '. ', ' '])
    max_chunk_size_char: 最大切塊字符數(shù)
    """
    chunks = []
    ifnot text:
        return []

    # 如果文本已經(jīng)小于最大塊大小,直接返回
    if len(text) <= max_chunk_size_char:
        return [text]

    # 嘗試當(dāng)前最大的分隔符
    if separators:
        current_separator = separators[0]
        remaining_separators = separators[1:]
        
        parts = text.split(current_separator)
        
        for part in parts:
            part_stripped = part.strip()
            if part_stripped: # 確保不是空字符串
                if len(part_stripped) > max_chunk_size_char:
                    # 如果部分仍然太大,遞歸調(diào)用更小的分隔符
                    chunks.extend(recursive_chunking(part_stripped, remaining_separators, max_chunk_size_char))
                else:
                    chunks.append(part_stripped)
    else: # 沒有更多分隔符可用,直接按字符切分(作為兜底)
        for i in range(0, len(text), max_chunk_size_char):
            chunks.append(text[i:i + max_chunk_size_char])
            
    return [chunk for chunk in chunks if chunk] # 過濾空塊

print("\n--- 17. 遞歸切塊 ---")
# 模擬一個非常長的段落,需要遞歸切分
long_paragraph = "這是一個非常非常長的段落,它包含了多句話,并且可能在語義上可以被分割。我們希望這個段落能夠被智能地切分成更小的部分,以便于RAG系統(tǒng)處理。如果直接固定大小切塊,可能會切斷句子的上下文,導(dǎo)致信息丟失。所以,我們需要一個更靈活的策略來處理這種長文本。RAG的成功很大程度上取決于切塊的質(zhì)量。我們在這里模擬一個非常長的輸入,以測試遞歸切塊的能力。請注意,這個段落的長度遠(yuǎn)遠(yuǎn)超過了我們設(shè)定的最大塊大小,所以它將被進一步切分。切塊的藝術(shù)在于平衡信息的完整性和粒度。適當(dāng)?shù)那袎K能夠幫助大模型更好地理解檢索到的信息,從而生成更準(zhǔn)確、更相關(guān)的回答。這是一項技術(shù)挑戰(zhàn),也是RAG優(yōu)化的關(guān)鍵一步。通過不同的分隔符進行遞歸切分,我們可以確保每個塊都不會過大,同時盡量保持語義的完整性。當(dāng)遇到一個超長的段落時,首先嘗試用段落符切分,如果還超長,就用句號切分,再超長就用逗號,直到達(dá)到預(yù)設(shè)的最大長度。"

separators = ['\n\n', '. ', ',', ' '] # 嘗試從大到小的分隔符
chunks_recursive = recursive_chunking(long_paragraph, separators, max_chunk_size_char=100)
for i, chunk in enumerate(chunks_recursive):
    print(f"Chunk {i+1} (len={len(chunk)}):\n'{chunk}'")

18. 嵌入切塊(Embedding Chunking):

  • 場景使用:當(dāng)你的文檔完全非結(jié)構(gòu)化,缺乏任何標(biāo)點、標(biāo)題或清晰的段落分隔,或者簡單的啟發(fā)式切塊效果不佳時。這種方法特別適合處理口語化的轉(zhuǎn)錄文本、網(wǎng)絡(luò)爬取的混亂數(shù)據(jù)流等。它基于語義相似度來決定切塊邊界,從而在缺乏顯式結(jié)構(gòu)的情況下創(chuàng)建有意義的塊。
  • 優(yōu)點:

     a.應(yīng)對無結(jié)構(gòu)文本: 對沒有明確結(jié)構(gòu)的信息非常有效,能自動識別語義邊界。

     b.語義準(zhǔn)確性高: 直接利用句子嵌入的語義信息,確保切塊內(nèi)容的相關(guān)性。

     c.自動化程度高: 無需手動定義規(guī)則或關(guān)鍵詞,自動化程度高。

  • 缺點:

     a.計算成本高: 需要為所有句子生成嵌入,這比簡單的文本分割計算量更大。

     b.模型依賴: 效果嚴(yán)重依賴所使用的嵌入模型的質(zhì)量和適用性。

     c.閾值敏感: 相似度閾值的設(shè)置對最終切塊結(jié)果有很大影響,需要仔細(xì)調(diào)優(yōu)。

# pip install sentence-transformers
from sentence_transformers import SentenceTransformer

# 加載一個預(yù)訓(xùn)練的句子嵌入模型 (首次運行可能需要下載)
try:
    embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
except Exception:
    print("Failed to load sentence-transformers model. Please ensure you have internet or download it manually.")
    # 提供一個備用/跳過策略
    embedding_model = None

def embedding_chunking(text, model=embedding_model, similarity_threshold=0.7):
    """
    先嵌入所有句子,然后根據(jù)相似度聚合。
    """
    if model isNone:
        print("Embedding model not loaded, skipping embedding chunking demo.")
        return [text] # 返回原始文本或進行其他默認(rèn)切塊

    sentences = sent_tokenize(text)
    if len(sentences) <= 1:
        return sentences

    sentence_embeddings = model.encode(sentences)
    
    chunks = []
    current_chunk_sentences = [sentences[0]]

    for i in range(1, len(sentences)):
        # 計算當(dāng)前句子與前一個句子嵌入的余弦相似度
        similarity = cosine_similarity([sentence_embeddings[i]], [sentence_embeddings[i-1]])[0][0]
        
        if similarity < similarity_threshold:
            # 如果相似度低,則認(rèn)為語義不連續(xù),結(jié)束當(dāng)前塊
            chunks.append(" ".join(current_chunk_sentences))
            current_chunk_sentences = [sentences[i]]
        else:
            current_chunk_sentences.append(sentences[i])
            
    if current_chunk_sentences: # 添加最后一個塊
        chunks.append(" ".join(current_chunk_sentences))
        
    return chunks

print("\n--- 18. 嵌入切塊 ---")
if embedding_model:
    chunks_embedding = embedding_chunking(sample_text_long, similarity_threshold=0.5) # 調(diào)整閾值以觀察不同效果
    for i, chunk in enumerate(chunks_embedding[:3]):
        print(f"Chunk {i+1}:\n'{chunk}'")
    print("...")
else:
    print("跳過嵌入切塊演示,因為SentenceTransformer模型未加載。")

19. Agentic / 基于LLM切塊(Agentic / LLM-based Chunking):

  • 場景使用:適用于極其復(fù)雜、高度非結(jié)構(gòu)化且難以用規(guī)則或啟發(fā)式方法有效切塊的文本。例如,包含大量口語、多主題交織、推理鏈條復(fù)雜的會議討論、自由形式的用戶反饋、專業(yè)領(lǐng)域的專家報告等。當(dāng)人類判斷是最佳的切塊方式,但又需要自動化時,可以考慮讓LLM來“智能”地完成這個任務(wù)。
  • 優(yōu)點:

     a.高度智能: LLM能夠理解文本的深層含義、邏輯關(guān)系和上下文,從而做出更符合語義的切塊決策。

     b.靈活性和適應(yīng)性強: 可以應(yīng)對各種復(fù)雜和未知的文本結(jié)構(gòu)。

     c.減少人工干預(yù): 在一些傳統(tǒng)方法難以處理的場景下,可以自動化切塊過程。

  • 缺點:

     a.成本高昂: 調(diào)用大型LLM進行切塊會產(chǎn)生顯著的API費用和計算延遲。

     b.速度較慢: LLM推理速度通常比基于規(guī)則或嵌入的切塊慢得多。

     c.不可控性: LLM的切塊決策可能不夠穩(wěn)定或可解釋,有時會出現(xiàn)“意料之外”的分割。

     d.token限制: 需要將文本分成LLM可以處理的較小段落進行處理。

def mock_llm_chunking_decision(text_segment):
    """
    模擬LLM決定如何切塊。
    在實際中,需要給LLM提供文本段落和切塊規(guī)則,讓它返回分割點或直接返回切好的塊。
    例如,可以給LLM一個Prompt:
    "給定以下文本,請將其分割成語義連貫的、不超過200字的獨立片段,并以'---CHUNK---'作為分隔符返回:"
    """
    # 模擬LLM智能地將文本分割成幾個邏輯塊
    if"RAG"in text_segment and"幻覺"in text_segment:
        return ["RAG技術(shù)有效解決了大模型幻覺問題。", "其核心在于結(jié)合檢索和生成能力。"]
    elif"切塊"in text_segment and"影響"in text_segment:
        return ["切塊策略直接影響檢索的質(zhì)量。", "如果切塊過大,會增加LLM處理負(fù)擔(dān);如果過小,則可能丟失上下文。"]
    else:
        # 如果LLM無法智能切分,就回退到句子切塊
        return sent_tokenize(text_segment)

def agentic_llm_based_chunking(text, max_segment_for_llm=500):
    """
    使用LLM來決定切塊邊界。
    由于LLM調(diào)用成本,通常我們會先將大文本切分成適合LLM處理的段落,
    然后讓LLM對這些段落進行細(xì)粒度切塊。
    """
    # 先進行一個粗粒度的切塊(例如,按段落或固定大小),確保每個段落大小適合LLM處理
    coarse_chunks = fixed_size_chunking(text, max_segment_for_llm, overlap=0)
    
    final_chunks = []
    for chunk in coarse_chunks:
        # 模擬LLM對每個粗粒度塊進行智能切分
        llm_decided_sub_chunks = mock_llm_chunking_decision(chunk)
        final_chunks.extend(llm_decided_sub_chunks)
        
    return [c.strip() for c in final_chunks if c.strip()]

print("\n--- 19. Agentic / 基于LLM切塊 ---")
chunks_llm_based = agentic_llm_based_chunking(sample_text_long, max_segment_for_llm=300)
for i, chunk in enumerate(chunks_llm_based[:5]):
    print(f"Chunk {i+1}:\n'{chunk}'")
print("...")

20. 分層切塊(Hierarchical Chunking):

  • 場景使用:對于結(jié)構(gòu)清晰、具有多級標(biāo)題的復(fù)雜文檔,如書籍、學(xué)術(shù)論文、法律法規(guī)、復(fù)雜的公司規(guī)章制度、帶有嚴(yán)格目錄的技術(shù)文檔。當(dāng)你需要支持用戶在不同粒度(從章節(jié)概覽到具體段落)進行檢索,并且LLM在生成回答時需要理解信息的層級關(guān)系時,這種方法是理想選擇。
  • 優(yōu)點:

     a.全面性與粒度兼顧: 提供了多粒度的檢索能力,用戶可以先獲取高層級概覽,再深入細(xì)節(jié)。

     b.保留結(jié)構(gòu)上下文: 每個塊都帶有其所屬的層級信息(如章節(jié)標(biāo)題),LLM在處理時能更好地理解其在文檔中的位置和作用。

     c.提升檢索效率: 可以根據(jù)查詢的廣度在不同層級進行檢索,提高效率。

  • 缺點:

      a.實現(xiàn)最復(fù)雜: 需要復(fù)雜的解析器來識別和構(gòu)建文檔的層級結(jié)構(gòu),并處理各種邊緣情況。

      b.存儲冗余: 某些內(nèi)容可能在不同層級的塊中重復(fù)出現(xiàn)(如一個段落既是其小節(jié)塊的一部分,也是其章節(jié)塊的一部分),增加存儲負(fù)擔(dān)。

      c.依賴文檔結(jié)構(gòu): 對于非結(jié)構(gòu)化文檔或結(jié)構(gòu)混亂的文檔,無法應(yīng)用。

class HierarchicalChunk:
    def __init__(self, content, level, title=None, children=None):
        self.content = content
        self.level = level
        self.title = title
        self.children = children if children isnotNoneelse []

    def __repr__(self):
        returnf"Level {self.level} '{self.title or self.content[:30]}...'"

def parse_markdown_hierarchy(text):
    """
    解析Markdown文本,構(gòu)建分層結(jié)構(gòu)。
    返回一個包含頂級HierarchicalChunk對象的列表。
    """
    lines = text.split('\n')
    
    # 存儲當(dāng)前的層級路徑,方便構(gòu)建嵌套結(jié)構(gòu)
    # Stack stores (level, parent_chunk)
    root_chunks = []
    current_path = [(0, None)] # (level, parent_chunk)

    for line in lines:
        stripped_line = line.strip()
        ifnot stripped_line:
            continue

        match = re.match(r'^(#+)\s*(.*)$', stripped_line)
        if match:
            level = len(match.group(1)) # 標(biāo)題級別
            title = match.group(2).strip()
            
            new_chunk = HierarchicalChunk(cnotallow="", level=level, title=title)
            
            # 回溯到正確的父級
            while current_path and current_path[-1][0] >= level:
                current_path.pop()

            if current_path and current_path[-1][1]: # 有父級
                current_path[-1][1].children.append(new_chunk)
            else: # 頂級標(biāo)題
                root_chunks.append(new_chunk)
            
            current_path.append((level, new_chunk))
        else: # 普通內(nèi)容,添加到當(dāng)前最低層級塊的內(nèi)容
            if current_path and current_path[-1][1]:
                # 如果是第一個內(nèi)容行,直接賦值,否則追加
                if current_path[-1][1].content:
                    current_path[-1][1].content += "\n" + line
                else:
                    current_path[-1][1].content = line
            else: # 沒有標(biāo)題的開頭內(nèi)容,作為頂級塊
                ifnot root_chunks or root_chunks[-1].level != 0or root_chunks[-1].title: # 如果沒有頂級塊或者上一個是標(biāo)題,就創(chuàng)建一個新的
                     root_chunks.append(HierarchicalChunk(cnotallow=line, level=0))
                else: # 追加到第一個無標(biāo)題頂級塊
                     root_chunks[-1].content += "\n" + line

    # 遞歸清理并整合內(nèi)容
    def consolidate_chunks(chunk_list):
        final_chunks = []
        for chunk in chunk_list:
            # 將標(biāo)題本身和內(nèi)容整合到content中
            full_content = ""
            if chunk.title:
                full_content += "#" * chunk.level + " " + chunk.title + "\n"
            full_content += chunk.content.strip()
            
            if full_content: # 確保內(nèi)容不為空
                 final_chunks.append(HierarchicalChunk(full_content, chunk.level, chunk.title))
            
            if chunk.children:
                final_chunks.extend(consolidate_chunks(chunk.children))
        return final_chunks

    return consolidate_chunks(root_chunks)


print("\n--- 20. 分層切塊 ---")
# 使用一個更適合分層切塊的結(jié)構(gòu)化文本
hierarchical_text = """
# 第一章 RAG概述
RAG是一種強大的AI技術(shù)。

## 1.1 RAG的原理
結(jié)合檢索和生成。
### 1.1.1 檢索部分
從知識庫中獲取信息。

## 1.2 RAG的優(yōu)勢
減少幻覺,提升準(zhǔn)確性。

# 第二章 切塊策略
切塊是RAG的關(guān)鍵一步。
"""
hierarchical_chunks = parse_markdown_hierarchy(hierarchical_text)
for i, chunk_obj in enumerate(hierarchical_chunks):
    print(f"Chunk {i+1} (Level {chunk_obj.level}, Title: '{chunk_obj.title}'):\n'{chunk_obj.content}'")

RAG調(diào)優(yōu)進階:21種切塊策略,不光有代碼,更有超詳細(xì)場景、優(yōu)缺點分析!-AI.x社區(qū)

21. 模態(tài)感知切塊(Modality-Aware Chunking):

  • 場景使用:適用于包含不同類型數(shù)據(jù)(文本、圖像、表格、圖表、代碼等)的多模態(tài)文檔,如多媒體報告、帶有圖表的PDF文檔、網(wǎng)頁內(nèi)容。當(dāng)每種模態(tài)的信息都需要以其最適合的方式處理(例如,文本切塊,圖像生成描述,表格轉(zhuǎn)換為結(jié)構(gòu)化數(shù)據(jù))時,這種方法至關(guān)重要。
  • 優(yōu)點:

     a.優(yōu)化信息處理: 針對不同模態(tài)采用最佳處理方式,確保每種信息的完整性和可讀性。

     b,提升多模態(tài)檢索: 能夠支持跨模態(tài)的查詢,例如查詢“關(guān)于產(chǎn)品銷量的圖表”。

     c.豐富LLM上下文: 為LLM提供更全面的信息視圖,包括文本描述和結(jié)構(gòu)化數(shù)據(jù)。

  • 缺點:

      a.實現(xiàn)最復(fù)雜: 需要圖像識別、表格檢測、文本內(nèi)容分析等多種技術(shù)結(jié)合,甚至需要多模態(tài)LLM支持。

      b.工具依賴: 需要集成多個不同的解析庫和AI模型。

      c.成本高昂: 多模態(tài)處理通常涉及更復(fù)雜的模型和更高的計算資源。

def modality_aware_chunking(text):
    """
    分離不同模態(tài)的內(nèi)容(文本、表格)。
    這里只處理文本和Markdown表格。
    """
    chunks = []
    lines = text.split('\n')
    current_chunk_lines = []
    
    in_table_block = False

    for line in lines:
        stripped_line = line.strip()

        # 檢查是否是Markdown表格行
        is_table_line = stripped_line.startswith('|') and'|'in stripped_line[1:]

        if is_table_line:
            ifnot in_table_block:
                # 結(jié)束之前的文本塊
                if current_chunk_lines:
                    chunks.append({"type": "text", "content": "\n".join(current_chunk_lines).strip()})
                    current_chunk_lines = []
                in_table_block = True
            current_chunk_lines.append(line)
        else:
            if in_table_block:
                # 結(jié)束表格塊
                if current_chunk_lines:
                    chunks.append({"type": "table", "content": "\n".join(current_chunk_lines).strip()})
                    current_chunk_lines = []
                in_table_block = False
            current_chunk_lines.append(line)

    # 處理最后一個塊
    if current_chunk_lines:
        chunk_type = "table"if in_table_block else"text"
        chunks.append({"type": chunk_type, "content": "\n".join(current_chunk_lines).strip()})
        
    return [chunk for chunk in chunks if chunk['content']]

print("\n--- 21. 模態(tài)感知切塊 ---")
chunks_modality = modality_aware_chunking(sample_text_mixed_format)
for i, chunk in enumerate(chunks_modality):
    print(f"Chunk {i+1} (Type: {chunk['type']}):\n'{chunk['content']}'")


BONUS:混合切塊(Hybrid Chunking):集大成者,無往不利!

  • 場景使用:當(dāng)你的數(shù)據(jù)非常復(fù)雜,單一的切塊策略無法完美解決問題時。這是一種實踐中非常常見的方案,你可以根據(jù)具體的數(shù)據(jù)特點和業(yè)務(wù)需求,靈活地組合上述一種或多種策略。比如,先用段落切塊,再對過長的段落進行遞歸切塊;或者先識別表格并單獨處理,然后對剩余文本進行語義切塊。
  • 優(yōu)點:

     a.高度定制化: 可以根據(jù)特定文檔類型和應(yīng)用場景,設(shè)計出最匹配的切塊流程。

     b.兼顧多種需求: 結(jié)合不同策略的優(yōu)勢,在語義完整性、塊大小、處理效率等方面找到最佳平衡。

     c.解決復(fù)雜問題: 能有效應(yīng)對單一策略無法處理的復(fù)雜文檔結(jié)構(gòu)和內(nèi)容。

  • 缺點:

     a.實現(xiàn)和調(diào)試復(fù)雜: 組合多種策略會顯著增加代碼的復(fù)雜性和調(diào)試難度。

     b.參數(shù)調(diào)優(yōu): 多個策略的參數(shù)需要協(xié)同調(diào)優(yōu),工作量大。

     c.無通用模板: 混合切塊是高度定制的,沒有一個放之四海而皆準(zhǔn)的方案。

def hybrid_chunking(text, primary_strategy, secondary_strategy, primary_args={}, secondary_args={}):
    """
    混合切塊策略示例:先用一種策略粗切,再用另一種策略細(xì)切。
    primary_strategy: 第一階段切塊函數(shù) (如 paragraph_based_chunking)
    secondary_strategy: 第二階段切塊函數(shù) (如 recursive_chunking)
    """
    # 步驟1:用主要策略進行粗粒度切塊
    coarse_chunks = primary_strategy(text, **primary_args)
    
    final_chunks = []
    # 步驟2:對每個粗粒度塊,再用次要策略進行細(xì)粒度切塊
    for chunk in coarse_chunks:
        # 如果粗粒度塊仍然太大或需要進一步細(xì)分
        if len(chunk) > 500: # 假設(shè)一個啟發(fā)式條件,可以根據(jù)token數(shù)或語義復(fù)雜度來定
            # 注意:這里需要確保secondary_strategy能夠處理傳入的參數(shù)
            fine_grained_chunks = secondary_strategy(chunk, **secondary_args)
            final_chunks.extend(fine_grained_chunks)
        else:
            final_chunks.append(chunk)
            
    return [chunk for chunk in final_chunks if chunk.strip()]

print("\n--- BONUS: 混合切塊 ---")
# 示例:先按段落切塊,然后對超過一定長度的段落進行遞歸切塊
chunks_hybrid = hybrid_chunking(
    sample_text_long + sample_text_structured,
    primary_strategy=paragraph_based_chunking,
    primary_args={}, # 段落切塊不需要額外參數(shù)
    secondary_strategy=recursive_chunking,
    secondary_args={'separators': ['. ', ','], 'max_chunk_size_char': 200}
)

for i, chunk in enumerate(chunks_hybrid[:5]):
    print(f"Chunk {i+1} (len={len(chunk)}):\n'{chunk}'")
print("...")

深入理解每種切塊策略的應(yīng)用場景、優(yōu)缺點,再結(jié)合代碼實現(xiàn),你就能在RAG的實踐中更加游刃有余。記住,切塊是RAG成功的基石之一,選擇合適的策略,往往能讓你的RAG系統(tǒng)事半功倍!

本文轉(zhuǎn)載自??Halo咯咯??    作者:基咯咯

?著作權(quán)歸作者所有,如需轉(zhuǎn)載,請注明出處,否則將追究法律責(zé)任
標(biāo)簽
已于2025-8-25 08:50:38修改
收藏
回復(fù)
舉報
回復(fù)
相關(guān)推薦