コンテンツにスキップ

グラフデータベース

本プロジェクトでは、グラフデータベースとして Amazon Neptune DB Serverless を使用しています。ドキュメント分析時に抽出・正規化されたコアエンティティをナレッジグラフに保存し、ベクトル検索だけでは実現が難しいエンティティ接続ベースのトラバーサルを可能にしています。

観点ベクトル検索 (LanceDB)グラフトラバーサル (Neptune)キーワードグラフ (LanceDB + Neptune)
検索方法セマンティック類似度エンティティ関係トラバーサルキーワード埋め込み類似度 + グラフトラバーサル
強み「類似コンテンツ」の発見検索結果から「関連コンテンツ」の発見コンセプトキーワードによるページの発見
入力ユーザークエリ検索結果のQA IDキーワード文字列
データcontent_combined + vector embeddingsコアエンティティノード + MENTIONED_IN エッジグラフキーワード (name + embedding) + Neptune エンティティ

これらの検索方法は、エージェントが Search MCP ツール を通じて併用します:

  • search___summarize — ドキュメントに対するハイブリッド検索
  • search___graph_traverse — 検索結果のQA IDからのグラフトラバーサル
  • search___graph_keyword — LanceDB グラフキーワードによるキーワード類似検索

Step Functions Workflow
→ Distributed Map (max 30 concurrency)
→ SegmentAnalyzer
→ Parallel:
+- AnalysisFinalizer (SQS → LanceDB)
+- PageDescriptionGenerator (Haiku)
'- EntityExtractor (Haiku) → S3 (graph_entities)
→ GraphBuilder Lambda:
1. Collect entities from all segments (S3)
2. Deduplicate (exact name match)
3. Normalize → Core entities (Sonnet via Strands structured output)
4. Store core entity names in LanceDB (add_graph_keywords)
5. Save work files to S3 (entities.json, analyses.json)
→ GraphBatchSender (Map) → GraphService Lambda (VPC) → Neptune

グラフトラバーサル (読み取りパス — 検索結果から)

Section titled “グラフトラバーサル (読み取りパス — 検索結果から)”
Agent → MCP Gateway → Search MCP Lambda (graph_traverse)
→ GraphService Lambda (VPC): search_graph (entity traversal from qa_ids)
→ LanceDB Service Lambda: Segment content retrieval
→ Bedrock Claude Haiku: Result summarization

キーワードグラフ検索 (読み取りパス — キーワードから)

Section titled “キーワードグラフ検索 (読み取りパス — キーワードから)”
Agent → MCP Gateway → Search MCP Lambda (graph_keyword)
→ LanceDB Service: search_graph_keywords (embedding similarity)
→ SHA256 hash entity names → Neptune entity ~id
→ GraphService Lambda (VPC): raw_query (find connected qa_ids)
→ LanceDB Service: get_by_qa_ids (content retrieval)
→ Bedrock Claude Haiku: Result summarization
Frontend → Backend API → GraphService Lambda (VPC)
→ get_entity_graph: Project-wide entity graph
→ get_document_graph: Document-level detailed graph

Neptune に保存されるノードとリレーションシップの構造です。クエリ言語として openCypher を使用しています。

ノード説明主要プロパティ
Documentドキュメントid, project_id, workflow_id, file_name, file_type
Segmentドキュメントのページ/セクションid, project_id, workflow_id, document_id, segment_index
AnalysisQA分析結果id, project_id, workflow_id, document_id, segment_index, qa_index, question
Entityコアエンティティ (正規化済み)id, project_id, name
リレーションシップ方向説明
BELONGS_TOSegment → Documentセグメントがドキュメントに所属
BELONGS_TOAnalysis → Segment分析がセグメントに所属
NEXTSegment → Segmentページ順序 (次のセグメント)
MENTIONED_INEntity → Analysisエンティティが特定のQAで言及 (confidence, context)
RELATED_TODocument → Document手動のドキュメント間リンク (reason, label)

Neptune はセカンダリインデックスをサポートしていないため、ノードの ~id プロパティが唯一のO(1)直接ルックアップ手段です。各ノードタイプのIDは意味のある複合キーとして設計されており、インデックスなしでも高速な検索が可能です。

