Open9

Langchain.jsのSQL Agentを試す(Cloud SQL)

hosaka313hosaka313

目標

いまさらながら、Langchainを用いて、自然言語でMySQLを操作してみる。

果たしてモデルの向上で使えるレベルになっているだろうか。

使うもの

  • langchain.js(TypeScript)
    • version 0.1.17
  • Cloud SQL(Cloud SQL Auth Proxy)
    • 結果的にはCloud SQL特有のエラーには遭遇しなかった。
hosaka313hosaka313

Cloud SQL作成

最小構成。一番安い、サンドボックスで作成。

テーブル作成

CREATE TABLE baseball_players (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100),
    position VARCHAR(50),
    team VARCHAR(100),
    nationality VARCHAR(50)
);

データ追加

データはChatGPTで作成。選手情報は細部が異なっている可能性あり。

INSERT INTO baseball_players (name, position, team, nationality) VALUES
('クレイトン・カーショウ', '投手', 'ドジャース', 'アメリカ'),
('マイク・トラウト', '外野手', 'エンゼルス', 'アメリカ'),
('ミゲル・カブレラ', '一塁手', 'タイガース', 'ベネズエラ'),
('フェルナンド・タティス・ジュニア', '遊撃手', 'パドレス', 'ドミニカ共和国'),
('ホセ・アルトゥーベ', '二塁手', 'アストロズ', 'ベネズエラ'),
('ヤディアー・モリーナ', '捕手', 'カージナルス', 'プエルトリコ'),
('マックス・シャーザー', '投手', 'メッツ', 'アメリカ'),
('ロナルド・アクーニャ・ジュニア', '外野手', 'ブレーブス', 'ベネズエラ'),
('フランシスコ・リンドーア', '遊撃手', 'メッツ', 'プエルトリコ'),
('ハビアー・バエズ', '二塁手/遊撃手', 'タイガース', 'プエルトリコ'),
('ジェイコブ・デグロム', '投手', 'メッツ', 'アメリカ'),
('ノーラン・アレナド', '三塁手', 'カージナルス', 'アメリカ'),
('フレディ・フリーマン', '一塁手', 'ドジャース', 'アメリカ'),
('ジャスティン・バーランダー', '投手', 'アストロズ', 'アメリカ'),
('ゲリット・コール', '投手', 'ヤンキース', 'アメリカ'),
('ブライス・ハーパー', '外野手', 'フィリーズ', 'アメリカ'),
('マニー・マチャド', '三塁手', 'パドレス', 'ドミニカ共和国'),
('アーロン・ジャッジ', '外野手', 'ヤンキース', 'アメリカ'),
('トレバー・ストーリー', '遊撃手', 'レッドソックス', 'アメリカ'),
('クリスチャン・イエリッチ', '外野手', 'ブルワーズ', 'アメリカ'),
('コーディ・ベリンジャー', '外野手', 'ドジャース', 'アメリカ'),
('ピート・アロンソ', '一塁手', 'メッツ', 'アメリカ'),
('タイラー・オニール', '外野手', 'カージナルス', 'カナダ'),
('カルロス・コレア', '遊撃手', 'ツインズ', 'プエルトリコ'),
('イチロー', '外野手', 'マリナーズ', '日本'),
('バベ・ルース', '外野手', 'ヤンキース', 'アメリカ'),
('ハンク・アーロン', '外野手', 'ブレーブス', 'アメリカ'),
('デレク・ジーター', '遊撃手', 'ヤンキース', 'アメリカ'),
('大谷翔平', '投手/指名打者', 'エンゼルス', '日本');

Cloud SQL Admin APIの有効化

Auth Proxyを使うための前提。

hosaka313hosaka313

プロジェクト作成

$pnpm init

依存の追加

ORMにはtypormを採用しているようです。

$pnpm add langchain @langchain/openai typorm dotenv
hosaka313hosaka313

gcloud CLIのセットアップ

Cloud SQLを作成したプロジェクトで認証します。

$gcloud auth login
$gcloud config set projects <PROJECT_ID>
$gcloud auth application-default login

Cloud SQL Auth Proxyをダウンロード

