pgvector × Djangoで始めるRAG開発|LlamaIndex活用レシピ

pika1.0で作成した動画の画像 New Challenge
pika1.0で作成した動画の画像

本記事では、私が実際に AI 開発の現場で Django・pgvector・LlamaIndex を組み合わせながら、ジョブ作成フローからベクトルデータベース構築、そして最終的に RAG(Retrieval Augmented Generation)を動かすまでの手順を、そのままの目線で解説していきます。途中では、コーディングエラーや環境依存の不具合、モデルの互換性問題なども発生しましたが、それらをどう乗り越えたかも含めて“実録”としてまとめました。単に技術手順を並べるだけではなく、開発の裏側で AI エージェントがどう考え、どう最適解へ導いていくか、そのプロセスごと体系的に整理しています。Django でベクトル検索を導入したい方や、LlamaIndex を本番システムへ組み込みたい方に最適なロードマップになるはずです。

Django とベクトル検索の関係を俯瞰する─なぜ今 pgvector なのか

この第1章では、なぜ Django プロジェクトにベクトル検索を統合するのか、その背景と現在の技術潮流についてお話しします。従来の全文検索(Elasticsearch や MeiliSearch)が扱うキーワード検索とは異なり、ベクトル検索は「意味の近さ」を数値的に評価できます。この特性により、自然言語処理や生成 AI の時代において、知識検索・FAQ システム・大規模ドキュメント検索などのビジネス領域で重要性が急速に高まりました。

特に pgvector は PostgreSQL の拡張として利用できるため、既存の Django + Postgres 環境に自然に組み込める利点があります。追加の検索サーバーを立てる必要がなく、本番運用に向けた保守性も高いのがポイントです。また、LlamaIndex との相性が非常に良く、RAG ワークフローまで一気通貫でつなげられます。本章では、その全体像をまずクリアにし、後続の実装フェーズに向けた整理を行います。

1.1 ベクトル検索とは何か──キーワード検索との違い

キーワード検索は文字列一致をベースに動作し、「含まれる単語の一致度」でスコアを算出します。一方ベクトル検索は、文書を「多次元ベクトル」に変換し、その距離(類似度)を計算します。
類似度 = cos(ベクトルA, ベクトルB)
この仕組みは、意味が似ていれば単語が違ってもヒットするため、現代の AI 時代に不可欠な検索手法といえます。

検索方式特徴メリット
キーワード検索文字列一致を評価高速・実装が簡単
ベクトル検索意味の近さを評価抽象的・曖昧な質問に強い

1.2 Django と pgvector が相性の良い理由

Django は ORM を中心に設計されており、PostgreSQL との連携が非常に強力です。pgvector は PostgreSQL の拡張として提供されるため、新しいインフラを構築せずに現在の DB のまま利用できる点が魅力です。
また、pgvector は以下の特徴を持ちます。

  • 高速な類似度計算(cosine / L2 / inner product)
  • 近似検索(HNSW)をサポート
  • PostgreSQL のトランザクション管理と連携
  • Django ORM から直接呼び出し可能

特に本番運用では「新しいサーバーを増やしたくない」「ストレージやバックアップを一元管理したい」という要望が多く、それらを満たすのが pgvector の利点だといえます。

1.3 ベクトル検索が必要とされるユースケース

現在、多くの企業が生成 AI を導入する中で、以下のようなケースでベクトル検索は必須となっています。

  • 企業内ナレッジ検索(マニュアル・議事録・サポートFAQ)
  • カスタマーサポートの自動応答(RAG)
  • 文書分類・類似文書検出
  • ユーザー投稿やレビューの意味解析

特に RAG は生成 AI の品質を左右する重要な技術であり、提示するコンテキストの精度が上がるほど、応答の自然さや正確さが向上します。この土台を支えるのがベクトル検索であり、その中核にあるのが pgvector + LlamaIndex という構成です。

pgvector を用いた Django データモデル設計──高精度検索を支える基盤

