Home COLING 2025論文解説: MAC-SQL — マルチエージェント協調によるText-to-SQLフレームワーク
投稿
キャンセル

📄 COLING 2025論文解説: MAC-SQL — マルチエージェント協調によるText-to-SQLフレームワーク

本記事は COLING 2025 “MAC-SQL: A Multi-Agent Collaborative Framework for Text-to-SQL” の解説記事です。

論文概要(Abstract)

Text-to-SQLタスクにおいて、大規模データベーススキーマ(数十〜数百テーブル)への対応と複雑なクエリの正確なSQL生成は依然として困難な課題である。MAC-SQL(Multi-Agent Collaborative SQL)は、この課題をSelector、Decomposer、Refinerの3つの専門エージェントの協調で解決するフレームワークを提案している。各エージェントが責務を明確に分担することで、モノリシックなSQL生成アプローチと比較して精度と解釈性の両方を向上させている。

この記事は Zenn記事: LangGraph×Claude Sonnet 4.6でSQL統合Agentic RAGを実装する の深掘りです。

情報源

  • 会議名: COLING 2025(International Conference on Computational Linguistics)
  • : 2025
  • URL: https://aclanthology.org/2025.coling-main.36/
  • 著者: Bing Wang, Changyou Ren, Jian Yang, Xinnian Liang, Jiaqi Bai, LinZheng Chai, Zhao Yan, Qian-Wen Zhang et al.

カンファレンス情報

COLINGについて: COLINGは計算言語学分野で最も歴史のある国際会議の1つであり、自然言語処理(NLP)の幅広いテーマを扱う。2025年大会でText-to-SQL関連の論文が採択されたことは、NLコミュニティにおけるSQL生成タスクの重要性を反映している。

技術的詳細(Technical Details)

3エージェント協調アーキテクチャ

MAC-SQLは、SQL生成プロセスを3つの独立したエージェントに分割する:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
自然言語クエリ + 大規模DBスキーマ
            ↓
┌─────────────────────────────────┐
│  Agent 1: Selector               │
│  → 大規模スキーマから関連テーブル・ │
│    カラムを選択(スキーマプルーニング)│
└─────────────┬───────────────────┘
              ↓ 縮小されたスキーマ
┌─────────────────────────────────┐
│  Agent 2: Decomposer             │
│  → 複雑なクエリをサブクエリに分解  │
│  → 各サブクエリのSQL生成          │
│  → 最終SQLの結合                 │
└─────────────┬───────────────────┘
              ↓ 候補SQL
┌─────────────────────────────────┐
│  Agent 3: Refiner                │
│  → SQL実行・エラー検出            │
│  → エラー時は修正SQL生成          │
│  → 結果の妥当性チェック           │
└─────────────┬───────────────────┘
              ↓
          最終SQL + 実行結果

Agent 1: Selector(スキーマプルーニング)

大規模データベース(100+テーブル)から、クエリに関連するテーブルとカラムのみを選択する。

\[S' = \text{Select}(q, S) \quad \text{where} \quad |S'| \ll |S|\]

ここで、

  • $S$: 元のフルスキーマ(テーブル数: $S$)
  • $S’$: プルーニング後のサブスキーマ
  • $q$: 自然言語クエリ

Selectorの重要性: 著者らは、LLMのコンテキストウィンドウに入りきらない大規模スキーマをそのまま渡すと、(1) テーブル名の取り違え、(2) 不要なJOINの生成、(3) トークンコストの増大が発生すると述べている。Selectorによるスキーマプルーニングは、これらの問題を軽減する。

Zenn記事との対応: Zenn記事のSQLDatabaseで設定するinclude_tablesパラメータは、手動でのスキーマプルーニングに相当する。MAC-SQLのSelectorはこれをLLMベースで自動化する点が異なる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
from typing import TypedDict

class SelectorState(TypedDict):
    """Selectorの状態"""
    query: str
    full_schema: dict
    selected_tables: list[str]
    selected_columns: dict[str, list[str]]