ノードID形式
Document{document_id}doc_abc123
Segment{workflow_id}_{segment_index:04d}wf_abc123_0042
Analysis{workflow_id}_{segment_index:04d}_{qa_index:02d}wf_abc123_0042_00
EntitySHA256({project_id}:{name}) の先頭16文字a1b2c3d4e5f6g7h8
  • Segment/Analysis: ワークフローID + セグメントインデックス (+ QAインデックス) で構成されているため、IDだけで親子関係を推測可能
  • Entity: プロジェクトID + 正規化名のハッシュを使用するため、複数セグメントから抽出された同一エンティティが自然に単一ノードにマージ (MERGE) される
Document (report.pdf)
├── Segment (page 0) ──NEXT──→ Segment (page 1) ──NEXT──→ ...
│ └── Analysis (QA 0) ←──MENTIONED_IN── Entity ("Prototyping")
│ └── Analysis (QA 1) ←──MENTIONED_IN── Entity ("AWS")
└── Segment (page 1)
└── Analysis (QA 0) ←──MENTIONED_IN── Entity ("Prototyping")
└── Analysis (QA 0) ←──MENTIONED_IN── Entity ("Innovation Flywheel")

コアエンティティ「Prototyping」は、ページ0の「Prototype」とページ1の「AWS Prototyping」から正規化されたため、ページ0とページ1を接続しています。


項目
クラスター IDidp-v2-neptune
エンジンバージョン1.4.1.0
インスタンスクラスdb.serverless
キャパシティ最小: 1 NCU, 最大: 2.5 NCU
サブネットPrivate Isolated
認証IAM Auth (SigV4)
ポート8182
クエリ言語openCypher

Neptune と直接通信するゲートウェイ Lambda です。Neptune エンドポイントにアクセスするため、VPC (Private Isolated Subnet) 内にデプロイされています。

項目
関数名idp-v2-graph-service
ランタイムPython 3.14
タイムアウト5分
VPCPrivate Isolated Subnet
認証IAM SigV4 (neptune-db)

サポートされるアクション:

カテゴリアクション説明
書き込みadd_segment_linksDocument + Segment ノードの作成、BELONGS_TO/NEXT リレーションシップの作成
add_analysesAnalysis ノードの作成、Segment への BELONGS_TO
add_entitiesEntity ノードの MERGE、Analysis への MENTIONED_IN
link_documentsDocument 間の双方向 RELATED_TO の作成
unlink_documentsDocument 間の RELATED_TO の削除
delete_analysisAnalysis ノードの削除 + 孤立した Entity のクリーンアップ
delete_by_workflowワークフローの全グラフデータの削除
読み取りsearch_graphQA IDベースのグラフトラバーサル (Entity → MENTIONED_IN → 関連 Segment)
raw_query任意の openCypher クエリをパラメータ付きで実行
get_entity_graphプロジェクト全体のエンティティグラフクエリ (可視化用)
get_document_graphドキュメントレベルの詳細グラフクエリ (可視化用)
get_linked_documentsドキュメントリンク関係のクエリ

3. EntityExtractor Lambda (Step Functions)

Section titled “3. EntityExtractor Lambda (Step Functions)”

Distributed Map 内で AnalysisFinalizer および PageDescriptionGenerator と並列に実行されます。

項目
関数名idp-v2-entity-extractor
ランタイムPython 3.14
タイムアウト5分
モデルBedrock Haiku 4.5
出力構造化出力 (Pydantic モデル)
スタックWorkflowStack

機能:

  • 構造化出力を使用してAI分析結果からエンティティを抽出
  • S3に保存せずにエンティティを返すテストモード (mode: "test") をサポート (プロンプトチューニング用)
  • graph_entities をS3セグメントデータに保存

Distributed Map の完了後、DocumentSummarizer の前に実行されます。

項目
関数名idp-v2-graph-builder
ランタイムPython 3.14
タイムアウト15分
スタックWorkflowStack

