🌟

議事録データ・RAGのための評価ツール「rag-embedding-evaluation-tool」を開発した話

に公開

🎯 この記事の対象読者

  • 自治体DXを推進している職員の方(特に、AIやデータ活用に関心のある方)
  • RAGアプリケーションを開発している、またはこれから挑戦したいエンジニアの方
  • 地方議会の議事録など、大量のテキストデータをどうにかして宝の山に変えたい!と思っているすべての方

💡 この記事を読むと得られること

  • RAGの検索精度向上における「データ前処理」の重要性が、腹落ちレベルで理解できます。
  • Google Colabを使って、エンベディングモデルやデータ分割方法の違いが検索結果にどう影響するかを、サクッと比較・評価する具体的な方法がわかります。
  • 自作したrag-embedding-evaluation-toolの基本的な使い方と、現場での活用イメージが掴めます。

🚀 はじめに:なぜこの記事を書いたか

最近、我々の界隈では RAG (Retrieval-Augmented Generation) を活用した生成AIチャットボットが大きな注目を集めていますよね。特に、過去の膨大な議事録データを読み込ませて、「あの件についての答弁、どうだったっけ?」をAIに聞けるようにする取り組みは、多くの自治体で検討されているのではないでしょうか。

しかし、RAGの精度は、裏側で動く「どんなデータを」「どうやってAIが理解できる形(ベクトル)に変換するか」という、泥臭いデータの前処理に大きく左右されるのが現実です。

「この前処理、本当に効果あるのかな?」
「どのAIモデルを使えば、一番賢く答えてくれるんだろう?」

この疑問を感覚ではなく、ちゃんとデータで比較・検証できれば、実用化への大きな一歩を踏み出せるはず。そんな思いから、データの前処理やモデルの違いによる検索結果の変化を、誰の目にも分かりやすく比較できるツール**rag-embedding-evaluation-tool**を開発しました。

この記事では、そのツールの紹介と、開発の背景の試行錯誤を共有したいと思います!


😫 RAG精度評価の落とし穴:見えない課題と終わらない試行錯誤

RAG開発の現場では、こんな「あるある」に頭を抱えている方も少なくないはずです。

  • 手探りのパラメータ調整: 「とりあえず動いた!」はいいものの、チャンクサイズ(データの分割単位)はこれでいいのか?オーバーラップは?どのエンベディングモデルが最適なのか?確信が持てないまま開発を進めてしまう。
  • 評価のブラックボックス化: 前処理の方法を変えても、その結果がどう良くなったのか(あるいは悪くなったのか)を定量的に評価するのが難しい。「なんだか良くなった気がする」では、住民や上司への説明責任は果たせません。
  • 終わらない改善ループ: 最適解が見えないまま、何度も何度も前処理を試行錯誤するうち、時間と気力だけが削られていく...。

まさに、こんなつらいループに陥りがちです。

作業のフロー


✨ 解決策:RAG評価ツール「rag-embedding-evaluation-tool」

この「手探り状態」から脱却するために開発したのが、rag-embedding-evaluation-tool です。

入力した質問文(クエリ)に対して、用意した全ドキュメント(議事録データなど)との「意味の近さ」をコサイン類似度というスコアで算出し、ランキング形式で分かりやすく表示します。

これにより、「Aという前処理」と「Bという前処理」の結果を並べて比較し、どちらがより的確な文書を上位に持ってきているかを一目で判断できるようになります。

項目 内容
ツール名 rag-embedding-evaluation-tool
概要 入力したクエリに対して、全ドキュメントの類似度スコアを算出し、ランキング形式で表示します。データの前処理やエンベディングモデルを変えながら、検索結果の変化を簡単に比較・検証できます。
利用シーン RAG開発の評価・検証フェーズ、エンベディングモデルの選定、最適なチャンク戦略(データ分割方法)の模索など
GitHubリポジトリ https://github.com/HosoyaYusaku/rag-embedding-evaluation-tool

🛠️ 技術スタックと選定理由

今回のツール開発で採用した技術は以下の通りです。誰でも手軽に試せることを第一に考えました。

技術 役割 選定理由
Python 開発言語 多くのAI・データサイエンスライブラリが揃っており強力。何よりGoogle Colabで簡単に実行できるのが最高に便利です。
柔軟なドキュメント分割 データ前処理 RAGの精度を左右するチャンク戦略を柔軟に試せるように、1行を1ドキュメントとして分割したり、JSONオブジェクトごとに分割したりといった複数の方法に対応できるようにしました。

📜 コードのハイライト

このツールの心臓部である、検索ボタンがクリックされた時の処理を少しだけお見せします。やっていることは「クエリと全ドキュメントをベクトルに変換し、総当たりでコサイン類似度を計算してランキングを作る」というシンプルなものです。