async def selector_agent(
    state: SelectorState,
    llm_client: object,
) -> SelectorState:
    """スキーマプルーニングを行うSelectorエージェント

    Args:
        state: 現在の状態(クエリとフルスキーマ)
        llm_client: LLMクライアント

    Returns:
        プルーニング後のスキーマ情報を含む更新された状態
    """
    prompt = f"""以下のデータベーススキーマから、
ユーザークエリに回答するために必要な
テーブルとカラムのみを選択してください。

クエリ: {state['query']}

スキーマ:
{format_schema(state['full_schema'])}

出力形式:
- テーブル名: [必要なカラム名のリスト]
"""

    response = await llm_client.generate(prompt)
    selected = parse_selection(response)

    return {
        **state,
        "selected_tables": selected["tables"],
        "selected_columns": selected["columns"],
    }

Agent 2: Decomposer(クエリ分解)

複雑な自然言語クエリをより単純なサブクエリに分解し、各サブクエリのSQLを生成してから最終SQLに結合する。

\[q \xrightarrow{\text{Decompose}} \{q_1, q_2, \ldots, q_k\} \xrightarrow{\text{Generate}} \{\text{SQL}_1, \ldots, \text{SQL}_k\} \xrightarrow{\text{Compose}} \text{SQL}_{\text{final}}\]

著者らによれば、このアプローチは以下の利点がある:

  1. 各サブクエリが単純になる: 単一テーブル操作やシンプルなJOINに分解されるため、LLMの生成精度が向上
  2. デバッグ容易性: どのサブクエリの変換で失敗したかを特定しやすい
  3. 再利用性: 同じサブクエリパターンを異なる複合クエリで再利用可能

Agent 3: Refiner(SQL検証・修正)

生成されたSQLを実際にデータベースで実行し、エラーが発生した場合は修正SQLを生成する自己修正ループを担当する。

