0Pricing
AI Prompt Engineering · レッスン

素朴なRAGを超えて

基本的な検索の限界を学びます。

「素朴なRAGを超えて」はCoddyKit上の無料AI Prompt Engineeringレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAI Prompt Engineering学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 AI Prompt Engineeringコースには全4レッスンが含まれています。

Naive RAGの動作

Naive RAGはベースラインです。ドキュメントをチャンクに分割し、埋め込みを作成してベクトルを保存し、クエリを埋め込み、コサイン類似度でtop-kを検索し、チャンクをプロンプトに詰めて生成します。強力な出発点ですが、大規模化すると予測可能な形で失敗します。

こうした失敗モードを理解することが、このコースで扱う高度な手法(リランキング、圧縮、クエリ書き換え)を学ぶ前提になります。

def naive_rag(query, k=5):
    q = embed(query)
    chunks = vector_store.search(q, k)        # top-k by cosine
    context = '\n\n'.join(c.text for c in chunks)
    return llm('Context:\n' + context + '\n\nQ: ' + query)

検索の再現率と適合率

Naiveなtop-kは、ベクトルの生の類似度を最適化しますが、関連性と表面的な意味の近さを混同します。ここにはトレードオフがあります。kが小さいと回答を取り逃がすリスクが高まり、再現率が低下します。一方、kが大きいと注意をそらす情報がコンテキストにあふれ、適合率が低下します。

検索を駆動する埋め込み類似度は、真の関連性を大まかに近似するものにすぎず、複数の後続問題の根本原因になります。

# The core dilemma
# small k -> may miss the gold chunk (recall problem)
# large k -> distractors crowd context (precision + cost problem)
# Advanced RAG decouples 'retrieve many' from 'use few'

埋め込みのミスマッチ問題

クエリとドキュメントは、しばしば異なる言語レジスターに属します。短い質問と長い叙述的な文章のような違いです。バイエンコーダーの埋め込みでは、表現が異なるという理由だけで、関連する回答が質問から遠い位置に配置されることがあります。これが語彙のミスマッチ問題です。

この問題に対処するため、検索前にクエリを書き換えてドキュメント空間に合わせるクエリ書き換えやHyDEなどの手法が使われます。

# Query:  'how do I revoke a token?'
# Doc:    'Token invalidation is performed via the /sessions endpoint.'
# Lexically and semantically distant -> bi-encoder may miss it
sim = cos(embed('how do I revoke a token?'),
          embed('Token invalidation via /sessions'))  # may be low

Lost in the Middle現象

正しいチャンクを取得できていても、多数のチャンクを詰め込むとlost-in-the-middle効果が起こります。モデルは長いコンテキストの中央に置かれた内容に十分注意を向けなくなります。10個中3番目の順位に埋もれた正しいチャンクは、実質的に無視される可能性があります。

このため、最適なチャンクをモデルが注意を向けやすい位置に置くリランキングや、何も埋もれないようにコンテキストを縮小する圧縮が必要になります。

# Retrieval rank != attention rank
# Place the highest-relevance chunk at the START or END,
# never stranded in the middle of a large concatenation.

注意をそらす情報への敏感さ

LLMは無関係なコンテキストの影響を受けやすい性質があります。もっともらしいが誤ったチャンクを追加すると、正しいチャンクも含まれている場合でさえ、回答が誤った方向に引っ張られる可能性があります。取得するコンテキストは多いほど常によいわけではありません。

だからこそ適合率が重要です。関連性の高い、リランキングと圧縮を施したコンテキストのほうが、関係の薄いチャンクを大量に詰め込むより優れていることがよくあります。

# Empirically: appending a single highly-similar but WRONG chunk
# can flip a previously-correct answer. RAG quality depends on
# keeping distractors OUT, not just getting the gold chunk IN.

チャンク分割の問題

固定サイズのチャンク分割では、文の途中でアイデアが分断されたり、主張とその根拠が分離されたり、構造的なコンテキスト(どのセクションか、どのドキュメントか)が失われたりします。単独ではまとまりのあるチャンクでも、周囲の情報がなければ役に立たなかったり、誤解を招いたりする可能性があります。

高度なパイプラインでは、意味を保つために、構造を考慮したチャンク分割、オーバーラップ、親ドキュメントへの拡張、メタデータを使用します。

def structure_aware_chunks(doc, max_tokens=400, overlap=50):
    sections = split_by_headings(doc)        # respect document structure
    chunks = []
    for sec in sections:
        for c in sliding_window(sec.text, max_tokens, overlap):
            chunks.append(Chunk(c, meta={'section': sec.title}))
    return chunks

セマンティック検索だけでは生じる不足

純粋なdense検索では、識別子、エラーコード、まれな固有名詞、API名など、完全一致が必要なケースを取り逃がします。まさにこのような場面で、ユーザーは文字どおりの正確さを期待します。ハイブリッド検索では、dense(セマンティック)とsparse(BM25/キーワード)のシグナルを融合し、両方をカバーします。

Reciprocal Rank Fusionは、重みを調整せずに2つの順位付きリストを統合できる、シンプルで堅牢な方法です。

