コンテンツにスキップ

EvoSpikeNet RAG システム - 日本語検索最適化仕様書(詳細版)

Version: 3.1
Date: 2026-05-23
Status: Implemented with Operational Hardening
Document ID: RAG-JP-SPEC-V3.1-001
Changes in v3.1: API 契約を現行実装へ同期。拡張クエリ間の RRF 加算、memory_boost を含む最終スコア再ソート、検索→フィードバックのテスト階層(Unit / Integration / System / E2E)を反映。


目次

  1. システム概要
  2. 要件定義
  3. 技術仕様詳細
  4. 3.5 EvoSpikeNet Core 記憶システム統合
  5. API 仕様
  6. 4.3 記憶統合 API フロー
  7. 検索アルゴリズム
  8. 5.3 記憶強化ランキング
  9. 評価・テスト
  10. デプロイメント
  11. トラブルシューティング
  12. 付録

1. システム概要

1.1 目的

EvoSpikeNet-Core RAG システムの日本語検索精度を向上させるため、 以下の課題に対応した統合ソリューション:

  • 表記揺れ: 「盆休み」「お盆休み」の相互検索
  • 固有名詞: プロジェクトID「EV-2024-001」の正確検索
  • 同義語: 「会議」「ミーティング」「カンファレンス」の統一

1.2 スコープ

対象 含む 含まない
言語 日本語(メイン)、英語 中国語、韓国語
検索 ハイブリッド(BM25 + Vector) Full-text only
機能 RAG Pipeline Generative Model
規模 1M+ ドキュメント Real-time Streaming

1.3 アーキテクチャ概観 — Memory-Augmented RAG

v3.0 より EvoSpikeNet Core SDK の記憶層(青色ノード)と統合した 記憶強化型 RAG に移行。

flowchart TD
    Q["User Query (Japanese)"]
    Q --> LD["言語検出\nLanguageDetector"]
    Q --> NER["固有表現抽出\nEntityRecognizer"]
    LD --> QN["クエリ正規化 & 拡張\nSudachiTokenizer + QueryExpander"]
    NER --> QN
    NER --> SM_Q["SemanticMemory\nretrieve_semantic_knowledge()"]
    SM_Q -->|"概念グラフ Boost 強化"| ENH["エンティティ Boost\n動的強化"]
    QN --> KW["Elasticsearch BM25\nSudachi アナライザ"]
    QN --> VEC["Milvus ベクター検索\nL2距離 384-dim"]
    ENH --> KW
    ENH --> VEC
    KW -->|"BM25 スコア"| RRF["RRF 融合\nk=60 + Entity Boost"]
    VEC -->|"L2 距離"| RRF
    RRF --> EP_ENR["EpisodicMemory\nintegrate_episodic_semantic()"]
    EP_ENR -->|"文脈強化"| TOPK["Top-K ドキュメント"]
    TOPK --> EP_RET["EpisodicMemory\nretrieve_memories()"]
    EP_RET -->|"過去セッション Rerank"| GEN["レスポンス生成"]
    GEN --> FB{"{ユーザー評価\nrating 1-5"}
    FB -->|"reward = rating/5"| STORE["EpisodicMemory\nstore_experience()"]
    STORE --> LTM["LongTermMemoryModule\nSNN 重要度ゲーティング"]
    LTM -->|"低重要度"| FORGET["ForgettingController\n自動削除"]
    style SM_Q fill:#e3f2fd,stroke:#1976D2
    style EP_ENR fill:#e3f2fd,stroke:#1976D2
    style EP_RET fill:#e3f2fd,stroke:#1976D2
    style STORE fill:#e3f2fd,stroke:#1976D2
    style LTM fill:#e8eaf6,stroke:#3F51B5
    style FORGET fill:#fce4ec,stroke:#C62828