def on_search_button_clicked(b):
    """★復元★: 検索ボタンがクリックされたときのメイン処理"""
    global documents, doc_embeddings, last_embedding_model, last_division_settings, current_query

    with output_area:
        clear_output()
        current_query = query_input.value
        embedding_model = embedding_model_selector.value
        current_division_settings = (division_method_selector.value, chunk_size_input.value, chunk_overlap_input.value, json_content_key_input.value)

        # ... (入力チェック) ...

        results = []
        doc_texts = [d['text'] for d in documents]

        # キーワード検索とベクトル検索の分岐
        if embedding_model == 'none':
            # ... (キーワード検索のロジック) ...
        else:
            # 埋め込みが未生成、またはモデル・分割設定が変更された場合に再生成
            if doc_embeddings is None or last_embedding_model != embedding_model or last_division_settings != current_division_settings:
                # ... (ドキュメントの埋め込みベクトルを生成) ...

            # クエリの埋め込みベクトルを生成
            query_embedding = get_embedding(current_query, embedding_model)
            if query_embedding is None: print("❌ クエリの埋め込みに失敗しました。"); return

            # コサイン類似度を計算し、結果をソート
            similarities = [1 - cosine(query_embedding, doc_emb) for doc_emb in doc_embeddings]
            sorted_indices = np.argsort(similarities)[::-1]
            results = [{'doc_info': documents[i], 'score': similarities[i]} for i in sorted_indices]

        # 結果の表示
        # ... (ランキング形式でテキストを出力) ...

    # 結果ダウンロードボタンの表示
    display_results_downloader(results, current_query)

💻 使い方

① 環境構築

環境構築は、驚くほど簡単です。

  1. Googleアカウントを用意する
  2. ブラウザでGoogle Colaboratoryを開く
  3. GitHubリポジトリにある*.ipynbファイルのコードを、開いたColabのノートブックに貼り付ける

以上です!面倒なインストール作業は一切必要ありません。

② ツールの実行

コードを貼り付けたら、上から順にセルを実行していくだけです。
UIが表示されたら、評価したいテキストファイル(またはJSONファイル)をアップロードし、試したいエンベディングモデルを選択。検索したいキーワードや質問文をクエリとして入力し、「検索」ボタンをクリックします。

③ 出力結果(Before / After)

このツールの真価は、前処理による結果の違いを客観的な指標で比較できる点にあります。

【Before】
例えば、議事録のテキストを単純に改行で分割しただけで検索した場合。
クエリ: 「子育て支援」に関する市長の答弁は?
→ 市長以外の議員の発言や、関係のない議題が上位にヒットしてしまうかもしれません。この時の類似度スコアとランキングを、まずは基準として見てみましょう。

【After】
次に、前処理ツールminutes-parserなどを使って、発言者や発言内容をきれいに構造化したJSONデータで、同じクエリを試してみましょう。
クエリ: 「子育て支援」に関する市長の答弁は?
→ さて、結果はどう変わるでしょうか。こちらの方がより的確な答弁が上位に来るかもしれませんし、逆に思わぬ結果になる可能性もあります。

重要なのは、前処理を変えることで結果がどう変化したかを「コサイン類似度」という共通の尺度で客観的に見比べられるようになることです。「Before」と「After」のランキングとスコアを比較し、「今回のケースでは、構造化した方がより関連性の高い文書を上位に持ってこれそうだ」といった仮説検証が可能になります。

このように、感覚ではなくデータに基づいて改善の方向性を判断するための「比較の環境」を提供することこそ、このツールが目指す役割です。


🧗 開発でつまづいた点と解決策

もちろん、開発は順風満帆ではありませんでした。特に苦労したのはこの2点です。

  • 課題1:前処理ツール(minutes-parser)と本ツールの連携

    • 原因・解決策: 議事録データを構造化する前処理ツールminutes-parserが出力するJSONL形式のデータを、この評価ツールが正しく解釈できるようにする必要がありました。地道ですが、何度もテストを繰り返し、入出力のフォーマットを丁寧に合わせることで、シームレスな連携を実現しました。ツールは単体で動くだけでなく、前後の工程と繋がってこそ価値を生むと痛感しました。
  • 課題2:評価結果の見やすい表示方式

    • 原因・解決策: 当初は、AIが扱うベクトルデータをそのまま表示しようとしましたが…数字の羅列が画面に並ぶだけで、人間には全く意味不明でした(笑)。これでは誰も使ってくれません。そこで、ベクトル間の角度を示す「コサイン類似度」を計算し、0から1のスコアに変換。それを元にランキング形式で表示することで、エンジニアでなくても直感的に「どの文書がクエリと一番近いか」を判断できるように工夫しました。
      当初の出力

🔭 今後の展望と野望

このツールはまだ生まれたばかり。やりたいことは沢山あります!

例えば、現在はJSONオブジェクト全体をベクトル化の対象としていますが、これでは不要な情報まで計算に含んでしまう可能性があります。今後は、"content": "..." のように、計算対象とするキーを特定できるようにし、よりノイズの少ない高精度な類似度計算を目指したいです。

さらに、計算結果を類似度スコア順に並べるだけでなく、メタデータ(例:会議の開催日)でソートできる機能も追加できれば、時系列で答弁の変化を追うような、より高度な分析も可能になるはずです。夢は広がります!


🔚 おわりに:小さな一歩が、大きな変化を生むと信じて

自治体DXやAI活用というと、何か壮大なシステムを想像しがちですが、私はこうした「ちょっとした検証を楽にする」「泥臭い作業を効率化する」という小さなツールの積み重ねが、結果的に大きな変化を生むと信じています。

RAGの精度に悩んでいる方、議事録データの活用方法を模索している方にとって、この記事とrag-embedding-evaluation-toolが、次の一歩を踏み出すための小さなきっかけになれば、これ以上嬉しいことはありません。

ぜひ、GitHubリポジトリを覗いてみてください。そして、もしよろしければStarを付けていただけると、開発の大きな励みになります!
最後まで読んでいただき、ありがとうございました!

Discussion