コンテンツにスキップ

データベース概要

このプロジェクトでは文書をページ単位で分析します。各ページは独立したセグメントに分離され、AI分析を経て、その結果がベクトル埋め込みとして保存されます。この構造では、ベクトル検索だけでは解決できない問題があります。

100ページの設計図面があると仮定します。1つの図面が2ページにまたがっている場合、ベクトル検索は各ページを独立して扱います。「バルブV-101の仕様」を検索すると42ページは見つかりますが、続きの43ページは内容が異なるため検索から漏れる可能性があります。

ベクトル検索のみの場合:
検索: "バルブV-101 仕様"
→ 42ページ(V-101 図面前半) ✓ 検索された
→ 43ページ(V-101 図面後半) ✗ 内容が異なるため漏れ
→ 78ページ(V-101 メンテナンス記録) ✗ 類似度が低く漏れ

文書が数十件の場合は大きな問題ではありませんが、数百〜数千件の文書が蓄積されると、ベクトル類似度だけでは関連ページを漏れなく見つけることが困難になります。

グラフデータベースのみで検索する方式も検討しました。エンティティ(V-101、バルブ、メンテナンスなど)と関係を抽出し、エンティティの接続を辿ることで関連ページをすべて発見できます。

グラフのみの場合:
検索: "バルブV-101 仕様"
→ Entity "V-101" 探索
→ 42ページ(MENTIONED_IN) ✓
→ 43ページ(MENTIONED_IN) ✓
→ 78ページ(RELATES_TO → "メンテナンス" → MENTIONED_IN) ✓

接続されたページはよく見つかりますが、問題がありました:

  • セマンティック検索不可: 「バルブの仕様」のように正確なエンティティ名でない自然言語の質問に弱い
  • エンティティ抽出品質に依存: LLMがエンティティを見落とすとグラフに接続がない
  • FTSなし: グラフDBは全文検索をサポートしない

ベクトル + グラフの組み合わせ

Section titled “ベクトル + グラフの組み合わせ”

両方のアプローチの強みを組み合わせます:

組み合わせ検索:
[ステップ1] ベクトル検索(LanceDB)
検索: "バルブV-101 仕様"
→ 42ページ(score: 0.92) ✓
→ 15ページ(score: 0.71) ✓ ← セマンティック類似度で発見
[ステップ2] グラフ探索(Neptune)
起点: 42ページのQA ID → Entity "V-101" 探索
→ 43ページ(V-101 → MENTIONED_IN) ✓ ← 続きのページを発見
→ 78ページ(V-101 → RELATES_TO → "メンテナンス" → MENTIONED_IN) ✓
→ すでに見つかった42ページは除外(重複防止)

ベクトル検索が意味的に関連するページを見つけ、グラフ探索がエンティティの接続を辿ってベクトル検索が見逃したページを補完します。


エージェントはMCPツールを通じて2つのデータベースを順次使用します。

ユーザーの質問: "バルブV-101の仕様とメンテナンス履歴を教えて"
[1] MCP Search Tool(summarize)
│ → LanceDB hybrid_search(ベクトル + FTS)
│ → Haiku要約
│ → 結果: 42ページ、15ページ(qa_ids含む)
[2] MCP Graph Tool(graph_search)
│ → 入力: qa_ids(ベクトル検索で得たQA ID)
│ → Neptune: QA ID → Analysis → Entity → RELATES_TO → Entity → Analysis → Segment
│ → LanceDB: 発見されたセグメントの本文取得(get_by_segment_ids)
│ → Haiku要約
│ → 結果: 43ページ、78ページ(ベクトル検索になかったページ)
[3] エージェントが両方の結果を統合して最終回答を生成

2つのデータベースを接続するキーはEntityです。同じエンティティを共有するAnalysisノードを通じて関連ページを発見します。

  • QA ID{workflow_id}_{segment_index}_{qa_index}): 各文書セグメントごとの分析結果の識別子
  • LanceDB: QA分析結果をqa_idで保存、検索結果にqa_idを返却
  • Neptune: 同じqa_idをAnalysisノードのidとして使用、EntityがMENTIONED_INで接続
LanceDB (qa_id: wf_abc_0042_00)
↕ 同一ID
Neptune (Analysis {id: wf_abc_0042_00})
── MENTIONED_IN → Entity ("V-101", EQUIPMENT) ← MENTIONED_IN ── Analysis {id: wf_abc_0078_00}
↕ 同一ID
LanceDB (qa_id: wf_abc_0078_00)

同じエンティティ「V-101」が複数のAnalysisノードで言及されるため、エンティティを共有するだけで関連ページを発見できます。


LanceDB(ベクトルDB)Neptune(グラフDB)
保存対象QA分析テキスト + ベクトル埋め込みエンティティ、関係、文書構造
検索方式ハイブリッド(ベクトル + FTS)グラフ走査(openCypher)
強み自然言語の質問、意味的類似度エンティティ間の接続、関係探索
弱みページ間の接続認識不可セマンティック検索不可、FTSなし
検索順序ステップ1(起点)ステップ2(拡張)
ストレージS3 Express One ZoneNeptune Serverless(VPC)
コストモデルS3料金のみ(サーバーレス)NCUベース(min 1, max 2.5)

文書がアップロードされると、両方のデータベースに同時にデータが構築されます。

Step Functions Workflow
├─ Map(セグメント単位で並列、max 30)
│ ├─ SegmentAnalyzer: AI分析(Claude Sonnet 4.5)
│ └─ AnalysisFinalizer:
│ ├─ SQS → LanceDB Writer → LanceDB Service
│ │ → キーワード抽出(Kiwi)+ ベクトル埋め込み(Nova)+ 保存
│ └─ エンティティ/関係抽出(Strands Agent)→ S3に保存
├─ GraphBuilder:
│ └─ S3からエンティティ収集 → 重複排除 → GraphService → Neptuneに保存
└─ DocumentSummarizer: 文書要約生成

AnalysisFinalizerでベクトル埋め込みとエンティティ抽出がセグメント単位で並列実行されるため、大量文書も効率的に処理できます。GraphBuilderはMap完了後に実行され、全エンティティを収集して重複を排除した後、Neptuneに保存します。


  • Vector Database — LanceDB、S3 Express One Zone、Kiwi韓国語形態素分析、ハイブリッド検索
  • Graph Database — Neptune DB Serverless、openCypher、エンティティ抽出、グラフ探索
  • DynamoDB — One Table Design、ワークフロー状態管理、セグメントメタデータ