凡例: - 青色ノード (#e3f2fd): evospikenet.episodic_memory モジュール - 紫色ノード (#e8eaf6): evospikenet.long_term_memory モジュール - 赤色ノード (#fce4ec): evospikenet.forgetting_controller モジュール

1.4 2026-05-23 実装状況と運用メモ

この仕様のコア範囲は EvoSpikeNet-Core/evospikenet/api_modules/rag_v2_api.pyEvoSpikeNet-Core/evospikenet/rag_memory_integrator.py を中心に実装済みです。以下は現行実装に基づく運用メモです。

  • API 実装:
    • POST /api/v2/rag/search
    • POST /api/v2/rag/feedback
    • GET /api/v2/rag/preprocessing/health
  • 前処理の責務分離:
    • SudachiTokenizer
    • EntityRecognizer
    • QueryExpander
  • 本番系ポリシー:
    • production/staging 判定時は依存未解決を fail-closed とし、疑似埋め込みやプレースホルダ応答へフォールバックしない。
  • 埋め込み方針:
    • SentenceTransformer 実エンコーダを必須とし、埋め込み次元は 384 に統一。
  • QueryExpander:
    • rule|llm|hybrid をサポート。
    • LLM 出力は {"expansions": [...]} の JSON のみ受理。
  • 品質ガード:
    • 多様性と冗長性を評価し、低品質時は rule バックエンドへ自動フォールバック。
  • 可観測性:
    • debug_info.preprocessing に品質メトリクス、ガード統計、履歴、query_hash サマリを出力。
    • 起動時ウォームアップとヘルス API により前処理コンポーネントの稼働可否を確認。
  • 主な運用設定:
    • RAG_V2_NER_*
    • RAG_V2_PREPROCESSING_*
    • RAG_V2_SUDACHI_*
    • RAG_V2_QUERY_EXPANDER_*
  • 今後の拡張:
  • 2026-05-23 検証反映:
    • 拡張クエリごとの検索結果は RRF スコアを加算して統合する。
    • memory_boost は最終スコアへ加算され、最終順位は final_score で再ソートする。
    • POST /api/v2/rag/feedbacksession_id を必須とし、保存後に memory_idimportance を返す。

2. 要件定義

2.1 機能要件

FR-1: ハイブリッド検索

要件: BM25 キーワード検索とベクター検索を統合

検索時間: < 500ms @ top-10
精度(NDCG@10): > 0.75 at production launch
処理スループット: 100 queries/sec

実装: RRF (k=60) によるスコア統合

FR-2: 日本語特化処理

要件: 日本語の表記揺れ・同義語に対応

Variation Coverage: > 80%
Entity Recognition Recall: > 80%
表記ゆれ正規化: < 5ms overhead

実装: - Sudachi によるトークナイズ - NER による固有表現抽出 - Query Expansion による同義語拡張

FR-3: 評価フレームワーク

要件: 検索精度を定期的に評価

テストケース数: >= 50
評価メトリクス: MRR, NDCG, Recall, Precision
自動評価スクリプト: 実装済み

2.2 非機能要件

NFR-1: パフォーマンス

メトリクス 要件 測定方法
P95 レイテンシ < 300ms query latency monitor
P99 レイテンシ < 800ms query latency monitor
Throughput >= 100 q/s load testing
インデックス再構築 < 4h full rebuild time

NFR-2: 可用性

Availability: >= 99.5%
MTTR (Mean Time To Recovery): < 5 min
Backup frequency: Daily

NFR-3: 拡張性

Document Scaling: Up to 10M docs
Language Support: Extensible to 3+ languages
Customization: ルール・辞書の外部管理

NFR-4: セキュリティ

Access Control: Role-based (RBAC)
Data Encryption: in-transit (TLS) and at-rest
Audit Logging: All query access

3. 技術仕様詳細

3.1 コンポーネント仕様

3.1.1 言語検出モジュール

Module: backend.text.LanguageDetector

class LanguageDetector:
    def __init__(self):
        self.hiragana_range = ('\u3040', '\u309f')
        self.katakana_range = ('\u30a0', '\u30ff')
        self.kanji_range = ('\u4e00', '\u9fff')

    def detect_language(self, text: str) -> str:
        """
        入力テキストから言語を判定

        Args:
            text: 入力テキスト

        Returns:
            "ja" | "en" | "mixed"

        Algorithm:
            1. 日本語文字の割合を計算
            2. 日本語文字 > 10% の場合は "ja"
            3. ASCII文字 > 90% の場合は "en"
            4. その他は "mixed"
        """
        japanese_count = sum(
            1 for c in text
            if (self.hiragana_range[0] <= c <= self.hiragana_range[1] or
                self.katakana_range[0] <= c <= self.katakana_range[1] or
                self.kanji_range[0] <= c <= self.kanji_range[1])
        )

        ja_ratio = japanese_count / len(text) if text else 0

        if ja_ratio > 0.1:
            return "ja"
        elif ja_ratio == 0:
            return "en"
        else:
            return "mixed"

    @property
    def precision(self):
        """言語検出精度: 98% @ standard test set"""
        pass

性能要件: - 実行時間: < 1ms - 精度: 98%


3.1.2 トークナイズ・正規化モジュール

Module: backend.elasticsearch_client.SudachiTokenizer

class SudachiTokenizer:
    """Sudachi による日本語トークナイズ"""

    def __init__(self):
        from sudachipy.dictionary import Dictionary
        from sudachipy.tokenizer import Tokenizer

        self.dictionary = Dictionary.open()
        self.tokenizer = Tokenizer(self.dictionary)

    def tokenize(self, text: str, mode: str = "search") -> List[str]:
        """
        テキストをトークンに分割

        Args:
            text: 入力テキスト
            mode: "search" (細粒度) | "normal" (標準)

        Returns:
            トークンリスト

        Examples:
            >>> tokenize("お盆休み")
            ["盆", "休み"]  # 接頭辞「お」は削除

            >>> tokenize("サーバーの障害")
            ["サーバ", "の", "障害"]  # 長音符が正規化
        """
        morphemes = self.tokenizer.tokenize(text, sudachipy.tokenizer.Tokenizer.SudachiMode.SEARCH)

        tokens = []
        for m in morphemes:
            # 基本形を取得(活用を正規化)
            base_form = m.dictionary_form()
            tokens.append(base_form)

        return tokens

    def normalize_query(self, query: str) -> str:
        """
        クエリを正規化

        処理:
        1. Unicode NFC → NFKC
        2. 長音符を統一(ー削除 or 短縮)
        3. 半角・全角を統一
        """
        import unicodedata

        # NFKC 正規化
        normalized = unicodedata.normalize("NFKC", query)

        # 長音符の削除(設定可能)
        normalized = normalized.replace("ー", "")

        return normalized

性能要件: - Tokenize 実行時間: < 5ms/query - Normalization overhead: < 2ms/query


3.1.3 Named Entity Recognition (NER)

Module: backend.rag_milvus.EntityRecognizer

class EntityRecognizer:
    """固有表現認識(NER)"""

    ENTITY_TYPES = {
        "PROJECT_ID": {"boost": 5.0, "pattern": r"^[A-Z]{2,3}-\d{4}-\d{3,4}$"},
        "PERSON": {"boost": 3.0, "pattern": None},
        "PRODUCT": {"boost": 4.0, "pattern": r"^[A-Za-z0-9 ]+(Pro|Core|Basic)$"},
        "DATE": {"boost": 2.0, "pattern": r"^\d{4}-\d{2}-\d{2}$"},
        "ORG": {"boost": 2.5, "pattern": None}
    }

    def __init__(self, model_name: str = "tner/roberta-large-japanese-char-luw-ner"):
        from transformers import pipeline
        self.nlp = pipeline("ner", model=model_name)

    def extract(self, text: str) -> Dict[str, List[Dict]]:
        """
        テキストから固有表現を抽出

        Returns:
            {
                "PROJECT_ID": [{"text": "EV-2024-001", "start": 3, "end": 13}],
                "PERSON": [...]
            }
        """
        entities = self.nlp(text)

        result = {}
        for ent in entities:
            label = ent["entity_group"]
            if label not in result:
                result[label] = []

            result[label].append({
                "text": ent["word"],
                "start": ent["start"],
                "end": ent["end"],
                "score": ent["score"]
            })

        return result

    def compute_boost_factor(self, entity_type: str) -> float:
        """固有表現タイプから boost 係数を計算"""
        return self.ENTITY_TYPES.get(entity_type, {}).get("boost", 1.0)

性能要件: - NER 実行時間: < 100ms/query - Entity Recognition Recall: >= 85%


3.1.4 Query Expansion

Module: backend.query_expander.QueryExpander

class QueryExpander:
    """クエリ拡張(同義語展開)"""

    def __init__(self, llm_backend, terms_dict: Dict[str, List[str]]):
        self.backend = llm_backend
        self.terms_dict = terms_dict
        self.expansion_cache = {}  # LRU cache

    def expand(self, query: str, max_expansions: int = 3) -> List[str]:
        """
        クエリを拡張(同義語を追加)

        Returns:
            [original_query, synonym_1, synonym_2, ...]

        Strategy:
        1. キャッシュをチェック
        2. 辞書ベースの拡張
        3. LLM ベースの拡張(遅い場合はスキップ)
        """
        if query in self.expansion_cache:
            return self.expansion_cache[query]

        expansions = [query]

        # 辞書ベース
        for word in query.split():
            if word in self.terms_dict:
                for syn in self.terms_dict[word][:max_expansions-1]:
                    new_q = query.replace(word, syn)
                    if new_q not in expansions:
                        expansions.append(new_q)

        # LLM ベース(オプション)
        if len(expansions) < max_expansions + 1:
            try:
                llm_expansions = self._llm_expand(query, max_expansions)
                expansions.extend(llm_expansions)
            except TimeoutError:
                pass  # Skip on timeout

        # キャッシュに保存
        self.expansion_cache[query] = expansions[:max_expansions+1]

        return self.expansion_cache[query]

    def _llm_expand(self, query: str, max_count: int) -> List[str]:
        """LLM による拡張"""
        prompt = f"""
クエリ「{query}」の同義表現を {max_count} つ提示してください。
JSON 形式: {{"expansions": ["表現1", "表現2", ...]}}
"""
        response = self.backend.generate(prompt, context="")
        import json
        return json.loads(response)["expansions"]

性能要件: - 拡張処理時間: < 50ms (キャッシュ済みは < 1ms) - キャッシュヒット率: 60-70%


3.2 検索パイプライン詳細

3.2.1 入力処理

def preprocess_query(query: str) -> Dict[str, Any]:
    """
    クエリの前処理
    """
    # 1. 言語検出
    lang = language_detector.detect(query)

    # 2. クエリ正規化
    normalized = sudachi_tokenizer.normalize_query(query)

    # 3. 固有表現抽出
    entities = entity_recognizer.extract(query)

    # 4. クエリ拡張(オプション)
    expansions = query_expander.expand(query) if ENABLE_EXPANSION else [query]

    return {
        "original": query,
        "normalized": normalized,
        "language": lang,
        "entities": entities,
        "expansions": expansions
    }

3.2.2 キーワード検索(Elasticsearch)

Elasticsearch クエリ:

{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "content": {
              "query": "クエリテキスト",
              "analyzer": "ja_sudachi_normalized",
              "operator": "and",
              "fuzziness": "AUTO"
            }
          }
        }
      ],
      "should": [
        {
          "term": {
            "entity_project": {
              "value": "EV-2024-001",
              "boost": 5.0
            }
          }
        },
        {
          "term": {
            "entity_person": {
              "value": "田中太郎",
              "boost": 3.0
            }
          }
        }
      ],
      "filter": [
        {
          "range": {
            "created_at": {
              "gte": "now-6M"
            }
          }
        }
      ]
    }
  },
  "size": 10,
  "track_scores": true
}

