Claude Media
Claude RAGの構築ガイド — 埋め込みと引用付き回答の実装

Claude RAGの構築ガイド — 埋め込みと引用付き回答の実装

ClaudeにRAG専用のAPIはありませんが、Voyage AIの埋め込みと引用付きの検索結果ブロックを組み合わせれば出典付きの検索拡張生成を組めます。実装手順とつまずきどころを解説。

ClaudeでRAGを組む3つの部品

Claudeモデル自体は埋め込み(embedding)を生成しません。RAG(Retrieval-Augmented Generation、検索拡張生成)をClaudeで組むときは、3つの部品を別々に用意します。検索用ベクトルを作る部品検索精度を上げる前処理検索結果を根拠に回答を生成する部品です。これらをMessages APIでつなぎ込む形になります。

対応する3つの部品はこちらです。①Voyage AIの埋め込み(Embeddings)、②Contextual Retrievalによるチャンクの前処理、③search_resultコンテンツブロックによる引用付き生成です。①と②は検索側(どのテキストを拾うか)、③は生成側(拾ったテキストをどう答えに落とし込み、出典を明示するか)を担当します。

前提として、Anthropic APIキーと、埋め込み用にVoyage AI(もしくは他ベンダー)のAPIキーが要ります。すでに検索基盤(ベクトルDBやElasticsearch)を持っている場合は、①②を自前の仕組みに置き換えて③だけ導入する形でも成立します。

ステップ1: Voyage AIで埋め込みを生成する

Anthropicは自社の埋め込みモデルを持たず、公式ドキュメントでVoyage AIを埋め込みプロバイダーとして案内しています。ドメイン特化モデルもあります。金融向けのvoyage-finance-2、法務向けのvoyage-law-2、コード・プログラミング文書向けのvoyage-code-3の3つです。汎用用途ではvoyage-4(バランス型)・voyage-4-large(最高精度)・voyage-4-lite(低レイテンシ・低コスト)から選びます。

pip install -U voyageai
export VOYAGE_API_KEY="<your-voyage-api-key>"

埋め込みを取得するコード例です。ここで重要なのはinput_typeパラメーターを必ず指定すること。検索対象のドキュメントには"document"、ユーザーの検索クエリには"query"を渡します。省略すると埋め込みの質が落ちるとVoyage側のドキュメントが明記しています。

import voyageai
 
vo = voyageai.Client()
 
# 検索対象のドキュメントを埋め込む
doc_embeddings = vo.embed(
    documents, model="voyage-4", input_type="document"
).embeddings
 
# ユーザーの質問を埋め込む
query_embedding = vo.embed(
    [query], model="voyage-4", input_type="query"
).embeddings[0]

生成された埋め込みは1024次元(既定)のベクトルで、コサイン類似度がそのまま内積と一致するよう正規化済みです。あとは任意のベクトルDB(pgvector、Pinecone、Chromaなど)に格納し、クエリベクトルとの近傍探索で候補チャンクを取得します。

ステップ2: Contextual Retrievalで検索精度を上げる

ドキュメントを数百トークン単位のチャンクに割ると、チャンク単体では「どの文書の、何の話か」が消えてしまい、埋め込みでもBM25でも拾われにくくなります。Anthropicのエンジニアリングブログが提案するContextual Retrievalは、チャンクの先頭に文脈を要約した短い一文を追加する前処理です。「このチャンクが何の文脈に属するか」を一文にまとめ、埋め込みとBM25インデックスの両方に反映します。

文脈一文の生成にはClaude 3 Haikuのような軽量モデルを使います。ドキュメント全体をPrompt Cachingでキャッシュしておけば、チャンクごとの追加コストを大きく抑えられます。Prompt Cachingの仕組みと適用判断はPrompt Cachingの解説記事にまとめてあります。

この前処理を手作業で組まない方法もあります。voyage-context-3 / voyage-context-4はVoyage AIのcontextualized chunk embeddingモデルです。ドキュメント全体(最大120,000トークン)を渡すと、チャンク単位のベクトルに文書全体の文脈が自動で反映され、手動のメタデータ付与が不要になります。パイプラインを自分で管理したいならContextual Retrieval、モデル側に任せたいならcontextualized embeddingが向いています。

