Text-to-SQLアプリケーション向けのAgentic RAGの作成

情報取得増強生成(RAG)と生成AIモデルの組み合わせは、クエリへの応答を改善することで自然言語処理に変化をもたらしました。エージェンティックRAGの領域では、タスクに対して単一のモデルに依存する従来の方法が、モジュラリティと自律性を導入することで強化されています。問題解決プロセスをエージェント内に統合されたツールに分解することで、エージェンティックRAGは精度、透明性、拡張性、デバッグ能力などの利点を提供しています。

Text-to-SQLのエージェンティックRAGのビジョン

従来のRAGシステムは、しばしば関連書類を取得し、1つのモノリシックモデルに応答を生成することに依存しています。これは一部のケースでは効果的な方法ですが、SQLの生成などの構造的出力の場合、このアプローチは最も効果的ではないかもしれません。ここで、エージェンティックRAGフレームワークの力を活用できます。そこでは、

  1. タスクをエージェント内のより小さな、より管理しやすいツールに分割する
  2. タスクを専門のツールに割り当てることで精度を向上させる
  3. 各ツールの推論とワークフローを追跡することで透明性を向上させる
  4. モジュラーデザインによってスケーリングとデバッグを簡素化する

このツールがどのように機能し、各コンポーネントがユーザーの質問を正確なSQLクエリに変換する際に果たす役割について話し合いましょう。

アーキテクチャの概要

構造は、テキストからSQLへのワークフロー内のツールを利用するエージェントから成り立っています。プロセスは以下のように要約できます。

ユーザークエリ → クエリ変換ツール → フューショットプロンプティングツール → ハイブリッド検索ツール → 再ランキングツール → テーブル検索ツール → プロンプト構築ツール → LLM実行ツール → SQL実行ツール → 最終出力

1. ユーザークエリ変換ツール

このツールは、LLMの理解を向上させるためにユーザークエリを処理します。曖昧さに対処し、ユーザーの質問を言い換え、略語をその形式に変換し、必要に応じてコンテキストを提供します。

強化

  • 時間的な参照を扱う。”今日まで”や”今まで”などの用語を明示的な日付にマップする。
  • 曖昧な単語を置き換える。例えば、”最近”は”過去7日間”に置き換えられることがあります。
  • 略語や省略形をそれらの名称に関連付ける。

入力:”最近の売上データを表示。”

変換されたクエリ:”過去7日間の売上データを取得(月次)。”

Python

 

from datetime import date, timedelta

def transform_query(user_query):
    # 開放的な時間的参照を扱う
    today = date.today()
    transformations = {
        "as of today": f"up to {today}",
        "till now": f"up to {today}",
        "recent": "last 7 days",
        "last week": f"from {today - timedelta(days=7)} to {today}",
    }
    
    for key, value in transformations.items():
        user_query = user_query.replace(key, value)

    # 一般的な略語をマップする
    abbreviations = {
        "MTD": "Month to Date",
        "YTD": "Year to Date",
    }
    for abbr, full_form in abbreviations.items():
        user_query = user_query.replace(abbr, full_form)

    return user_query

query_transform_tool = Tool(
    name="Query Transformer",
    func=transform_query,
    description="Refines user queries for clarity and specificity, handles abbreviations and open-ended terms."
)

2. フューショットプロンプティングツール

このツールはLLMに問題の種類を識別させるために呼び出しを行い、セットから質問を特定します(テンプレートに一致するとも言えます)。一致した質問は、例のSQLクエリを含むプロンプトを強化します。

例のワークフロー

1. 入力質問:”最近の7日間の製品別総売上を表示してください。”

2. 事前定義されたテンプレート:

  • “地域別に売上を表示。” → 例のSQL; SELECT 地域, SUM(売上)…
  • “製品別の合計売上を表示します。” → 例のSQL; SELECT product_name, SUM(sales) …

3. 最も類似した質問: “製品別の合計売上を表示します.”

4. 出力例SQL: SELECT product_name, SUM(sales) FROM …

Python

 

from langchain.chat_models import ChatOpenAI

llm = ChatOpenAI(model="gpt-4")

predefined_examples = {
    "Show sales grouped by region": "SELECT region, SUM(sales) FROM sales_data GROUP BY region;",
    "Show total sales by product": "SELECT product_name, SUM(sales) FROM sales_data GROUP BY product_name;",
}

def find_similar_question(user_query):
    prompt = "Find the most similar question type for the following user query:\n"
    prompt += f"User Query: {user_query}\n\nOptions:\n"
    for example in predefined_examples.keys():
        prompt += f"- {example}\n"
    prompt += "\nRespond with the closest match."

    response = llm.call_as_function(prompt)
    most_similar = response['content']
    return predefined_examples.get(most_similar, "")