スコアリング:

BM25(d, q) = IDF(qi) * (f(qi, d) * (k1 + 1)) / (f(qi, d) + k1 * (1 - b + b * |d| / avgdl))

パラメータ:
  k1 = 1.2  (term frequency saturation)
  b  = 0.75 (document length normalization)

3.2.3 ベクター検索(Milvus)

検索パラメータ:

search_params = {
    "metric_type": "L2",          # L2距離(コサイン類似度より安定)
    "params": {"nprobe": 10}      # プローブ数(精度と速度のトレード)
}

results = collection.search(
    data=[query_embedding],       # 384次元
    anns_field="embedding",
    param=search_params,
    limit=top_k * 2,              # オーバーサンプリング
    output_fields=["id", "text", "source"]
)

埋め込みモデル:

Model: paraphrase-multilingual-MiniLM-L12-v2
Dim: 384
Language: 50+ languages
Performance: 
  - Multilingual Understanding
  - Fast inference (< 20ms @ batch size 8)

3.2.4 RRF + Entity Boosting

def reciprocal_rank_fusion_with_entity_boost(
    keyword_results: List[Dict],
    vector_results: List[Dict],
    entities: Dict[str, List[str]],
    k: int = 60
) -> List[str]:
    """
    RRF + 固有表現ブースト
    """
    fused_scores = {}

    # RRF スコアを計算
    for i, result in enumerate(keyword_results):
        doc_id = result["id"]
        rrf_score = 1.0 / (k + i + 1)
        fused_scores[doc_id] = fused_scores.get(doc_id, 0) + rrf_score

    for i, result in enumerate(vector_results):
        doc_id = result["id"]
        rrf_score = 1.0 / (k + i + 1)
        fused_scores[doc_id] = fused_scores.get(doc_id, 0) + rrf_score

    # 固有表現マッチによるブースト
    for doc_id, score in fused_scores.items():
        entity_boost = 1.0

        # ドキュメントの固有表現を確認
        doc_entities = extract_entities_from_doc(doc_id)

        for query_entity, entity_type in entities.items():
            if query_entity in doc_entities:
                boost_factor = ENTITY_BOOST_MAP.get(entity_type, 1.0)
                entity_boost *= boost_factor

        fused_scores[doc_id] = score * entity_boost

    # スコアでソート
    ranked = sorted(fused_scores.items(), key=lambda x: x[1], reverse=True)
    return [doc_id for doc_id, _ in ranked]

Entity Boost Factors:

Entity Type Boost 理由
PROJECT_ID 5.0 完全一致で最優先
PRODUCT 4.0 製品名は重要
PERSON 3.0 人名は中程度
ORG 2.5 部門は参考情報
DATE 2.0 日付は補足情報

3.5 EvoSpikeNet Core 記憶システム統合

RAG 2.0 パイプラインは EvoSpikeNet-Core SDK の記憶層と統合し、Memory-Augmented RAG を実現します。
クエリのたびに過去の検索経験を活用し、セマンティック知識でエンティティ認識を補完、フィードバックを reward として長期記憶に蓄積します。

3.5.1 Core SDK 記憶コンポーネント一覧

クラス / モジュール ファイル RAG での役割
SemanticMemoryEntry evospikenet/episodic_memory.py 概念・定義・関連概念を保持するデータクラス
EpisodicMemoryEntry evospikenet/episodic_memory.py 検索セッション(context/action/outcome/reward)を保持
EpisodicMemory evospikenet/episodic_memory.py SemanticMemory + EpisodicMemory の中核エンジン (nn.Module)
LongTermMemoryModule evospikenet/long_term_memory.py EpisodicMemory + SNN リザーバ + 忘却コントローラのオーケストレーター
ForgettingController evospikenet/forgetting_controller.py 低重要度エピソードの自動削除
/api/memory/episodic/store evospikenet/api_modules/memory_api.py 経験の REST 永続化
/api/memory/semantic/store evospikenet/api_modules/memory_api.py 概念の REST 永続化
/api/memory/episodic/retrieve evospikenet/api_modules/memory_api.py 過去経験の検索
/api/memory/semantic/retrieve evospikenet/api_modules/memory_api.py 概念グラフ検索
/api/memory/associate evospikenet/api_modules/memory_api.py エピソード ↔ セマンティック横断連想

3.5.2 統合フロー全体図

sequenceDiagram
    actor User
    participant RAG as RAG Pipeline
    participant SM as SemanticMemory
    participant EM as EpisodicMemory
    participant LTM as LongTermMemoryModule

    User->>RAG: クエリ送信

    RAG->>SM: retrieve_semantic_knowledge(extracted_entities, top_k=5)
    SM-->>RAG: [(concept, score), ...] 概念グラフ

    note over RAG: エンティティ Boost を概念スコアで動的強化

    RAG->>EM: integrate_episodic_semantic(query_context)
    EM-->>RAG: enhanced_context (semantic_concepts, semantic_knowledge)

    note over RAG: Elasticsearch / Milvus 二重検索 + RRF 融合

    RAG->>EM: retrieve_memories(query_context, top_k=3)
    EM-->>RAG: 関連する過去検索セッション

    note over RAG: 過去セッション結果で Reranking

    RAG->>User: レスポンス返却

    User->>RAG: フィードバック (rating: 1-5)

    RAG->>LTM: store_spike_episode(spike_seq, context, reward=rating/5)
    LTM->>EM: store_experience(importance=SNN_gate)
    note over LTM: RecurrentRetentionCircuit (GRU) が重要度を決定
    LTM-->>RAG: RetentionSummary(memory_id, importance)

