AutoGenをガッツリ試してみる:SQLを利用してみよう
概要
こんなアドベントカレンダーを執筆していたら時間が経ってしまいましたが、引き続きAutoGenを使ってみます。これまでは、テキストで会話しつつ分析をPythonにやらせる流れでしたが、もう少し違うツールも使ってみたくなります。そこで本日はAgentでSQLの仕組みを使ってみようと思います。
原理
AutoGenから直接SQLを叩く仕組みを作るためには、保持しているテーブルをチェックしたり、SQLのクエリを生成して実行し、最後にテキストやグラフ・テーブルなど必要な形式で出力するなど、複数のステップに分かれるため、少し大変です。今回はLangChainの仕組みを使うことで簡易的にAutoGenからSQLを扱えるようにしてみましょう。
より具体的には、Function Callingとして、SQLを利用する仕組みを呼べるようにしておくことで実現します。SQLを利用する仕組みは、LangChainの中にあるagent toolkitを利用できます。ちなみに、DBを用意するのが楽だったのでSQLiteを利用していますが、LangChainで利用できるものであればMySQLでもPostgreSQLでも利用できると思います。
準備
いつものようにライブラリをインストールしていきましょう。今回は以下の他にも使うライブラリがありそうで、必要に応じてインストールしてください。
pip install pyautogen==0.2.2
pip install langchain==0.0.352
はじめに、Agentから利用するデータベースを作成しましょう。とりあえず簡易的なものを作っていきます。生徒テーブル、科目テーブル、テストの点数のテーブルを作っておきます。
import sqlite3
connection = sqlite3.connect('sample.db')
cursor = connection.cursor()
create_student_table = """CREATE TABLE students (
id INTEGER,
name TEXT NOT NULL,
age INTEGER NOT NULL
);"""
cursor.execute(create_student_table)
create_courses_table = """CREATE TABLE courses (
id INTEGER,
course TEXT NOT NULL
);"""
cursor.execute(create_courses_table)
create_grades_table = """CREATE TABLE grades (
name INTEGER NOT NULL,
course TEXT NOT NULL,
grade FLOAT NOT NULL
);"""
cursor.execute(create_grades_table)
students = [(1, "Taro", 20), (2, "Hanako", 19), (3, "Jiro", 21)]
courses = [(1, "Math"), (2, "English"), (3, "Physics")]
grades = [
("Taro", "Math", 85.0),
("Taro", "English", 90.0),
("Taro", "Physics", 88.0),
("Hanako", "Math", 75.0),
("Hanako", "English", 80.0),
("Hanako", "Physics", 82.0),
("Jiro", "Math", 92.0),
("Jiro", "English", 95.0),
("Jiro", "Physics", 89.0),
]
insert_students = """INSERT INTO students (id, name, age) VALUES (?, ?, ?);"""
cursor.executemany(insert_students, students)
insert_courses = """INSERT INTO courses (id, course) VALUES (?, ?);"""
cursor.executemany(insert_courses, courses)
insert_grades = """INSERT INTO grades (name, course, grade) VALUES (?, ?, ?);"""
cursor.executemany(insert_grades, grades)
connection.commit()
cursor.close()
Functionsの作成
はじめにLangChainの仕組みを使いデータベースを読み込みます。LangChainの提供するSQLDatabaseToolkitを利用することで、自然言語でのSQLの実行がきます。
from langchain.agents import create_sql_agent
from langchain.agents.agent_toolkits import SQLDatabaseToolkit
from langchain.utilities.sql_database import SQLDatabase
os.environ["OPENAI_API_KEY"] = "<API Key>"
sql = SQLDatabase.from_uri('sqlite:///sample.db')
llm = OpenAI(model_name="gpt-3.5-turbo-instruct", temperature=0)
toolkit = SQLDatabaseToolkit(db=sql, llm=llm)
以下のようにして、作成したtoolkitを呼び出すためのFunction Callingのスキーマを作ってみましょう。toolkitのパラメータをgenerate_llm_config関数で変換しtool_schemaに保存します。また、LangChainのtoolkitを呼び出すのはtool._runであり、これをfunction_mapに登録します。
def generate_llm_config(tool):
# LangChainのAgentツールからFunction schemaを作成
function_schema = {
"name": tool.name.lower().replace (' ', '_'),
"description": tool.description,
"parameters": {
"type": "object",
"properties": {},
"required": [],
},
}
if tool.args is not None:
function_schema["parameters"]["properties"] = tool.args
return function_schema
# Now use AutoGen with Langchain Tool Bridgre
tools = []
function_map = {}
for tool in toolkit.get_tools(): #debug_toolkit if you want to use tools directly
tool_schema = generate_llm_config(tool)
tools.append(tool_schema)
function_map[tool.name] = tool._run
Agentの作成
いつも通りAgentを作っていきましょう。複数の会話が連続する中で、タスクを終了した時にOpenAIのAgentがTERMINATEを返すのを読み込んで、UserProxyは動作を停止します。また、先ほど作成したfunction_mapを登録します。
import autogen
config_list = [{
'model': 'gpt-4-1106-preview',
'api_key': '<API Key>'
}]
# Construct the llm_config
llm_config = {
"functions": tools,
"config_list": config_list, # Assuming you have this defined elsewhere
"timeout": 120,
}
user_proxy = autogen.UserProxyAgent(
name="user_proxy",
is_termination_msg=lambda x: x.get("content", "") and x.get("content", "").rstrip().endswith("TERMINATE"),
human_input_mode="NEVER",
max_consecutive_auto_reply=10,
code_execution_config={"work_dir": "tmp", "use_docker": False},
)
# Register the tool and start the conversation
user_proxy.register_function(
function_map = function_map
)
chatbot = autogen.AssistantAgent(
name="chatbot",
system_message="For coding tasks, only use the functions you have been provided with. Reply TERMINATE when the task is done.",
llm_config=llm_config,
)
実行結果(テーブルの概要)
まずはテーブルの内容の説明をしてもらいました。長いので結果のみ出力します。なお、SQLの場合利用できるツールは、sql_db_query、sql_db_schema、sql_db_list_tables、sql_db_query_checkerがありますが、今回はsql_db_list_tablesを利用してテーブルリストを抽出してからsql_db_schemaでテーブルの詳細を見ているようで、多段階での作業ができることを確認できました。
Q: どんなデータを持っていますか
A: 以下のスキーマとサンプルデータを持つ3つのテーブルがデータベースに存在します:
1. `courses` テーブル:
- `id`: コースID(整数)
- `course`: コース名(テキスト、null不可)
サンプルデータ:
```
| id | course |
|----|---------|
| 1 | Math |
| 2 | English |
| 3 | Physics |
```
2. `grades` テーブル:
- `name`: 学生名(整数、null不可)
- `course`: コース名(テキスト、null不可)
- `grade`: 成績(浮動小数点数、null不可)
サンプルデータ:
```
| name | course | grade |
|-------|---------|-------|
| Taro | Math | 85.0 |
| Taro | English | 90.0 |
| Taro | Physics | 88.0 |
```
3. `students` テーブル:
- `id`: 学生ID(整数)
- `name`: 学生名(テキスト、null不可)
- `age`: 年齢(整数、null不可)
サンプルデータ:
```
| id | name | age |
|----|--------|-----|
| 1 | Taro | 20 |
| 2 | Hanako | 19 |
| 3 | Jiro | 21 |
```
これらの情報を使って、さらにクエリを作成したり、データを分析することができます。何か特定の情報が必要であれば、質問してください。
実行結果(データベースへの数値の質問)
データベースに対して、生徒は何人ですか?と質問を投げてみましょう。
user_proxy.initiate_chat(
chatbot,
message="生徒は何人ですか?",
llm_config=llm_config,
)
結果は以下になりました。
現在、生徒は3人です。
続いて、微妙に複雑なクエリを投げてみましょう。
Q: 英語の点数が一番高い生徒は
A: The student with the highest English score is Taro.
終わりに
このようにFunction Callingを併用することで、LangChainの資産の流用ができます。今回の実験では、その仕組を利用してSQLを扱えるようにしてみました。DBは様々なシーンで使われている割に、特に非エンジニアの場合、クエリを書くのが苦手な人も多いので、こういった機能は有用ではないでしょうか。
この記事が気に入ったらサポートをしてみませんか?