RAGパイプライン設計

Ingestion フロー(ドキュメント登録時)

1. ファイル受付・安全性チェック
   python-magic でバイナリシグネチャを検証(拡張子偽装対策)
   PDF → PyMuPDF(fitz)※JavaScript実行なし設定
   Word (.docx) → python-docx
   Excel (.xlsx) → openpyxl(シート・セルをテキスト化)
   └→ テキスト抽出

2. ingestion_status を 'processing' に更新

3. チャンク分割(LlamaIndex SentenceSplitter)
   chunk_size = 512 tokens(日本語では約300〜400文字相当に注意)
   chunk_overlap = 50 tokens
   paragraph_separator = "\n\n"(条文の段落境界を尊重)

4. Embedding生成(sentence-transformers)
   モデル: intfloat/multilingual-e5-large(モデルバージョン固定・revisionをconfigに記載)
   ※ 必ず "passage: " prefix を付与してからEmbedding
   例: f"passage: {chunk.text}"

5. ChromaDB登録(単一コレクション: company_documents)
   メタデータ: {document_id, version_id, category_id, chunk_index, title}
   カテゴリ絞り込みは where={"category_id": "xxx"} フィルタで実現

6. PostgreSQL tsvector 更新
   SudachiPy でトークナイズ → content_tsv カラムに格納
   GIN インデックスにより高速全文検索が可能

7. ingestion_status を 'completed'(または'failed' + error_message)に更新

※ Ingestion中にチャットされた場合は旧バージョンのインデックスで回答(整合性より可用性優先)
※ 失敗時は管理画面から「再実行」ボタンで手動リトライ可能

Retrieval フロー(チャット時)

1. 質問をEmbedding(同モデルで)
   ※ 必ず "query: " prefix を付与してからEmbedding
   例: f"query: {user_question}"

2. Hybrid Search(並列実行)
   ├ Vector Search: ChromaDB(top_k=10、カテゴリフィルタ適用)
   └ PostgreSQL全文検索: tsvector + SudachiPyトークナイズ(top_k=10)
   └→ スコア統合:Reciprocal Rank Fusion(RRF)

3. Reranker(上位10 → 3件)
   モデル: hotchpotch/japanese-reranker-cross-encoder-large-v1(日本語対応・ローカル実行)
   ※ cross-encoder/ms-marco-MiniLM-L-6-v2は英語専用のため使用禁止

4. Prompt構築
   システムプロンプト + 検索結果3件 + ユーザー質問

5. Ollama呼び出し
   モデル: gemma3:27b(推奨)or gemma3:12b(速度優先)
   └→ テキスト回答 + Mermaid図(ユーザーがUIでON/OFFを制御可能)

プロンプト設計(チャットAPI)

システムプロンプト:
あなたは社内規約に関する質問に答えるアシスタントです。
以下の【参考文書】のみを根拠として回答してください。
参考文書に記載がない場合は「この内容は規約に記載されていません」と答えてください。

{mermaid_instruction}  ← UIのトグルに応じて動的に挿入

【参考文書】
{context}

---

【ユーザーの質問】(500文字以内)
{question}

---mermaid_instruction(Mermaid ONの場合)---
手順や流れを説明する場合は、回答の末尾に以下のMermaid記法でフロー図を出力してください。
```mermaid
flowchart TD
    ...
```
Mermaid図が不要な場合は出力しないでください。

---mermaid_instruction(Mermaid OFFの場合)---
(空文字列 → Mermaid図を出力しない)

プロンプトインジェクション対策:

  • ユーザー入力は500文字上限
  • ユーザー入力部分を --- で明確に区切る
  • 質問内容をすべてロギングし、異常パターンを検知可能にする

レスポンス形式

{
  "text": "育児休業は最大1年間取得できます。...",
  "mermaid": "flowchart TD\n  A[申請書記入] --> B[上長承認] --> C[人事部提出]",
  "sources": [
    {
      "document_id": "uuid",
      "title": "就業規則",
      "version_no": 3,
      "chunk_index": 12
    }
  ],
  "session_id": "uuid"
}

Mermaid構文チェック(バックエンド):

  • re.search(r'```mermaid\n(.+?)\n```', response, re.DOTALL) で抽出
  • 許可リスト(flowchart, sequenceDiagram, gantt 等)に含まれる図タイプのみ通過
  • 構文エラーまたは許可リスト外の場合は mermaid: null として返却

フロントエンド設計

画面一覧

管理者向け

  1. ログイン画面 /login
  2. パスワード変更画面 /change-passwordmust_change_password=true 時に強制リダイレクト)
  3. ダッシュボード /admin — 登録ドキュメント数・カテゴリ統計・Ingestion失敗アラート
  4. ドキュメント一覧 /admin/documents — 検索・フィルタ・Ingestionステータス表示
  5. ドキュメント詳細・編集 /admin/documents/:id — バージョン管理・再Ingestionボタン付き
  6. カテゴリ管理 /admin/categories
  7. ユーザー管理 /admin/users

社員向け

  1. チャット画面 /chat — メッセージ入力・Mermaid図表示・カテゴリフィルタ・Mermaidトグル

JWT認証フロー(HttpOnly Cookie方式)

ログイン:
POST /auth/login → サーバーが Set-Cookie: access_token=...; HttpOnly; SameSite=Strict
                                   Set-Cookie: refresh_token=...; HttpOnly; SameSite=Strict

APIリクエスト:
axios: { withCredentials: true } で自動的にCookieを送信
フロントエンドはトークン文字列を保持しない(LocalStorage/Zustand保存禁止)

ユーザー情報管理:
GET /auth/me のレスポンス(role, name, must_change_password)のみZustandで保持

401発生時:
POST /auth/refresh → 新しいAccess TokenをCookieにセット → 元のリクエストをリトライ

Mermaid.jsセキュリティ設定(必須)

import mermaid from 'mermaid';
import DOMPurify from 'dompurify';

mermaid.initialize({
  startOnLoad: false,
  securityLevel: 'strict',  // SVGへのスクリプト埋め込みを禁止
});

// レンダリング前にサニタイズ
const sanitized = DOMPurify.sanitize(mermaidCode, {
  ALLOWED_TAGS: [],  // テキストのみ許可(SVG生成はmermaid.jsに任せる)
});

主要コンポーネント

  • ChatWindow — メッセージ一覧表示・自動スクロール
  • MessageBubble — テキスト(react-markdown) + Mermaid図(サニタイズ済み)レンダリング
  • MermaidToggle — Mermaid図出力ON/OFFのUIトグル
  • DocumentUploadForm — ドキュメント登録(Drag&Drop対応)
  • VersionHistoryTable — バージョン履歴・復元ボタン・Ingestionステータスバッジ
  • IngestionStatusBadge — pending/processing/completed/failed の視覚表示
  • CategoryFilter — チャット画面でのカテゴリ絞り込み(複数選択)
  • SourceAccordion — 引用元ドキュメントの折りたたみ表示

状態管理

  • Zustand — ユーザー情報(role, name, must_change_password)のみ。JWTトークンは保持しない
  • React Query (TanStack Query) — APIデータフェッチ・キャッシュ
  • axioswithCredentials: true を基本設定。401時のRefreshトークンリトライインターセプター