3.5.3 SemanticMemory 統合仕様

目的

NER で抽出したエンティティを SemanticMemory の概念グラフで拡張し、Entity Boost 係数を動的に強化します。
また、起動時にドメイン語彙辞書(YAML)を事前ロードすることで、NER モデルが拾えない社内固有語を補完します。

統合ポイント: クエリ前処理フェーズ

flowchart LR
    ENT["抽出エンティティ\n{PROJECT_ID: ['EV-2024-001']}"]
    ENT --> SM_R["EpisodicMemory\n.retrieve_semantic_knowledge\n(concept, top_k=5)"]
    SM_R --> REL["関連概念 + スコア\n[('Q-PFC Loop', 0.82),\n ('AEG Phase4', 0.71),\n ('適応制御', 0.65)]"]
    REL --> BOOST["動的 Boost 係数\nbase_boost × (1 + score × 0.5)"]
    BOOST --> ES["Elasticsearch\nBoost 強化クエリ"]
    BOOST --> MV["Milvus\n検索補助クエリ"]

SemanticMemoryEntry ↔ RAG フィールドマッピング

フィールド RAG での対応データ
concept エンティティ名 "EV-2024-001"
definition プロジェクト/用語の概要 "EvoSpikeNet フェーズ4 Q-PFC制御統合"
related_concepts 関連プロジェクト・技術用語 ["Q-PFC Loop", "AEG-Comm", "Phase 4"]
confidence 概念の確信度 0.95
embedding 概念ベクター (384-dim MiniLM) 起動時 SentenceTransformer でエンコード
access_count 検索頻度 (キャッシュ優先度に利用) 自動カウント

ドメイン語彙ローダーと動的 Boost 強化

# config/domain_terms.yaml の形式
# EV-2024-001:
#   definition: "EvoSpikeNet フェーズ4 Q-PFC制御統合プロジェクト"
#   related: ["Q-PFC Loop", "AEG-Comm", "適応制御"]

class RAGSemanticLoader:
    """EvoSpikeNet ドメイン語彙を SemanticMemory に事前登録。"""

    def __init__(self, episodic_memory: EpisodicMemory):
        self.em = episodic_memory

    def load_domain_vocabulary(self, path: str) -> int:
        """YAML 辞書から SemanticMemory に概念を一括登録。起動時 1 回実行。"""
        import yaml
        with open(path) as f:
            terms = yaml.safe_load(f)
        for concept, meta in terms.items():
            self.em.add_semantic_concept(
                concept=concept,
                definition=meta["definition"],
                related_concepts=meta.get("related", []),
            )
        return len(terms)

    def enrich_entity_boost(
        self,
        entities: Dict[str, List[str]],
        base_boost_map: Dict[str, float],
    ) -> Dict[str, float]:
        """
        SemanticMemory の概念スコアで Entity Boost を動的強化。

        例: PROJECT_ID 基本 5.0 × (1 + 0.82 × 0.5) = 7.05

        Returns:
            entity_text → 強化後 boost 係数
        """
        enriched: Dict[str, float] = {}
        for entity_type, entity_list in entities.items():
            base = base_boost_map.get(entity_type, 1.0)
            for text in entity_list:
                knowledge = self.em.retrieve_semantic_knowledge(text, top_k=1)
                if knowledge:
                    _, conf = knowledge[0]
                    enriched[text] = base * (1.0 + conf * 0.5)
                else:
                    enriched[text] = base
        return enriched

性能要件:

処理 目標時間
retrieve_semantic_knowledge() < 10ms (< 10,000 概念)
起動時 load_domain_vocabulary() < 5s (< 5,000 用語)
enrich_entity_boost() < 15ms (エンティティ数 × 10ms)

3.5.4 EpisodicMemory 統合仕様

目的

検索セッションを EpisodicMemoryEntry として保存し、ユーザー評価 (rating) を reward として記録します。
蓄積された経験を用いて過去の類似セッションを参照し、Reranking に活用します。

EpisodicMemoryEntry ↔ RAG セッションマッピング

フィールド RAG での対応データ
id セッション ID "rag_20260520_001"
timestamp クエリ実行時刻 datetime.now()
context クエリ + メタデータ dict {"query": "EV-2024-001進捗", "language": "ja", "entities": {...}}
action 検索パラメータ {"top_k": 5, "expansion": True, "entity_boost": True}
outcome 取得ドキュメント ID リスト ["DOC_A", "DOC_B", "DOC_C"]
reward ユーザー評価 / 5.0 rating / 5.00.0 〜 1.0
importance SNN ゲーティング出力 LongTermMemoryModule が自動計算
embedding クエリ埋め込み (384-dim) MiniLM エンコード

統合シーケンス: 検索時のコンテキスト強化

flowchart TD
    QC["query_context\n{query, language, entities, normalized}"]
    QC --> IES["EpisodicMemory\n.integrate_episodic_semantic(query_context)"]
    IES --> EC["enhanced_context\n+ semantic_concepts: ['Q-PFC Loop']\n+ semantic_knowledge: ['Q-PCFはフェーズ4の...']"]
    EC --> RM["EpisodicMemory\n.retrieve_memories(query_context, top_k=3)"]
    RM --> PS["過去セッション\n[{doc_ids, reward=0.8, score=0.91}, ...]"]
    PS --> RR["Reranking\n過去高評価ドキュメントのスコアを加算"]
    EC --> RR
    RR --> FINAL["最終 Top-K\n記憶強化後ランキング"]

検索セッション記録アダプター

class RAGEpisodicRecorder:
    """RAG 検索セッションを EpisodicMemory に記録するアダプター。"""

    def __init__(self, episodic_memory: EpisodicMemory, encoder: SentenceTransformer):
        self.em = episodic_memory
        self.encoder = encoder

    def record_session(
        self,
        query: str,
        query_context: Dict[str, Any],
        search_params: Dict[str, Any],
        retrieved_doc_ids: List[str],
        user_rating: Optional[float] = None,
    ) -> str:
        """
        検索セッションをエピソード記憶に保存。

        Args:
            user_rating: ユーザー評価 1-5。None の場合は 0.5 (中立) を設定。

        Returns:
            memory_id: 保存した記憶の ID
        """
        query_embedding = torch.tensor(
            self.encoder.encode(query), dtype=torch.float32
        )
        reward = (user_rating / 5.0) if user_rating is not None else 0.5

        return self.em.store_experience(
            context={
                "query": query,
                "language": query_context.get("language"),
                "entities": query_context.get("entities", {}),
                "normalized_query": query_context.get("normalized"),
            },
            action=search_params,
            outcome=retrieved_doc_ids,
            reward=reward,
            context_embedding=query_embedding,
        )

    def retrieve_similar_sessions(
        self,
        query_context: Dict[str, Any],
        top_k: int = 3,
        min_importance: float = 0.3,
    ) -> List[Dict[str, Any]]:
        """
        現在のクエリに類似した過去の検索セッションを取得。
        Reranking 用の参照データとして利用する。
        """
        results = self.em.retrieve_memories(query_context, top_k=top_k)
        return [
            {
                "doc_ids": entry.outcome if isinstance(entry.outcome, list) else [],
                "reward": entry.reward,
                "score": score,
                "timestamp": entry.timestamp,
            }
            for entry, score in results
            if entry.importance >= min_importance
        ]

