コンテンツにスキップ

Vector Database

このプロジェクトでは、Amazon OpenSearch Serviceの代わりにLanceDBをベクトルデータベースとして使用しています。LanceDBはオープンソースのサーバーレスベクトルデータベースで、データをS3に直接保存し、専用クラスターインフラが不要です。LinderaICU4Xベースの多言語トークナイザーと組み合わせることで、多言語文書に対するハイブリッド検索(ベクトル+全文検索)を実現しています。

言語セマンティック検索(ベクトル)全文検索(FTS)検索モード
韓国語OOハイブリッド(ベクトル + FTS)
日本語OOハイブリッド(ベクトル + FTS)
中国語OOハイブリッド(ベクトル + FTS)
英語およびその他の言語OOハイブリッド(ベクトル + FTS)

LinderaはCJK言語(韓国語、日本語、中国語)に対して辞書ベースのトークン化を提供し、ICU4Xはその他の言語に対してUnicodeワードセグメンテーションを処理します。これにより、すべてのサポート言語で正確なFTSキーワード抽出が可能です。

このプロジェクトはPoC/プロトタイプであり、コスト効率が重要な要素です。

項目OpenSearch ServiceLanceDB (S3)
インフラ専用クラスター(最低2〜3ノード)クラスター不要(サーバーレス)
アイドルコスト未使用時でも課金S3ストレージコストのみ
設定の複雑さドメイン構成、VPC、アクセスポリシーS3バケット + DynamoDBロックテーブル
スケーリングノードスケーリングが必要S3と共に自動拡張
推定月額コスト(PoC)$200〜500+(t3.medium x2最低)$1〜10(S3 + DDBオンデマンド)

書き込みパス:
Analysis Finalizer → SQS (Write Queue) → LanceDB Writer Lambda
→ LanceDB Service Lambda (Rust)
├─ Toka Lambda (Rust): キーワード抽出 (Lindera / ICU4X)
├─ Bedrock Nova: ベクトル埋め込み (1024d)
└─ LanceDB: S3 Express One Zoneに保存
読み取りパス:
MCP Search Tool Lambda
→ LanceDB Service Lambda (Rust): ハイブリッド検索 (ベクトル + FTS)
→ Bedrock Claude Haiku: 検索結果の要約
削除パス:
Backend API(プロジェクト削除)
→ LanceDB Service Lambda: drop_table
S3 Express One Zone (Directory Bucket)
└─ idp-v2/
├─ {project_id_1}/ ← プロジェクトごとに1つのLanceDBテーブル
│ ├─ data/
│ └─ indices/
└─ {project_id_2}/
├─ data/
└─ indices/
DynamoDB (Lock Table)
PK: base_uri | SK: version
└─ LanceDBテーブルの同時アクセス管理

ベクトルDBのコアサービスです。Rustで実装され(cargo-lambda-cdk)、メモリ使用量とコールドスタートが最適化されています。

項目
関数名idp-v2-lance-service
ランタイムRust (cargo-lambda-cdk)
アーキテクチャARM64
メモリ1024 MB
タイムアウト5分

サポートアクション:

アクション説明
add_recordQAレコード追加(キーワード抽出 + 埋め込み + 保存)
delete_recordQA IDまたはセグメントIDで削除
get_segments_by_document_idドキュメントの全セグメント取得
get_by_segment_idsセグメントIDリストで本文取得(Graph MCPで使用)
hybrid_searchハイブリッド検索(ベクトル + FTS、query_type='hybrid'
list_tables全プロジェクトテーブル一覧
countプロジェクトテーブルのレコード数取得
delete_by_workflowワークフローIDで全レコード削除
drop_tableプロジェクトテーブル全体を削除

Rust Lambdaを使用する理由:

Rustは従来のDocker Python Lambdaと比較してメモリ使用量が大幅に削減され、コールドスタートが高速です。Scale-to-zeroが可能なサーバーレスベクトルDBサービスにとって重要な特性です。

LanceDB ServiceがFTSキーワード抽出に使用する多言語トークナイザーサービスです。

項目
関数名idp-v2-toka
ランタイムRust (cargo-lambda-cdk)
アーキテクチャARM64
メモリ1024 MB
トークナイザーLindera(CJK辞書ベース)、ICU4X(Unicodeワードセグメンテーション)

分析パイプラインからの書き込みリクエストを受信し、LanceDB Serviceに委任するSQSコンシューマーです。

項目
関数名idp-v2-lancedb-writer
ランタイムPython 3.14
メモリ256 MB
タイムアウト5分
トリガーSQS(idp-v2-lancedb-write-queue
同時実行数1(順次処理)

同時実行数を1に設定し、LanceDBテーブルへの同時書き込み競合を防止しています。

AIチャット中にエージェントが文書を検索する際、LanceDB Service Lambdaを直接呼び出すMCPツールです。

ユーザークエリ → Bedrock Agent Core → MCP Gateway
→ Search Tool Lambda → LanceDB Service Lambda (hybrid_search)
→ Bedrock Claude Haiku: 検索結果の要約 → レスポンス
項目
スタックMcpStack
ランタイムNode.js 22.x (ARM64)
タイムアウト30秒
環境変数LANCEDB_FUNCTION_ARN(SSM経由)

各QA分析結果がレコードとして保存されます。1つのセグメント(ページ)に複数のQAが存在できるため、QA単位でレコードが作成されます:

class DocumentRecord(LanceModel):
workflow_id: str # ワークフローID
document_id: str # 文書ID
segment_id: str # "{workflow_id}_{segment_index:04d}"
qa_id: str # "{workflow_id}_{segment_index:04d}_{qa_index:02d}"
segment_index: int # セグメントページ/チャプター番号
qa_index: int # QA番号(0から)
question: str # AIが生成した質問
content: str # content_combined(埋め込みソースフィールド)
vector: Vector(1024) # Bedrock Nova埋め込み(ベクトルフィールド)
keywords: str # トークン化キーワード(FTSインデックス)
file_uri: str # 元ファイルS3 URI
file_type: str # MIMEタイプ
image_uri: Optional[str] # セグメント画像S3 URI
created_at: datetime # タイムスタンプ
  • プロジェクトごとに1テーブル: テーブル名 = project_id
  • QA単位保存: セグメントごとに複数のQAが独立レコードとして保存(qa_idで一意識別)
  • content: 全前処理結果を統合したテキスト(OCR + BDA + PDFテキスト + AI分析)
  • vector: LanceDB埋め込み関数で自動生成(Bedrock Nova、1024次元)
  • keywords: Lindera/ICU4Xで抽出したトークン(FTSインデックス)。LinderaはCJK言語を辞書ベースでトークン化し、ICU4Xはその他の言語をUnicodeワードセグメンテーションで処理

TokaはLinderaICU4Xを組み合わせたRustベースの多言語トークナイザーLambdaです。

カスタムトークナイザーを使用する理由

Section titled “カスタムトークナイザーを使用する理由”

LanceDBの内蔵FTSトークナイザーはCJK言語を十分にサポートしていません。CJK言語(韓国語、日本語、中国語)は膠着語であったり単語境界がないため、単純なスペースベースのトークン化では不十分です。例:

韓国語入力: "인공지능 기반 문서 분석 시스템을 구축했습니다."
Toka出力: ["인공지능", "기반", "분석", "시스템", "구축", "했", "."]
日本語入力: "文書分析システムを構築しました"
Toka出力: ["文書", "分析", "システム", "構築", "し"]
言語トークナイザー方式
韓国語Lindera (lindera-ko-dic)辞書ベース形態素分析
日本語Lindera (lindera-ipadic)辞書ベース形態素分析
中国語Lindera (lindera-cc-cedict)辞書ベースセグメンテーション
その他ICU4XUnicodeワードセグメンテーション

すべての検索はLanceDB Service Lambdaで処理されます。LanceDBのネイティブquery_type='hybrid'を使用してベクトル検索と全文検索を統合します。

検索クエリ: "문서 분석 결과 조회"
├─ [1] Tokaキーワード抽出(LanceDB Service Lambda経由)
│ → ["문서", "분석", "결과", "조회"]
├─ [2] LanceDBネイティブハイブリッド検索
│ → table.search(query=keywords, query_type='hybrid')
│ → ベクトル検索(Nova埋め込み)+ FTS自動マージ
│ → Top-K結果(_relevance_score)
└─ [3] 結果の要約(MCP Search Tool Lambda)
→ Bedrock Claude Haikuで検索結果に基づく回答を生成

// StorageStack
const expressStorage = new CfnDirectoryBucket(this, 'LanceDbExpressStorage', {
bucketName: `idp-v2-lancedb--use1-az4--x-s3`,
dataRedundancy: 'SingleAvailabilityZone',
locationName: 'use1-az4',
});

S3 Express One Zoneは一桁ミリ秒のレイテンシを提供し、ベクトル検索のような頻繁な読み書きパターンに最適化されています。

// StorageStack
const lockTable = new Table(this, 'LanceDbLockTable', {
partitionKey: { name: 'base_uri', type: AttributeType.STRING },
sortKey: { name: 'version', type: AttributeType.NUMBER },
billingMode: BillingMode.PAY_PER_REQUEST,
});

複数のLambda関数が同じデータセットに同時アクセスする際の分散ロックを管理します。

キー説明
/idp-v2/lancedb/lock/table-nameDynamoDBロックテーブル名
/idp-v2/lancedb/express/bucket-nameS3 Expressバケット名
/idp-v2/lancedb/express/az-idS3 Express可用性ゾーンID
/idp-v2/lancedb/function-arnLanceDB Service Lambda関数ARN

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

Section titled “コンポーネント依存関係マップ”

LanceDBに依存するすべてのコンポーネントを示したダイアグラムです:

graph TB
    subgraph Write["Write Path"]
        Writer["LanceDB Writer"]
        QA["QA Regenerator"]
    end

    subgraph Read["Read Path"]
        MCP["MCP Search Tool<br/>(Agent)"]
    end

    subgraph Delete["Delete Path"]
        Backend["Backend API<br/>(プロジェクト削除)"]
    end

    subgraph Core["Core Service"]
        Service["LanceDB Service<br/>(Rust Lambda)"]
        Toka["Toka<br/>(Rust Lambda)"]
    end

    subgraph Storage["Storage Layer"]
        S3["S3 Express One Zone"]
        DDB["DynamoDB Lock Table"]
    end

    Writer -->|invoke| Service
    QA -->|invoke| Service
    MCP -->|invoke<br/>hybrid_search| Service
    Backend -->|invoke<br/>drop_table| Service

    Service --> S3 & DDB
    Service -->|invoke| Toka

    style Storage fill:#fff3e0,stroke:#ff9900
    style Core fill:#e8f5e9,stroke:#2ea043
    style Write fill:#fce4ec,stroke:#e91e63
    style Read fill:#e3f2fd,stroke:#1976d2
    style Delete fill:#f3e5f5,stroke:#7b1fa2
コンポーネントスタックアクセスタイプ説明
LanceDB ServiceLanceServiceStack読み書きコアDBサービス(Rust Lambda)
TokaLanceServiceStackトークン化多言語トークナイザー(Rust Lambda)
LanceDB WriterWorkflowStack書き込み(Service経由)SQSコンシューマー、Serviceに委任
Analysis FinalizerWorkflowStack書き込み(SQS/Service経由)セグメントを書き込みキューに送信、再分析時に削除
QA RegeneratorWorkflowStack書き込み(Service経由)Q&Aセグメント更新
MCP Search ToolMcpStack読み取り(Service直接呼び出し)エージェント文書検索ツール
Backend APIApplicationStack削除(Service経由)プロジェクト削除時にdrop_tableを呼び出し

本番環境でAmazon OpenSearch Serviceに移行する場合、以下のコンポーネントの修正が必要です。

コンポーネント現在(LanceDB)変更後(OpenSearch)範囲
LanceDB Service LambdaRust Lambda + LanceDBOpenSearchクライアント(CRUD + 検索)全面交換
LanceDB Writer LambdaSQS → LanceDB Service呼び出しSQS → OpenSearchインデックス書き込み呼び出し先交換
MCP Search ToolLambda invoke → LanceDB ServiceLambda invoke → OpenSearch検索呼び出し先交換
StorageStackS3 Express + DDBロックテーブルOpenSearchドメイン(VPC)リソース交換
コンポーネント理由
Analysis FinalizerSQSにメッセージ送信のみ(キューインターフェース不変)
FrontendDB直接アクセスなし
Step Functions WorkflowLanceDB直接依存なし
Phase 1: ストレージ層の交換
- VPC内にOpenSearchドメインを作成
- StorageStackリソース交換(S3 Express + DDBロック削除)
- 韓国語トークン化のためのNori分析器設定(Toka/Lindera代替)
Phase 2: 書き込みパスの交換
- LanceDB Service → OpenSearchインデクシングサービスに変更
- 文書スキーマ変更(OpenSearchインデックスマッピング)
- 埋め込み用OpenSearch neural ingest pipeline追加
Phase 3: 読み取りパスの交換
- MCP Search Tool LambdaのinvokeターゲットをOpenSearch検索サービスに変更
- Toka依存関係削除(Noriが韓国語トークン化を処理)
Phase 4: LanceDB依存関係の削除
- Rust Lambda関数削除(LanceDB Service、Toka)
- S3 ExpressバケットおよびDDBロックテーブルの削除
項目内容
韓国語トークン化OpenSearchにはNori分析器が内蔵されておりToka/Lindera削除可能
ベクトル検索OpenSearch k-NNプラグイン(HNSW/IVF)がLanceDBベクトル検索を代替
埋め込みOpenSearch neural searchでingest pipelineから自動埋め込み可能、または事前計算済み埋め込みを使用
コストOpenSearchは稼働中のクラスターが必要。HAのための最低2ノードクラスター
SQSインターフェースSQS書き込みキューパターンは維持可能、コンシューマーロジックのみ変更