few_shot_tool = Tool(
    name="Few-Shot Prompting",
    func=find_similar_question,
    description="Finds the most similar question type using an additional LLM call and retrieves the corresponding example SQL."
)

3. ハイブリッド検索ツール

堅牢な検索のために、このツールは意味的検索、BM25に基づくキーワード検索、およびキーワードベースのマッピングを組み合わせています。これらの検索方法からの検索結果は、相互ランクフュージョンを使用してまとめられます。

それがどのようにまとまるのか?

キーワードテーブルマッピング

このアプローチは、クエリに含まれるキーワードをテーブルにマッピングします。例えば:

  • “sales”の存在により、salesテーブルが選定されます。
  • “product”の存在により、productsテーブルが選定されます。

キーワードオーバーラップマッピング(BM25)

これは関連性に基づいてテーブルを選定するキーワードオーバーラップに基づく検索方法です。これにはBM25技術を適用します。これにより、ユーザー検索に関連性の高い論文が順に並べられます。この検索技術は、用語の飽和度およびTF-IDF(用語頻度-逆文書頻度)を考慮します。

用語頻度(TF)は、指定された文書内の用語の出現頻度を測定するのに役立ちます。逆文書頻度(IDF)アプローチは、すべての文書に表示される単語を重要性を低下させる点を強調します。

長い論文に偏りを防ぐために文書長を考慮した正規化が行われます。

与えられた:

  • sales_data: “sales,” “date,” “product.”
  • products: “product,” “category.”
  • orders: “order,” “date,” “customer.”
  • financials: “revenue,” “profit,” “expense.”

User query: “Show total sales by product.”

  • Identify terms in the user query: [“sales,” “product”].
  • Sort every document (based on frequency and relevance of these terms) in DataBaseTable.

Relevance of documents:

  • sales: “sales”と”product”の両方による高い関連性
  • products: “product”による高い関連性
  • orders: “sales”のみのため低い関連性
  • financials: 関連性なし

Output:

Ranked list: [products, sales_data, orders, financials]

Semantic Search

この検索手法では、ベクトル埋め込みを利用して意味的に類似したテーブルを見つけます。これは、文書(テーブルベクトル)とユーザークエリベクトルとの間のコサイン類似性などの類似性スコアを計算することによって達成されます。

Reciprocal Rank Fusion

BM25と意味検索の結果を逆順ランクフュージョン戦略を使って結合することは、以下で詳しく説明されています。

BM25と意味検索の逆順ランクフュージョン(RRF)の結合:

RRFは、複数のランキングアルゴリズム(例:BM25と意味検索)の結果を組み合わせる方法です。個々の方法でのランクに基づいて各文書にスコアを割り当て、複数の方法で上位にランクされた文書にはより高いスコアを付けます。

RRFの式:

RRF(d) = Σ(r ∈ R) 1 / (k + r(d))

ここで:

  • dは文書
  • Rはランカー(検索方法)のセット
  • kは定数(通常60)
  • r(d)は検索方法rでの文書dのランク

ステップバイステップの例

入力データ。

1. BM25のランキング結果:

  • products(ランク1)
  • sales_data(ランク2)
  • orders(ランク3)

2. 意味検索のランキング結果:

  • sales_data(ランク1)
  • financials(ランク2)
  • products(ランク3)

ステップバイステップの結合

各表について、スコアを計算します:

1. sales_data

  • BM25ランク=2、意味ランク=1
  • RRFスコア=(1/60+2) + (1/60+1)=0.03252

2. products

  • BM25ランク=1、意味ランク=3
  • RRFスコア=(1/60+1) + (1/60+3)=0.03226

3. orders

  • BM25ランク = 3、セマンティックランク = ランクなし
  • RRFスコア = (1/60+3)= 0.01587

4. 財務

  • BM25ランク = ランクなし、セマンティックランク = 2
  • RRFスコア = (1/60+2)=0.01613

5. RRFスコアでソート

  • 売上データ(セマンティック検索でのトップランクによる最高スコア)。
  • 製品(BM25からの高スコア)。
  • 注文(全体的に関連性が低い)。
  • 財務(重複が限られている)。

最終出力: [‘売上データ’, ‘製品’, ‘財務’, ‘注文’]

キーワードテーブルKeyword Tableマッピングを使用して取得したテーブルは常に含まれます。

Python

 

from rank_bm25 import BM25Okapi

