五種RAG分塊策略詳解 + LlamaIndex代碼演示
先前文章中提到,不斷優(yōu)化原始文檔解析和分塊策略是控制變量法下,是提高最后檢索效果天花板的務(wù)實(shí)做法,前面已經(jīng)介紹了 MinerU vs DeepDoc 在文檔解析方面的效果對(duì)比。
MinerU vs DeepDoc:集成方案+圖片顯示優(yōu)化
關(guān)于文檔解析部分簡單的結(jié)論是,MinerU 無疑是值得關(guān)注和嘗試的一個(gè)文檔解析框架,但具體效果還要結(jié)合特定項(xiàng)目文檔做仔細(xì)橫評(píng)。
我目前在常規(guī)項(xiàng)目中,主要是對(duì)照使用 DeepDoc 和 MinerU 兩個(gè)方法。后續(xù)計(jì)劃花時(shí)間再針對(duì) PaddleOCR、Mistra OCR 等工具做進(jìn)一步的測評(píng),感興趣的可以蹲一下。
這篇試圖說清楚:
業(yè)界常用的五種 RAG 分塊策略核心思想、LlamaIndex 代碼演示以及 RAGFlow/Dify 等框架實(shí)踐思路。
本篇中 RAG 分塊相關(guān)圖示均來自https://blog.dailydoseofds.com/p/5-chunking-strategies-for-rag 下述相關(guān)圖片出處不再做單獨(dú)說明
以下,enjoy:
1、RAG 與分塊的重要性
在正式開始前,老規(guī)矩溫故知新,先來復(fù)習(xí)下傳統(tǒng) RAG 出現(xiàn)的背景、典型 RAG 流程以及分塊的重要意義所在。
1.1典型 RAG 流程
為了讓大模型能夠回答私有知識(shí)的問題,拋開高成本的 LLM 微調(diào)方法外,我們可以選擇在提問時(shí),直接傳入私有知識(shí)相關(guān)的參考信息,這種方法更加簡便且高效。
然而,這種方法的缺點(diǎn)很明顯。畢竟提示詞長度有限,當(dāng)私有數(shù)據(jù)量過大時(shí),傳入所有背景信息可能導(dǎo)致提示詞過長,從而影響模型的處理效率或達(dá)到長度上限。
而 RAG 巧妙地將 LLM 的生成能力與外部知識(shí)庫的信息檢索能力結(jié)合起來:
接收問題 (Query): 用戶向系統(tǒng)提出查詢。
信息檢索 (Retrieval): 系統(tǒng)在向量數(shù)據(jù)庫或搜索引擎中查找與問題相關(guān)的文檔片段。
上下文增強(qiáng) (Augmentation): 將檢索到的信息片段整合進(jìn)發(fā)送給 LLM 的提示 (Prompt) 中。
答案生成 (Generation): LLM 基于原始問題和增強(qiáng)的上下文生成最終回答。
在這個(gè)流程中,要實(shí)現(xiàn)高質(zhì)量的檢索,需要對(duì)原始知識(shí)文檔進(jìn)行有效的預(yù)處理,這也就引出了 RAG 流程中一個(gè)至關(guān)重要的準(zhǔn)備工作——文檔分塊 (Chunking)。
1.2為啥分塊這么重要
分塊,簡單來說就是將原始文檔按照某種策略分割成更小的、語義相對(duì)完整、適合進(jìn)行索引和檢索的文本單元(Chunks)的過程。 這一步通常在文檔被送入向量數(shù)據(jù)庫進(jìn)行 Embedding(向量化)之前完成。分塊策略選擇和執(zhí)行質(zhì)量,是構(gòu)建一個(gè) RAG 應(yīng)用的準(zhǔn)確性基礎(chǔ)。結(jié)合以下三種情形,會(huì)更好理解些:
分塊過大: 可能導(dǎo)致檢索到的單個(gè)塊包含過多無關(guān)信息(噪音),增加了 LLM 理解上下文的難度,降低了答案的精確性,甚至可能超出 LLM 的上下文窗口限制。
分塊過小或切分不當(dāng): 可能破壞原文的語義連貫性,導(dǎo)致一個(gè)完整的知識(shí)點(diǎn)被拆散到多個(gè)塊中。檢索時(shí)可能只召回了部分信息,使得 LLM 無法獲得完整的背景,難以生成全面、準(zhǔn)確的答案。
未能適應(yīng)文檔結(jié)構(gòu): 不同的文檔類型(如論文、手冊(cè)、報(bào)告、網(wǎng)頁)具有不同的結(jié)構(gòu)特點(diǎn)。死板的分塊方式可能無法有效利用標(biāo)題、列表、表格等結(jié)構(gòu)信息,影響信息提取的完整性。
2、五種分塊策略詳解
2.1固定大小分塊
核心思想
這是最直觀、最簡單的分塊方式。按照預(yù)先設(shè)定的固定長度( 最大 token 數(shù))將文本進(jìn)行切割。為了盡量減少信息損失,通常會(huì)在相鄰的塊之間保留一部分重疊內(nèi)容(Overlap)。
優(yōu)點(diǎn)
實(shí)現(xiàn)簡單,處理速度快。
塊大小統(tǒng)一,便于批量處理和管理,能精確控制輸入 LLM 的 token 數(shù)量。
缺點(diǎn)
容易在句子或語義完整的表達(dá)中間被“攔腰斬?cái)唷?,破壞文本的語義連貫性。
可能將關(guān)聯(lián)緊密的關(guān)鍵信息分散到不同的塊中,影響后續(xù)檢索和理解的完整性。
適用場景
處理結(jié)構(gòu)簡單、對(duì)語義連貫性要求不高的文本。
需要快速實(shí)現(xiàn)或作為基線對(duì)比策略時(shí)。
對(duì)塊大小有嚴(yán)格限制的應(yīng)用。
2.2語義分塊
核心思想
嘗試根據(jù)文本的語義含義進(jìn)行切分,將語義關(guān)聯(lián)緊密的句子或段落聚合在一起。通常做法是先將文本分成基礎(chǔ)單元(如句子),然后計(jì)算相鄰單元的語義相似度(例如通過嵌入向量的余弦相似度),如果相似度高于某個(gè)閾值,則合并這些單元,直到相似度顯著下降時(shí)才創(chuàng)建一個(gè)新的塊。
優(yōu)點(diǎn)
能更好地保持文本的語義連貫性和上下文的完整性。
生成的塊通常包含更集中的信息,有助于提升檢索精度。
為 LLM 提供更高質(zhì)量的上下文,有助于生成更連貫、相關(guān)的回答。
缺點(diǎn)
實(shí)現(xiàn)相對(duì)復(fù)雜,依賴于嵌入模型的質(zhì)量和相似度閾值的設(shè)定。
閾值可能需要根據(jù)不同的文檔類型進(jìn)行調(diào)整和優(yōu)化。
計(jì)算成本通常高于固定大小分塊。
適用場景
對(duì)上下文理解和語義連貫性要求較高的場景,如問答系統(tǒng)、聊天機(jī)器人、文檔摘要等。
處理敘事性或論述性較強(qiáng)的長文本。
2.3遞歸分塊
核心思想
采用“分而治之”的策略。首先嘗試使用一組優(yōu)先級(jí)較高的、較大的文本分隔符(如段落、章節(jié)標(biāo)記)來分割文本。然后,檢查分割出的每個(gè)塊的大小。如果某個(gè)塊仍然超過預(yù)設(shè)的大小限制,就換用下一組優(yōu)先級(jí)更低、更細(xì)粒度的分隔符(如句子結(jié)束符、換行符)對(duì)其進(jìn)行再次分割,此過程遞歸進(jìn)行,直到所有塊都滿足大小要求。
優(yōu)點(diǎn)
在保持一定語義結(jié)構(gòu)的同時(shí),能靈活地控制塊的大小。
比固定大小分塊更能尊重原文的自然結(jié)構(gòu)。
適應(yīng)性較好,是一種常用的通用分塊策略。
缺點(diǎn)
實(shí)現(xiàn)比固定大小分塊更復(fù)雜一些。
需要預(yù)先定義好一組有效的分隔符及其優(yōu)先級(jí)順序。
注:兩個(gè)段落(紫色)被識(shí)別為初始?jí)K,接著第一個(gè)段落再被拆成更小的塊。這種方式既保留了語義完整性,又能靈活控制分塊大小。
適用場景
適用于大多數(shù)類型的文檔,特別是那些具有一定層次結(jié)構(gòu)(如章節(jié)、段落、列表)但又不完全規(guī)整的文檔。
是許多 RAG 框架(如 LangChain)中推薦的默認(rèn)策略之一。
2.4基于文檔結(jié)構(gòu)的分塊
核心思想
直接利用文檔本身固有的、明確的結(jié)構(gòu)元素(如標(biāo)題層級(jí)、章節(jié)、列表項(xiàng)、表格、代碼塊、Markdown 標(biāo)記等)來定義塊的邊界。目標(biāo)是使每個(gè)塊盡可能對(duì)應(yīng)文檔中的一個(gè)邏輯組成部分。
優(yōu)點(diǎn)
能最大程度地保留文檔的原始邏輯結(jié)構(gòu)和上下文信息。
塊的劃分方式自然,符合人類的閱讀和理解習(xí)慣。
缺點(diǎn)
強(qiáng)依賴于文檔本身具有清晰、一致的結(jié)構(gòu),這在現(xiàn)實(shí)世界的文檔中并非總是得到滿足。
生成的塊大小可能差異巨大,某些塊可能非常長,超出 LLM 的處理限制。
通常需要結(jié)合遞歸分塊等方法來處理過大的塊。
適用場景
特別適合處理結(jié)構(gòu)化或半結(jié)構(gòu)化特征明顯的文檔,如技術(shù)手冊(cè)、法律合同、API 文檔、教程、帶有章節(jié)的書籍、格式良好的 Markdown 文件等。
注:某些結(jié)構(gòu)下的分塊長度差異較大,可能會(huì)超出模型支持的 token 限制??梢钥紤]結(jié)合遞歸切分法來處理。
2.5基于 LLM 的分塊
核心思想
不再依賴固定的規(guī)則或啟發(fā)式方法,而是利用大型語言模型 (LLM) 自身的理解能力來判斷文本的最佳分割點(diǎn)??梢酝ㄟ^設(shè)計(jì)合適的提示 (Prompt),讓 LLM 將文本分割成語義上內(nèi)聚且與其他部分相對(duì)獨(dú)立的塊。
優(yōu)點(diǎn)
理論上具有最高的潛力,能實(shí)現(xiàn)最符合語義邏輯的分割效果,因?yàn)?LLM 能更深入地理解文本內(nèi)容、上下文和細(xì)微差別。
缺點(diǎn)
計(jì)算成本最高昂,處理速度最慢,因?yàn)樯婕暗蕉啻握{(diào)用 LLM。
需要精心設(shè)計(jì)有效的 Prompt 來指導(dǎo) LLM 完成分塊任務(wù)。
同樣受到 LLM 本身上下文窗口大小的限制。
適用場景場景:
對(duì)分塊質(zhì)量有極致要求,并且能夠承擔(dān)高昂計(jì)算成本和較慢處理速度的場景。
用于處理語義關(guān)系特別復(fù)雜、傳統(tǒng)方法難以處理的文本。
也可能作為更復(fù)雜策略(如生成摘要樹、構(gòu)建知識(shí)圖譜)的一部分。
3、LlamaIndex 分塊策略演示
為了更好的理解五種不同的分塊策略原理和實(shí)現(xiàn)細(xì)節(jié),下面以 LlamaIndex 為例,展示五種策略對(duì)應(yīng)的示例 python 代碼。
在具體介紹前,先補(bǔ)充說明兩個(gè)問題:LlamaIndex 是什么?以及為什么不選擇 RAGFlow/Dify 等框架來進(jìn)行分塊策略的演示?
3.1LlamaIndex 掃盲介紹
LlamaIndex 也是一個(gè)非常流行的開源數(shù)據(jù)框架,與 Ragflow 不同,LlamaIndex 更像是一個(gè)靈活的工具箱或庫,提供了豐富、模塊化的組件來處理 RAG 流程中的各個(gè)階段,特別是數(shù)據(jù)加載、轉(zhuǎn)換(包括分塊/節(jié)點(diǎn)解析)、索引和查詢。
主要組件特點(diǎn)
數(shù)據(jù)連接器 (Data Connectors): 支持從各種來源(文件、API、數(shù)據(jù)庫等)加載數(shù)據(jù)。
數(shù)據(jù)索引 (Data Indexes): 提供多種索引結(jié)構(gòu)(如向量存儲(chǔ)索引、列表索引、關(guān)鍵詞表索引、樹狀索引、知識(shí)圖譜索引等)來組織數(shù)據(jù),以適應(yīng)不同的查詢需求。
節(jié)點(diǎn)解析/文本分割 (Node Parsing / Text Splitting): 這是 LlamaIndex 處理分塊的核心部分。它提供了多種可配置的文本分割器 (Text Splitters),讓開發(fā)者可以精細(xì)地控制文檔如何被分割成“節(jié)點(diǎn) (Nodes)”(LlamaIndex 中對(duì)“塊/Chunk”的稱呼)。
檢索器 (Retrievers): 基于索引,提供不同的方式來檢索與查詢相關(guān)的節(jié)點(diǎn)。
查詢引擎 (Query Engines): 結(jié)合檢索器和 LLM,構(gòu)建端到端的查詢和響應(yīng)能力。
代理 (Agents): 構(gòu)建更復(fù)雜的、可以自主規(guī)劃和使用工具的 LLM 應(yīng)用。
模塊化和可擴(kuò)展性: 開發(fā)者可以方便地組合、替換或自定義各個(gè)組件。
演示底層機(jī)制的優(yōu)勢
LlamaIndex 的設(shè)計(jì)哲學(xué)就是提供明確的、可編程的接口。對(duì)于分塊(節(jié)點(diǎn)解析),我們可以直接在代碼中:
導(dǎo)入特定的 TextSplitter 類: LlamaIndex 提供了與多種分塊策略對(duì)應(yīng)的類。
實(shí)例化分割器并配置參數(shù): 顯式地設(shè)置塊大小、重疊大小、分隔符、模型(用于語義分割)、結(jié)構(gòu)解析規(guī)則等。
調(diào)用分割器的 split_text 或類似方法: 將原始文本輸入,直接獲得分割后的節(jié)點(diǎn)列表。
下文會(huì)通過具體的 Python 代碼清晰和直接地展示五種分塊策略的實(shí)現(xiàn)。
3.2高集成度的局限
像 Ragflow、Dify 這樣封裝度較高的框架,提供的開箱即用的端到端解決方案,必然會(huì)通過 UI 或 API 參數(shù)將底層的實(shí)現(xiàn)細(xì)節(jié)抽象化。
這雖然方便用戶快速搭建應(yīng)用,但對(duì)于希望理解“引擎蓋”下面發(fā)生了什么的用戶來說會(huì)不夠透明,難以窺見不同策略的具體代碼實(shí)現(xiàn)和細(xì)微差別。具體來說:
抽象層級(jí)的差異: Ragflow/Dify 等平臺(tái)為了易用性,會(huì)將底層的分塊邏輯封裝在更高級(jí)的選項(xiàng)(如 Ragflow 的 chunk_method 下拉菜單和相關(guān)配置)。用戶無法直接編寫或修改 LlamaIndex 那樣的底層分塊代碼。
平臺(tái)特定實(shí)現(xiàn): 即便 Ragflow 提供了一個(gè)名為 "Paper" 的分塊方法,其內(nèi)部的具體實(shí)現(xiàn)邏輯、使用的分隔符、遞歸策略等細(xì)節(jié),與 LlamaIndex 的 MarkdownNodeParser 組合有所不同。
配置而非編碼: 在這些平臺(tái)上,用戶更多的是通過圖形界面 (UI) 或平臺(tái)的 Python API 來配置分塊選項(xiàng),而不是直接編寫分塊算法的代碼。
3.3LlamaIndex 五種分塊策略代碼參考
固定大小分塊:
可以使用SentenceSplitter(設(shè)置 chunk_size控制token數(shù),chunk_overlap 控制重疊) 或 TokenTextSplitter。代碼會(huì)清晰展示如何設(shè)置大小和重疊。
from llama_index.core.node_parser import SentenceSplitter
splitter = SentenceSplitter(chunk_size=128, chunk_overlap=20)
nodes = splitter.get_nodes_from_documents(documents) # 'documents' 是加載后的文檔對(duì)象
# 可以打印 nodes[0].get_content() 查看第一個(gè)塊的內(nèi)容
語義分塊:
LlamaIndex 提供了 SemanticSplitterNodeParser。需要配置一個(gè)嵌入模型,并設(shè)定相似度閾值 (breakpoint_percentile_threshold)。
from llama_index.core.node_parser import SemanticSplitterNodeParser
from llama_index.embeddings.openai import OpenAIEmbedding # 或其他嵌入模型
embed_model = OpenAIEmbedding()
splitter = SemanticSplitterNodeParser(
buffer_size=1, breakpoint_percentile_threshold=95, embed_model=embed_model
)
nodes = splitter.get_nodes_from_documents(documents)
遞歸分塊
SentenceSplitter 本身就具有一定遞歸特性,它會(huì)按順序嘗試使用不同的分隔符(默認(rèn)從 \n\n 到 . 到 等)??梢宰远x paragraph_separator 等參數(shù)。更復(fù)雜的遞歸(如構(gòu)建摘要樹)可能涉及 HierarchicalNodeParser。
from llama_index.core.node_parser import SentenceSplitter
# 默認(rèn)行為就是遞歸的,可以定制分隔符
splitter = SentenceSplitter(
separator=" ", # 可以簡化分隔符用于演示
chunk_size=256,
chunk_overlap=30,
paragraph_separator="\n\n\n", # 示例:自定義段落分隔符
)
nodes = splitter.get_nodes_from_documents(documents)
基于文檔結(jié)構(gòu)的分塊
LlamaIndex 有專門的 MarkdownNodeParser 或 JSONNodeParser。對(duì)于 PDF 中提取的 Markdown,MarkdownNodeParser 能很好地利用標(biāo)題、列表等結(jié)構(gòu)。
from llama_index.core.node_parser import MarkdownNodeParser
parser = MarkdownNodeParser()
nodes = parser.get_nodes_from_documents(documents) # 假設(shè) documents 是加載的 Markdown 內(nèi)容
# 對(duì)于從 PDF 解析得到的 Markdown 尤其有效
基于 LLM 的分塊
雖然沒有一個(gè)現(xiàn)成的 LLMTextSplitter,但 LlamaIndex 的靈活性允許我們構(gòu)建自定義的 NodeParser,在其中調(diào)用 LLM 來決定分割點(diǎn)或生成摘要塊。例如結(jié)合 LLM 進(jìn)行章節(jié)總結(jié)或主題劃分,或者利用 LLM 對(duì)元數(shù)據(jù)進(jìn)行分析來指導(dǎo)分割。這部分后續(xù)我結(jié)合具體案例再做演示。
4、寫在最后
4.1如何在 Ragflow/Dify 中應(yīng)用不同分塊策略
上述 LlamaIndex 的演示只是提供了評(píng)估的思路和方向,不能替代在目標(biāo)平臺(tái)上的實(shí)際驗(yàn)證。各位可以利用上述LlamaIndex 演示中的原理,去解讀 Ragflow/Dify等框架提供的分塊選項(xiàng)。
以RAGFlow為例,相關(guān)分塊選項(xiàng)和上述的五種分塊策略很難一一映射,結(jié)合官方的python api解讀參考如下:
固定大小分塊: Ragflow 的 "naive" 方法最接近這個(gè)概念,尤其是當(dāng)配置了 chunk_token_num 時(shí)。它可能也結(jié)合了 delimiter 進(jìn)行分割。
語義分塊: API 文檔中沒有直接命名為 "semantic" 的選項(xiàng)。然而,某些高級(jí)方法(像 "qa"、"knowledge_graph" 某種程度上)可能隱含了語義理解。但 API 沒有提供一個(gè)像 LangChain 那樣明確的 Semantic Splitter 選項(xiàng)。
遞歸分塊: 像 "book" 或 "paper" 這樣的方法內(nèi)部猜測是采用了遞歸或基于結(jié)構(gòu)的分塊邏輯,會(huì)先嘗試按大綱(章節(jié)、標(biāo)題)分割,如果塊太大再按段落或句子遞歸分割。但API 文檔沒有明確說明其內(nèi)部遞歸邏輯。
基于文檔結(jié)構(gòu)的分塊: "paper", "book", "laws", "presentation", "table" 這些方法明顯是針對(duì)特定文檔結(jié)構(gòu)的,它們會(huì)優(yōu)先利用文檔的固有結(jié)構(gòu)(標(biāo)題、章節(jié)、表格結(jié)構(gòu)、幻燈片等)來定義塊邊界。
基于 LLM 的分塊:API 中沒有通用的、直接讓 LLM 決定分塊邊界的選項(xiàng)。
這里需要特別說明的是,在RAGFlow中創(chuàng)建知識(shí)庫環(huán)節(jié),自動(dòng)關(guān)鍵詞/問題提取不是LLM-based Chunking,而是分塊后的 LLM 增強(qiáng)。但RAPTOR(遞歸摘要樹,多粒度檢索) 策略涉及對(duì)初始?jí)K進(jìn)行聚類,并使用 LLM 對(duì)聚類進(jìn)行摘要,生成新的、更高層次的“摘要塊”。 這個(gè)策略可以被認(rèn)為是“基于 LLM 的分塊”的一種高級(jí)形式或應(yīng)用。雖然它可能建立在初始分塊之上,但它確實(shí)利用 LLM 生成了新的、語義層級(jí)更高的塊(摘要),這些新塊是文檔內(nèi)容的一種 LLM 驅(qū)動(dòng)的重新組織和分割。它不僅僅是分析現(xiàn)有塊,而是創(chuàng)造了新的塊邊界和內(nèi)容。
4.2成熟框架的分塊策略定制問題
如果你想實(shí)現(xiàn)的功能(例如一種非常特定的、Ragflow 沒有內(nèi)置或通過 API 參數(shù)暴露的分塊方法,或者你想徹底改變其檢索邏輯)超出了 Ragflow 提供的 API 和配置選項(xiàng)的范疇,理論上唯一的途徑就是修改 Ragflow 的源代碼。
但像 Ragflow 這樣的框架通常有復(fù)雜的內(nèi)部結(jié)構(gòu)和依賴關(guān)系,理解并安全地修改其核心代碼需要投入大量時(shí)間和精力。而且鑒于 RAGFlow 在常態(tài)化更新中,創(chuàng)建一個(gè) Ragflow 的“定制分支”意味著官方的更新、補(bǔ)丁或新功能無法直接合并,需要自己手動(dòng)同步或重新應(yīng)用你的修改,維護(hù)成本很高。
類似Dify 支持編寫插件來添加自定義功能,而無需修改核心代碼。RAGFlow 等類似框架后續(xù)預(yù)期都會(huì)陸續(xù)支持更加靈活的擴(kuò)展機(jī)制,建議短期內(nèi)不要在二開上耗費(fèi)精力。
4.3使用 LlamaIndex 替代 Ragflow/Dify?
這是我近期被問到比較多的一個(gè)問題,我在實(shí)施或咨詢的一些項(xiàng)目中,部分企業(yè)選擇直接基于 LlamaIndex(或 LangChain 等類似庫)來構(gòu)建自己的企業(yè)級(jí) RAG 應(yīng)用,而不是使用封裝好的平臺(tái)。
除了可以更好的與現(xiàn)有技術(shù)棧集成外,企業(yè)可以完全掌控 RAG 流程的每一個(gè)環(huán)節(jié),選擇最適合需求的模型(嵌入、LLM、重排)、向量數(shù)據(jù)庫、索引策略、檢索邏輯等,并進(jìn)行深度定制和優(yōu)化。
但問題也隨著而來,這需要開發(fā)或集成幾乎所有“外圍”組件,包括 UI、API、數(shù)據(jù)庫、部署運(yùn)維在內(nèi)的所有周邊系統(tǒng)。 畢竟LlamaIndex 雖然提供了 RAG 的核心“引擎”和“管道”,但它本身不是一個(gè)可以直接部署給最終用戶的完整應(yīng)用。
一般的建議是,針對(duì)企業(yè)級(jí)知識(shí)庫項(xiàng)目落地,如果需求與某個(gè)成熟框架的功能高度匹配,且對(duì)定制化要求不高,或者時(shí)間緊迫需要快速驗(yàn)證,那么選擇 Ragflow/Dify 顯然更高效。但如果企業(yè)對(duì) RAG 的性能、邏輯有深度定制的需求,希望完全掌控技術(shù)棧,擁有較強(qiáng)的內(nèi)部研發(fā)能力,或者需要將 RAG 深度嵌入現(xiàn)有復(fù)雜系統(tǒng),那么基于 LlamaIndex 自建通常是更長遠(yuǎn)、更靈活的選擇,盡管前期投入更大。