def rrf(dense_ranks, sparse_ranks, k0=60):
    scores = {}
    for ranks in (dense_ranks, sparse_ranks):
        for rank, doc_id in enumerate(ranks):
            scores[doc_id] = scores.get(doc_id, 0) + 1.0 / (k0 + rank)
    return sorted(scores, key=scores.get, reverse=True)

古く検証できないコンテキスト

Naive RAGには鮮度や出典の概念がありません。古いドキュメントを取得する可能性があり、主張を出典に結び付ける組み込みの方法もないため、信頼性が損なわれ、幻覚の検出も難しくなります。

高度なシステムでは、メタデータ(タイムスタンプ、出典、バージョン)を付与してその情報でフィルタリングし、回答を検証できるように生成モデルへチャンクIDの引用を要求します。

def filtered_retrieve(q, after_date):
    cands = vector_store.search(embed(q), k=50)
    fresh = [c for c in cands if c.meta['date'] >= after_date]
    return fresh  # then re-rank; generator must cite c.id

フィードバックがなければ適応もない

Naive RAGは盲目的に検索します。検索に失敗したことも、検索は不要と判断することも、処理を反復することもできません。高度なパターンでは、関連性チェック、条件付き検索、結果が弱い場合にクエリを書き換えるマルチステップ(エージェント型)検索を追加します。

パイプラインは単一のフォワードパスではなく、自己評価を行うループになります。

def adaptive_rag(q):
    chunks = retrieve(q)
    if relevance_score(q, chunks) < 0.4:
        q2 = rewrite_query(q)            # reformulate and retry
        chunks = retrieve(q2)
    if relevance_score(q, chunks) < 0.4:
        return 'I could not find this in the sources.'
    return generate(q, chunks)

高度なRAGスタック

これらの失敗をまとめると、高度なパイプラインは、メタデータ付きの構造を考慮したチャンク分割、高い再現率を持つハイブリッド検索、適合率を高めるクロスエンコーダーのリランカー、コンテキストを収めて焦点を絞るコンテキスト圧縮、ミスマッチを解消するクエリ書き換え / HyDE、引用を伴う関連性ゲートを積み重ねます。

次のレッスンでは各レイヤーを構築します。一貫した考え方は、広く検索し、その後に積極的に絞り込み、洗練することです。

def advanced_rag(q):
    cands = hybrid_retrieve(rewrite_query(q), k=50)  # high recall
    top = rerank(q, cands)[:8]                       # precision
    ctx = compress(q, top)                            # focus + fit
    return generate_with_citations(q, ctx)            # verifiable

最適化の前に測定する

新しい仕組みを追加する前に、実際にどの失敗が起きているのかを診断してください。retrieval recall@k(正解チャンクがそもそも取得されたか)と、回答精度(生成モデルがそのチャンクを使ったか)を分けて測定します。再現率の問題と適合率の問題では、必要な対策が異なります。

両方の段階に計測を組み込んでください。本当の問題がチャンク分割やクエリのミスマッチなのに、安易にリランカーを追加してはいけません。

def diagnose(eval_set):
    return {
        'recall@5':  recall_at_k(eval_set, k=5),     # retrieval health
        'recall@50': recall_at_k(eval_set, k=50),    # ceiling with rerank
        'answer_acc': answer_accuracy(eval_set),      # generation health
    }

確認テスト

RAGの失敗モードを診断します。

まとめ

重要なポイント:

  • Naive RAG(チャンク分割、埋め込み、top-k検索、詰め込み、生成)は、予測可能な失敗を伴う強力なベースラインです。
  • バイエンコーダーの類似度は関連性の大まかな近似にすぎず、クエリとドキュメントのレジスターのミスマッチによって再現率が低下します。
  • コンテキストは多いほどよいわけではありません。kが増えると、注意をそらす情報への敏感さやLost in the Middle現象によって回答の質が低下します。
  • チャンク分割の問題、セマンティック検索だけでは生じる不足、情報の古さ、フィードバックの欠如が、Naive RAGを制限します。
  • 高度なRAGでは広く検索してから絞り込みます。ハイブリッド検索、リランキング、圧縮、クエリ書き換え、関連性ゲートを使い、最適化の前に再現率と回答精度を分けて測定します。

よくある質問

「素朴なRAGを超えて」レッスンは無料ですか?

はい。「素朴なRAGを超えて」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Prompt Engineeringコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Prompt Engineeringコースには全4レッスンが含まれています。

「素朴なRAGを超えて」で何を学びますか?

基本的な検索の限界を学びます。 ブラウザで直接実行するハンズオンコードでAI Prompt Engineeringを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

AI Prompt Engineeringを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのAI Prompt Engineeringは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「素朴なRAGを超えて」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このAI Prompt Engineeringレッスンでコードを書いて実行できますか?

はい。すべてのAI Prompt Engineeringレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 素朴なRAGを超えて
  2. 取得チャンクの再ランキング
  3. コンテキスト圧縮
  4. クエリ書き換えとHyDE
← AI Prompt Engineeringに戻る