LangChain用に句読点で分割してくれるTextSplitterを作ってみた

スポンサーリンク

LangChainでテキストを分割するとき、ライブラリに含まれるRecursiveCharacterTextSplitterをそのまま使っても問題ないんだけど、なんとなく素人考えで、分割したテキストも読みやすい方がいいかなーと思って、句読点で分割してくれるTextSplitterを作ってみました。

ソースコード

"\n\n"で分割して、長過ぎたら"\n"で分割して、それでも長過ぎたら句読点で分割してと繰り返して適切な長さになるまで分割します。

from typing import Any

from langchain.text_splitter import RecursiveCharacterTextSplitter, CharacterTextSplitter


class JapaneseCharacterTextSplitter(RecursiveCharacterTextSplitter):
    def __init__(self, **kwargs: Any):
        separators = ["\n\n", "\n", "。", "、", " ", ""]
        super().__init__(separators=separators, **kwargs)

使い方とテスト

プログラム

text_list = [
    "あ\n\nい\n\nう\n\nえ\n\nお",
    "あいうえお。\n\nかきくけこ。",
    "あいうえお。\nかきくけこ。",
    "あいうえお。かきくけこ。",
    "あいうえお、 かきくけこ。",
    "あいうえお かきくけこ。",
    "あいうえおかきくけこ。",
]

japanese_spliter = JapaneseCharacterTextSplitter(
    chunk_size=6,
    chunk_overlap=0,
)

for text in text_list:
    text_chunks = japanese_spliter.split_text(text)
    display_text = text.replace("\n", "\\n")
    max_chunk_length = max(len(chunk) for chunk in text_chunks)
    print(f"text={display_text} doc_list={text_chunks} max_chunk_length={max_chunk_length}")

出力される結果

text=あ\n\nい\n\nう\n\nえ\n\nお doc_list=['あ\n\nい', 'う\n\nえ', 'お'] max_chunk_length=4
text=あいうえお。\n\nかきくけこ。 doc_list=['あいうえお。', 'かきくけこ。'] max_chunk_length=6
text=あいうえお。\nかきくけこ。 doc_list=['あいうえお。', 'かきくけこ。'] max_chunk_length=6
text=あいうえお。かきくけこ。 doc_list=['あいうえお', 'かきくけこ。'] max_chunk_length=6
text=あいうえお、 かきくけこ。 doc_list=['あいうえお', 'かきくけこ'] max_chunk_length=5
text=あいうえお かきくけこ。 doc_list=['あいうえお', 'かきくけこ'] max_chunk_length=5
text=あいうえおかきくけこ。 doc_list=['あいうえおか', 'きくけこ'] max_chunk_length=6

作った経緯

LangChainのサンプルを動かしていると "openai.error.InvalidRequestError: This model's maximum context length is 4097 tokens. However, your messages resulted in 27344 tokens. Please reduce the length of the messages." と表示されて動かなくなることがよくあった。

このエラー自体は最大トークン数を超えているので表示されるんだけど、要因を調べているとCharacterTextSplitterを使って文章を一定の大きさに分割したつもりが、全然分割できていなかった。CharacterTextSplitterは指定したseparatorで区切ってくれるけど、それでも分割できなかったら諦める挙動だった。。。 LangChainのドキュメントを読むとデフォルトの推奨はRecursiveCharacterTextSplitterなので、それを使ってもいいんだけど、もう少し日本語の処理にちょうど良さそうなSplitterが欲しかったので作ってみました。