処理フロー:

  1. Document + Segment 構造の作成 — Neptune にドキュメント/セグメントノードと BELONGS_TO, NEXT リレーションシップを作成
  2. S3からセグメント分析結果を読み込み — 全セグメントの分析データを収集
  3. Analysis ノードの作成 — QAペアごとに Analysis ノードをバッチ作成
  4. エンティティの収集 — EntityExtractor がセグメントごとに抽出済みの graph_entities を収集
  5. 重複排除 — 同一名のエンティティをマージ (大文字小文字を区別しない)
  6. 正規化 → コアエンティティ — LLMが関連エンティティを緩やかにグループ化 (表記揺れ、形態素変化、概念的包含)。1つのエンティティが複数のコアエンティティグループに所属可能。コアエンティティはメンバーの mentioned_in リストを吸収
  7. コアエンティティ名を LanceDB に保存 — ドキュメント横断キーワード検索のための add_graph_keywords
  8. 作業ファイルをS3に保存 — GraphBatchSender 用の entities.jsonanalyses.json

AIエージェントが使用するグラフ検索ツールで、Search MCP Lambda に統合されています。

項目
スタックMcpStack
ランタイムNode.js 22.x (ARM64)
タイムアウト5分

ツール:

MCP ツール説明
graph_traverse検索結果のQA IDを起点としてグラフをトラバーサルし、関連ページを発見
graph_keywordLanceDB でキーワード類似度によりコアエンティティを検索し、Neptune 経由で接続されたページを発見

graph_traverse フロー:

1. Receive qa_ids from search___summarize results
2. QA ID → Analysis node → MENTIONED_IN ← Entity node (all entities, no limit)
3. Entity → MENTIONED_IN → Other Analysis → Segment (single UNWIND query)
4. Exclude source segments, filter by document_id
5. Fetch segment content from LanceDB (get_by_segment_ids)
6. Summarize with Bedrock Claude Haiku
7. Filter sources to only Haiku-cited segments

graph_keyword フロー:

1. Receive keyword query
2. Search LanceDB graph_keywords by embedding similarity (top 3)
3. Hash matched entity names → Neptune entity ~id (SHA256)
4. Query Neptune: Entity → MENTIONED_IN → Analysis (get qa_ids)
5. Fetch content from LanceDB (get_by_qa_ids)
6. Summarize with Bedrock Claude Haiku

エンティティ抽出は EntityExtractor Lambda で実行され、AnalysisFinalizer および PageDescriptionGenerator と並列にセグメントごとに処理されます。Step Functions の Distributed Map 内で実行されるため、最大30セグメントが同時にエンティティを抽出します。

信頼性の高いJSONレスポンスを得るために、Strands Agent と Pydantic 構造化出力を使用しています。

項目
モデルBedrock Haiku 4.5
フレームワークStrands SDK (Agent + structured_output_model)
入力セグメントAI分析結果 + ページ説明
出力entities[] (Pydantic EntityExtractionResult)

全セグメントの処理完了後、GraphBuilder がLLMを使用してエンティティを正規化します:

項目
モデルBedrock Sonnet 4.6 (1M context)
フレームワークStrands SDK (Agent + structured_output_model)
入力重複排除済みの全エンティティ (コンテキスト付き) + 既存の LanceDB キーワード
出力コアエンティティグループ (NormalizationResult)

正規化ルール:

  • 積極的にグループ化 — 接続の見落としよりも過剰接続の方が望ましい
  • 表記揺れ (スペース、句読点、略語)
  • 形態素変化 (単数/複数、動詞/名詞形)
  • 概念的包含 (特定の用語がより広い概念を含む)
  • 言語間の変形
  • 1つのエンティティが複数のコアグループに所属可能
  • コアエンティティ名はメンバー名または広く知られた標準用語を使用
{
"entities": [
{
"name": "AWS Prototyping",
"mentioned_in": [
{
"segment_index": 1,
"qa_index": 0,
"context": "AWS prototyping program and methodology"
}
]
}
]
}
Input entities: Prototype (page 0), AWS Prototyping (page 1), AWS (page 0), Amazon Web Services (page 1)
Core entities:
- "Prototyping" → [Prototype, AWS Prototyping] → connected to pages 0, 1
- "AWS" → [AWS, Amazon Web Services, AWS Prototyping] → connected to pages 0, 1

