コンテンツにスキップ

RAG検索 日本語助詞問題 検証レポート

[!NOTE] 最新の実装状況は 機能実装ステータス (Remaining Functionality) を参照してください。

実装ノート(アーティファクト): トレーニングスクリプトが出力する artifact_manifest.json と推奨CLIフラグについては docs/implementation/ARTIFACT_MANIFESTS.md を参照してください。

作成日: 2025年12月13日

このドキュメントの目的と使い方

  • 目的: RAG日本語検索での助詞混入問題の原因・対策を整理し、修正の指針を示す。
  • 対象読者: RAG実装/検索担当エンジニア、QA。
  • まず読む順: 概要 → 問題の詳細 → 検証/再現手順 → 対策案。
  • 関連リンク: 分散脳スクリプトは examples/run_zenoh_distributed_brain.py、PFC/Zenoh/Executive詳細は implementation/PFC_ZENOH_EXECUTIVE.md

概要

RAG(Retrieval-Augmented Generation)システムで日本語を検索した際に、助詞(は、が、を、に、で、など)が検索結果やトークン選択に含まれる問題を調査・検証しました。


問題の詳細

発見された問題点

  1. Janomeトークナイザーの助詞処理
  2. wakati=True モードでは助詞も含めて全てのトークンを返す
  3. 品詞情報を取得するには wakati=False が必要
  4. 現在の実装では品詞情報を活用していない

  5. RAG内部のトークナイズ関数

  6. _extractive_answer メソッド内の tokens_of 関数が助詞をフィルタリングしていない
  7. Janomeの wakati=True を使用しているため、助詞がそのまま含まれる
  8. フォールバック時の正規表現でも助詞除去を行っていない

  9. TF-IDFスコアリングへの影響

  10. 助詞は頻出するためTF(Term Frequency)は高くなる
  11. 全文書に出現するためIDF(Inverse Document Frequency)は低くなる傾向
  12. しかし、クエリに助詞が含まれる場合はマッチングに影響する可能性がある

  13. Elasticsearch kuromojiフィルター

  14. 設定では kuromoji_part_of_speech フィルターが助詞除去を行う
  15. kuromojiプラグインがない環境では助詞除去されない
  16. 実際の動作確認が必要

対象コード箇所

1. evospikenet/rag_milvus.py

NOTE: The implementation referenced below has been moved to the rag-system/ directory. For runtime usage prefer the RAG API exposed by rag-system and evospikenet.rag_client.

ファイル: rag_milvus.py

問題のあるコード(修正前):

def tokens_of(text):
    if not text:
        return []
    if self.janome_tokenizer:
        try:
            # janome Tokenizer().tokenize(..., wakati=True) yields tokens
            return [t for t in self.janome_tokenizer.tokenize(text, wakati=True) if t.strip()]
        except Exception:
            pass
    # Fallback: simple word regex (works for Latin scripts and basic tokenization)
    return re.findall(r"\w+", text.lower())

問題点: - wakati=True では品詞情報が取得できない - 助詞が含まれたままトークンリストに追加される - フォールバック時も助詞除去なし

2. evospikenet/elasticsearch_client.py

ファイル: elasticsearch_client.py

kuromoji設定:

"analyzer": {
    "ja_kuromoji_analyzer": {
        "type": "custom",
        "tokenizer": "kuromoji_tokenizer",
        "filter": [
            "kuromoji_baseform",  # Convert to dictionary form
            "kuromoji_part_of_speech",  # Remove particles and auxiliary verbs
            "cjk_width",  # Normalize full-width and half-width characters
            "ja_stop",  # Remove Japanese stop words
            "lowercase"  # Lowercase for English text
        ]
    }
}

備考: - kuromoji_part_of_speech フィルターが助詞除去を担当 - kuromojiプラグインの有無で動作が変わる - 実環境での動作確認が必要


実装した解決策

解決策: トークナイズ時の助詞フィルタリング

アプローチ: 解決策A(品詞情報活用) + 解決策B(ストップワードリスト)の組み合わせ

修正後のコード:

def tokens_of(text):
    if not text:
        return []

    # Japanese particles and auxiliary verbs to filter out
    japanese_stopwords = {
        # 助詞 (particles)
        'は', 'が', 'を', 'に', 'へ', 'で', 'と', 'から', 'まで', 'より',
        'の', 'や', 'か', 'も', 'など', 'さえ', 'こそ', 'って', 'つつ',
        'ながら', 'たり', 'だけ', 'ばかり', 'くらい', 'ほど', 'し',
        'だの', 'やら', 'なり', 'とか', 'ね', 'よ', 'な', 'ぞ', 'ぜ', 'わ',
        'のに', 'ので', 'けど', 'けれど', 'けれども', 'が', 'て',
        # 助動詞 (auxiliary verbs)
        'です', 'ます', 'だ', 'である', 'でした', 'ました', 'た', 'う',
        'よう', 'そう', 'れる', 'られる', 'せる', 'させる', 'ない',
        # その他の機能語
        'こと', 'もの', 'ため', 'ところ', 'はず', 'わけ'
    }

    if self.janome_tokenizer:
        try:
            # Use wakati=False to get POS (part-of-speech) information
            tokens = []
            for token in self.janome_tokenizer.tokenize(text):
                parts = str(token).split('\t')
                if len(parts) >= 2:
                    surface = parts[0]
                    info = parts[1].split(',')
                    # Extract POS tag
                    if len(info) >= 1:
                        pos = info[0]
                        # Filter out particles (助詞), auxiliary verbs (助動詞), and symbols (記号)
                        if pos not in ['助詞', '助動詞', '記号'] and surface.strip():
                            # Additional check: filter known stopwords
                            if surface not in japanese_stopwords:
                                tokens.append(surface)
            return tokens
        except Exception:
            pass

    # Fallback: simple word regex with stopword filtering
    tokens = re.findall(r"\w+", text.lower())
    return [t for t in tokens if t not in japanese_stopwords]

改善点

  1. 品詞情報の活用
  2. wakati=False を使用して品詞タグを取得
  3. 助詞(助詞)、助動詞(助動詞)、記号を除外

  4. ストップワードリストの追加

  5. 一般的な助詞・助動詞の包括的なリスト
  6. Janomeが利用できない環境でも機能

  7. フォールバック時の保護

  8. 正規表現トークナイズ後もストップワードでフィルタリング
  9. 環境に依存しない動作

  10. 二重チェック

  11. 品詞タグでフィルタリング後、さらにストップワードリストでチェック
  12. より確実な助詞除去

検証結果

修正箇所

ファイル 行番号 変更内容
rag_milvus.py 271-320 tokens_of 関数を修正し、助詞フィルタリングを実装

期待される効果

Before(修正前)

# テキスト: "EvoSpikeNetは分散脳シミュレーションです"
tokens = ['EvoSpikeNet', 'は', '分散', '脳', 'シミュレーション', 'です']
# 助詞 'は' と助動詞 'です' が含まれる ❌

After(修正後)

# テキスト: "EvoSpikeNetは分散脳シミュレーションです"
tokens = ['EvoSpikeNet', '分散', '脳', 'シミュレーション']
# 助詞と助動詞が除外される ✅

TF-IDFへの影響

修正前: - 助詞がトークンに含まれる - クエリに助詞があるとマッチングに影響 - 意味のない単語で類似度計算が汚染される

修正後: - 内容語(名詞、動詞など)のみでTF-IDF計算 - より正確な文書類似度 - 抽出的回答の品質向上


テストケース

1. 基本的な助詞フィルタリング

入力テキスト:

"EvoSpikeNetは分散脳シミュレーションのフレームワークです。"

期待される出力:

['EvoSpikeNet', '分散', '脳', 'シミュレーション', 'フレームワーク']

除外される語: - は(助詞) - の(助詞) - です(助動詞)

2. 複雑な文の処理

入力テキスト:

"これが問題です。私を助けてください。"

期待される出力:

['これ', '問題', '私', '助け', 'て', 'ください']

除外される語: - が(助詞) - です(助動詞) - を(助詞)

3. クエリのトークナイズ

入力クエリ:

"分散脳シミュレーションとは何ですか"

期待される出力:

['分散', '脳', 'シミュレーション', '何']