第2章では、Django と pgvector を組み合わせたデータモデル設計を具体的に見ていきます。pgvector は PostgreSQL に自然に統合される拡張であり、既存のアプリケーション構成を崩さずに高度な意味検索を導入できます。特に、ベクトル次元の設計は軽視されがちですが、モデルの大きさや利用するエンコーダー次第で精度が変わるため、事前に検討すべき重要な要素です。また、Django ORM は拡張型フィールドに対しても柔軟性が高く、pgvector フィールドを自然に扱えるため、わずかな追加コードで本格的な検索が可能です。本章では、モデル定義、マイグレーション、類似検索クエリの書き方まで、一つの流れとして整理しています。

2.1 モデルに pgvector を導入する基本構造

pgvector を Django に統合するには django-pgvector パッケージを利用します。モデルのベクトルフィールドは以下のようになります。


from pgvector.django import VectorField

class Document(models.Model):
    title = models.CharField(max_length=255)
    content = models.TextField()
    embedding = VectorField(dimensions=1536)

ベクトル次元は採用するエンコーダーの出力サイズに合わせます。
例:OpenAI text-embedding-3-small → 1536 次元。
埋め込み生成処理はバッチ化することで DB 書き込み性能が大幅に安定します。

2.2 マイグレーションと pgvector 拡張の有効化

pgvector は PostgreSQL の拡張であるため、以下の SQL を事前に実行する必要があります。


CREATE EXTENSION IF NOT EXISTS vector;

マイグレーションファイルでは、Django が VectorField を適切に認識し、通常のフィールド同様に扱ってくれます。

2.3 類似検索クエリの実装

類似検索は Django ORM から直接記述できます。


query_embedding = embed("検索したい文章")
Document.objects.order_by(
    "embedding__cosine_distance"  # cosine 類似度
)

cosine_distancel2_distance は pgvector が内部で最適化しています。
さらに HNSW インデックスを作成することで検索速度が大幅に改善されます。



LlamaIndex による RAG 構築──データ投入からクエリ応答まで

第3章では、LlamaIndex を用いて Django + pgvector のデータベースに格納した文書をもとに RAG(Retrieval Augmented Generation)を構築する手順を解説します。LlamaIndex はデータローダー、インデックス、リトリーバーの三層構造になっているため、Django アプリに統合する際も比較的容易に取り扱えます。特に最近のバージョンでは、PgvectorStore という専用ストアが用意されており、データベースとインデックスの同期が簡単です。また、OpenAI 以外のモデル、LLama 3.1 や Groq LLaMA、Gemma などとも柔軟に連携できるため、将来的なモデル切り替えにも強い設計が可能です。

3.1 LlamaIndex と pgvector の連携構造

LlamaIndex では以下のように pgvector ストアを設定します。


from llama_index.vector_stores.pgvector import PGVectorStore

vector_store = PGVectorStore.from_params(
    database="mydb",
    table="document",
    dimensions=1536,
)

この時点で Django のモデルと同一のテーブルを共有できます。

3.2 インデックス生成とデータ同期

ドキュメントを投入するコードは以下のようになります:


from llama_index.core import SimpleDirectoryReader, GPTVectorStoreIndex

docs = SimpleDirectoryReader("docs").load_data()
index = GPTVectorStoreIndex.from_documents(docs, vector_store=vector_store)
index.storage_context.persist()

RAG の品質は「前処理」「分割単位」「メタデータ」の3つで大きく変わります。

3.3 LLM と接続してクエリを実行する

実際の RAG 応答は以下のようになります:


query_engine = index.as_query_engine(similarity_top_k=5)
response = query_engine.query("この製品の保証期間は?")
print(response)

必要に応じて ChatPromptTemplate を与えることで応答品質を安定させられます。



ジョブ作成・非同期処理・バッチ化──ベクトル生成の実運用設計

第4章では、実際の運用で問題になりやすい「埋め込み生成の非同期化」とバッチ処理の設計を扱います。ベクトル生成は API 呼び出しが多く、同期処理にしてしまうとレスポンスが非常に遅くなります。そのため、Celery・Django-Q・RQ などのジョブキューを活用して非同期処理にすることが重要です。さらに、文書が増えてくると数千〜数万のベクトル生成が必要となり、API のレート制限にも影響します。本章ではクリーンアーキテクチャ的な分離を行い、ジョブキューの実装から、エラー処理、再実行ポリシーまで、実運用を見据えた形で整理しています。

