DBC Tech Academy / AI / 中級

RAG と
ロングコンテキスト
の使い分けLong Context か RAG か。社内文書をもとに答えさせる方式を、4 つの数字で決める

社内規程や設計書をもとに、LLM に答えさせたい。そう考えたとき、多くの現場が最初に RAG (Retrieval-Augmented Generation) の構成図を描き始めます。しかし社員 30 人・規程 50 ファイル程度なら、ロングコンテキスト (Long Context) に文書を丸ごと載せるだけで足りることがあります。ここでは、どこまでが「足りる」のかを料金・待ち時間・正答率・更新頻度の 4 つで判定し、組む前に方式を決める手順を扱います。

which one do you need
$ python compare.py --docs ./rules --ask "有給の繰越上限は?" 50 ファイル / 197,000 字 → 197,000 tokens (使用率 20%) 入力tok 課金/回 最初tok 出典 ロングコンテキスト 197,400 $1.251 3.0s 要実装 同上(キャッシュ) 197,400 $0.116 1.1s 要実装 メタデータで絞る(5件) 20,100 $0.143 0.8s 要実装 RAG(上位 8 チャンク) 6,800 $0.052 1.0s 構造で残る --------------------------------------------------------- 月 600 問(1 日 20 問・キャッシュヒット率 80%) ロングコンテキスト $206 絞り込み $58 RAG $31 + DB $70

キャッシュを効かせれば 1 回 $0.116 まで下がりますが、それでも RAG の 2 倍です。月額では $206 と $101、差は月 $105 です。この額で RAG を作って運用し続ける価値があるかどうかが、判断のすべてになります。

Start here

RAG を組むかどうかは、
最後に決める。

RAG には、分割 (チャンキング)・埋め込み (embedding)・ベクトル DB・再インデックスという、作って動かし続けるものがあります。ロングコンテキストには、そのどれもありません。だから最初に問うべきは「RAG をどう組むか」ではなく、「RAG を組まずに済むか」です。

まず
Long
Context移るのは数字が求めたときだけ

文書の合計トークンがコンテキストウィンドウ (context window) の半分に収まるなら、ロングコンテキストで組んで構いません。

この前提を満たさないなら、そもそもロングコンテキストは選べません。満たすなら、RAG に移る理由は 4 つの数字のどれかが閾値を割ったときだけです。数字を測らずに RAG を選ぶと、要らなかった構成を作って運用し続けることになります。

前提 : 文書の合計トークン ÷ コンテキストウィンドウ <= 50% 満たさない -> メタデータで絞る(節 06)か RAG 満たす -> 下の 4 つで、移るかどうかを決める

分子の合計トークンは、推定せず実物を数えます。日本語は概ね 1 文字が 1 トークン前後ですが、比率は文書の中身で変わります。

n = client.messages.count_tokens( model="claude-opus-5", messages=[{"role": "user", "content": all_docs_text}], ).input_tokens

コンテキストウィンドウそのものではなく半分を前提に置くのは、上限近くまで詰めると中ほどや後ろに置いた文書が使われにくくなるからです。この現象は Lost in the Middle と呼ばれ、入力が長いほど強く出ます。入ることと、読まれることは別の話です。この理由は節 02 で扱います。

4 つの数字と、移る閾値

数字ロングコンテキストのままにするRAG に移る
料金月額の差が、RAG の構築と運用にかける額を下回る月額の差が、それを上回り続ける見込み
待ち時間キャッシュが効いた状態で、最初のトークンまでが 3 秒以内3 秒を超え、利用者が待てない
正答率現場の 30 問で、ロングコンテキストが RAG を下回らない下回る。とくに矛盾する文書が混ざる場合
更新頻度1 日に何度も更新され、即時の反映が要る更新が週 1 回より粗く、再インデックスが間に合う

更新頻度だけ向きが逆になっている点に注意してください。文書がよく変わるほどロングコンテキストが有利です。RAG は再インデックスを回すまで古い内容を返し続けますが、ロングコンテキストは現物を読み直すだけで反映されます。

料金

ロングコンテキストは文書量に比例して増え、RAG は文書量が増えてもほぼ一定です。ここだけを見ると RAG が勝ちますが、ベクトル DB の月額と作る手間が差を埋めます。

待ち時間