ローカルからCloud SQLにアクセスするためにCloud SQL Auth Proxyをダウンロードします。

筆者はM1 Macを使用しているので、そのタブの指示に従います。

curl -o cloud-sql-proxy https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.6.1/cloud-sql-proxy.darwin.arm64

chmod +x cloud-sql-proxy

https://cloud.google.com/sql/docs/mysql/connect-auth-proxy?hl=ja#mac-m1

./cloud-sql-proxy --address 0.0.0.0 --port 1234 <CONNECTION_NAME>
hosaka313hosaka313

read-only userの作成

Langchainのドキュメントにも書かれていますが、SQLツールはだいぶ危険です。
誤ってレコードの破壊的変更をしないように、read-only userを作成します。

Disclaimer ⚠️
The query chain may generate insert/update/delete queries. When this is not expected, use a custom prompt or create a SQL users without write permissions.

https://python.langchain.com/docs/integrations/toolkits/sql_database

CREATE USER 'readonly_user'@'%' IDENTIFIED BY 'zenn';
GRANT SELECT ON database_name.* TO 'readonly_user'@'%';
SHOW GRANTS for 'readonly_user'@'%';

Cloud SQLの管理画面からユーザーを作ると、デフォルトでcloudsqlsuperuserという権限が与えられるので、注意です。

正常に権限が絞れているとUPDATE時などに下記のエラーが出ます。

ERROR 1142 (42000): UPDATE command denied to user 'langchain'@'cloudsqlproxy~<ip>' for table '<table>'


管理画面から作成するとcloudsqlsuperuserとなる

hosaka313hosaka313

実行

いざ実行。

https://js.langchain.com/docs/modules/chains/popular/sqlite

import { OpenAI } from "@langchain/openai";
import { SqlDatabase } from "langchain/sql_db";
import { createSqlAgent, SqlToolkit } from "langchain/agents/toolkits/sql";
import { DataSource } from "typeorm";
import * as dotenv from "dotenv";

dotenv.config();

export const run = async (input: string) => {
  const datasource = new DataSource({
    type: "mysql",
    database: "test",
    host: "127.0.0.1",
    port: 1234,
    username: "readonly_user",
    password: "zenn",
  });
  const db = await SqlDatabase.fromDataSourceParams({
    appDataSource: datasource,
    includesTables: ["baseball_players"],
  });
  const model = new OpenAI({ temperature: 0, modelName: "gpt-3.5-turbo" });
  const toolkit = new SqlToolkit(db, model);
  const executor = createSqlAgent(model, toolkit);
  console.log(`Executing with input "${input}"...`);

  const result = await executor.invoke({ input });

  console.log(`Got output ${result.output}`);

  console.log(
    `Got intermediate steps ${JSON.stringify(
      result.intermediateSteps,
      null,
      2
    )}`
  );

  await datasource.destroy();
};

run("クエリ内容")
  .then(console.log)
  .catch(console.error);

エラー

DriverPackageNotInstalledError: Mysql package has not been found installed. Try to install it: npm install mysql --save

指示に従う。

$pnpm add mysql

コード実行

✅ "大谷翔平"のレコードを取得

まず、クエリ内容を"get 大谷翔平"としました。

結果は下記。

Executing with input "get 大谷翔平"...
Got output 大谷翔平 is a player in the baseball_players table with the name 大谷翔平.
Got intermediate steps [
  {
    "action": {
      "tool": "list-tables-sql",
      "toolInput": "",
      "log": "Action: list-tables-sql\nAction Input: \"\""
    },
    "observation": "baseball_players"
  },
  {
    "action": {
      "tool": "query-sql",
      "toolInput": "SELECT * FROM baseball_players WHERE name='大谷翔平' LIMIT 1;",
      "log": " I should query the baseball_players table for the player with the name 大谷翔平.\nAction: query-sql\nAction Input: \"SELECT * FROM baseball_players WHERE name='大谷翔平' LIMIT 1;\""
    },
    "observation": "[{\"id\":29,\"name\":\"大谷翔平\",\"position\":\"投手/指名打者\",\"team\":\"エンゼルス\",\"nationality\":\"日本\"}]"
  }
]

テーブル一覧をチェックし、正確に回答できました。