除外される語: - と(助詞) - は(助詞) - です(助動詞) - か(助詞)


既知の制限事項

1. Janome未インストール環境

状況: Janomeがインストールされていない場合

動作: - 正規表現ベースのトークナイズにフォールバック - ストップワードリストのみで助詞を除外 - 品詞情報は利用できない

制限: - 未知の助詞や新しい表現は除外されない可能性 - 辞書形への正規化が行われない

2. ストップワードリストの網羅性

現在の対応: - 一般的な助詞・助動詞を含むリスト(40以上) - 口語表現も一部含む

制限: - 全ての助詞・助動詞を網羅しているわけではない - 方言や古語には対応していない

3. 英語テキストへの影響

状況: 英語テキストに日本語のストップワードが含まれる場合

影響: - ほとんど影響なし(英語では助詞の文字が単独で出現することは稀) - 正規表現トークナイズは英語にも適用される


今後の改善案

短期的な改善

  1. ストップワードリストの拡充
  2. より多くの助詞・助動詞を追加
  3. 口語表現の充実化
  4. 方言への対応

  5. Elasticsearchとの整合性確認

  6. kuromoji設定の動作検証
  7. Milvusとの結果の比較

  8. 単体テストの追加

  9. tokens_of 関数の単体テスト
  10. 様々な日本語パターンでのテスト

長期的な改善

  1. 高度な形態素解析の採用
  2. MeCabの統合検討
  3. SudachiPyの採用検討
  4. より正確な品詞タグ付け

  5. 言語検出の改善

  6. 文単位での言語検出
  7. 混在テキストへの対応

  8. 機械学習ベースのフィルタリング

  9. 重要語抽出モデルの活用
  10. コンテキストを考慮したフィルタリング

参考資料

日本語の品詞体系

品詞 説明 除外対象
名詞 物事の名前 分散、脳、シミュレーション
動詞 動作や状態 走る、食べる、ある
形容詞 性質や状態 大きい、美しい、新しい
副詞 修飾語 とても、すぐに、ゆっくり
助詞 文法的関係を示す は、が、を、に、で
助動詞 動詞や形容詞に付く です、ます、た、ない
接続詞 文をつなぐ そして、しかし、または
感動詞 感情を表す ああ、おお、えっ
記号 句読点など 。、!、?

Janome品詞タグ

参考: Janome公式ドキュメント

表層形\t品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用型,活用形,原形,読み,発音

:

走る\t動詞,自立,*,*,五段・ラ行,基本形,走る,ハシル,ハシル
は\t助詞,係助詞,*,*,*,*,は,ハ,ワ
です\t助動詞,*,*,*,特殊・デス,基本形,です,デス,デス

Elasticsearch kuromoji設定

参考: Elasticsearch kuromoji plugin

kuromoji_part_of_speech フィルター: - 助詞を除去: stoptags パラメータで指定 - デフォルトで助詞・助動詞を除去

kuromoji_baseform フィルター: - 動詞や形容詞を辞書形に変換 - 「走った」→「走る」


まとめ

実施した対策

  1. Janome品詞情報の活用: wakati=False で品詞タグを取得し、助詞・助動詞・記号を除外
  2. 包括的なストップワードリスト: 40以上の助詞・助動詞をカバー
  3. フォールバック保護: 正規表現でもストップワード除去を実施
  4. 二重チェック機構: 品詞タグ + ストップワードで確実に除外

解決される問題

  • ✅ RAG検索結果から助詞が除外される
  • ✅ TF-IDF計算が内容語のみで行われる
  • ✅ 抽出的回答の品質が向上する
  • ✅ より正確な文書類似度計算

継続的なモニタリング

今後も以下の点を監視し、必要に応じて改善を行います:

  1. 実際のRAGクエリでの助詞の出現頻度
  2. 抽出的回答の品質評価
  3. ユーザーフィードバック
  4. Elasticsearchとの結果の整合性

関連ファイル

ファイル 役割
rag_milvus.py RAGシステムのメイン実装
elasticsearch_client.py Elasticsearch統合
test_japanese_rag_particle_issue.py 検証スクリプト
test_rag_debug.py RAGデバッグスクリプト

最終更新: 2025年12月13日