🕌

LangChain×BM25でMarkdown文書をキーワード検索する

に公開

背景

RAG (Retrieval Augmented Generation)システムにおいて、検索手法として一般的に用いられるのはベクトル検索です。これは、クエリと文書の意味的な類似度に基づいて関連情報を取得する手法で、自然言語による曖昧な問い合わせに対して有効です。一方で、文字列の部分一致が必要なクエリに関しては弱いという点があります。例えば、社内ナレッジ検索のような固有名詞や略語、技術用語が頻出するケースでは、ベクトル検索だけでは対応しきれない可能性があります。
そういった課題の改善策として、ベクトル検索とキーワード検索(フルテキスト検索) を組み合わせたハイブリッド検索が有効とされています。
そのため、その中でもキーワード検索とLangChainを用いた実装方法に焦点を当てて調査しました。


対象読者

  • Python 初心者〜中級者
  • RAGの基礎知識がある方
  • 特定のドメインを扱うRAGシステムを構築したい方

環境

項目 バージョン
OS Windows 11 Pro
ランタイム Python 3.10.17
主要ライブラリ langchain==0.3.25, sudachidict-core==20250515

キーワード検索とは

キーワード検索とは、文字列の一致度を基に検索する手法であり、明確なキーワードが含まれるクエリに適していると言えます。例えば、「岡山の観光スポットは?」というクエリに対してキーワード検索を行うと、「岡山」「観光」「スポット」といった単語を含む情報を検索ソースと照らし合わせ、単語の一致度(Score)の高い文章を検索結果として出力します。代表的なスコアリング手法としてTF-IDFBM25が挙げられます。

事前準備

  1. 必要なライブラリのインストール
  2. 文書データの準備

手順

1. 必要なライブラリのインストール

キーワード検索の検証に用いたライブラリは以下の通りです。

requirements.txt
certifi>=2025.4.26
langchain>=0.3.25
langchain-community>=0.3.24
sudachidict-core>=20250515
rank-bm25
pip install -r requirements.txt

2. 文書データの準備

検証では、Markdown形式の社内ドキュメントを想定したサンプルを用いました。サンプルはLLMに生成してもらいました。

wiki.md
# CoreSphere 技術スタック詳細

## 1. はじめに
このドキュメントは、架空のIT企業「NexaTech Solutions」が開発する統合業務プラットフォーム「CoreSphere」の技術スタックとその仕様・利用方法を記述したものです。

---

## 2. システム概要
### 2.1 システム名称
**CoreSphere**

### 2.2 CoreSphereとは?
CoreSphereは、企業の業務プロセスを一元管理し、チームのコラボレーションを強化するためのクラウドベースの業務管理プラットフォームです。  
AIを活用した業務自動化、リアルタイム分析、統合コミュニケーション機能を提供し、チームの生産性を向上させます。  
**主な特徴:**  
- **AIによる業務効率化:** 自動タスク分類とスケジュール調整  
- **統合データ分析:** 高度なBI機能によるリアルタイム可視化  
- **セキュアなコラボレーション:** チームメンバー間の情報共有と権限管理  

---

## 3. 技術スタックの詳細
### 3.1 フロントエンド
| ソフトウェア | 説明 | 利用方法 |
|-------------|------|---------|
| **NovaUI** | Reactベースのコンポーネントライブラリ。動的UIを構築。 | SPAのフロント開発、UI設計。 |
| **ChartX** | 高度なデータ可視化ツール。グラフやダッシュボードを作成可能。 | 分析結果を視覚化するレポート作成に使用。 |
| **StateFlow** | 状態管理ライブラリ。非同期処理を管理。 | グローバルな状態管理に利用し、パフォーマンス向上。 |
| **FlexDesign** | 軽量CSSフレームワーク。レスポンシブ対応。 | デザインシステムを統一し、スタイル管理を簡素化。 |
| **WebForge** | モジュールバンドルツール。コード最適化。 | フロントエンドアプリのビルド時に利用。 |

~~~~~~~~~~~~~~~~

3. Markdownファイルの分割

LangChainのMarkdownHeaderTextSplitterRecursiveCharacterTextSplitterを用いて、文書を見出し単位+指定したチャンクサイズで分割します。

keyword_search.py
from langchain_text_splitters import MarkdownHeaderTextSplitter
from langchain_text_splitters import RecursiveCharacterTextSplitter
 
from langchain.schema import Document
 
def split(file: str) -> list[Document]:
    """マークダウンファイルを見出し単位と一定のチャンクサイズで分割する
    Args:
    file: マークダウンファイル
    Returns:
    分割されたドキュメント配列
    """
    # セクションを分ける見出しの定義
    headers_to_split_on = [("#", "header1"), ("##", "header2"), ("###", "header3")]
 
    # Markdown見出しの分割設定
    markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
 
    # Markdownファイルの分割
    documents = markdown_splitter.split_text(file)
 
    # チャンクサイズの設定
    text_splitter = RecursiveCharacterTextSplitter(
    chunk_size = 256,
    chunk_overlap = 50,
    separators=["\n\n", "\n", "。", "、", "\u200b"]
    )
    # チャンクサイズ設定による文書分割の処理
    return text_splitter.split_documents(documents)