✅ 大谷翔平さんはどこのチームに所属していますか?

Executing with input "大谷翔平さんはどこのチームに所属していますか?"...
Got output エンゼルス
Got intermediate steps [
  {
    "action": {
      "tool": "list-tables-sql",
      "toolInput": "",
      "log": "Action: list-tables-sql\nAction Input: "
    },
    "observation": "baseball_players"
  },
  {
    "action": {
      "tool": "query-checker",
      "toolInput": "SELECT team FROM baseball_players WHERE name='大谷翔平'",
      "log": " I should query the baseball_players table to find the team that 大谷翔平さん belongs to.\nAction: query-checker\nAction Input: SELECT team FROM baseball_players WHERE name='大谷翔平'"
    },
    "observation": "\n\nSELECT team FROM baseball_players WHERE name='大谷翔平'"
  },
  {
    "action": {
      "tool": "query-sql",
      "toolInput": "SELECT team FROM baseball_players WHERE name='大谷翔平'",
      "log": " I should execute the query to get the team that 大谷翔平さん belongs to.\nAction: query-sql\nAction Input: SELECT team FROM baseball_players WHERE name='大谷翔平'"
    },
    "observation": "[{\"team\":\"エンゼルス\"}]"
  }
]

データが古いですが、正確に回答できています。

✅ 日本(Japan)出身の選手名を列挙してください。

Executing with input "日本(Japan)出身の選手名を列挙してください。"...
Got output イチロー、大谷翔平
Got intermediate steps [
  {
    "action": {
      "tool": "list-tables-sql",
      "toolInput": "",
      "log": "Action: list-tables-sql\nAction Input: "
    },
    "observation": "baseball_players"
  },
  {
    "action": {
      "tool": "info-sql",
      "toolInput": "baseball_players",
      "log": "I can query the `baseball_players` table to find the names of players from Japan.\nAction: info-sql\nAction Input: baseball_players"
    },
    "observation": "CREATE TABLE baseball_players (\nid int NOT NULL, name varchar , position varchar , team varchar , nationality varchar ) \nSELECT * FROM `baseball_players` LIMIT 3;\n id name position team nationality\n 1 クレイトン・カーショウ 投手 ドジャース アメリカ\n 2 マイク・トラウト 外野手 エンゼルス アメリカ\n 3 ミゲル・カブレラ 一塁手 タイガース ベネズエラ\n"
  },
  {
    "action": {
      "tool": "query-sql",
      "toolInput": "SELECT name FROM baseball_players WHERE nationality = '日本' LIMIT 10;",
      "log": "I can now construct a query to retrieve the names of players from Japan.\nAction: query-sql\nAction Input: SELECT name FROM baseball_players WHERE nationality = '日本' LIMIT 10;"
    },
    "observation": "[{\"name\":\"イチロー\"},{\"name\":\"大谷翔平\"}]"
  }
]

info-sqlステップで実データを確認しているようです。

少し意地悪をしてみます。

❌ Japan出身の選手名を列挙してください。

Executing with input "Japan出身の選手名を列挙してください。"...
Got output I don't know
Got intermediate steps [
  {
    "action": {
      "tool": "list-tables-sql",
      "toolInput": "",
      "log": "Action: list-tables-sql\nAction Input: "
    },
    "observation": "baseball_players"
  },
  {
    "action": {
      "tool": "info-sql",
      "toolInput": "baseball_players",
      "log": "I can query the `baseball_players` table to find players from Japan.\nAction: info-sql\nAction Input: baseball_players"
    },
    "observation": "CREATE TABLE baseball_players (\nid int NOT NULL, name varchar , position varchar , team varchar , nationality varchar ) \nSELECT * FROM `baseball_players` LIMIT 3;\n id name position team nationality\n 1 クレイトン・カーショウ 投手 ドジャース アメリカ\n 2 マイク・トラウト 外野手 エンゼルス アメリカ\n 3 ミゲル・カブレラ 一塁手 タイガース ベネズエラ\n"
  },
  {
    "action": {
      "tool": "query-sql",
      "toolInput": "SELECT name FROM baseball_players WHERE nationality = 'Japan' LIMIT 10;",
      "log": "I can now construct a query to retrieve the names of players from Japan.\nAction: query-sql\nAction Input: SELECT name FROM baseball_players WHERE nationality = 'Japan' LIMIT 10;"
    },
    "observation": "[]"
  }
]