性能要件:

処理 目標時間
store_experience() < 20ms (非同期推奨)
retrieve_memories() < 30ms (< 10,000 エピソード)
integrate_episodic_semantic() < 15ms

3.5.5 LongTermMemoryModule 統合仕様

LongTermMemoryModuleEpisodicMemory + LargeScaleSpikeReservoir + RecurrentRetentionCircuit(GRU ベース)を統合し、
スパイキングニューラルネットワーク (SNN) による重要度ゲーティングで長期保持するセッションを自動選別します。

SNN 重要度ゲーティングフロー

flowchart TD
    SESSION["RAG 検索セッション\n(query, docs, reward)"]
    SESSION --> ENCODE["SentenceTransformer\nquery → 384-dim embedding"]
    ENCODE --> SPIKE["spike_sequence\n[T=1, N=384]"]
    SPIKE --> GRU["RecurrentRetentionCircuit\nGRU retention gate"]
    GRU --> GATE["retention_importance ∈ [0.05, 1.0]"]
    GATE --> HIGH{"importance\n>= 0.7?"}
    HIGH -->|"Yes: 統合"| LT_STORE["EpisodicMemory\n長期保持"]
    HIGH -->|"No: < 0.3"| FORGET["ForgettingController\n削除"]
    HIGH -->|"0.3 〜 0.7"| COMPRESS["optimize_compression()\n圧縮保存(30日超)"]
    GATE --> RESERVOIR["LargeScaleSpikeReservoir\nスパイク状態記録"]
    style GRU fill:#fff3e0,stroke:#FF9800
    style GATE fill:#fff3e0,stroke:#FF9800
    style FORGET fill:#fce4ec,stroke:#F44336
    style COMPRESS fill:#f3e5f5,stroke:#9C27B0

LongTermMemoryModule 統合アダプター

class RAGLongTermIntegrator:
    """
    RAG セッションを LongTermMemoryModule 経由で保存。
    SNN ゲーティングにより重要なセッションのみ長期保持する。
    """

    def __init__(self, ltm: LongTermMemoryModule, encoder: SentenceTransformer):
        self.ltm = ltm
        self.encoder = encoder

    def store_with_snn_gating(
        self,
        query: str,
        query_context: Dict[str, Any],
        retrieved_doc_ids: List[str],
        reward: float,
        semantic_tags: Optional[List[str]] = None,
    ) -> "RetentionSummary":
        """
        SNN 重要度ゲーティングを経由してセッションを長期記憶に保存。

        embedding → [T=1, N=384] の spike_sequence に変換して投入。
        SNN が重要度を計算し、threshold 以下は ForgettingController が削除。

        Returns:
            RetentionSummary: memory_id, spike_records, importance
        """
        embedding = self.encoder.encode(query)
        spike_sequence = torch.tensor(embedding, dtype=torch.float32).unsqueeze(0)

        return self.ltm.store_spike_episode(
            spike_sequence=spike_sequence,
            context=query_context,
            action={"retrieved_docs": retrieved_doc_ids},
            outcome=retrieved_doc_ids,
            reward=reward,
            semantic_tags=semantic_tags or [],
        )

忘却・圧縮ポリシー

パラメータ 推奨値 説明
consolidation_threshold 0.7 長期統合(完全保持)する重要度閾値
forget_threshold 0.3 削除する重要度閾値
compression_ratio 0.1 圧縮対象の割合(古い低重要度の 10%)
max_memories 10,000 最大保持エピソード数
compress_age_threshold_days 30 30 日超のエピソードを圧縮候補とする

3.5.6 Embedding 次元統一方針

コンポーネント 次元 モデル / 備考
Milvus ベクター検索 384 paraphrase-multilingual-MiniLM-L12-v2
EpisodicMemory(embedding_dim=) 384 デフォルト 512 から変更して統一
LongTermMemoryModule(state_dim=) 384 同上
SemanticMemoryEntry.embedding 384 semantic_encoder (384→192)
memory_api.py placeholder → 384 np.random(...768) を MiniLM に置換
# 推奨初期化設定
episodic_memory = EpisodicMemory(
    embedding_dim=384,          # MiniLM-L12-v2 に統一
    max_memories=10_000,
    consolidation_threshold=0.7,
    forget_threshold=0.3,
    device="cpu",
)

ltm = LongTermMemoryModule(
    state_dim=384,
    time_steps=1,
    embedding_dim=384,
    device="cpu",
)

注意: memory_api.pynp.random.default_rng(seed).random(768) はプレースホルダーです。
本番実装では SentenceTransformer("paraphrase-multilingual-MiniLM-L12-v2").encode(text) に置き換えてください。


3.5.7 RAGMemoryIntegrator — 統合ファサードクラス仕様

すべての記憶統合コンポーネントを一元管理するファサードです。
RAG パイプラインはこのクラスのみを使用し、内部の記憶層の詳細を隠蔽します。

classDiagram
    class RAGMemoryIntegrator {
        +EpisodicMemory em
        +LongTermMemoryModule ltm
        +SentenceTransformer encoder
        +RAGSemanticLoader semantic_loader
        +RAGEpisodicRecorder episodic_recorder
        +RAGLongTermIntegrator ltm_integrator
        +from_config(path) RAGMemoryIntegrator
        +enrich_query_context(context) Dict
        +record_search_session(session_id, query, ctx, params, docs)
        +record_feedback(session_id, rating) str
    }
    class RAGSemanticLoader {
        +load_domain_vocabulary(path) int
        +enrich_entity_boost(entities, base_map) Dict
    }
    class RAGEpisodicRecorder {
        +record_session(query, ctx, params, docs, rating) str
        +retrieve_similar_sessions(ctx, top_k) List
    }
    class RAGLongTermIntegrator {
        +store_with_snn_gating(query, ctx, docs, reward, tags) RetentionSummary
    }
    RAGMemoryIntegrator --> RAGSemanticLoader
    RAGMemoryIntegrator --> RAGEpisodicRecorder
    RAGMemoryIntegrator --> RAGLongTermIntegrator
    RAGMemoryIntegrator --> EpisodicMemory
    RAGMemoryIntegrator --> LongTermMemoryModule