実行結果
[Document(metadata={'header1': 'CoreSphere 技術スタック詳細', 'header2': '1. はじめに'}, page_content='このドキュメントは、架空のIT企業「NexaTech Solutions」が開発する統合業務プラットフォーム「CoreSphere」の技術スタックとその仕様・利用方法を記述したものです。  \n---'), Document(metadata={'header1': 'CoreSphere 技術スタック詳細', 'header2': '2. システム概要', 'header3': '2.1 システム名称'}, page_content='**CoreSphere**'), Document(metadata={'header1': 'CoreSphere 技術スタック詳細', 'header2': '2. システム概要', 'header3': '2.2 CoreSphereとは?'}, page_content='CoreSphereは、企業の業務プロセスを一元管理し、チームのコラボレーションを強化するためのクラウドベースの業務管理プラットフォームです。\nAIを活用した業務自動化、リアルタイム分析、統合コミュニケーション機能を提供し、チームの生産性を向上させます。\n**主な特徴:**\n- **AIによる業務効率化:** 自動タスク分類とスケジュール調整\n- **統合データ分析:** 高度なBI機能によるリアルタイム可視化\n- **セキ ュアなコラボレーション:** チームメンバー間の情報共有と権限管理  \n---'), ~

4. 文章検索

BM25Retrieverはそのままでは日本語非対応のため、**形態素解析(分かち書き)**を行います[1]
分かち書きにはSudachiPyを使用し、辞書にはSudachiDict-coreを指定しました(フルバージョンは証明書エラーでインストール出来なかった為)。
参考実装はshimajiroxyzさんのQiita記事:【langchain】BM25Retriever/TFIDFRetrieverを日本語に対応させるを活用させていただきました。


結果

架空のプラットフォームであるCoreSphereって何?というクエリに対し、BM25によるキーワード検索を行います[2]

keyword_search.py
if __name__ == "__main__":
    file_path = './wiki.md'
    chunk_scores = []

    with open(file_path) as f:
        file_text = split(f.read())

        # Metadataのheaderをページコンテンツと結合
        for doc in file_text:
            header = doc.metadata.get("header3", "")  # header3がある場合取得
            doc.page_content = header + "\n" + doc.page_content  # header3を検索対象に組み込む

        retriever = BM25Retriever.from_documents(file_text, preprocess_func=preprocess_func, k=3)
        scores = retriever.vectorizer.get_scores(preprocess_func("CoreSphereって何?"))

        for text, score in zip(file_text, scores):
            chunk_scores.append({"text": text, "score": score})

        top_3 = sorted(chunk_scores, key=lambda x: x["score"], reverse=True)[:3]

        for split_text in top_3:
            print(split_text["score"])
            print(split_text["text"].metadata)
            print(split_text["text"].page_content)
実行結果
2.5704315292127333
{'header1': 'CoreSphere 技術スタック詳細', 'header2': '2. システム概要', 'header3': '2.2 CoreSphereとは?'}
2.2 CoreSphereとは?
CoreSphereは、企業の業務プロセスを一元管理し、チームのコラボレーションを強化するためのクラウドベースの業務管理プラットフォームです。
AIを活用した業務自動化、リアルタイム分析、統合コミュニケーション機能を提供し、チームの生産性を向上させます。
**主な特徴:**
- **AIによる業務効率化:** 自動タスク分類とスケジュール調整
- **統合データ分析:** 高度なBI機能によるリアルタイム可視化
- **セキュアなコラボレーション:** チームメンバー間の情報共有と権限管理
---
1.734437696606237
{'header1': 'CoreSphere 技術スタック詳細', 'header2': '2. システム概要', 'header3': '2.1 システム名称'}
2.1 システム名称
**CoreSphere**
1.1087150661399454
{'header1': 'CoreSphere 技術スタック詳細', 'header2': '1. はじめに'}

このドキュメントは、架空のIT企業「NexaTech Solutions」が開発する統合業務プラットフォーム「CoreSphere」の技術スタックとその仕様・利用方法を記述したものです。
---

該当するキーワードが含まれるチャンクのみ取り出せました。


まとめ & 次のステップ

  • 日本語のキーワード検索には、分かち書きが必要。
  • Markdown形式のドキュメントを扱う際は、構造に基づいた分割が効果的。
  • 次のステップ:EnsembleRetrieverを用いたハイブリッド検索の実装。

参考リンク

脚注
  1. 【langchain】BM25Retriever/TFIDFRetrieverを日本語に対応させる ↩︎

  2. BM25 | ️ LangChain ↩︎

セリオ株式会社 テックブログ

Discussion