最初のトークンが出るまでの時間 (TTFT / Time To First Token) は、入力トークン数にほぼ比例します。キャッシュに載っている入力は数分の 1 の速さで通ります。

正答率

ロングコンテキストは検索で落とす心配がない代わりに、矛盾する文書を両方読んで混同します。RAG は上位に入らなかった文書を最初から見ません。

更新頻度

更新から回答に反映されるまでの時間です。ロングコンテキストは読み直せば即時、RAG は再インデックスの周期より速くはなりません。

Three paths

3 つの方式で違うのは、
誰が文書を選ぶかだけ。

3 つの方式は、LLM が答えを書くところまでは同じです。違いは、渡す文書を誰が選ぶかの 1 点だけにあります。選ぶ主体が違うので、外し方も違います。

ロングコンテキスト 選ぶのは LLM 文書一式(50 件) 毎回そのまま全部 LLM 回答 作るものが無い 更新は読み直すだけ メタデータ で絞る 選ぶのは規則 文書一式 メタデータで選ぶ 部署・種別・年度 LLM 回答 検索器を持たない RAG 選ぶのは検索器 文書一式 分割 埋め込み ベクトル DB 上位 8 件 質問で選ぶ LLM 回答 外し方も、選ぶ主体で決まります ロングコンテキストが外すとき:矛盾する新旧の文書を両方読んで混ぜる。詰めるほど中ほどが使われにくくなる(Lost in the Middle)。 RAG が外すとき:必要なチャンクが上位に入らず、モデルの手元にそもそも正解が無い。1 件も届かなければ、推測で埋める。

この図が示しているのは、経路の違いと、文書を選ぶ主体がどこにあるかです。段数の多さは料金や速さを表しません。RAG の各段でどんな失敗が起きるかは、RAG の精度が出ない原因の切り分けで 7 段階に分けて扱っています。

ロングコンテキストは、検索で落としません

RAG の正答率は、検索が正しいチャンク (chunk) を上位に置けた割合を超えられません。ロングコンテキストにはこの制約がありません。文書の中に答えがある限り、モデルの手元には必ず届いています。

とくに強いのは、文書全体をまたぐ質問です。「この規程集で、退職に関わる条項をすべて挙げてください」のような質問は、上位 8 件を返す検索とは相性が悪く、ロングコンテキストなら素直に答えられます。

ロングコンテキストが弱いのは、量ではなく矛盾です

改定前の規程と改定後の規程が両方入っていると、モデルは両方を読んで混ぜます。RAG では片方しか上位に来ないため、たまたま正しく見えることがあります。

対処は、渡す前に有効な版だけにすることです。版を判定できないなら、それは方式の問題ではなく文書管理の問題で、RAG に変えても解決しません。

What to measure

料金・待ち時間・
正答率・更新頻度を、
推測せずに測る。

どれも 1 日で測れます。測らずに決めた方式は、後から覆すのに作ったものを捨てる必要が出ます。順に、測り方を見ていきましょう。

料金は、数えたトークンに質問数を掛ける

節 01 で数えた合計トークンが、そのまま 1 回あたりの入力になります。あとは月の質問数を掛けるだけです。

1 回の入力 = 合計トークン + 質問と指示 月額 ≒ 1 回の入力 × 月の質問数 × 入力単価 + 回答トークン × 月の質問数 × 出力単価
  • まだ動いていないなら、質問数は置きで構いません。想定利用者数 × 1 人あたり 1 日の質問数 × 稼働日数で足ります。
  • 入力側は、プロンプトキャッシュで大きく下がります。その効き方は節 04 で扱います。ここでは素の額を出しておきます。
  • RAG 側にはベクトル DB の月額を足します。API の料金だけで比べると、RAG が実際より安く見えます。

待ち時間は、最初のトークンまでを測る

利用者が体感するのは、回答が出終わるまでではなく最初のトークンが出るまでです。この値を TTFT (Time To First Token) と呼び、入力トークン数にほぼ比例します。

# ストリーミングで、最初のテキストまでを測る t0 = time.perf_counter() with client.messages.stream(...) as stream: for text in stream.text_stream: ttft = time.perf_counter() - t0 break
  • キャッシュに載っているかで数倍変わります。初回と 2 回目を分けて測ってください。
  • RAG は検索の時間が上乗せされます。埋め込み (embedding) の呼び出しと検索で、合わせて 0.3 秒前後を見込みます。