誘導通り、「日本」ではなく、Japanにしたところ、ダメでした。

✅ Japan出身の選手名を列挙してください。(gpt-4-0125-preview)

モデルを変えて再挑戦。

Executing with input "Japan出身の選手名を列挙してください。"...
Got output The names of players from Japan are イチロー and 大谷翔平.
Got intermediate steps [
  {
    "action": {
      "tool": "list-tables-sql",
      "toolInput": "",
      "log": "Action: list-tables-sql\nAction Input: "
    },
    "observation": "baseball_players"
  },
  {
    "action": {
      "tool": "info-sql",
      "toolInput": "baseball_players",
      "log": "I need to check the schema of the baseball_players table to understand its structure and find out which columns might contain information about players' names and their country of origin.\nAction: info-sql\nAction Input: baseball_players"
    },
    "observation": "CREATE TABLE baseball_players (\nid int NOT NULL, name varchar , position varchar , team varchar , nationality varchar ) \nSELECT * FROM `baseball_players` LIMIT 3;\n id name position team nationality\n 1 クレイトン・カーショウ 投手 ドジャース アメリカ\n 2 マイク・トラウト 外野手 エンゼルス アメリカ\n 3 ミゲル・カブレラ 一塁手 タイガース ベネズエラ\n"
  },
  {
    "action": {
      "tool": "query-checker",
      "toolInput": "SELECT name FROM baseball_players WHERE nationality = '日本' LIMIT 10;",
      "log": "The baseball_players table contains a column for nationality, which I can use to filter for players from Japan. The name column will provide the names of the players.\nAction: query-checker\nAction Input: SELECT name FROM baseball_players WHERE nationality = '日本' LIMIT 10;"
    },
    "observation": "The provided SQLite query does not exhibit any of the common mistakes listed. It correctly selects the first 10 names from the `baseball_players` table where the nationality is Japanese ('日本'). The query properly quotes the string literal for the nationality condition. There are no uses of NOT IN with NULL values, UNION vs. UNION ALL issues, BETWEEN for exclusive ranges, data type mismatches in predicates, improper quoting of identifiers, incorrect number of arguments for functions, incorrect casting to data types, or improper columns used for joins. Therefore, the original query is reproduced below without any changes:\n\n```sql\nSELECT name FROM baseball_players WHERE nationality = '日本' LIMIT 10;\n```"
  },
  {
    "action": {
      "tool": "query-sql",
      "toolInput": "SELECT name FROM baseball_players WHERE nationality = '日本' LIMIT 10;",
      "log": "The query checker has confirmed that my query is correct. Now, I will execute the query to get the names of players from Japan.\nAction: query-sql\nAction Input: SELECT name FROM baseball_players WHERE nationality = '日本' LIMIT 10;"
    },
    "observation": "[{\"name\":\"イチロー\"},{\"name\":\"大谷翔平\"}]"
  }
]

うまくいきました。info-sqlでデータを覗いて、日本語だったので察してくれたようです。

大谷翔平さんはエンゼルスからドジャースに移籍しました。データベースを更新してください。

read-onlyのユーザー以外で破壊的操作も検証してみます。

