本記事では、F1(Formula 1)データを対象にした「テキスト→SQL変換エージェント」の動作概要や、エージェントが参照するナレッジベース(Knowledge Base)の詳細を解説します。さらに、実際のStreamlitアプリでの実行結果ログも交えて、エージェントがどのようにSQLを生成・実行し、どのような回答を返すのかを示します。
📢 マルチモーダルなAIエージェントを構築するための軽量フレームワーク「Agno」で遊んでみる②
SQLエージェントを動かしてみた!!!
これはマジで面白い!!!
やはりエージェント系はシミュレーションゲーム!! https://t.co/8U8j9EZRCd pic.twitter.com/9A3qzGdct2— Maki@Sunwood AI Labs. (@hAru_mAki_ch) February 5, 2025
プロジェクト概要
このサンプルプロジェクトの目的は、自然言語での質問を自動的にSQLクエリへ変換し、F1データ(1950年~2020年)に対して実行→結果を返すというエージェントを構築することです。
具体的には以下のような特徴があります。
-
F1の歴史的データ
1950年~2020年までのレース結果やドライバー情報、チーム(コンストラクターズ)情報、最速ラップなどを一括管理。 -
Agentic RAG
エージェントが自身でナレッジベースに問い合わせ、テーブルのメタデータやサンプルクエリを参照する仕組み。より正確なSQLクエリを生成できるようになります。 -
Streamlit + Docker Compose
- StreamlitによるUI(チャット形式のインターフェース)。
- Docker Composeで
pgvector
(PostgreSQL拡張)とアプリが同時起動。 - 簡単にローカル環境で試せます。
ディレクトリ構成と主なファイル
リポジトリのディレクトリ構成はざっくり以下の通りです。
├── agents.py <-- エージェントの定義・設定
├── app.py <-- Streamlitアプリ(UI部)
├── docker-compose.yml <-- DBとアプリの起動設定
├── Dockerfile <-- コンテナのビルド設定
├── load_f1_data.py <-- CSVをPostgreSQLにロード
├── load_knowledge.py <-- ナレッジベースをベクトルDBにロード
├── knowledge/ <-- テーブルメタデータやサンプルSQL, JSONファイル
└── utils.py <-- Streamlit用ユーティリティ等
2.1 agents.py
- Agnoの
Agent
クラスを継承し、「SQLエージェント」を実装。 - LLM(OpenAI GPT-4など)を選択し、SQLツール・ファイル操作ツールを追加。
- ナレッジベース(後述)やセッション管理(PostgresAgentStorage)を設定し、エージェントの「振る舞いのルール」(instructions)を詳細に記述。
2.2 app.py
- Streamlitでチャット形式のUIを作成。
agents.py
のエージェントを呼び出し、ユーザーからの質問を受け取り、エージェントが生成する回答やSQLをリアルタイム表示。
2.3 load_f1_data.py / load_knowledge.py
load_f1_data.py
: F1データ(CSV形式)をS3からダウンロード → pandas で読み込み → PostgreSQLにテーブルとして保存。load_knowledge.py
:knowledge/
ディレクトリのファイル(テーブルJSONやサンプルSQL)をベクトルDB(pgvector)に取り込み、エージェントの検索用ナレッジベースを構築。
2.4 knowledge/
- テーブル構造を記したJSONファイル (例:
drivers_championship.json
、race_wins.json
など) - サンプルクエリが書かれたSQLファイル (例:
sample_queries.sql
) - このフォルダに入っている情報がエージェントの問い合わせ元となる。
ナレッジベースの詳細
本プロジェクトが特に特徴的なのは、「Agentic RAGを活用し、エージェントがナレッジベースを随時検索して適切なSQLを組み立てる」という点です。
3.1 ナレッジベースの構成
-
テーブルのメタデータ (JSON)
- 各テーブルのカラム名・型・意味を記載。
- 例:
race_wins.json
{ "table_name": "race_wins", "table_description": "...", "table_columns": [ { "name": "index", "type": "int", ... }, { "name": "venue", "type": "text", ... }, ... ] }
-
サンプルSQL (
sample_queries.sql
)- よくある分析例(「チャンピオンが何回優勝したか」「コンストラクターの順位とレース勝利数の比較」など)をクエリ例として記載。
-
補足情報(テキストベース)
- たとえば「
date
列はDD Mon YYYY
フォーマットなのでTO_DATE
関数を使うと良い」といった注意事項。
- たとえば「
3.2 読み込みと活用フロー
-
CombinedKnowledgeBase
TextKnowledgeBase
とJSONKnowledgeBase
をまとめた複合KBで、ファイル内容を全て取り込みます。 -
PgVector
取り込んだテキストをベクトルとして変換し、pgvectorテーブルに格納。- ユーザー質問の意図に合うテーブルやサンプルクエリをベクトル検索で素早く見つけ出す。
-
エージェントのツール呼び出し
search_knowledge_base(table_name)
: エージェントが、自分でこのツールを呼び出し、対象テーブルやルールを検索。describe_table(table_name)
: データベース側の実テーブル定義を確認。
-
サンプルクエリを参考にSQL組み立て
- 例:
date
列から年を抽出する場合 →EXTRACT(YEAR FROM TO_DATE(date, 'DD Mon YYYY'))
を流用。
- 例:
実行手順
4.1 環境変数の設定
.env
ファイル内にOpenAIのAPIキーなどを記載します(例: OPENAI_API_KEY="sk-..."
)。
4.2 Docker Composeで起動
docker compose up -d
- 初回起動時、
load_f1_data.py
やload_knowledge.py
が自動実行され、F1データとナレッジベースがDBに取り込まれます。 - 数分かかる場合があるので完了を待ちます。
4.3 アプリにアクセス
ブラウザで http://localhost:8509
を開くと、StreamlitチャットUIが表示されます。
自然言語で質問すると、エージェントが最適なSQLを組み立てて実行→結果を返すフローを体験できます。
4.4 停止
docker compose down
永続化ボリュームを含め完全削除したい場合は docker compose down -v
を使います。
実際の処理ログ(一例)
以下は、Streamlitアプリ上で行われたユーザーとのやりとりの抜粋です。
(ログには「どのテーブルをdescribeしたか」「実行したSQL」「結果」が表示されています。)
5.1 テーブル一覧を問い合わせる
利用可能なテーブルを教えてください
エージェントの回答例:
以下のテーブルが利用可能です:
constructors_championship ...
drivers_championship ...
fastest_laps ...
race_results ...
race_wins ...
5.2 各テーブルのカラム詳細を取得
これらのテーブルについて詳しく説明してください
エージェントは search_knowledge_base
および describe_table
を呼び出し、メタデータをまとめて表示します。
ログのツール呼び出し例:
🛠️ Describe Table
Arguments:
{
"table_name":"drivers_championship"
}
Results:
[ "index: BIGINT()", "year: BIGINT()", "position: TEXT()", ... ]
5.3 「最も多くレースに勝ったドライバーは?」
最も多くのレースに優勝したドライバーは誰ですか?
エージェントが自動生成したSQLの例(ログより):
SELECT name, COUNT(*) AS win_count
FROM race_wins
GROUP BY name
ORDER BY win_count DESC
LIMIT 1
結果:
「ルイス・ハミルトン」 95回の優勝
5.4 「年間のレース数の推移を表示して」
race_wins
テーブルには「year」列がないため、date列から年を抽出する必要があると判断。
エージェントはサンプルSQLを検索して TO_DATE(date, 'DD Mon YYYY')
を活用し、年ごとのレース数をカウント。
結果として、1950年に7レース、2019年に21レースなど、年ごとの増減が一覧表示されます。
5.5 「2010年以降、最も多くのレースに勝利したドライバーとチームの順位は?」
以下のように、driversチャンピオンシップとrace_winsを結合しながら、さらにconstructors_championshipの順位を取得する複雑なクエリを作成。ログにはツール呼び出し(knowledge base検索やSQL実行)が順次記録されています。
最終表示例:
年 | ドライバー | チーム | 勝利数 | チーム順位 |
---|---|---|---|---|
2010 | Fernando Alonso | Ferrari | 5 | 2 |
2011 | Sebastian Vettel | Red Bull Racing Renault | 11 | 1 |
2012 | Sebastian Vettel | Red Bull Racing Renault | 5 | 1 |
2013 | Sebastian Vettel | Red Bull Racing Renault | 13 | 1 |
2014 | Lewis Hamilton | Mercedes | 11 | 1 |
2015 | Lewis Hamilton | Mercedes | 10 | 1 |
2016 | Lewis Hamilton | Mercedes | 10 | 2 |
2017 | Lewis Hamilton | Mercedes | 9 | 1 |
2018 | Lewis Hamilton | Mercedes | 11 | 1 |
2019 | Lewis Hamilton | Mercedes | 11 | 1 |
2020 | Lewis Hamilton | Mercedes | 11 | 1 |
まとめと応用
-
ナレッジベース
- テーブルのメタデータ・サンプルSQL・日付フォーマット注意点などを格納し、エージェントは質問に応じて必要な情報を検索・取得。
- これにより、ユーザーが「年ごとの推移が見たい」と要望したとき、自動的に適切なクエリを組み立てられます。
-
エージェントの流れ
- ユーザー質問を受け取り、
- ナレッジベースからテーブル定義・ルールを参照、
- クエリを生成しSQLを実行、
- 結果を整形して返す。
-
拡張・応用
- F1以外のデータも、同じ形式(JSONでテーブル情報、サンプルSQL)でナレッジベースを整備すれば、同じ仕組みで動かせます。
- 質問例やサンプルクエリが豊富になるほど、エージェントの回答精度は高まります。
おわりに
本記事では、Docker Composeで動くSQLエージェントがAgentic RAGを通じてナレッジベースから情報を取得し、F1データを自由に分析できるしくみをまとめました。
実際のログにある通り、ユーザーは自然言語で「最も多く優勝したドライバーは?」「2010年以降で一番勝ったドライバーは?」などと尋ねるだけで、エージェントが適切なクエリを組み立てて結果を返します。
もし本プロジェクトを試してみたい場合は、手元でリポジトリをgit clone
し、.env
に自分のAPIキーをセット → docker compose up -d
すればすぐにF1データの分析を始められます。ぜひご活用ください。
コメント