正答率は、現場の質問 30 問で比べる

作った人が思いつく質問ではなく、実際に聞かれている質問を 30 問集めます。問い合わせ窓口の履歴が最良の材料です。

  • 30 問を下回らないでください。10 問では、2 問の差が実力差か偶然かを区別できません。
  • 同じ質問を両方式に流し、人が採点します。正しい・惜しい・違うの 3 段階で足ります。
  • 違った質問だけを見返します。ロングコンテキストが外した質問に矛盾する文書が絡んでいるなら、直すのは方式ではなく文書です。

更新頻度は、反映までの時間で見る

測るのは更新の回数ではなく、更新してから回答が変わるまでの時間です。ここが業務の許容を超えると、正しく動いていても信用されなくなります。

  • ロングコンテキストは、読み直した瞬間に反映されます。ただしプロセス起動時に一度だけ読み込む作りだと、いつまでも古い内容を渡し続けます。
  • RAG は再インデックスの周期が下限です。夜間バッチなら最大 1 日遅れます。1 日に何度も更新される文書では、この遅れが誤答になります。

4 つのうち、順番に意味があるのは料金と待ち時間だけです。この 2 つは文書量と質問数から計算で先に出せるので、次の節のキャッシュを踏まえて机上で詰め、正答率と更新頻度は実物を動かしてから測ります。計算だけで RAG に決まる規模なら、正答率を測る前に判断できます。

Prompt caching

同じ文書を毎回
送り直さない。

ロングコンテキストの料金と待ち時間は、プロンプトキャッシュ (prompt caching) を使うかどうかで桁が変わります。これを入れずにロングコンテキストの費用を試算すると、実際の 3 倍以上の額が出ます。比較の前提として、先に仕組みを押さえます。

区切りを置く位置が、すべてを決めます

キャッシュは前方一致 (prefix match) で効きます。リクエストは決まった順に組み立てられ、キャッシュの区切り (cache breakpoint) より前が 1 バイトでも変わると、その後ろは全部キャッシュから外れます。したがって、毎回同じものを前に、毎回変わるものを後ろに置きます。

組み立ての順 tools -> system -> messages └────── 毎回同じ ──────┘▲ ここに区切りを置く └─ 質問・日時・利用者名は、この後ろ
# 文書ブロックを並べ、その最後に区切りを置く content = [doc_block(d) for d in docs] content[-1]["cache_control"] = {"type": "ephemeral"} content.append({"type": "text", "text": question})

システムプロンプトに現在日時や利用者名を入れると、毎回すべてが書き直しになります。料金は下がらず、書き込みの割増だけが乗って請求額が 1.25 倍に増えます。効いているかどうかは、応答の cache_read_input_tokens で確かめてください。この値が 0 のままなら、区切りより前に毎回変わる文字列が入っています。

単価と、損益が分かれる回数

キャッシュへの書き込みには割増があり、読み出しは 10 分の 1 になります。したがって「何回読まれるか」で損得が決まります。

TTL書き込み読み出し損益が分かれる
5 分1.25 倍0.1 倍2 回目から
1 時間2 倍0.1 倍3 回目から

倍率は、キャッシュを使わずに同じ入力を送ったときの単価に対するものです。TTL (Time To Live) 5 分で 2 回送れば 1.25 + 0.1 = 1.35 倍となり、キャッシュを使わずに 2 回送る 2 倍より安くなります。

どちらの TTL を選ぶか

読み出しは、そのたびに TTL の時計を延ばします。質問の間隔が 5 分より短く続く限り、TTL 5 分で足ります。間隔が 5 分から 1 時間の範囲で開くときだけ、TTL 1 時間が割増に見合います。

ここでいう間隔は、前の質問が始まってから次の質問が始まるまでです。回答の生成にかかった時間も、この間隔に含まれます。

RAG 側では、キャッシュはほとんど効きません。渡すチャンクが質問ごとに入れ替わるため、前方一致する部分が指示文だけになるからです。逆に言えば、キャッシュの恩恵を受けられるのは、毎回同じものを送るロングコンテキストのほうです。この非対称が、次の節で見る判断の分かれ目を大きく動かします。文書一式を先にコンテキストへ載せ、キャッシュに常駐させたまま質問だけを差し替えるこの構成は、CAG (Cache-Augmented Generation) とも呼ばれます。