Executing with input "大谷翔平さんはエンゼルスからドジャースに移籍しました。データベースを更新してください。"...
Got output 大谷翔平さんはエンゼルスからドジャースに移籍しました。データベースが更新されました。
Got intermediate steps [
  {
    "action": {
      "tool": "list-tables-sql",
      "toolInput": "",
      "log": "Action: list-tables-sql\nAction Input: \"\""
    },
    "observation": "baseball_players"
  },
  {
    "action": {
      "tool": "query-checker",
      "toolInput": "UPDATE baseball_players SET team = 'Dodgers' WHERE name = '大谷翔平'",
      "log": " I should check the query before executing it.\nAction: query-checker\nAction Input: \"UPDATE baseball_players SET team = 'Dodgers' WHERE name = '大谷翔平'\""
    },
    "observation": "\n\nUPDATE baseball_players SET team = 'Dodgers' WHERE name = '大谷翔平';"
  },
  {
    "action": {
      "tool": "query-sql",
      "toolInput": "UPDATE baseball_players SET team = 'Dodgers' WHERE name = '大谷翔平'",
      "log": " I should execute the query to update the database.\nAction: query-sql\nAction Input: \"UPDATE baseball_players SET team = 'Dodgers' WHERE name = '大谷翔平'\""
    },
    "observation": "{\"fieldCount\":0,\"affectedRows\":1,\"insertId\":0,\"serverStatus\":34,\"warningCount\":0,\"message\":\"(Rows matched: 1  Changed: 1  Warnings: 0\",\"protocol41\":true,\"changedRows\":1}"
  }
]

英語で登録されてしまいました。

✅ 大谷翔平さんはブルージェイズに移籍しました。大谷翔平さんのteamをブルージェイズに更新してください(読み取りユーザーでの実行)

読み取りユーザーに変更して、起きてはいけない破壊的なクエリを投げてみます。

Executing with input "大谷翔平さんはブルージェイズに移籍しました。大谷翔平さんのteamをブルージェイズに更新してください。"...
Got output I don't know.
Got intermediate steps [
  {
    "action": {
      "tool": "list-tables-sql",
      "toolInput": "",
      "log": "Action: list-tables-sql\nAction Input: "
    },
    "observation": "baseball_players"
  },
  {
    "action": {
      "tool": "query-sql",
      "toolInput": "UPDATE baseball_players SET team = 'Blue Jays' WHERE name = '大谷翔平';",
      "log": "I can query the baseball_players table to update the team for Shohei Ohtani.\nAction: query-sql\nAction Input: UPDATE baseball_players SET team = 'Blue Jays' WHERE name = '大谷翔平';"
    },
    "observation": "QueryFailedError: ER_TABLEACCESS_DENIED_ERROR: UPDATE command denied to user 'readonly_user'@'cloudsqlproxy~<ip>' for table 'baseball_players'"
  }
]
 "observation": "QueryFailedError: ER_TABLEACCESS_DENIED_ERROR: UPDATE command denied to user 'readonly_user'@'cloudsqlproxy~<ip>' for table 'baseball_players'"

とエラーが出て、回答はI don't know.となりました。

❌ 二刀流の選手を抽出してください。

少し曖昧な質問をしてみます。