\[\text{SQL}_{t+1} = \text{Refine}(\text{SQL}_t, \text{Error}_t, S')\]

ここで、

  • $\text{SQL}_t$: 時刻$t$のSQL候補
  • $\text{Error}_t$: 実行エラーメッセージ
  • $S’$: サブスキーマ

著者らは、Refinerの自己修正ループを最大3回に制限している。これは、AWSブログで推奨されているMAX_RETRIES=3と同じ設計判断であり、無限ループの防止とコスト制御のバランスを取った値である。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
async def refiner_agent(
    sql: str,
    schema: dict,
    db_connection: object,
    max_retries: int = 3,
) -> tuple[str, str | None]:
    """SQL検証・修正エージェント

    Args:
        sql: 候補SQL
        schema: サブスキーマ
        db_connection: DB接続
        max_retries: 最大リトライ回数

    Returns:
        (最終SQL, 実行結果 or None)
    """
    for attempt in range(max_retries):
        try:
            result = await db_connection.execute(sql)

            # 結果の妥当性チェック
            if is_empty_result(result):
                # 空結果の場合、WHERE句の緩和を試行
                sql = await relax_where_clause(sql, schema)
                continue

            return sql, result
        except SyntaxError as e:
            sql = await fix_syntax_error(sql, str(e), schema)
        except ColumnNotFoundError as e:
            sql = await fix_column_reference(sql, str(e), schema)
        except Exception as e:
            sql = await regenerate_with_error(sql, str(e), schema)

    return sql, None  # max_retries到達

LangGraph StateGraphとの対応

MAC-SQLの3エージェント構成は、LangGraphのStateGraphで自然に実装できる。Zenn記事のアーキテクチャとの対応関係は以下の通り:

MAC-SQLZenn記事のSQL検索ノードLangGraph実装
Selectorinclude_tables設定新規ノード追加
Decomposersql_db_schema + SQL生成既存ノードの拡張
Refinersql_db_query_checker既存ノードの拡張
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from langgraph.graph import StateGraph, END

# MAC-SQL風のStateGraph構築
graph = StateGraph(MACSSQLState)

graph.add_node("selector", selector_agent)
graph.add_node("decomposer", decomposer_agent)
graph.add_node("refiner", refiner_agent)
graph.add_node("generate_answer", generate_answer_node)

graph.set_entry_point("selector")
graph.add_edge("selector", "decomposer")
graph.add_edge("decomposer", "refiner")
graph.add_edge("refiner", "generate_answer")
graph.add_edge("generate_answer", END)

app = graph.compile()

実験結果(Results)

著者らが報告したBIRD・Spiderベンチマークでの結果(論文の実験セクションより):

ベンチマーク単一LLM (GPT-4)MAC-SQL (GPT-4)改善幅
BIRD dev著者らの報告によりベースライン改善達成スキーマプルーニングと自己修正による精度向上
Spider devベースライン改善達成クエリ分解による複雑クエリ対応力の向上

各エージェントの貢献分析(著者らのアブレーション実験より):

構成効果
Selectorなし(フルスキーマ)大規模DB(50+テーブル)で精度が顕著に低下
Decomposerなし(直接生成)複雑なJOIN/サブクエリで精度低下
Refinerなし(検証なし)構文エラー・実行時エラーが増加
3エージェント全体各コンポーネントの相乗効果で最高精度

著者らは、特にSelector(スキーマプルーニング)の効果が大きく、50テーブル以上のデータベースではSelectorの有無で精度が大きく変化すると報告している。

制約と限界:

  • 3エージェント分のLLM APIコールが必要(単一パスの約3倍のコスト)
  • エージェント間の通信オーバーヘッドによるレイテンシ増加
  • Selectorのスキーマプルーニング精度がパイプライン全体のボトルネックになりうる

実装のポイント(Implementation)

段階的な導入戦略

MAC-SQLの3エージェントを一度に導入するのではなく、効果の大きいコンポーネントから段階的に導入することが推奨される:

  1. Phase 1: Refiner(自己修正)の導入 — 最小の変更で精度向上が見込める。Zenn記事のsql_db_query_checkerをRefinerに拡張し、実行エラー時の再生成ループを追加する

  2. Phase 2: Selector(スキーマプルーニング)の導入 — テーブル数が10を超えるDBで有効。LLMによるテーブル・カラム選択ノードを追加する

  3. Phase 3: Decomposer(クエリ分解)の導入 — 複雑な複合クエリ(3テーブル以上のJOIN等)が頻繁に発生する場合に有効

コスト最適化

著者らの手法を実運用に適用する際のコスト最適化:

  • クエリ複雑度に基づく動的切替: 単純なクエリ(単一テーブル)は直接SQL生成、複雑なクエリ(マルチテーブルJOIN)のみMAC-SQLパイプラインを適用
  • Selectorのキャッシュ: 同一DBスキーマに対するSelector結果をキャッシュし、同様のクエリパターンでの再計算を回避
  • 軽量LLMの活用: SelectorとRefinerには軽量モデル(Haiku等)を使用し、Decomposerのみ高性能モデル(Sonnet等)を使用

実運用への応用(Practical Applications)

MAC-SQLのマルチエージェント構成は、Zenn記事のSQL統合Agentic RAGにおけるSQL検索品質を向上させる具体的な設計パターンを提供している:

  1. 大規模企業DB対応: 100+テーブルの企業データベースでは、Selectorによるスキーマプルーニングが必須となる
  2. 複合クエリ対応: 「先月の各部署の売上合計と前月比」のような複合クエリは、Decomposerによる分解が有効
  3. 運用信頼性: Refinerの自己修正ループにより、SQL実行エラーの自動回復が可能になる

まとめ

MAC-SQLは、Text-to-SQLをSelector(スキーマプルーニング)・Decomposer(クエリ分解)・Refiner(SQL検証・修正)の3エージェントに分割することで、精度と解釈性を同時に向上させるフレームワークである。各エージェントはLangGraphのノードとして自然に実装でき、Zenn記事のSQL統合Agentic RAGの拡張として段階的に導入可能である。

参考文献


:::message 本記事はAI(Claude Code)により自動生成された、COLING 2025採択論文の解説記事です。論文の内容を正確に伝えることを目指していますが、解釈に誤りがある可能性があります。必ず原論文をご確認ください。 :::

この投稿は CC BY 4.0 でライセンスされています。