EvoSpikeNet RAGシステム詳細解説
[!NOTE] 最新の実装状況は 機能実装ステータス (Remaining Functionality) を参照してください。
実装ノート(アーティファクト): トレーニングスクリプトが出力する
artifact_manifest.jsonと推奨CLIフラグについてはdocs/implementation/ARTIFACT_MANIFESTS.mdを参照してください。
作成日: 2025年12月10日 最終更新日: 2026年2月19日(RAGアップロード/バージョン管理実装同期)
Copyright: 2025 Moonlight Technologies Inc. All Rights Reserved.
Author: Masahiro Aoki
NOTE: 実装は
rag-system/に切り出され、ランタイムでは EvoSpikeNet ノードは RAG API 経由でのみやり取りします。 詳細と起動手順はrag-system/README.mdを参照してください。
このドキュメントの目的と使い方
- 目的: RAGシステムの処理フロー・技術仕様・実装箇所を一望し、開発/運用で参照できるようにする。
- 対象読者: RAG実装/運用担当、QA、PM。
- まず読む順: 目次 → RAGシステム概要 → 文書登録/検索フロー → 技術仕様。
- 関連リンク: 分散脳スクリプトは
examples/run_zenoh_distributed_brain.py、PFC/Zenoh/Executive詳細は implementation/PFC_ZENOH_EXECUTIVE.md。
目次
1. RAGシステム概要
EvoSpikeNetのRAG(Retrieval-Augmented Generation)システムは、ハイブリッド検索アーキテクチャを採用しています。これは、意味的類似性に基づくベクトル検索と、キーワードマッチングに基づく全文検索を並列実行し、結果をReciprocal Rank Fusion (RRF) アルゴリズムで統合することで、高精度な文書検索と生成を実現するシステムです。
1.1. 主要コンポーネント
| コンポーネント | 役割 | 技術スタック |
|---|---|---|
| Milvus | ベクトルデータベース(意味的検索) | ベクトル次元: 384次元、インデックス: IVF_FLAT |
| Elasticsearch | 全文検索エンジン(キーワード検索) | BM25アルゴリズム、kuromoji日本語トークナイザー |
| SentenceTransformer | テキストのベクトル化 | paraphrase-multilingual-MiniLM-L12-v2(多言語対応) |
| RRFアルゴリズム | 検索結果の統合 | k=60 |
| LLMバックエンド | テキスト生成 | HuggingFace / SNN / Standard LM |
1.2. データ構造
Milvusコレクション rag_kb のスキーマ:
{
"id": INT64 (Primary Key, auto_id=True),
"embedding": FLOAT_VECTOR (dim=384),
"text": VARCHAR (max_length=65535), # 最大65,535文字
"source": VARCHAR (max_length=255)
}
2. 文書登録処理フロー
文書をRAGシステムに登録する処理は、二重登録(Dual Indexing)戦略を採用しています。同じ文書を、ベクトル検索用のMilvusとキーワード検索用のElasticsearchの両方に登録することで、後続の検索時に両方の検索エンジンを活用できるようにします。
2.1. 処理フロー図
sequenceDiagram
participant U as "ユーザー/UI"
participant A as "add_user_text"
participant E as "Embedding Model"
participant M as "Milvus Collection"
participant ES as "Elasticsearch"
U->>+A: 文書テキスト, ソース情報
Note over A: 入力検証: 空文字チェック
A->>+E: テキストのエンコード
Note over E: SentenceTransformer: paraphrase-multilingual-MiniLM-L12-v2
E-->>-A: 384次元ベクトル
par Milvusへの登録
A->>+M: Insert Entity: embedding, text, source
Note over M: Auto-ID生成, IVF_FLATインデックス更新
M->>M: Flush: 永続化
M-->>-A: doc_id: Primary Key
and Elasticsearchへの登録
A->>+ES: Index Document: id=doc_id, content=text
Note over ES: BM25インデックス構築, Kuromoji Tokenization 日本語
ES-->>-A: 成功確認
end
A-->>-U: Added document with ID: doc_id
2.2. エラーハンドリング
- Milvus接続失敗:
_ensure_milvus_connection関数により、コンテナ起動時の遅延を考慮し、最大3回のリトライ(5秒間隔)が実行されます。
3. 検索処理フロー
検索処理は、並列ハイブリッド検索 + RRF統合 + LLM生成の3段階で構成されます。
3.1. 全体フロー図
graph TD
A[ユーザークエリ] --> B[クエリサニタイズ]
B --> C{並列実行}
C --> D1[ベクトル検索<br/>Milvus]
C --> D2[キーワード検索<br/>Elasticsearch]
D1 --> E1[Embedding化]
E1 --> E2[L2距離計算]
E2 --> F1[Top-K結果<br/>+ スコア]
D2 --> G1[Kuromoji<br/>トークナイズ]
G1 --> G2[BM25スコアリング]
G2 --> F2[Top-K結果<br/>+ スコア]
F1 --> H[RRF統合<br/>k=60]
F2 --> H
H --> I[統合ランキング]
I --> J[重複排除]
J --> K[上位文書取得]
K --> L[コンテキスト構築]
L --> M[プロンプト生成]
M --> N[LLM推論]
N --> O[幻覚抑制<br/>後処理]
O --> P[ユーザーへ返却]
3.2. 生成ステップ詳細
ステップ1: 言語検出とプロンプト選択
クエリの言語(日本語/英語)を検出し、それぞれに最適化されたプロンプトテンプレートを自動で選択します。これにより、言語に応じた自然な回答生成を促します。
ステップ2: コンテキスト構築と切り詰め
検索で得られた文書を連結し、\n---\nで区切った単一のテキストを構築します。LLMのコンテキストウィンドウを超えないよう、合計文字数を最大3000文字に制限する切り詰め処理が行われます。
ステップ3: Extractive-Firstモード
_extractive_answerメソッド(TF-IDFベース)を用いて、まず抽出型の回答を試みます。コンテキスト内の文章から直接的な答えが見つかった場合は、LLMによる生成を行わずにその結果を返すことで、幻覚のリスクを低減し、応答速度を向上させます。
ステップ4: LLM推論
抽出型で回答できなかった場合のみ、LLMバックエンド(HuggingFace, SNN等)を用いて回答を生成します。繰り返しを抑制するため、repetition_penalty=1.5などのパラメータが設定されます。
ステップ5: 後処理と幻覚抑制
生成された回答に対し、以下の後処理を適用して品質を向上させます。 - コンテキストとの語彙重複度チェック: 生成された回答と元のコンテキストの単語の重なりを計算します。重複度が25%未満の場合、コンテキストを無視して回答が生成された(幻覚)可能性が高いと判断し、抽出型回答へのフォールバックを試みます。 - 無関係なコンテンツのフィルタリング: URLや広告のような無関係なテキストが生成された場合、それを破棄して「情報不足」という回答を返します。
4. 技術仕様詳細
4.1. Milvus設定
- コレクションスキーマ:
id(PK),embedding(384次元),text(65535文字),source(255文字) - インデックス:
IVF_FLAT(クラスタ数nlist=128) - 類似度指標:
L2(ユークリッド距離) - 検索パラメータ:
nprobe=10(検索時に探索するクラスタ数)
ベクトル検索の数理
Milvusは、クエリベクトル \(\mathbf{q} \in \mathbb{R}^{384}\) に対して、コレクション内の全文書ベクトル \(\mathbf{d}_i\) との距離を計算します。
L2距離(ユークリッド距離):
小さいほど類似度が高いと判断され、Top-K文書が返されます。
IVF_FLATインデックスの仕組み:
- 文書ベクトルを \(K\) 個のクラスタに分割(デフォルト
nlist=128) - クエリ時、クエリベクトルに最も近い
nprobe=10個のクラスタを選択 - 選択されたクラスタ内の文書のみ詳細に距離計算(全探索より高速)
4.2. Embedding Model仕様
- モデル:
paraphrase-multilingual-MiniLM-L12-v2 - 出力次元: 384
- 対応言語: 100以上(日本語、英語含む)
- アーキテクチャ: BERT-based Transformer (12層)
- 学習目的: パラフレーズ検出(意味的類似文の識別)
テキストエンコード処理
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
embedding = model.encode(text) # shape: (384,)
内部では以下の処理が行われます:
- トークナイゼーション: サブワード単位への分割(BPEベース)
- Embeddings Layer: トークンIDを埋め込みベクトルに変換
- Transformer Encoder: 12層の自己注意機構で文脈を考慮
- Pooling: [CLS]トークンまたは平均プーリングで文全体の表現を生成
- 正規化: L2正規化により単位ベクトルに変換
4.3. Elasticsearch BM25スコアリング
ElasticsearchはBM25 (Best Matching 25) アルゴリズムを使用してキーワード検索を行います。
BM25スコア計算式
文書 \(d\) とクエリ \(q\) のBM25スコア:
ここで: - \(\text{TF}(t, d)\): 文書 \(d\) における用語 \(t\) の出現頻度 - \(\text{IDF}(t) = \log\left(\frac{N - n(t) + 0.5}{n(t) + 0.5}\right)\): 逆文書頻度(レア単語ほど高スコア) - \(N\): 総文書数、\(n(t)\): 用語 \(t\) を含む文書数 - \(|d|\): 文書の長さ(単語数) - \(\text{avgdl}\): 平均文書長 - \(k_1 = 1.2\): TF飽和パラメータ - \(b = 0.75\): 文書長正規化係数
4.4. RRFパラメータと統合アルゴリズム
Reciprocal Rank Fusion (RRF) は、異なる検索システムの結果を統合するアルゴリズムです。
RRFスコア計算式
文書 \(d\) のRRFスコア:
ここで: - \(R\): 検索システムの集合(本システムでは \(R = \{\text{Milvus}, \text{Elasticsearch}\}\)) - \(\text{rank}_r(d)\): システム \(r\) における文書 \(d\) のランク順位(1始まり) - \(k = 60\): ランクバイアス補正定数
RRF統合の例
| 文書ID | Milvusランク | ESランク | RRFスコア計算 | 総スコア |
|---|---|---|---|---|
| doc_1 | 1位 | 3位 | \(\frac{1}{60+1} + \frac{1}{60+3}\) | 0.0323 |
| doc_2 | 2位 | 1位 | \(\frac{1}{60+2} + \frac{1}{60+1}\) | 0.0323 |
| doc_3 | 3位 | 2位 | \(\frac{1}{60+3} + \frac{1}{60+2}\) | 0.0320 |
文書は最終的にRRFスコアの降順にソートされます。
実装コード (evospikenet/rag_milvus.py)
rrf_scores = {}
k = 60
# Milvus結果の処理
for rank, doc_id in enumerate(milvus_results, start=1):
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + 1 / (k + rank)
# Elasticsearch結果の処理
for rank, doc_id in enumerate(es_results, start=1):
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + 1 / (k + rank)
# スコア降順にソート
ranked_docs = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)
5. 実装コード解説
5.1. CRUD操作
evospikenet/rag_milvus.pyには、MilvusとElasticsearchの両データベースを一貫して操作するための以下のヘルパー関数が実装されています。
- add_user_text(): 文書の追加
- get_all_data(): 全文書の取得
- update_document(): 文書の更新
- delete_document(): 文書の削除
5.2. EvoRAGクラス
RAGシステムの主要なロジックをカプセル化したクラスです。
- __init__(): LLMバックエンド (huggingface, snn等) を選択し、Milvus/Elasticsearchクライアントを初期化します。
- retrieve(): ハイブリッド検索とRRF統合を実行します。
- generate(): 幻覚抑制ロジックを含むテキスト生成を実行します。
- rag(): retrieveとgenerateを組み合わせたエンドツーエンドのパイプラインを提供します。
6. 高度な機能
6.1. SNN連携とニューロン活動可視化
rag_with_vis()メソッドは、llm_type='snn'の場合にのみ利用可能な特殊機能です。
- 目的: RAGパイプラインの実行中に、内部のSNNモデルがどのように情報を処理しているかを可視化・分析する。
- 仕組み:
1. DataMonitorHookをSNNモデルの各レイヤーにアタッチ。
2. 通常のRAGパイプライン(プロンプトのトークン化、フォワードパス)を実行。
3. この過程で発生した全ニューロンのスパイク発火と膜電位の時系列データをキャプチャ。
4. 収集したデータをrag_neuron_data.ptというファイルにシリアライズして保存。
- 利用: 保存された.ptファイルは、examples/visualize_rag_neurons.pyなどのオフライン分析スクリプトで読み込み、発火ラスタープロットなどの詳細な可視化が可能です。
6.2. テスト容易性のためのモック実装
ユニットテストやCI/CD環境でMilvusのような外部サービスに依存しないように、EVOSPIKENET_TEST_MODE環境変数が設定されている場合にMilvusへの接続をモックする機能が組み込まれています。
MockCollectionクラス: MilvusのCollectionオブジェクトを模倣するインメモリのモッククラス。insertやsearchといった主要なメソッドをシミュレートします。_get_or_create_collection()の分岐: この関数内で環境変数をチェックし、テストモードであれば実際のMilvus接続の代わりにMockCollectionのインスタンスを返します。
この設計により、RAGシステムのロジックを外部サービスから独立して高速かつ安定的にテストすることが可能になっています。
6.3. RAGクエリ処理デバッグ機能 ⭐ NEW (2025年12月17日追加)
目的: RAGシステムの内部処理過程を可視化し、検索品質とLLM応答の透明性を向上させる。
デバッグ情報の内容
フロントエンドUIで「クエリ処理の詳細を表示」チェックボックスを有効にすると、以下の詳細情報が表示されます:
- クエリ分析:
- 検出された言語(日本語/英語)
- 抽出されたキーワード(形態素解析後)
-
使用されたプロンプトタイプ
-
ベクトル検索結果:
- 取得された文書ID
- 各文書のL2距離スコア
-
文書内容のプレビュー(最大200文字)
-
キーワード検索結果:
- 取得された文書ID
- 各文書のBM25スコア
-
文書内容のプレビュー(最大200文字)
-
RRF融合プロセス:
- 各検索結果のランキング
- RRFスコア計算過程(k=60)
-
最終的な統合ランキング
-
生成詳細:
- コンテキスト文字数
- プロンプト文字数
- 使用されたプロンプトテンプレートのプレビュー
- 応答タイプ(Extractive / Generative / Hallucination Fallback)
実装詳細
バックエンド (evospikenet/rag_milvus.py):
def retrieve(self, query, top_k=3, return_debug_info=False):
# ... 検索処理 ...
if return_debug_info:
debug_info = {
'vector_results': [
{
'doc_id': str(doc_id),
'score': float(score),
'text_preview': text[:200]
}
for doc_id, score, text in vector_results
],
'keyword_results': [...], # 同様の構造
'rrf_scores': dict(rrf_scores)
}
return docs, debug_info
フロントエンド (frontend/pages/rag.py):
@callback(
Output('rag-output', 'children'),
Input('rag-submit', 'n_clicks'),
State('show-debug-info', 'value'), # チェックボックス
prevent_initial_call=True
)
def handle_query(n_clicks, show_debug):
rag = EvoRAG(llm_type='huggingface')
result = rag.rag(query, return_debug_info=show_debug)
if show_debug and 'debug_info' in result:
# デバッグ情報の詳細なUIカードを生成
return create_debug_display(result)
6.4. Elasticsearchリインデックス機能 ⭐ NEW (2025年12月17日追加)
背景: MilvusとElasticsearchの両方に文書を登録する必要があるが、一方のみに登録されている場合、ハイブリッド検索が正常に機能しません。
解決策: Milvusに既存の全文書を取得し、Elasticsearchに一括インデックスするスクリプトを提供。
使用方法
python reindex_elasticsearch.py
実装 (reindex_elasticsearch.py):
<!-- from evospikenet.rag_milvus import _get_or_create_collection -->
<!-- TODO: update or remove - import flient import get_es_client -->
def reindex_all():
# Milvusから全文書を取得
collection = _get_or_create_collection()
results = collection.query(
expr="id >= 0",
output_fields=["id", "text"]
)
# Elasticsearchに一括インデックス
es_client = get_es_client()
for doc in results:
es_client.index(
index="rag_kb",
id=doc['id'],
document={'content': doc['text']},
refresh=True
)
検証スクリプト (test_rag_debug_keywords.py):
- ベクトル検索とキーワード検索の両方が正常に動作することを確認
- 各検索結果に文書プレビューが含まれることを検証
参考ファイル:
- evospikenet/rag_milvus.py: RAGシステムのコア実装、デバッグ機能統合
- evospikenet/elasticsearch_client.py: Elasticsearchクライアント
- evospikenet/rag_backends.py: LLMバックエンド統合
- frontend/pages/rag.py: 知識ベース管理UI、デバッグ表示機能
- reindex_elasticsearch.py: Elasticsearchリインデックススクリプト
- test_rag_debug_keywords.py: デバッグ機能検証テスト
7. 文書アップロード/パーサー実装状況(Plan E完了)
最終更新日: 2026年2月22日
ステータス: 実装完了・運用中(RAGファイルアップロード + バージョン付与パイプラインが本番稼働)
関連ドキュメント: REMAINING_FEATURES.md
7.1. 現行フロー
POST /upload_file(rag_api.py) がアップロードを受け取り、サーバー側で拡張子・MIMEを検証後、parse_document→chunk_text_auto→ 埋め込み生成 → Milvus/Elasticsearch 登録を実行。- バリデーション:
file_validator.pyのDEFAULT_ALLOWEDとDEFAULT_ALLOWED_MIMEで許可する拡張子/MIME を制御。許可拡張子は.txt,.md,.pdf,.docx,.doc,.xlsx,.xls,.pptx,.ppt,.gdoc,.html。MIME検出できない場合も拡張子チェックを通過させない。 - パース:
document_parsers.pyのParserRegistryが拡張子→パーサーを決定し、metadata["parser"]とmetadata["source_path"]を付与。 - チャンク:
chunk_text_auto(..., target_chunk_tokens=400)がチャンクIDとdoc_keyをメタデータに埋め込み、各チャンクをベクトル化して Milvus・Elasticsearch に登録。 - 応答:
rag_api.pyでchunks_indexed,document_ids,doc_key,versionを返却し、フロントエンドとSDKがそのまま利用。
7.2. サポート拡張子とパーサー
| 拡張子 | パーサー | 備考 |
|---|---|---|
| .txt | TextParser | UTF-8テキストをそのまま読み込み |
| .md | MarkdownParser | UTF-8で読み込み、metadata["parser"]="markdown" を付与 |
| .markdown | MarkdownParser | ParserRegistry対応済み。アップロード許可は validator 追加後に有効化(現状 .md のみ許可) |
| PdfParser | PyMuPDF必須。ページ数メタデータを付与し、PyMuPDF未導入時はフォールバック読み込み | |
| .doc/.docx | WordParser | python-docxで段落/表を抽出。ZIPフォールバック実装 |
| .xls/.xlsx | ExcelParser | openpyxlで全シートを走査し、sharedStringsフォールバックを持つ |
| .ppt/.pptx | PowerPointParser | python-pptxでスライド/表のテキストを抽出 |
| .gdoc/.html | GoogleDocsParser | エクスポート済みプレーンテキスト扱い |
7.3. バージョン管理とインデックス
doc_keyはファイル名の小文字化。Elasticsearch のget_latest_version(doc_key)で最新バージョンを取得し、次のversionを自動採番。elasticsearch_client.pyがdoc_key,version,chunk_id,source_filename,checksum,indexed_atを付与してチャンクごとに格納。- Milvus には
sourceとして<filename>#v<version>を保存するため、検索結果からバージョンが判別可能。 - SHA-1チェックサムを保持し、重複アップロード検知やトレースに利用できる。
7.4. クライアントとサンプル
- Pythonクライアント:
rag_client.pyのupload_file。 - SDK:
sdk.pyのrag_upload_file/rag_upload_file_async。 - サンプル:
rag_markdown_sdk.pyが Markdown をアップロードして検索する最小例。 - UI:
rag.pyが/upload_fileを呼び出し、アップロード結果とバージョンを表示。最新リリースではストリーミング/バックグラウンドチェックボックス、 セッションID入力およびジョブステータス照会機能が追加され、大容量ドキュメントの分割アップロードを対話的に行えるようになった。
7.5. 今後のフォローアップ
.markdown拡張子をアップロードバリデータに追加し、ParserRegistry設定と完全同期させる。- python-magic / PyMuPDF / python-docx / openpyxl / python-pptx が未導入の場合のセットアップ手順を FAQ に追記する。
- バージョン履歴のUI表示を Dash 側で拡充し、
doc_keyベースの履歴取得/ロールバックAPIを整理する(既存の versioned index を活用)。
7.6. 大容量ファイル対応 & 差分UI改善計画
目的
1GBを超える文書を安心してアップロード・検索できるようにし、ユーザーがバージョン間の差分を直感的に把握できるUIを提供する。
7.6.1 大容量ファイル対応
課題 - メモリ消費が増大し、アップロード時にサーバーがOOMになる - 解析/チャンク化が同期処理で時間を要しタイムアウトする - Elasticsearch/Milvusへのインデックス負荷
計画
1. ストリーミングパース/チャンク化エンジン
- parse_document と chunk_text_auto をストリーミング対応版に拡張し、
PDF/Word/Excel/PPT 各パーサは逐次処理APIを利用してテキストを逐一出力。
- トークン化はジェネレータパターンを導入し、全文保持を回避。
2. 分割アップロードAPI
- フロントエンド/SDK に multipart アップロードを追加。クライアントが
100MB 等に分割して送信し、サーバー側で再結合せずに各パートを逐次解析。
3. バックグラウンドジョブ化
- FastAPI の BackgroundTasks または Celery/RQ で処理を非同期化し、
ジョブIDを返却して進捗ポーリング/通知を行う。
- ジョブメタデータに start_time, end_time, progress、完了時は
doc_key/version を付与し /upload_status で取得可能。
- Redis をバックエンドに利用した場合、ワーカーは進捗を
upload:channel:<job_id> へパブリッシュし、メインプロセスは
そのチャネルを購読して即時通知が可能。WebSocket(/ws/progress/{job_id})
で購読するサンプルも用意。
- フロントエンドUIではジョブID入力後に自動ポーリングする
dcc.Interval が導入され、発生する progress 値をバープログレス
表示できる。
4. リソース制限と監査
- Kubernetes/Cgroup によるメモリ制限、ログにサイズ・処理時間を記録。
5. パフォーマンステスト
- 1GB/5GB ダミーファイルで連続アップロードベンチを行い、
時間・メモリ・CPU 使用量を測定。
7.6.2 差分表示UIの改善
課題 - テキスト差分のみ、画像や表の変更がわかりづらい - 大きな文書でスクロールが重い - 比較ナビゲーションが煩雑
計画
1. リッチ差分コンポーネント
- Markdown/HTML差分を react-diff-viewer などで強化し、
見出し折りたたみ・語句ハイライト・色分けを追加。
- 表差分はセル背景色で変化を表示、画像差分はスライダー。
2. 差分サマリページ
- 変更行数・章統計を自動生成しワンクリックジャンプを実現。
3. 事前計算とキャッシュ
- バージョン登録時に差分パッチを生成・保存し、UIは必要時取得。
4. 大規模ドキュメント対策
- 仮想スクロール (react-virtualized) と page/section 分割で読み込み軽量化。
5. ユーザーテストとフィードバック
- 社内ワークショップでUX評価し、課題をスプリントに反映。
スケジュール(見積)
- 2026/2–3: ストリーミングパーサ試作 & 分割アップロード設計
- 2026/3–4: バックグラウンドジョブ化とパフォーマンステスト
- 2026/4–5: 差分UIプロトタイプ開発とレビュー
- 2026/5: ドキュメント更新・デプロイ手順整備
リスクと緩和策
- パースバグ:ライブラリ固定とCIテストで早期検出
- 分割アップロード失敗:再送・チェックサム検証を実装
- UIパフォーマンス:Lazy loading/仮想化でスクロール遅延回避
この計画により、大容量文書対応と差分閲覧体験が飛躍的に向上する見込みである。
8. まとめ
本ドキュメントでは、EvoSpikeNet の RAG システムについて、ハイブリッド検索の実装からファイルアップロード・バージョン管理までを整理しました。
現在の強み:
- ✅ ハイブリッド検索(Milvus + Elasticsearch)と RRF による高精度検索
- ✅ 多言語対応(日本語・英語)と Extractive-first での幻覚抑制
- ✅ /upload_file によるファイル取り込みとバージョニング付きインデックス
- ✅ SDK/フロントエンド連携サンプル(Markdownアップロード例を含む)
フォローアップ:
- 📅 .markdown のバリデータ解禁と依存ライブラリ導入手順の明文化
- 📅 バージョン履歴 UI の改善とロールバック運用手順の追記
関連ドキュメント:
- REMAINING_FEATURES.md - Plan E 状況とバックログ
- rag-system/README.md - RAG サービス単体の起動・API手順
- docs/SDK_API_REFERENCE.md - SDK からの RAG 利用方法