Executing with input "二刀流の選手を抽出してください。"...
Got output クレイトン・カーショウ, マイク・トラウト, マックス・シャーザー, ロナルド・アクーニャ・ジュニア, ジェイコブ・デグロム, ジャスティン・バーランダー, ゲリット・コール, ブライス・ハーパー, アーロン・ジャッジ, クリスチャン・イエリッチ
Got intermediate steps [
  {
    "action": {
      "tool": "list-tables-sql",
      "toolInput": "",
      "log": "Action: list-tables-sql\nAction Input: "
    },
    "observation": "baseball_players"
  },
  {
    "action": {
      "tool": "info-sql",
      "toolInput": "baseball_players",
      "log": "I can query the `baseball_players` table to find players who are 二刀流.\nAction: info-sql\nAction Input: baseball_players"
    },
    "observation": "CREATE TABLE baseball_players (\nid int NOT NULL, name varchar , position varchar , team varchar , nationality varchar ) \nSELECT * FROM `baseball_players` LIMIT 3;\n id name position team nationality\n 1 クレイトン・カーショウ 投手 ドジャース アメリカ\n 2 マイク・トラウト 外野手 エンゼルス アメリカ\n 3 ミゲル・カブレラ 一塁手 タイガース ベネズエラ\n"
  },
  {
    "action": {
      "tool": "query-sql",
      "toolInput": "SELECT name, position, team, nationality FROM baseball_players WHERE position = '投手' AND position = '外野手' LIMIT 10;",
      "log": "I can now construct a query to find 二刀流 players in the `baseball_players` table.\nAction: query-sql\nAction Input: SELECT name, position, team, nationality FROM baseball_players WHERE position = '投手' AND position = '外野手' LIMIT 10;"
    },
    "observation": "[]"
  },
  {
    "action": {
      "tool": "query-sql",
      "toolInput": "SELECT name, position, team, nationality FROM baseball_players WHERE position = '投手' OR position = '外野手' LIMIT 10;",
      "log": "I made a mistake in my query. I need to use the OR operator instead of AND to find players who are both 投手 and 外野手.\nAction: query-sql\nAction Input: SELECT name, position, team, nationality FROM baseball_players WHERE position = '投手' OR position = '外野手' LIMIT 10;"
    },
    "observation": "[{\"name\":\"クレイトン・カーショウ\",\"position\":\"投手\",\"team\":\"ドジャース\",\"nationality\":\"アメリカ\"},{\"name\":\"マイク・トラウト\",\"position\":\"外野手\",\"team\":\"エンゼルス\",\"nationality\":\"アメリカ\"},{\"name\":\"マックス・シャーザー\",\"position\":\"投手\",\"team\":\"メッツ\",\"nationality\":\"アメリカ\"},{\"name\":\"ロナルド・アクーニャ・ジュニア\",\"position\":\"外野手\",\"team\":\"ブレーブス\",\"nationality\":\"ベネズエラ\"},{\"name\":\"ジェイコブ・デグロム\",\"position\":\"投手\",\"team\":\"メッツ\",\"nationality\":\"アメリカ\"},{\"name\":\"ジャスティン・バーランダー\",\"position\":\"投手\",\"team\":\"アストロズ\",\"nationality\":\"アメリカ\"},{\"name\":\"ゲリット・コール\",\"position\":\"投手\",\"team\":\"ヤンキース\",\"nationality\":\"アメリカ\"},{\"name\":\"ブライス・ハーパー\",\"position\":\"外野手\",\"team\":\"フィリーズ\",\"nationality\":\"アメリカ\"},{\"name\":\"アーロン・ジャッジ\",\"position\":\"外野手\",\"team\":\"ヤンキース\",\"nationality\":\"アメリカ\"},{\"name\":\"クリスチャン・イエリッチ\",\"position\":\"外野手\",\"team\":\"ブルワーズ\",\"nationality\":\"アメリカ\"}]"
  }
]

WHERE position = '投手' OR position = '外野手' としてしまいました。

hosaka313hosaka313

ソースコードを除く

Toolkitのなかみ

sql.js
  new QuerySqlTool(db),
  new InfoSqlTool(db),
  new ListTablesSqlTool(db),
  new QueryCheckerTool({ llm }),

QueryCheckerToolが面白そうなので、promptを覗く。

tools/sql.js
`
    {query}
Double check the sqlite query above for common mistakes, including:
- Using NOT IN with NULL values
- Using UNION when UNION ALL should have been used
- Using BETWEEN for exclusive ranges
- Data type mismatch in predicates
- Properly quoting identifiers
- Using the correct number of arguments for functions
- Casting to the correct data type
- Using the proper columns for joins

If there are any of the above mistakes, rewrite the query. If there are no mistakes, just reproduce the original query.`

Agentのプロンプト

export const SQL_PREFIX = `You are an agent designed to interact with a SQL database.
Given an input question, create a syntactically correct {dialect} query to run, then look at the results of the query and return the answer.
Unless the user specifies a specific number of examples they wish to obtain, always limit your query to at most {top_k} results using the LIMIT clause.
You can order the results by a relevant column to return the most interesting examples in the database.
Never query for all the columns from a specific table, only ask for a the few relevant columns given the question.
You have access to tools for interacting with the database.
Only use the below tools. Only use the information returned by the below tools to construct your final answer.
You MUST double check your query before executing it. If you get an error while executing a query, rewrite the query and try again.

DO NOT make any DML statements (INSERT, UPDATE, DELETE, DROP etc.) to the database.

If the question does not seem related to the database, just return "I don't know" as the answer.`;
export const SQL_SUFFIX = `Begin!

Question: {input}
Thought: I should look at the tables in the database to see what I can query.
{agent_scratchpad}`;

プロンプトのカスタマイズ