Anthropicが公開しているベンチマークでは、埋め込みだけの従来RAGに対してContextual Embeddingsで検索の取りこぼし率が35%改善したと報告されています。BM25側にも同じ文脈を反映するContextual BM25まで組み合わせると49%改善、さらにrerankingを重ねると67%改善しました。仕組みの詳細とベンチマークの内訳はContextual Retrievalの解説記事にまとめています。先を急ぐなら、この節は読み飛ばして構いません。

なお、知識ベース全体が200,000トークン(おおよそ500ページ)未満なら話は変わります。Anthropicは「検索を組まずPrompt Cachingで全文を毎回渡す方がシンプルで結果も良い」と案内しています。RAGを組む前に、そもそも検索が必要な規模かを確認する価値があります。

ステップ3: 検索結果に出典を付けて回答させる

検索で候補チャンクが揃ったら、それをClaudeにどう渡すかが生成側の設計です。Anthropicが推奨する方法は、search_resultという専用のコンテンツブロックです。Webの検索結果を引用するのと同じ仕組みをRAGにも使えるようにしたもので、Claude Haiku 3を除く全ての現行モデルが対応しています。

search_resultブロックはsource(URLやkb://article-1234のような内部識別子)・titlecontent(テキストブロックの配列)を持ちます。citations.enabledtrueにすると、Claudeが根拠にした箇所を自動で引用します。特別なプロンプト指示は不要で、質問を投げるだけで該当テキストブロックに出典が付きます。

渡し方は2通りあります。1つはカスタムツールの戻り値として動的に返す方法、もう1つはユーザーメッセージの中に直接search_resultブロックを並べる方法です。

from anthropic.types import (
    MessageParam,
    SearchResultBlockParam,
    TextBlockParam,
)
 
search_result = SearchResultBlockParam(
    type="search_result",
    source="kb://product-guide",
    title="Product Configuration Guide",
    content=[
        TextBlockParam(
            type="text",
            text="タイムアウトの既定値は30秒で、10〜120秒の範囲で調整できます。",
        )
    ],
    citations={"enabled": True},
)
 
response = client.messages.create(
    model="claude-opus-5",
    max_tokens=1024,
    messages=[
        MessageParam(
            role="user",
            content=[
                search_result,
                TextBlockParam(type="text", text="タイムアウトはどこまで伸ばせますか"),
            ],
        )
    ],
)

Claudeの応答はtextブロックの配列になり、根拠にした箇所にはcitations配列が付きます。各引用にはsourcetitleが含まれます。加えて、content配列のどのブロックを根拠にしたかを示すstart_block_index / end_block_indexも持ちます。根拠テキストそのものであるcited_textも含まれ、出力トークンとしてカウントされません。

ステップ4: 動的な検索をツール呼び出しに組み込む

検索対象が更新され続けるなら、ステップ3のブロックをカスタムツールの戻り値として返す構成にします。ツール定義自体は通常のツール呼び出しと変わりません。Claudeがsearch_knowledge_baseのようなツールを呼び、アプリ側がベクトル検索を実行します。その結果をsearch_resultブロックの配列にまとめ、tool_resultとして返します。

ここで1点だけ制約があります。tool_resultの中身がsearch_resultブロックを含むなら、同じtool_result内の他のブロックもすべてsearch_resultでなければなりません。検索結果に添える説明文を返したい場合は、各search_resultcontent配列の中にテキストブロックとして含めます。

会話ループ(ツール呼び出し→検索→結果を返す→最終応答)を自前で書くのは面倒です。Claude Agent SDKはツール実行ループを内蔵しているため、カスタムツールとして検索関数を1つ登録するだけで済みます。Anthropic APIのMessages構造やツール利用、Batch処理など周辺機能の全体像はAnthropic API完全ガイドにまとめています。

実装方法の使い分け早見表

同じ「Claudeにドキュメントを読ませて答えさせる」目的でも、Anthropicは複数の入口を用意しています。RAGパイプラインを組むときにどれを選ぶかの目安です。

実装方法向いている場面出典の付き方
ツール呼び出し経由のsearch_result向いている場面検索対象が更新され続ける動的なRAG出典の付き方自動(citations有効時)
トップレベルのsearch_result向いている場面事前取得済み・キャッシュ済みの検索結果を渡す出典の付き方自動(citations有効時)
documentブロック + citations向いている場面PDFやプレーンテキストを1〜数ファイルまるごと渡す出典の付き方自動(ページ・文字位置)
Files API経由のdocumentブロック向いている場面同じファイルを複数の会話で繰り返し参照する出典の付き方citationsを有効化可能

検索対象が数百件以上のチャンクに分かれるならsearch_resultが向いています。渡すドキュメントが数点で全文を読ませたいだけなら、Files APIとdocumentブロックの組み合わせの方がシンプルです。

よくあるつまずき

  • input_typeを省略する — Voyageの埋め込みはinput_type="query""document"で内部的に異なるプロンプトが付加されます。省略すると検索精度が落ちるため、両方の呼び出しで明示します。
  • citationsのON/OFFを混在させる — 1回のリクエストに含まれるsearch_resultブロックは、citationsを全部有効にするか全部無効にするかのどちらかで、混在させるとエラーになります。
  • tool_result内でsearch_resultと他のブロックを混ぜる — 同じtool_resultにsearch_result以外のブロックを混ぜるとバリデーションエラーになります。補足テキストは各search_resultのcontent側に入れます。
  • assistantメッセージにsearch_resultを入れるsearch_resultはユーザーメッセージ(ツール結果を含む)専用で、assistant側に含めると拒否されます。
  • Structured outputsとcitationsの併用output_config.formatcitations.enabledを同じリクエストで併用すると400エラーになります。構造化出力が必要な場合は、引用情報を別リクエストで取得するか、citations無しで運用します。
  • 200,000トークン未満でも検索基盤を組んでしまう — 知識ベースが小規模なら、Prompt Cachingでドキュメント全体を毎回渡す方が実装もデバッグも簡単です。RAGは「検索しないと収まらない規模」になってから導入する選択肢です。

よくある質問

ClaudeのAPIだけでRAGは完結しますか

完結しません。埋め込み生成にはVoyage AIなど外部の埋め込みプロバイダーが必要で、ベクトル検索自体もpgvectorやPineconeのような検索基盤側の役割です。Claudeが担うのは、検索結果を根拠に回答を生成し、出典を引用する部分です。

Contextual Retrievalは必須ですか

必須ではありません。埋め込みとBM25だけの従来型RAGでも動きますが、チャンク分割で文脈が失われやすい場合に取りこぼしを大きく減らせる前処理です。既存のRAGパイプラインに文脈一文を追加するだけで適用できるため、精度に不満が出てから導入しても遅くありません。

search_resultとdocumentブロックの違いは

documentブロックはPDFやプレーンテキストなど1つのファイル全体を渡し、Claude側が文単位などでチャンキングして引用します。search_resultは検索パイプライン側であらかじめチャンク化・ランキング済みのテキスト片を、出典(URLや内部ID)付きで渡す形式です。動的なRAGでは検索結果をそのまま渡せるsearch_resultの方が扱いやすくなります。

埋め込みモデルはVoyage AI以外でも使えますか

使えます。AnthropicはVoyage AIを案内していますが、埋め込み生成自体はどのプロバイダーでも構いません。生成したベクトルをベクトルDBに格納し、検索結果をsearch_resultブロックとしてClaudeに渡す後段の設計は共通です。

まとめ

ClaudeでRAGを組む作業は、3つの部品をMessages API経由でつなぐ設計に集約されます。埋め込み(Voyage AI)・検索精度の前処理(Contextual Retrieval)・引用付き生成(search_resultブロック)の3つです。小規模な知識ベースなら話は別です。Prompt Cachingで全文を毎回渡す方が早く、精度も安定します。検索が必要になった段階でsearch_resultブロックとcitationsを組み込むのが無理のない順番です。まずはFiles APIとdocumentブロックで手早く動くものを作ります。検索対象が増えてきたら、Voyageの埋め込みとContextual Retrievalを足していきます。この順番なら、実装コストと精度のバランスを取りやすくなります。

この記事を共有:XはてブLinkedIn