class RAGMemoryIntegrator:
    """EvoSpikeNet Core 記憶システムと RAG を統合するファサード。"""

    @classmethod
    def from_config(cls, config_path: str) -> "RAGMemoryIntegrator":
        """
        memory_config.yaml から初期化。

        memory_config.yaml:
          embedding_dim: 384
          max_memories: 10000
          encoder_model: paraphrase-multilingual-MiniLM-L12-v2
          domain_dict_path: config/domain_terms.yaml
        """
        import yaml
        with open(config_path) as f:
            cfg = yaml.safe_load(f)
        em = EpisodicMemory(
            embedding_dim=cfg.get("embedding_dim", 384),
            max_memories=cfg.get("max_memories", 10_000),
        )
        ltm = LongTermMemoryModule(
            state_dim=cfg.get("embedding_dim", 384),
            time_steps=1,
            embedding_dim=cfg.get("embedding_dim", 384),
        )
        encoder = SentenceTransformer(
            cfg.get("encoder_model", "paraphrase-multilingual-MiniLM-L12-v2")
        )
        return cls(em, ltm, encoder, cfg.get("domain_dict_path"))

    def enrich_query_context(self, query_context: Dict[str, Any]) -> Dict[str, Any]:
        """
        クエリコンテキストを SemanticMemory + EpisodicMemory で強化。
        1. integrate_episodic_semantic() でセマンティック知識融合
        2. enrich_entity_boost() でエンティティ Boost を動的強化
        3. retrieve_similar_sessions() で過去類似セッション取得
        """
        enhanced = self.em.integrate_episodic_semantic(query_context)
        enhanced["enriched_boost"] = self.semantic_loader.enrich_entity_boost(
            query_context.get("entities", {}), ENTITY_BOOST_MAP
        )
        enhanced["past_sessions"] = self.episodic_recorder.retrieve_similar_sessions(
            query_context, top_k=3
        )
        return enhanced

    def record_search_session(
        self,
        session_id: str,
        query: str,
        query_context: Dict[str, Any],
        search_params: Dict[str, Any],
        retrieved_doc_ids: List[str],
    ) -> None:
        """検索セッションを一時記録(フィードバック待機)。"""
        self._pending_sessions[session_id] = {
            "query": query,
            "query_context": query_context,
            "search_params": search_params,
            "retrieved_doc_ids": retrieved_doc_ids,
        }

    def record_feedback(self, session_id: str, rating: int) -> Optional[str]:
        """
        ユーザーフィードバックを受け取り LongTermMemoryModule 経由で保存。

        Args:
            rating: 1〜5(5 が最高評価、reward = rating / 5.0 に変換)

        Returns:
            memory_id(セッション不明の場合は None)
        """
        session = self._pending_sessions.pop(session_id, None)
        if not session:
            return None
        summary = self.ltm_integrator.store_with_snn_gating(
            query=session["query"],
            query_context=session["query_context"],
            retrieved_doc_ids=session["retrieved_doc_ids"],
            reward=rating / 5.0,
            semantic_tags=list(session["query_context"].get("entities", {}).keys()),
        )
        return summary.memory_id

4. API 仕様

4.1 検索 API

Endpoint: POST /api/v2/rag/search

Request:

{
  "query": "プロジェクトEV-2024-001の進捗報告",
  "top_k": 5,
  "language": "auto",
  "enable_query_expansion": true,
  "enable_entity_boosting": true,
  "return_debug_info": false
}

Response:

{
  "status": "success",
  "query": "プロジェクトEV-2024-001の進捗報告",
  "language": "ja",
    "session_id": "rag_20260520_143022_001",
  "results": [
    {
      "id": "DOC_001",
      "rank": 1,
      "score": 0.8234,
            "memory_boost": 0.12,
            "source": "rrf+memory",
      "text": "プロジェクトEV-2024-001は...",
      "metadata": {
        "project_id": "EV-2024-001",
        "updated_at": "2026-05-20"
      }
    }
  ],
    "memory_context": {
        "semantic_concepts": ["Q-PFC Loop", "AEG Phase4"],
        "past_sessions_used": 2,
        "entity_boost_enriched": {"EV-2024-001": 7.05}
    },
  "execution_time_ms": 123,
  "debug_info": null
}

エラーレスポンス:

{
  "status": "error",
  "code": "INVALID_QUERY",
  "message": "Query is too short (min 2 characters)",
  "details": {}
}

4.2 フィードバック API

Endpoint: POST /api/v2/rag/feedback

Request:

{
    "session_id": "rag_20260520_143022_001",
  "query": "プロジェクトEV-2024-001の進捗報告",
  "doc_id": "DOC_001",
  "rating": 5,
  "comment": "期待通りの結果",
  "user_id": "user_123"
}

Response:

{
  "status": "success",
  "feedback_id": "fb_12345",
    "memory_id": "ep_20260520_143155",
    "importance": 0.72,
  "recorded_at": "2026-05-20T10:30:00Z"
}


4.3 記憶統合 API フロー