def hybrid_search(query):
    # キーワードベースのマッピング
    keyword_to_table = {
        "sales": "sales_data",
        "product": "products",
    }
    keyword_results = [table for keyword, table in keyword_to_table.items() if keyword in query.lower()]

    # BM25検索
    bm25 = BM25Okapi(["sales_data", "products", "orders", "financials"])
    bm25_results = bm25.get_top_n(query.split(), bm25.corpus, n=5)

    # セマンティック検索
    semantic_results = vector_store.similarity_search(query, k=5)

    # 逆順ランク融合
    def reciprocal_rank_fusion(results):
        rank_map = {}
        for rank, table in enumerate(results):
            rank_map[table] = rank_map.get(table, 0) + 1 / (1 + rank)
        return sorted(rank_map, key=rank_map.get, reverse=True)

    combined_results = reciprocal_rank_fusion(bm25_results + semantic_results)

    return list(set(keyword_results + combined_results))

hybrid_search_tool = Tool(
    name="Hybrid Search",
    func=hybrid_search,
    description="Combines keyword mapping, BM25, and semantic search with RRF for table retrieval."
)

4. 再ランク付けツール

このツールは、最も関連性の高いテーブルが優先されることを保証します。

  • 入力テーブル: [“売上データ”, “製品”, “財務”]
  • 再ランク付けロジック
    • 各テーブルについて、クエリとテーブルの説明を連結して関連スコアを計算します。
    • 関連スコアでソートします。
  • 出力: [“売上データ”, “製品”]

Re-rankingロジックに少し詳しく:

クロス-エンコーダは、結合されたクエリとテーブルの説明を単一の入力ペアとして分析して関連スコアを計算します。このプロセスには、

  • ペア入力が含まれます。クエリと各テーブルの説明がペアとしてまとめられ、クロス-エンコーダに入力されます。
  • 共同エンコーディング。別々のエンコーダ(例:bi-encodersとは異なり、クロス-エンコーダはペアを共同でエンコードし、クエリとテーブルの説明の文脈や依存関係をよりよく捉えることができます。
  • スコアリング。モデルは、各ペアに対してテーブルがクエリにどれだけ適合するかを示す関連スコアを出力します。
Python

 

from transformers import pipeline

reranker = pipeline("text-classification", model="cross-encoder/ms-marco-TinyBERT-L-2")

def re_rank_context(query, results):
    scores = [(doc, reranker(query + " " + doc)[0]['score']) for doc in results]
    return [doc for doc, score in sorted(scores, key=lambda x: x[1], reverse=True)]

re_rank_tool = Tool(
    name="Re-Ranker",
    func=re_rank_context,
    description="Re-ranks the retrieved tables based on relevance to the query."
)

5. プロンプト構築ツール

このツールは、ユーザーの洗練されたクエリ、取得したスキーマ、およびFew-Shot Prompting Toolからの例を取り入れて、言語モデルの詳細なプロンプトを構築します。

SQLクエリを生成することが得意な人物と仮定し、以下を行うSQLクエリを生成してください:過去7日間の商品別の総売上を取得します。

関連するテーブル:

  1. sales_data:[sales、date、product_id]の列を含みます。
  2. products:[product_id、product_name]の列を含みます。

例えばSQL:

Plain Text

 

SELECT product_name, SUM(sales) FROM sales_data JOIN products ON sales_data.product_id = products.product_id GROUP BY product_name;

将来の展望

このシステムは複数のツールを持つ単一エージェントを使用して、モジュラリティを簡素化し複雑さを減らしていますが、将来的にはマルチエージェントフレームワークを検討することができます。以下を探索する可能性があります:

  1. コンテキスト取得専用エージェント。セマンティック検索とキーワード検索用に別々のエージェントを用意します。
  2. タスク固有のエージェント。SQLの検証や最適化に特化したエージェント。
  3. エージェント間の協力。タスクの委任を管理する調整エージェントを使用します。

このアプローチは、企業レベルの展開において特にスケーラビリティを向上させ、より洗練されたワークフローを可能にします。

結論

Agentic RAGは、テキストからSQLへのアプリケーションに対するスケーラブルでモジュラーなアプローチを提供し、構造化されたクエリタスクの解決に役立ちます。ハイブリッド検索、再ランキング、フューショットプロンプティング、およびダイナミックプロンプト構築を単一エージェントフレームワーク内に組み込むことで、このシステムは正確性、透明性、および拡張性を確保します。この強化されたワークフローは、自然言語の質問を実行可能なSQLクエリに変換するための強力な設計図を示しています。

Source:
https://dzone.com/articles/creating-an-agentic-rag-for-text-to-sql-applications