導入
文書1つをまるごと索引に入れると、検索は「その文書まるごと」しか取り出せません。質問に関係する一部分がほしいだけなのに、関係ない段落まで一緒についてきてしまいます。小さく分けておけば、質問に関係する箇所だけをピンポイントで取り出せます。
説明
さらにもう1つ理由があります。RAGでは検索で見つけた文章をLLM(大規模言語モデル)に渡して回答を作らせますが、LLMに一度に渡せる文章の量には上限があります。これをコンテキスト長と呼びます。長い文書をそのまま渡すとこの上限を超えてしまうことがあるため、あらかじめ小さく分けておく必要があります。
まずは、これから扱う文書がどれくらいの長さなのかを見てみましょう。
document = """Retrieval-Augmented Generation combines a retriever and a generator.
The retriever finds relevant passages from a knowledge base.
The generator is a large language model that writes an answer.
Splitting long documents into smaller chunks makes retrieval precise."""
print("Total characters:", len(document))
print("Total words:", len(document.split()))
print("Number of lines:", len(document.splitlines()))
# 行ごとの長さを見てみる(分割の単位を考える材料)
for i, line in enumerate(document.splitlines(), 1):
print(f"line {i}: {len(line)} chars")
len(document) は文字数、document.split() は空白区切りで単語に分けた個数、document.splitlines() は改行で行に分けたリストです。行ごとの長さを見ると、「どこで区切るのが自然か」の材料になります。
チャンクの大きさには、はっきりしたトレードオフがあります。小さすぎると文脈が途中で切れてしまい、逆に大きすぎると質問に無関係な内容まで混ざってしまう(しかもコンテキスト長を圧迫する)。ちょうどいい大きさを探るのが、この章のテーマです。次のレッスンから、具体的な切り方を1つずつ試していきます。
やってみよう
document の文字列を書き換えて、len(document) や len(document.split()) がどう変わるか確かめてみましょう。行数(splitlines())が増えるように改行を足してみるのもよい練習です。
演習
document の単語数(document.split() の要素数)を print で表示してください。
ヒント1を見る
document.split() はスペースや改行で区切った単語のリストを返します。
ヒント2を見る
そのリストの長さを len() で数えます。