「詳しい人に聞く」を、いつまで続けるのか
社内の専門資料――マニュアル、設計書、コマンド集――は、たいてい「詳しい人に聞く」運用になりがちです。属人化はわかっていても、ChatGPTのようなクラウドAIには機密資料をそのまま入れにくい。かといって「RAGを入れよう」と言っても、検索精度・コスト・運用がどうなるのか見えないまま止まってしまう。入門記事のデモと、実際に現場で動かせる仕組みの間には、地味だけれど大きなギャップがあります。
このギャップを埋めるために、社内文書を取り込んでローカルLLMで質問に答えるRAG基盤「KnowLocal」を作りました。資料本文もLLMへの質問も一切外部に出さず、社内LANだけで検索から回答まで完結します。今回はその実装と、検索精度をどう測って、どう上げたかを書きます。
まず結論: 検索精度は「測れば伸ばせる」
KnowLocalの核心は、RAGの5ステップのうち④「関連箇所を検索する」の精度です。実際に計測した数値がこちらです(743command.pdfというコマンド集・17問を対象にMRRで評価)。
| 構成 | MRR |
|---|---|
| ベクトル検索のみ | 0.146 |
| ハイブリッド検索+リランク+MMR(現行) | 0.618 |
「ベクトル検索さえ入れれば精度が上がる」というのは誤りで、ここまで上げるには複数の工夫を積み重ねる必要がありました。以下、実際にやったことを順を追って書きます。
※ この数値はあくまで1つの資料・17問での実測値です。資料の種類によって数値は変わるので、導入時は自部門の実データで評価セットを作って測ることをおすすめします。
RAGの5ステップ
KnowLocalの処理は、取り込み①〜③と検索④、回答⑤の5段階に分かれています。精度の勝負はほぼ④に集約されます。
① 資料を読む
対応形式はPDF / Word / Excel / PowerPoint / CSV / Markdown / テキスト。doc_embedding.pyが入口です。図表は後から--figures-onlyオプションで追加でき、既存のテキスト索引を壊さずに拡張できるようにしています。
② チャンク分割
chunk_size=500 / overlap=100。資料は長く、検索は「関係ある段落」単位で行う必要があります。区切りの優先順位は「空行 → 改行 → 。 → 、」で、文の途中では切りません。境界で文が切れてしまっても、前後100字を重ねることで情報の欠損を防いでいます。
③ 埋め込み・保存
埋め込みモデルはparaphrase-multilingual-MiniLM-L12-v2(日本語対応)。社内文書はPostgreSQL + pgvectorに、監視ログ(別プロダクトのAI Monitor Agentが出力するイベントログ)はFAISS + pickleに、と用途で使い分けています。取り込み後はAPIの再起動が不要(ホットリロード)なので、資料を追加しながら運用できます。
④ 関連箇所を検索する ― ここが本丸
意味検索(pgvector)とキーワード検索(BM25)をRRF(Reciprocal Rank Fusion)で統合し、BGE-reranker-v2-m3でリランクし、最後にMMRで重複を除いて根拠を選びます。
なぜベクトル検索だけでは足りないのか。実際に触ってわかったのは、コマンド名や型番が多い資料ではBM25(キーワード検索)がよく効くということです。「エラーコード E-204」のような固有名詞混じりの質問は、意味的な類似度よりも字面の一致の方が強い手がかりになります。一方で、言い換えや文脈依存の質問には意味検索が効きます。両方をRRFで統合してから、CrossEncoder方式のリランカーで並べ替え、MMRで似た候補ばかりが並ばないようにする――この3段構成にして、はじめてベクトル検索単体を大きく上回りました。
似たコマンドが並ぶマニュアルでは、正しいページの隣のページを答えてしまうことがあります。これに対しては、出典を複数表示する、「p.◯の手順は?」とページを指定できるようにする、章ごとに分割して取り込む、といった運用上の回避策を用意しています。
⑤ ローカルLLMで回答する
Ollamaにpullしたモデル(例: qwen3:8b、gemma3:12b)を自由に指定できます。実装上おもしろいのは、複数モデルに同じ根拠で回答させて、根拠整合性スコアが最も高いものを自動採用する仕組みにしていることです。CLIに--show-allを付ければ全モデルの回答も見比べられます(モデル数を増やすほど時間はかかります)。採用したモデル名・スコア・出典(資料名・ページ)は回答の末尾に必ず表示します。
全体アーキテクチャ
PDF/Word/Excel/PPT/CSV/MD/TXT
│
▼
doc_embedding.py(抽出・分割)
│
┌────┴────┐
▼ ▼
PostgreSQL figure_rag / ColPali
+pgvector (図・視覚索引・任意)
│
▼
ユーザーの質問
│
├─ 意味検索(pgvector)
└─ キーワード検索(BM25)
│
▼ RRFハイブリッド
▼ リランク(BGE-reranker-v2-m3)
▼ MMRで根拠選択
▼ Ollama(qwen3 / gemma3)
▼
根拠付き回答 → Open WebUI
外部クラウドへは、モデルの初回ダウンロードを除いて資料も質問も一切送信されません。監視ログ(AI Monitor Agentが出力するイベント)も同じ基盤で横断検索できるようにしてあります。
他ツールとどう違うのか
Dify・LangChain・LM Studioは競合というより、レイヤーが異なると捉えています。
| 観点 | KnowLocal | Dify | LangChain | LM Studio |
|---|---|---|---|---|
| 位置づけ | 社内RAG中核 | 運用プラットフォーム | 部品ライブラリ | LLM実行環境 |
| RAGパイプライン | ◎ 標準装備 | ○ 設定で構築 | △ 自分で組む | × なし |
| 検索精度 | ◎ 実測で改善 | △ 設定依存 | ○ 部品次第 | − |
| 外部非送信 | ◎ 設計前提 | △ セルフホスト | − 実装次第 | ◎ ローカル |
| 運用UI | △ WebUI連携 | ◎ 標準 | × 自前 | ○ チャット |
| Agent/拡張 | △ 固定系 | ◎ ワークフロー | ◎ 豊富 | × なし |
KnowLocalが勝つのは検索精度・説明可能性・ローカル完結。弱いのは非エンジニア向けUI・Agent機能・コネクタの幅です。ここで得た精度チューニングのノウハウは、Dify/LangChainに載せ替えても活かせるはずです。
制約は隠さずに書いておく
- 実務ではNVIDIA GPUが前提です(CPUのみでも動きますが実用速度ではありません)。VRAM目安は16GB(図検索とbest-of方式の同時実行を考えると)、小モデルのみなら8GBでも動きます。
- ロール(viewer / staff / admin)・文書ACL・監査ログは実装済みですが、**既定は
auth_required=false**です。部署横断で使う場合は認証を有効にし、SSO化など運用設計を詰める必要があります。 - Anthropicが提案しているContextual Retrieval(チャンクを埋め込む前にLLMで文脈を付ける方式)は採用していません。ローカルLLMではチャンクごとの前処理が取り込みを大幅に遅くし、回答生成用のGPUと取り合いになるためです。代わりにchunk_overlap + BM25ハイブリッド + リランクで精度を出す方針にしました。全件に前処理をかけるのではなく、精度が出ない資料だけ個別にPoCで判断する運用にしています。
動かし方
# Linux
docker compose up -d # pgvector
python3 -m venv .venv && pip install -r requirements.txt
ollama pull qwen3:8b gemma3:12b
uvicorn best_of_server:app --host 0.0.0.0 --port 8100
Windows環境でもDockerなしで動かせます(PostgreSQL+pgvectorをネイティブ導入、Ollama for Windows + 最新NVIDIAドライバ、CUDA版torchを先に入れてからpip install、という手順です。詳しくはリポジトリのMANUAL.md第8章を見てください)。
さいごに
「RAGを入れたい」と「実際に現場で運用できる精度が出るRAG」の間には、思っていたより距離がありました。ベクトル検索だけでは0.146、ハイブリッド+リランク+MMRまでやって0.618。この差は、検索の仕組みを1つずつ検証しながら積み上げないと出てきません。
導入検証は1部門・1機種を対象に4〜8週間ほどが目安です。決まらないうちは、既存のオンプレGPUで様子を見ながら続けても構わないと思っています。
ソースは以下に公開しています。興味があれば覗いてみてください。