少しカスタマイズしたい場合は、agentの引数にprefixsuffixを渡します。
日本語で回答させたい場合などに追記するのが良いでしょう。

hosaka313hosaka313

おわりに

モデルの能力が飛躍的に向上したので期待していましたが、いまだ実務で使うとなると、おっかいないなという印象を受けました。(Joinも試してみようと思いましたが、やめました。)

reliableなセーフガード機構とLLMに最適化されたデータ作りが当面の課題に見えます。

hosaka313hosaka313

追記:関連レコードの操作

CREATE TABLE hitting_results (
    id INT AUTO_INCREMENT PRIMARY KEY,
    player_id INT,
    game_date DATE,
    result VARCHAR(50),
    rbis INT DEFAULT 0, -- 打点
    runs INT DEFAULT 0, -- 得点
    home_runs INT DEFAULT 0, -- ホームラン
    FOREIGN KEY (player_id) REFERENCES baseball_players(id)
);
INSERT INTO hitting_results (player_id, game_date, result, rbis, runs, home_runs) VALUES
(29, '2023-07-01', 'ホームラン', 1, 1, 1),
(29, '2023-07-01', '三振', 0, 0, 0),
(29, '2023-07-01', '四球', 0, 1, 0),
(29, '2023-07-01', '二塁打', 2, 1, 0);
  const db = await SqlDatabase.fromDataSourceParams({
    appDataSource: datasource,
+    includesTables: ["baseball_players", "hitting_results"],
  });

❌ 大谷翔平さんの打席結果を調べてください

エラーになった。

Executing with input "大谷翔平さんの打席結果を調べてください"...
OutputParserException [Error]: Could not parse LLM output: I couldn't find any batting results for 大谷翔平.

✅ 大谷翔平さんの打席結果を調べてください(gpt-4)

モデルの暴力で解決するか...?

+  const model = new OpenAI({ temperature: 0, modelName: "gpt-4-0125-preview" });

1回こけたが、再試行で下記の結果を得た。