// Neptune DB Serverless Cluster
const cluster = new neptune.CfnDBCluster(this, 'NeptuneCluster', {
dbClusterIdentifier: 'idp-v2-neptune',
engineVersion: '1.4.1.0',
iamAuthEnabled: true,
serverlessScalingConfiguration: {
minCapacity: 1,
maxCapacity: 2.5,
},
});
// Serverless Instance
const instance = new neptune.CfnDBInstance(this, 'NeptuneInstance', {
dbInstanceClass: 'db.serverless',
dbClusterIdentifier: cluster.dbClusterIdentifier!,
});
VPC (10.0.0.0/16)
└─ Private Isolated Subnet
├─ Neptune DB Serverless (port 8182)
└─ GraphService Lambda (SG: VPC CIDR → 8182 allowed)

GraphService Lambda のみが VPC 内にデプロイされています。GraphBuilder Lambda と Search MCP Lambda は VPC 外から Lambda invoke で GraphService を呼び出します。

キー説明
/idp-v2/neptune/cluster-endpointNeptune クラスターエンドポイント
/idp-v2/neptune/cluster-portNeptune クラスターポート
/idp-v2/neptune/cluster-resource-idNeptune クラスターリソース ID
/idp-v2/neptune/security-group-idNeptune セキュリティグループ ID
/idp-v2/graph-service/function-arnGraphService Lambda 関数 ARN

コンポーネント依存関係マップ

Section titled “コンポーネント依存関係マップ”
graph TB
    subgraph Build["Graph Build (Step Functions)"]
        EE["EntityExtractor<br/>(Entity Extraction)"]
        GB["GraphBuilder<br/>(Normalization + Core Entities)"]
    end

    subgraph Search["Graph Search (Agent)"]
        GT["graph_traverse<br/>(Search MCP)"]
        GK["graph_keyword<br/>(Search MCP)"]
    end

    subgraph Viz["Graph Visualization (Frontend)"]
        BE["Backend API"]
    end

    subgraph Core["Core Service (VPC)"]
        GS["GraphService Lambda"]
    end

    subgraph Storage["Storage Layer"]
        Neptune["Neptune DB Serverless"]
        LanceDB["LanceDB (graph_keywords)"]
    end

    EE -->|"S3<br/>(graph_entities)"| GB
    GB -->|"invoke<br/>(add_entities)"| GS
    GB -->|"invoke<br/>(add_graph_keywords)"| LanceDB
    GT -->|"invoke<br/>(search_graph)"| GS
    GK -->|"invoke<br/>(search_graph_keywords)"| LanceDB
    GK -->|"invoke<br/>(raw_query)"| GS
    BE -->|"invoke<br/>(get_entity_graph, get_document_graph)"| GS

    GS -->|"openCypher<br/>(IAM SigV4)"| Neptune

    style Storage fill:#fff3e0,stroke:#ff9900
    style Core fill:#e8f5e9,stroke:#2ea043
    style Build fill:#fce4ec,stroke:#e91e63
    style Search fill:#e3f2fd,stroke:#1976d2
    style Viz fill:#f3e5f5,stroke:#7b1fa2
コンポーネントスタックアクセスタイプ説明
GraphServiceWorkflowStack読み書きコア Neptune ゲートウェイ (VPC 内)
EntityExtractorWorkflowStack書き込み (S3)セグメントごとのエンティティ抽出 (並列)
GraphBuilderWorkflowStack書き込み (GraphService + LanceDB 経由)コアエンティティの正規化 + グラフ構築
graph_traverseMcpStack読み取り (GraphService + LanceDB 経由)検索結果からのエージェントグラフトラバーサル
graph_keywordMcpStack読み取り (LanceDB + GraphService 経由)キーワードベースのエージェントグラフ検索
Backend APIApplicationStack読み取り (GraphService 経由)フロントエンドグラフ可視化