Move the line

判断の分かれ目は、
どこにあるか。

文書量・質問数・キャッシュヒット率・モデルを動かすと、3 つの方式の月額と TTFT が入れ替わります。 自社に近い値を入れて、どちら側にいるのかを確かめてみましょう。

-ロングコンテキストの月額
-RAG の月額(DB 込み)
-コンテキスト使用率
-TTFT(キャッシュ時)

キャッシュヒット率は、前の質問から 5 分以内に次が来る割合とほぼ同じです。利用が朝夕に固まる社内システムでは 7 割から 9 割になります。RAG 側は渡すチャンクが毎回変わるため、キャッシュを効かせていません。

この図が再現しているのは、文書量を増やすとロングコンテキストだけが線形に伸び、RAG はほぼ横ばいのまま残るという向きと、その 2 本が交わる位置が質問数とモデルで動くことです。金額の絶対値は、実際の文書のトークン数・出力の長さ・ベクトル DB の構成で変わります。自社の値は count_tokens の実測と、契約しているデータベースの請求額で置き換えてください。また、この図は正答率を計算していません。正答率は前の節のとおり、現場の 30 問で実測する数字です。

試算に使っている値

項目値根拠
入力 / 出力の単価$5 / $25Opus 5。Sonnet 5 は $2 / $10、Haiku 4.5 は $1 / $5(100 万トークンあたり)
日本語のトークン換算1 文字 = 1 tok概算。実測は count_tokens
質問と指示400 tok毎回変わる部分
回答700 tok出力側の単価が掛かる
RAG が渡すチャンク800 tok × 8 件文書量が増えても変わらない
ベクトル DB$70 / 月小規模のマネージド構成
メタデータで絞った後に残る割合10%50 件のうち 5 件

動かすと見えること

文書を増やすと、ロングコンテキストだけが伸びます。RAG は渡すチャンクの数が固定なので、文書が 10 倍になっても API の料金は変わりません。文書量が増え続ける見込みなら、いずれ必ず交差します。

質問数を下げると、ロングコンテキストが逆転します。RAG にはベクトル DB の固定費があるため、質問が少ないほど不利になります。1 日 10 問なら、多くの設定でロングコンテキストのほうが安く済みます。

モデルを替えると、判断の分かれ目が大きく動きます。入力の単価が下がると、ロングコンテキストの側だけが安くなります。コンテキストウィンドウの小さいモデルでは、そもそもロングコンテキストが成立しなくなります。

The middle option

メタデータで絞って、
丸ごと渡す。

ロングコンテキストがコンテキストウィンドウに収まらない、あるいは料金が見合わない。それでも RAG を組むほどではない。この間を埋めるのが、ファイルのメタデータで数件に絞ってから、その数件を丸ごと渡す形です。メタデータフィルタ (metadata filtering) だけで済み、分割も埋め込みもベクトル DB も持ちません。

# 質問から絞り込みのキーを取り出す DEPT = {"人事": "hr", "経理": "fin", "情報システム": "it"} MAX_DOCS = 8 dept = next((v for k, v in DEPT.items() if k in question), None) docs = [d for d in ALL_DOCS if dept is None or d.dept == dept] # 絞り切れないときは、あらかじめ決めた文書セットを渡す if len(docs) > MAX_DOCS: docs = BUNDLES[dept] if dept else BUNDLES["common"]

キーの取り出しは、正規表現や語の一致で足りることがほとんどです。足りなければ、質問を LLM に渡して種別を 1 語で答えさせます。分類のための呼び出しが 1 回増え、0.2 秒ほど延びます。それでも、ベクトル DB を建てて再インデックスを回し続けるより軽い作りです。

選べる条件と、確かめ方

この形を選べるのは、絞り込みのキーがファイルのメタデータで表せるときだけです。部署・文書種別・年度・製品名のように、ファイル名やディレクトリで表現できるなら成立します。表現できないなら RAG です。

確かめ方は、必要なファイルが絞り込みの中に入っていた割合です。現場の 30 問について、答えに要るファイルを人が指定し、絞り込み後に残っているかを数えます。9 割を切るなら絞りすぎで、文書セットを広げるか RAG に移ります。