Executing with input "大谷翔平さんの打席結果を調べてください"...
Got output The most recent batting results for Shohei Ohtani are as follows:
1. On 2023-06-30, he hit a ホームラン (home run) with 1 home run, 1 RBI, and scored 1 run.
2. On 2023-06-30, he struck out (三振) with 0 home runs, 0 RBIs, and scored 0 runs.
3. On 2023-06-30, he walked (四球) with 0 home runs, 0 RBIs, and scored 1 run.
4. On 2023-06-30, he hit a 二塁打 (double) with 0 home runs, 2 RBIs, and scored 1 run.
Got intermediate steps [
  {
    "action": {
      "tool": "list-tables-sql",
      "toolInput": "",
      "log": "Action: list-tables-sql\nAction Input: "
    },
    "observation": "baseball_players, hitting_results"
  },
  {
    "action": {
      "tool": "info-sql",
      "toolInput": "hitting_results",
      "log": "I need to check the schema of the hitting_results table to understand how to query for Shohei Ohtani's batting results.\nAction: info-sql\nAction Input: hitting_results"
    },
    "observation": "CREATE TABLE hitting_results (\ngame_date date , home_runs int , id int NOT NULL, player_id int , rbis int , result varchar , runs int ) \nSELECT * FROM `hitting_results` LIMIT 3;\n game_date home_runs id player_id rbis result runs\n 1 29 Sat Jul 01 2023 00:00:00 GMT+0900 (日本標準時) ホームラン 1 1 1\n 2 29 Sat Jul 01 2023 00:00:00 GMT+0900 (日本標準時) 三振 0 0 0\n 3 29 Sat Jul 01 2023 00:00:00 GMT+0900 (日本標準時) 四球 0 1 0\n"
  },
  {
    "action": {
      "tool": "info-sql",
      "toolInput": "baseball_players",
      "log": "I need to find Shohei Ohtani's player_id from the baseball_players table to query his batting results.\nAction: info-sql\nAction Input: baseball_players"
    },
    "observation": "CREATE TABLE baseball_players (\nid int NOT NULL, name varchar , nationality varchar , position varchar , team varchar ) \nSELECT * FROM `baseball_players` LIMIT 3;\n id name nationality position team\n 1 クレイトン・カーショウ 投手 ドジャース アメリカ\n 2 マイク・トラウト 外野手 エンゼルス アメリカ\n 3 ミゲル・カブレラ 一塁手 タイガース ベネズエラ\n"
  },
  {
    "action": {
      "tool": "query-checker",
      "toolInput": "SELECT id FROM baseball_players WHERE name = '大谷翔平'",
      "log": "I need to find Shohei Ohtani's player_id by querying the baseball_players table.\nAction: query-checker\nAction Input: SELECT id FROM baseball_players WHERE name = '大谷翔平'"
    },
    "observation": "The provided SQLite query does not exhibit any of the common mistakes listed. It is a simple SELECT statement querying for the `id` of a baseball player with a specific name. The query correctly uses single quotes for the string literal, which is the proper way to denote string values in SQL. There are no uses of NOT IN with NULL values, UNION vs. UNION ALL issues, BETWEEN for exclusive ranges, data type mismatches, improper quoting of identifiers, incorrect number of arguments for functions, incorrect casting, or improper columns for joins. Therefore, the original query is reproduced below without any modifications:\n\n```sql\nSELECT id FROM baseball_players WHERE name = '大谷翔平'\n```"
  },
  {
    "action": {
      "tool": "query-sql",
      "toolInput": "SELECT id FROM baseball_players WHERE name = '大谷翔平'",
      "log": "The query checker indicates that my query is correct, so I should execute it to find Shohei Ohtani's player_id.\nAction: query-sql\nAction Input: SELECT id FROM baseball_players WHERE name = '大谷翔平'"
    },
    "observation": "[{\"id\":29}]"
  },
  {
    "action": {
      "tool": "query-checker",
      "toolInput": "SELECT game_date, result, home_runs, rbis, runs FROM hitting_results WHERE player_id = 29 ORDER BY game_date DESC LIMIT 10",
      "log": "Now that I have Shohei Ohtani's player_id, which is 29, I can query the hitting_results table for his batting results.\nAction: query-checker\nAction Input: SELECT game_date, result, home_runs, rbis, runs FROM hitting_results WHERE player_id = 29 ORDER BY game_date DESC LIMIT 10"
    },
    "observation": "The provided SQLite query does not exhibit any of the common mistakes listed. It correctly orders the results by `game_date` in descending order and limits the output to the 10 most recent records for `player_id = 29`. The query syntax appears correct for its intended purpose, and none of the specific issues mentioned (such as misuse of NOT IN with NULL values, incorrect use of UNION/UNION ALL, misuse of BETWEEN for exclusive ranges, data type mismatches, improper quoting, incorrect function arguments, incorrect casting, or improper join columns) are present in this query. Therefore, the original query is reproduced below without changes:\n\n```sql\nSELECT game_date, result, home_runs, rbis, runs FROM hitting_results WHERE player_id = 29 ORDER BY game_date DESC LIMIT 10\n```"
  },
  {
    "action": {
      "tool": "query-sql",
      "toolInput": "SELECT game_date, result, home_runs, rbis, runs FROM hitting_results WHERE player_id = 29 ORDER BY game_date DESC LIMIT 10",
      "log": "The query checker indicates that my query is correct, so I should execute it to get the most recent batting results for Shohei Ohtani.\nAction: query-sql\nAction Input: SELECT game_date, result, home_runs, rbis, runs FROM hitting_results WHERE player_id = 29 ORDER BY game_date DESC LIMIT 10"
    },
    "observation": "[{\"game_date\":\"2023-06-30T15:00:00.000Z\",\"result\":\"ホームラン\",\"home_runs\":1,\"rbis\":1,\"runs\":1},{\"game_date\":\"2023-06-30T15:00:00.000Z\",\"result\":\"三振\",\"home_runs\":0,\"rbis\":0,\"runs\":0},{\"game_date\":\"2023-06-30T15:00:00.000Z\",\"result\":\"四球\",\"home_runs\":0,\"rbis\":0,\"runs\":1},{\"game_date\":\"2023-06-30T15:00:00.000Z\",\"result\":\"二塁打\",\"home_runs\":0,\"rbis\":2,\"runs\":1}]"
  }
]