現行実装では、RAG v2 API は REST 経由の /api/memory/* 呼び出しではなく、RAGMemoryIntegrator を同一プロセス内で呼び出して記憶層と統合します。/api/memory/* は外部クライアント向けの管理 API として残し、RAG v2 の高速パスではインプロセス連携を優先します。

統合呼び出しシーケンス

sequenceDiagram
    participant CLI as RAG Client
    participant RAGAPI as POST /api/v2/rag/search
    participant INTEG as RAGMemoryIntegrator
    participant EM as EpisodicMemory
    participant LTM as LongTermMemoryModule

    CLI->>RAGAPI: POST /api/v2/rag/search\n{query, top_k, enable_entity_boosting}

    RAGAPI->>INTEG: enrich_query_context(query_context)
    INTEG->>EM: integrate_episodic_semantic()\nretrieve_memories(top_k=3)
    EM-->>INTEG: semantic_concepts + past_sessions
    INTEG-->>RAGAPI: {enriched_boost, past_sessions}

    note over RAGAPI: 拡張クエリごとに Elasticsearch + Milvus 検索\n→ RRF スコアを doc_id ごとに加算
    RAGAPI->>INTEG: compute_memory_boost(doc_id, past_sessions)
    INTEG-->>RAGAPI: memory_boost
    note over RAGAPI: final_score = rrf_score × entity_boost + memory_boost\nで再ソートして rank を確定

    RAGAPI-->>CLI: {results: [...], session_id: "rag_xxx", execution_time_ms: 180}

    CLI->>RAGAPI: POST /api/v2/rag/feedback\n{session_id, rating: 4}

    RAGAPI->>INTEG: record_feedback(session_id, rating)
    INTEG->>LTM: store_with_snn_gating(reward=rating/5)
    LTM-->>INTEG: RetentionSummary(memory_id, importance)
    INTEG-->>RAGAPI: {memory_id, importance}

    RAGAPI-->>CLI: {status: "success", feedback_id: "fb_xxx", memory_id, importance}

現行実装の統合マトリクス

RAG 処理フェーズ 実装呼び出し 目的
クエリ前処理(エンティティ拡張) RAGMemoryIntegrator.enrich_query_context() 概念グラフでエンティティ Boost 強化
検索結果 Reranking RAGEpisodicRecorder.retrieve_similar_sessions() 過去の類似セッションで結果補正
拡張クエリ統合 rag_v2_api 内の RRF 加算 同一 doc_id が複数拡張で出た場合に加点
記憶スコア反映 RAGMemoryIntegrator.compute_memory_boost() final_scorememory_boost を加算
フィードバック記録 RAGMemoryIntegrator.record_feedback() reward = rating/5 で長期記憶へ保存
ドメイン語彙更新 RAGSemanticLoader.load_domain_vocabulary() 新規概念・プロジェクトの追加
外部管理 API /api/memory/* REST クライアント向けの記憶管理

拡張レスポンス形式(v3.0 追加フィールド)

POST /api/v2/rag/search のレスポンスに記憶統合情報を追加します:

{
  "status": "success",
  "query": "プロジェクトEV-2024-001の進捗報告",
  "language": "ja",
  "session_id": "rag_20260520_143022_001",
  "results": [
    {
      "id": "DOC_001",
      "rank": 1,
      "score": 0.8234,
      "memory_boost": 0.12,
      "source": "rrf+memory",
      "text": "プロジェクトEV-2024-001は...",
      "metadata": {"project_id": "EV-2024-001"}
    }
  ],
  "memory_context": {
    "semantic_concepts": ["Q-PFC Loop", "AEG Phase4"],
    "past_sessions_used": 2,
    "entity_boost_enriched": {"EV-2024-001": 7.05}
  },
  "execution_time_ms": 187
}

新フィールド説明:

フィールド 説明
session_id string フィードバック紐付け用のセッション ID
results[].memory_boost float 記憶強化によるスコア加算値
results[].source string "rrf" / "rrf+memory"
memory_context.semantic_concepts List[str] SemanticMemory から取得した関連概念
memory_context.past_sessions_used int Reranking に使用した過去セッション数
memory_context.entity_boost_enriched Dict 動的強化後の Boost 係数マップ

フィードバック API(v3.0 拡張)

Endpoint: POST /api/v2/rag/feedback

{
  "session_id": "rag_20260520_143022_001",
  "query": "プロジェクトEV-2024-001の進捗報告",
  "doc_id": "DOC_001",
  "rating": 4,
  "comment": "期待通りの結果",
  "user_id": "user_123"
}

Response(v3.0):

{
  "status": "success",
  "feedback_id": "fb_12345",
  "memory_id": "epi_20260520_143155",
  "importance": 0.72,
  "recorded_at": "2026-05-20T14:31:55Z"
}

追加フィールド:

フィールド 説明
memory_id EpisodicMemory に保存した記憶の ID
importance SNN ゲーティングが計算した重要度 (0.05〜1.0)

5. 検索アルゴリズム

5.1 言語別の検索戦略

日本語クエリの場合(v3.0: 記憶強化フロー)

Query: "プロジェクトEV-2024-001の進捗報告"
       ↓
[Normalize] → "プロジェクトEV-2024-001の進捗報告"
       ↓
[Tokenize with Sudachi] → ["プロジェクト", "EV-2024-001", "進捗", "報告"]
       ↓
[Entity Extract] → {PROJECT_ID: ["EV-2024-001"]}
       ↓
[SemanticMemory.retrieve_semantic_knowledge("EV-2024-001", top_k=5)]
  → 関連概念: [("Q-PFC Loop", 0.82), ("AEG Phase4", 0.71), ("適応制御", 0.65)]
  → Boost 強化: PROJECT_ID = 5.0 × (1 + 0.82 × 0.5) = 7.05
       ↓
[EpisodicMemory.integrate_episodic_semantic(query_context)]
  → semantic_concepts: ["EV-2024-001", "Q-PFC Loop"]
  → semantic_knowledge: ["EV-2024-001 はフェーズ4のQ-PFC制御統合プロジェクト"]
       ↓
[Query Expansion (Optional)]
  - Dictionary + LLM: ["EV-2024-001進捗", "フェーズ4状況報告"]
       ↓
[Dual Search]
  - Elasticsearch: BM25 (Sudachi + 強化 Boost 7.05)
  - Milvus: L2 384-dim vector search
       ↓
[RRF Fusion + Entity Boost (7.05)] → ランキング
       ↓
[EpisodicMemory.retrieve_memories(query_context, top_k=3)]
  → 過去セッション: [{doc_ids:["DOC_A","DOC_B"], reward:0.8, score:0.91}, ...]
  → 過去高評価ドキュメントのスコアを +memory_boost で加算
       ↓
[Top-5 ドキュメント] → レスポンス生成
       ↓
[ユーザー評価: rating=4] → reward = 0.8
       ↓
[LongTermMemoryModule.store_spike_episode(spike_seq, context, reward=0.8)]
  → RecurrentRetentionCircuit (GRU) → importance = 0.72
  → EpisodicMemory.store_experience(importance=0.72)
       ↓
[長期記憶に保存: memory_id="epi_20260520_001"] (以後の類似クエリで活用)

英語クエリの場合

Query: "project report"
       ↓
[Language Detect] → "en"
       ↓
[Normalize] → No Japanese normalization
       ↓
[Tokenize] → ["project", "report"]
       ↓
[Entity Extract] → {} 
       ↓
[Dual Search with English Analyzer]
  - Elasticsearch: Standard analyzer (stop words, stemming)
  - Milvus: Same as above
       ↓
[RRF Fusion] → Merged ranking

5.2 Ranking Algorithm

Score Computation:

rrf_score(doc) = Σ 1 / (60 + rank_i)
base_score = rrf_score * entity_multiplier

Where:
    rank_i は BM25 / Vector / query expansion 経路ごとの順位
    entity_multiplier は SemanticMemory 強化後の最大 Boost 係数

詳細:

def compute_final_score(
    bm25_score: float,
    vector_score: float,
    entity_multiplier: float = 1.0,
    rrf_position: int = 0
) -> float:
    """
    基礎スコア = RRFスコア × 固有表現ブースト
    """
    # RRF スコア
    rrf_score = 1.0 / (60 + rrf_position + 1)

    # 固有表現ブースト
    final = rrf_score * entity_multiplier

    return final

5.3 記憶強化ランキング

v3.0 で追加されたランキング層。SemanticMemory と EpisodicMemory からの情報を RRF スコアに加算します。

スコア計算式(v3.0)

final_score = rrf_score * entity_boost_enriched + memory_boost
変数 説明 範囲
rrf_score RRF 融合スコア (k=60) 0 〜 1
entity_boost_enriched SemanticMemory 強化後の Boost 係数 1.0 〜 10.0
memory_boost 過去セッションからの補正スコア(内部で alpha=0.2 を適用済み) 0 〜 0.3

memory_boost 計算

def compute_memory_boost(
    doc_id: str,
    past_sessions: List[Dict[str, Any]],
    alpha: float = 0.2,
) -> float:
    """
    過去の類似セッションにおける高評価ドキュメントのスコアを加算。

    Args:
        doc_id: 対象ドキュメント ID
        past_sessions: retrieve_similar_sessions() の結果
        alpha: 記憶スコアの重み

    Returns:
        memory_boost: 加算スコア (0.0 〜 0.3)
    """
    boost = 0.0
    for session in past_sessions:
        if doc_id in session.get("doc_ids", []):
            # 過去の評価 (reward) × 類似度 (score) で加重
            boost += session["reward"] * session["score"] * alpha
    return min(boost, 0.3)  # 上限 0.3 でキャップ

記憶強化ランキング Mermaid フロー

flowchart TD
    RRF_SCORES["RRF スコア\n{DOC_A: 0.016, DOC_B: 0.015, DOC_C: 0.012}"]
    SM_BOOST["SemanticMemory\nenriched_boost\n{EV-2024-001: 7.05}"]
    EP_SESSIONS["EpisodicMemory\npast_sessions\n[{doc_ids:[DOC_A,DOC_B], reward:0.8, score:0.91}]"]

    RRF_SCORES --> APPLY_BOOST["entity_boost 適用\nDOC_A: 0.016 × 7.05 = 0.113"]
    SM_BOOST --> APPLY_BOOST

    APPLY_BOOST --> APPLY_MEM["memory_boost 加算\nDOC_A: 0.113 + (0.8×0.91×0.2) = 0.259\nDOC_B: 0.106 + (0.8×0.91×0.2) = 0.252"]
    EP_SESSIONS --> APPLY_MEM

    APPLY_MEM --> FINAL["最終ランキング\n1位: DOC_A (0.259)\n2位: DOC_B (0.252)\n3位: DOC_C (0.085)"]

6. 評価・テスト

6.1 テストケース

テストセット構成:

カテゴリ ケース数
表記揺れ 15 「盆休み」「お盆休み」
固有名詞 30+ 「EV-2024-001」「田中太郎」
合計 50+ -

6.2 評価メトリクス

Variation Cases

case: var_001
canonical_form: "盆休み"
variations: ["盆休み", "お盆休み", "盆期間"]
ground_truth_doc_id: "DOC_001"

評価:
- MRR = 1/rank (正解が出現した順位の逆数)
  - 1位: MRR = 1.0
  - 3位: MRR = 0.33
  - 見つからない: MRR = 0.0

目標: MRR_mean > 0.7

Entity Cases

case: ent_001
query: "プロジェクトEV-2024-001の進捗報告"
ground_truth_doc_ids: ["DOC_A", "DOC_B"]
should_not_match: ["DOC_C"]

評価:
- Recall = found_count / total_ground_truth
- Precision = found_count / returned_count
- F1 = 2 * (Precision * Recall) / (Precision + Recall)

目標: Recall > 0.8, Precision > 0.8, F1 > 0.75

6.3 自動評価スクリプト

# 表記揺れ評価
python backend/evaluate_robustness.py --variation-only

# 固有名詞評価
python backend/evaluate_robustness.py --entity-only

# 全体評価
python backend/evaluate_robustness.py --all

# 出力
$ csv: variation_results.csv, entity_results.csv
$ report: evaluation_report.txt

7. デプロイメント

7.1 環境要件

サーバー:

CPU: 8 cores (or equivalent)
Memory: 32GB (NER model + Milvus + Elasticsearch)
Storage: 500GB (index + backups)
GPU: Optional (CUDA 11.8+ for faster NER)

ソフトウェア:

Python: 3.10+
Elasticsearch: 8.0+
Milvus: 2.3+
Sudachi: Latest

7.2 インストール手順

# 1. 依存パッケージ
pip install -r requirements.txt

# 2. Elasticsearch 設定更新
curl -X PUT "localhost:9200/rag_kb_index_v2" \
  -H "Content-Type: application/json" \
  -d @elasticsearch_settings.json

# 3. Milvus インデックス再構築
python scripts/rebuild_milvus_index.py

# 4. テスト実行
python backend/evaluate_robustness.py --test-sample

# 5. デプロイ
docker build -t rag-v2:latest .
docker push registry/rag-v2:latest

7.3 ロールバック計画

もし問題が発生した場合:

1. トラフィック を Blue (v1) に切り替え
2. Green (v2) のログ確認
3. バグ修正 → 再テスト
4. 再デプロイ

ロールバック時間: 5 min

8. トラブルシューティング

Q1: MRR が 0.5 以下の場合

症状: 表記揺れに対応できていない

対策:

1. Sudachi の辞書が古い可能性
   → pip install --upgrade sudachi sudachipy_dict_small

2. 正規化ルールが不完全
   → elasticsearch_settings.json の char_filter を確認

3. ベクター検索の重みが低い
   → RRF の w_vector を 0.6 から 0.7 に上げる

Q2: Entity Recall が 80% 未満の場合

症状: 固有名詞が検出されない

対策:

1. NER モデルの精度確認
   → python -c "import tner; print(tner.__version__)"

2. Entity Boost が機能していない
   → Elasticsearch のマッピングを確認
   → entity_project フィールドが keyword 型か?

3. 固有表現辞書が不完全
   → internal_entities.yaml を追加

Q3: クエリが遅い場合(> 500ms)

症状: P95 レイテンシが SLA を超える

対策:

1. Query Expansion を無効化
   → ENABLE_EXPANSION = False

2. Milvus のプローブ数を減らす
   → nprobe: 10 → 5

3. Elasticsearch のクエリを単純化
   → entity_boosting のレイヤー数を減らす


9. 付録

9.1 依存関係一覧

RAG パイプライン依存:

sudachipy==0.7.5
sudachipy-dict-small==20240716
tner==1.8.0
elasticsearch==8.6.2
pymilvus==2.4.1
sentence-transformers==2.2.2
transformers==4.34.0
pandas==2.0.0
numpy==1.23.0
pyyaml==6.0
torch>=2.0.0

EvoSpikeNet Core SDK 依存(v3.0 追加):

evospikenet/episodic_memory.py     # EpisodicMemory, SemanticMemoryEntry
evospikenet/long_term_memory.py    # LongTermMemoryModule
evospikenet/forgetting_controller.py  # ForgettingController
evospikenet/snn_memory_extension.py   # LargeScaleSpikeReservoir
evospikenet/api_modules/memory_api.py  # REST API エンドポイント

設定ファイル(新規追加):

config/memory_config.yaml    # RAGMemoryIntegrator 設定
config/domain_terms.yaml     # ドメイン語彙辞書(SemanticMemory ロード用)

9.2 関連ドキュメント

Core SDK ソース参照: - evospikenet/episodic_memory.py - EpisodicMemory, SemanticMemoryEntry, EpisodicMemoryEntry - evospikenet/long_term_memory.py - LongTermMemoryModule, RetentionSummary - evospikenet/api_modules/memory_api.py - REST エンドポイント実装

9.3 参考リソース

9.4 FAQ

Q: Kuromoji から Sudachi への移行は直前にできるのか?
A: はい、Elasticsearch のアナライザ設定変更のみです。既存インデックスは新規インデックスへの移行を推奨(数時間)。

Q: Query Expansion は必須か?
A: オプションです。無効化すると +30ms 高速になります。

Q: NER の微調整は必要か?
A: Phase 1 では標準モデルで十分です。精度が 85% 未満の場合は Phase 7 で検討。


Document Version: 3.0
Last Updated: 2026-05-22
Approved By: RAG Development Team
Status: Implemented with Operational Hardening
v3.0 Changes: EvoSpikeNet Core 記憶システム(SemanticMemory / EpisodicMemory / LongTermMemoryModule)統合仕様を追加。Memory-Augmented RAG アーキテクチャへ移行。
2026-05-22 Update: RAG v2 の前処理ヘルス API、fail-closed 運用方針、QueryExpander 品質ガード、監視項目を反映。