文書セットを固定しておくと、キャッシュが効くようになります。絞り込みの結果が質問ごとに違うと、送る文書の並びが毎回変わってキャッシュから外れます。「人事」「経理」のようにセットを数種類に固定しておけば、セットごとにキャッシュが残ります。

Citations

どこに書いてあるかを、
答えに残す。

RAG では「どのチャンクを渡したか」が構造として残るので、出典は自然に付いてきます。ロングコンテキストでは全部を渡しているため、出典は作り込まなければ出ません。社内規程のように根拠が要る用途では、ここが方式の差になります。

content = [] for doc in docs: content.append({ "type": "document", "title": doc.name, "source": {"type": "text", "media_type": "text/plain", "data": doc.text}, "citations": {"enabled": True}, }) # 文書までが毎回同じ。ここが区切り content[-1]["cache_control"] = {"type": "ephemeral"} content.append({"type": "text", "text": question}) resp = client.messages.create( model="claude-opus-5", max_tokens=2000, system=SYSTEM_RULES, messages=[{"role": "user", "content": content}], )
# 回答は、引用の付いた塊と付かない塊に分かれて返る for block in resp.content: if block.type != "text": continue for c in block.citations or []: print(c.document_title, c.start_char_index, c.end_char_index, c.cited_text)

この作り込みで得られるもの

文書名と、その文書の何文字目から何文字目かが返ります。画面側でその位置を強調表示すれば、利用者は原文を自分で確かめられます。モデルに「[1] のように書いてください」と指示して番号を書かせる方法より確実です。指示は守られないことがありますが、この位置情報はモデルが生成した文とひも付いて返ります。

引用 (citations) を有効にすると、根拠の無い記述が減ります。出典を出す前提で書かせると、文書に無いことを書きにくくなるためです。

引用と構造化出力は同時に使えません。回答を JSON の形で受け取る設定にすると、引用を有効にしたリクエストはエラーになります。JSON で受けたい場合は、出典を本文中の番号として書かせる指示に切り替えてください。

文書は 1 ファイル 1 ブロックで渡します。全部を 1 つの文字列に連結して渡すと、返る文字位置が連結後の位置になり、どのファイルのどこかを画面側で復元できなくなります。

Read the symptom

症状から、
原因を見抜く。

ロングコンテキストで組んだ直後に持ち込まれる 3 つの症状です。どれも方式そのものの限界ではなく、作り方で直せます。原因がどこにあるかを考えてみましょう。

Myth vs. reality

方式を決め損なう、
3 つの判断。

どれも一見もっともらしく、実際には測れば逆の結論が出ます。

MISCONCEPTION 01

「コンテキストウィンドウに入るなら、全部読んでくれる」

入ることと使われることは別です。詰めるほど中ほどに置いた文書は拾われにくくなり、これは Lost in the Middle として知られています。さらに、矛盾する新旧の文書が両方入っていれば両方を読んで混ぜます。コンテキストウィンドウそのものではなく、その半分を目安にしてください。

MISCONCEPTION 02

「RAG のほうが常に安い」

1 回あたりの入力トークンは確かに数十分の 1 です。しかしベクトル DB の月額と、分割・埋め込み・再インデックスを作って回し続ける手間が加わります。質問数が少ない小規模では、月額で逆転します。API の料金だけで比べないでください。

MISCONCEPTION 03

「ロングコンテキストなら出典は要らない」

全部を渡していても、回答に出所は自動では付きません。文書ブロックの引用を有効にするか、番号を書かせる指示を入れて初めて残ります。根拠を示す必要がある用途では、これは方式を選んだ後に必ず発生する作り込みです。

Knowledge check

理解度を確かめよう。

答えを当てるだけでなく、なぜそう判断できるのかを説明できれば合格です。

YOUR PROGRESS 01 / 09

選択肢を1つ選んでください。

Decide before you build

組む前に、
トークンを数えて
方式を決める。

トークンを数える文書の合計を count_tokens で実測する。文字数からの推定で済ませない
半分に収まるか見るコンテキストウィンドウの 50% 以下なら、ロングコンテキストで組める
4 つの数字を測る料金と待ち時間は机上で、正答率と更新頻度は現場の 30 問で
差額と手間で決める月額の差が、RAG を作って運用し続ける手間に見合うか

ほかの講座を見る