コンテンツにスキップ

Vector Database

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

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

Kiwiは韓国語専用の形態素解析器であるため、FTSキーワードが正確に抽出されるのは韓国語文書のみです。英語などの他言語の文書は、Bedrock Novaベクトル埋め込みによるセマンティック検索で検索されます。ベクトル検索は言語に関係なく意味ベースで動作するため、すべての言語の文書を検索できます。

このプロジェクトは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 (Container)
├─ Kiwi: キーワード抽出
├─ Bedrock Nova: ベクトル埋め込み (1024d)
└─ LanceDB: S3 Express One Zoneに保存
読み取りパス:
MCP Search Tool Lambda
→ LanceDB Service Lambda (Container): ハイブリッド検索 (ベクトル + 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テーブルの同時アクセス管理

1. LanceDB Service Lambda(Container Image)

Section titled “1. LanceDB Service Lambda(Container Image)”

ベクトルDBのコアサービスです。lancedbkiwipiepyの合計サイズがLambdaデプロイ制限(250MB)を超えるため、Dockerコンテナイメージを使用しています。

項目
関数名idp-v2-lancedb-service
ランタイムPython 3.12(Container Image)
メモリ2048 MB
タイムアウト5分
ベースイメージpublic.ecr.aws/lambda/python:3.12
依存関係lancedb>=0.26.0, kiwipiepy>=0.22.0, boto3

サポートアクション:

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

Container Lambdaを使用する理由:

Kiwiの韓国語言語モデルファイルとLanceDBのネイティブバイナリを合わせると数百MBに達し、Lambdaの250MB zip制限を超えます。Dockerコンテナイメージ(最大10GB)を使用することでこの制約を解決しています。

分析パイプラインからの書き込みリクエストを受信し、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 # Kiwi抽出キーワード(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: Kiwiで抽出した韓国語形態素(FTSインデックス)。韓国語以外の言語はスペースベースのトークン化

Kiwi (Korean Intelligent Word Identifier)はC++で書かれたオープンソースの韓国語形態素解析器で、Pythonバインディング(kiwipiepy)を提供しています。

LanceDBの内蔵FTSトークナイザーは韓国語をサポートしていません。韓国語は膠着語であり、スペースだけでは単語を分離できません。例:

入力: "인공지능 기반 문서 분석 시스템을 구축했습니다"
Kiwi: "인공 지능 기반 문서 분석 시스템 구축"(名詞のみ抽出)

形態素分析なしでは、「시스템」で検索しても「시스템을」や「시스템에서」を含む文書を見つけることができません。

品詞タグ説明
NNG一般名詞문서, 분석, 시스템
NNP固有名詞AWS, Bedrock
NR数詞하나, 둘
NP代名詞이것, 그것
SL外国語Lambda, Python
SN数字1024, 3.5
SH漢字
XSN接尾辞前のトークンに結合(例: 생성+형 → 생성형)

フィルター:

  • 1文字韓国語ストップワード: 것, 수, 등, 때, 곳
  • 1文字の外国語、数字、漢字は保持

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

検索クエリ: "문서 분석 결과 조회"
├─ [1] Kiwiキーワード抽出(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/>(Container 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

    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 ServiceWorkflowStack読み書きコアDBサービス(Container 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 LambdaContainer 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分析器設定
Phase 2: 書き込みパスの交換
- LanceDB Service → OpenSearchインデクシングサービスに変更
- 文書スキーマ変更(OpenSearchインデックスマッピング)
- 埋め込み用OpenSearch neural ingest pipeline追加
Phase 3: 読み取りパスの交換
- MCP Search Tool LambdaのinvokeターゲットをOpenSearch検索サービスに変更
- Kiwi依存関係削除(Noriが韓国語トークン化を処理)
Phase 4: LanceDB依存関係の削除
- requirementsからlancedb, kiwipiepyを削除
- Container Lambda削除(通常Lambdaで対応可能)
- S3 ExpressバケットおよびDDBロックテーブルの削除
項目内容
韓国語トークン化OpenSearchにはNori分析器が内蔵されておりKiwi削除可能
ベクトル検索OpenSearch k-NNプラグイン(HNSW/IVF)がLanceDBベクトル検索を代替
埋め込みOpenSearch neural searchでingest pipelineから自動埋め込み可能、または事前計算済み埋め込みを使用
コストOpenSearchは稼働中のクラスターが必要。HAのための最低2ノードクラスター
SQSインターフェースSQS書き込みキューパターンは維持可能、コンシューマーロジックのみ変更