4.1 Celery を用いた非同期ベクトル生成

Celery は Django で最も採用率の高いジョブキューです。


@app.task
def generate_embedding(doc_id):
    doc = Document.objects.get(id=doc_id)
    doc.embedding = embed(doc.content)
    doc.save()

非同期化により、ユーザー操作とは独立してベクトル生成が進むため、UX が大幅に改善されます。

4.2 バッチ処理とレートリミット管理

大量文書の初回投入時には 100〜500 件単位で一括処理することが推奨されます。


SELECT id FROM document WHERE embedding IS NULL LIMIT 500;

API 呼び出しはまとめて実行することでコスト効率が向上します。
また、OpenAI などの API は秒間レート制限があるため、
バックオフ戦略(指数的遅延) を導入すると安定します。

4.3 エラー処理・再実行ポリシー

実運用では、通信失敗・レート制限・モデル切り替えなどのエラーが頻発します。
Celery の retry 機能を使うことで、安全に再実行できます。


@app.task(bind=True, max_retries=5)
def generate_embedding(self, doc_id):
    try:
        ...
    except Exception as e:
        raise self.retry(exc=e, countdown=30)

この構成により、埋め込み生成の信頼性が大きく向上します。



検索体験を実装する──API・フロント・UI/UX の最適化

第5章では、バックエンドで構築したベクトル検索をどのようにユーザー向け機能として公開するか、その UI/UX 設計を扱います。検索性能が高くても、「見せ方」が悪ければユーザーは良い体験ができません。ここでは API の設計、検索結果のランキング、関連度スコアの表示、絞り込み、エラーハンドリングなどのポイントを整理し、Cocoon・Next.js・HTMX など、現代的なフロント技術での統合方法を紹介します。

5.1 API 設計(REST / GraphQL / WebSocket)

一般的には REST API で十分ですが、以下のような選択肢があります。

方式メリット用途
REST簡単・Django と相性良い基本の検索 API
GraphQL柔軟・型安全SPA や複雑な UI
WebSocketリアルタイム性チャット・ライブ検索

5.2 Cocoon・WordPress での検索 UI 統合

WordPress(Cocoon)に Django API を接続する場合、
JavaScript(fetch API)で次のように呼び出します。


fetch("/api/search?q=マニュアル")
  .then(res => res.json())
  .then(showResults)

結果表示の UI は以下がポイントです:

  • キーワードハイライト
  • 関連度スコアの可視化
  • カテゴリー絞り込み
  • 要約を先頭に表示する

5.3 RAG チャット UI の最適化

RAG の回答には「引用元」を明示することで信頼性が大きく向上します。


回答:
〜〜〜〜〜

引用元:
・2024/03/10 マニュアル更新版
・製品資料:バージョン 2.1

さらに、回答の前に「検索した文書」を表示するフェーズを挟むと透明性が高まります。



まとめ:Django × pgvector × LlamaIndex で実現する次世代検索基盤

本記事では、Django と PostgreSQL(pgvector)、そして LlamaIndex を用いた現代的なベクトル検索および RAG システムの構築方法を、実務に近い目線で解説しました。単なる手順の羅列ではなく、非同期ジョブ、インデックス最適化、検索 UI、RAG のプロンプト設計までを包含することで、全文検索から意味検索へと進化する現在の AI 活用の方向性を一望できたと思います。ベクトル検索はもはや専門研究領域ではなく、一般の Web アプリケーションに標準搭載される技術へと変わりました。これを Django で実装することは、企業システム、ナレッジ検索、社内 AI アシスタントなど、多くの現場で大きな価値を生み出します。ぜひこの記事を参考に、あなたのアプリでも次世代検索を実装してみてください。

〆最後に〆

以上、間違い・ご意見は
以下アドレスまでお願いします。
全て返信できていませんが 見ています。
適時、改定をします。

nowkouji226@gmail.com

全体の纏め記事に戻る

タイトルとURLをコピーしました