曖昧な指示文からデータベースを操作できるか?生成AIによるText-to-SQLの変換精度を検証

こんにちは、AITC AIソリューショングループの阿田木です。

この記事では、自然言語によるデータベースの操作を可能にするText-to-SQL技術を検証します。

通常データベースを操作するには、SQLに代表されるデータベース操作言語が使用されますが、Text-to-SQL技術を応用することで、データベース操作言語に馴染みがないユーザーであっても一般的な日本語(自然言語)の文章でデータベースを操作することが可能になります。

このText-to-SQLを使用し、ユーザー側の指示文(クエリ)に対して生成AIがどれほどの精度でSQL文を生成し質問に対する回答を生成できるかを検証します。

はじめに

Text-to-SQLの重要性と基本的な概念の紹介

Text-to-SQLは、ユーザーが自然言語で入力したクエリをSQL文に変換する技術です。この技術はデータベース操作言語ではなく自然言語によりデータベースから情報取得し、エンジニアではない方でも自由にデータベースを操作可能にすることから、データ活用の民主化を促進すると言われています。

生成AI活用例としては、RAG(Retrieval-Augmented Generation)と言われる社内にあるPDFやテキスト文書を参照・要約させて生成AIに業務固有の知識を答えさせるユースケースが着目されていますが、社内データベースとChatGPTの連携をText-to-SQLによって連携させることも、お客様からの要求として頂いています。

生成AIを活用したText-to-SQLの魅力や実用性については以下の記事で詳しく説明しています。 aitc.dentsusoken.com

Text-to-SQLが実用化されれば、非常に便利で可能性を感じさせる技術となります。しかし、自然言語の曖昧さや多様性があるため、正確なSQLクエリの生成という点では課題もあります。また、ユーザーの意図が明確でない場合や、データベーススキーマとの不一致がある場合、誤った結果やエラーが発生する可能性があります。

ユーザークエリの曖昧さの原因

ユーザークエリの曖昧さの原因として、主に以下の3つが挙げられます。

  • 自然言語の多様性と曖昧性
    自然言語の曖昧さは、同じフレーズが異なる意味を持つ可能性があることに起因します。例えば、「売上高と純利益の増加率を集計して」というクエリでは、「売上高」と「純利益の増加率」の集計を行うのか、「売上高の増加率」と「純利益の増加率」の集計を行うのかわかりません。

  • ユーザーの意図の解釈の難しさ
    ユーザーの意図の解釈の難しさは、コンテキストの欠如や専門用語の不明瞭さによってもたらされます。ユーザーが何を求めているのか、その背景や目的を理解することが重要です。

  • データベーススキーマとの不一致
    ユーザーが使用する言葉と、データベースの列名や構造が一致しない場合に生じます。この不一致は、ユーザーのクエリとデータベースの間にギャップが生じ、ユーザーの要求を正確に満たすのが難しくなる原因となります。

ケーススタディ

実際にText-to-SQLを適用したクエリの紹介

ここからは、デモデータを用いてSQLデータベースを構築し、生成AIを活用したText-to-SQLの検証結果をご紹介します。

曖昧さに対する頑健性の実証

1. 本検証の目的

ユーザーからの質問を曖昧にしていった場合、Text-to-SQLがどの程度まで正しいSQLを生成し、質問に対して期待される回答ができるか(頑健性)を検証します。

2. 実験設定
  • 使用したアルゴリズム:SQLDatabaseChain
    LangChain Chainsの拡張機能であるSQLDatabaseChainを用います。ユーザーからの質問(ユーザークエリ)に対してSQLクエリを生成しデータベースを参照した後、最終的な回答を生成することが可能です。

api.python.langchain.com

  • 使用したDB:Azure CosmosDB for PostgreSQL
    今回の検証は小規模なデータベースですが、大規模なデータベースにも適用することを想定し、グローバル分散データベース サービスであるAzure CosmosDB for PostgreSQLを用いました。

  • 生成AIモデル:gpt-35-turbo-16k (temperature=0に設定)

  • 使用したプロンプト:
    SQLクエリを生成する際のプロンプトは以下です。LangChain に実装されているものをほぼそのまま用いました(日本語で出力するように追記)。input部分に質問文が入ります。

PROMPT_SUFFIX = """Only use the following tables:
{table_info}
Question: {input}"""

_postgres_prompt = """You are a PostgreSQL expert. Given an input question, first create a syntactically correct PostgreSQL query to run, 
then look at the results of the query and return the answer to the input question.  Unless the user specifies in the question a specific number of examples to obtain, 
query for at most {top_k} results using the LIMIT clause as per PostgreSQL. You can order the results to return the most informative data in the database.  
Never query for all columns from a table. You must query only the columns that are needed to answer the question. Wrap each column name in double quotes (") to denote them as delimited identifiers.  
Pay attention to use only the column names you can see in the tables below. Be careful to not query for columns that do not exist. Also, pay attention to which column is in which table.  
Pay attention to use CURRENT_DATE function to get the current date, if the question involves "today".  
Final answerは日本語の文章でお願いします。

Use the following format:

Question: Question here
SQLQuery: SQL Query to run
SQLResult: Result of the SQLQuery
Answer: Final answer here

"""
prompt = PromptTemplate(
    input_variables=["input", "table_info", "top_k"],
    template=_postgres_prompt + PROMPT_SUFFIX,
)
  • 使用したデータ:Oracleデータベースのサンプルデータからempテーブル (従業員テーブル)とdeptテーブル (部署テーブル)を1つにマージしたものを例に、日本語版に変換・100件まで拡張したものを使用します。(GPT4で生成)

docs.oracle.com

使用したデータの抜粋

  • 評価対象テーブル:merged_table
    上記のデータを利用

  • 評価基準
    〇:正解
    △:意図とは異なるが正解
    ×:不正解

3. 評価内容と結果
簡単な検証 

まずは質問文に曖昧さが含まれない基本的な質問から確認します。

  • 結果について:計算や比較を含む基本的なクエリも含めて、全て正解でした。

詳細は以下となります。
※「備考」や「生成されたSQLクエリ」の欄については間違ったものや考察が必要なもののみ記入しています

評価事項 実際の質問文 回答 正誤 備考 生成されたSQLクエリ
カラム指定なし merged_tableテーブルの情報は何件ありますか? Final answer: merged_tableテーブルの情報は100件あります。
カラム指定 merged_tableテーブルの一番salが大きい人のenameとそのsalは? Final answer: 一番salが大きい人のenameは中川 裕樹で、そのsalは797713です。
バリュー指定 merged_tableテーブルのTOP3の人の名前と、salを表示して。 Final answer: TOP3の人の名前は中川 裕樹、藤田 直子、岡本 真綾で、それぞれのsalは797713、793660、792546です。
カラム二つの&条件 merged_tableテーブルのlocationが東京の中で、salが720000以上のenameは誰か? 佐藤 充、前田 聡太郎、藤田 直子、中川 裕樹
期間絞り込み merged_tableテーブルのhiredateが2023年以降で最もsalが大きいenameは? 最もsalが大きいenameは「中村 明美」です。 SELECT ename FROM merged_table WHERE hiredate >= '2023-01-01' ORDER BY sal DESC LIMIT 1;
計算 merged_tableテーブルのjobごとの平均年収は? merged_tableテーブルのjobごとの平均年収は、以下の通りです:マネージャー: 495,213円 テクニカルサポート: 518,204円 セールス: 471,796円 エンジニア: 524,553円 SELECT job, AVG(sal) AS average_salary FROM merged_table GROUP BY job
比較 merged_tableテーブルのlocationが東京と福岡ではどちらが平均年収が高いか 東京の平均年収が高いです。 SELECT location, AVG(sal) AS average_salary FROM merged_table WHERE location IN ('東京', '福岡') GROUP BY location ORDER BY average_salary DESC LIMIT 1;

曖昧性の評価

次に本題である曖昧性の評価を行います。 ユーザーからの質問(もしくはテーマ)に対して、どこまで質問を曖昧にしても正解できるか検証します。

結果として、ほぼ全て正解となりましたが、以下のような課題もありました。

  • 期間を指定する場合はより厳密に指定する必要(例:2023年→2023年代)。
  • 婉曲な表現を用いた場合、捉え方によりミスを誘発しました。また、中央値の計算ではよくある間違いをしていました。
  • 極端な例ですが、質問を全てひらがなやカタカナにした場合も、スキーマを指定する部分でミスが発生しました。

詳細は以下となります。
※「備考」や「生成されたSQLクエリ」の欄については間違ったものや考察が必要なもののみ記入しています

ケース1:カラム名の指定方法

カラム名の指定方法をデータベーススキーマで定義されたカラム名から、関連する別の単語に変えます。

  • 元の質問文:locationが東京の中で、salが720000以上のenameは誰か?

  • 正解の回答:佐藤 充、前田 聡太郎、藤田 直子、中川 裕樹

  • 結果について:全て正解。カラム名の指定方法を変更しても、回答精度への影響が小さいことが確認できました。ただし、カラム名が業務知識に依存したものだとカラムの説明が別途必要です。

曖昧さの出し方 実際の質問文 回答 正誤 備考 生成されたSQLクエリ
カラム名の呼び方を変える merged_tableテーブルの場所が東京の中で、salaryが720000以上のenameは誰か? 佐藤 充、前田 聡太郎、藤田 直子、中川 裕樹 SELECT ename FROM merged_table WHERE location = '東京' AND sal >= 720000 LIMIT 5;
カラム名を一部記載しない merged_tableテーブルの東京の中で、salが720000以上は誰か? 東京の中で、salが720000以上の人は、佐藤 充、前田 聡太郎、藤田 直子、中川 裕樹です。 locationとenameを省略
カラム名を記載しない merged_tableテーブルの東京の中で、720000以上は誰か? 720000以上の給料をもらっている東京の人は、佐藤 充、前田 聡太郎、藤田 直子、中川 裕樹です。 720000が何でありそうかを推察していると思われる(賢い)
単位の変更 merged_tableテーブルの東京の中で、720000円以上は誰か? 720000円以上の給料をもらっている東京の従業員は、佐藤 充、前田 聡太郎、藤田 直子、中川 裕樹です。
ケース2:バリューの指定方法

今度は値の指定方法を変更してみます。

  • 元の質問文:locationが東京の中で、salが720000以上のenameは誰か?

  • 正解の回答:佐藤 充、前田 聡太郎、藤田 直子、中川 裕樹

  • 結果について:ほぼ全て正解しており、テーブル内のバリューの指定方法を変更した場合の影響は小さいといえそうです。ただし、存在しないバリューを指定した場合、英語で回答していることから回答精度への悪化につながりそうです。

曖昧さの出し方 実際の質問文 回答 正誤 備考 生成されたSQLクエリ
バリューの呼び方1 merged_tableテーブルのlocationがTokyoの中で、salが720000以上のenameは誰か? 佐藤 充、前田 聡太郎、藤田 直子、中川 裕樹 SELECT ename FROM merged_table WHERE location = '東京' AND sal >= 720000 LIMIT 5;
バリューの呼び方2 merged_tableテーブルのlocationがTokyoの中で、salがななじゅうにまん以上のenameは誰か? merged_tableテーブルのlocationがTokyoの中で、salがななじゅうにまん以上のenameは佐藤 充、前田 聡太郎、藤田 直子、中川 裕樹です。
バリューを一部記載しない merged_tableテーブルのlocationが東から始まるもので、salが720000以上のenameは誰か? 佐藤 充、前田 聡太郎、藤田 直子、中川 裕樹
存在しないバリュー merged_tableテーブルのlocationがニューヨークの中で、salが720000以上のenameは誰か? There is no employee in the merged_table table with a location of 'ニューヨーク' and a salary of 720000 or above. 「いない」が正解。しかし、回答が英語になってしまっている SELECT ename FROM merged_table WHERE location = 'ニューヨーク' AND sal >= 720000 LIMIT 5;
ケース3:指定方法(カラム、バリュー)の組み合わせ

カラム名と値の両者を変更します。

  • 元の質問文:merged_tableテーブルのlocationが東京と福岡ではどちらが平均年収が高いか

  • 正解の回答:東京

  • 結果について:「どちらが平均年収が高いか?」→「どっちが儲かるか?」といった別の表現にした場合、平均年収ではなく、年収の合計値を計算するクエリを生成してしまいました。「平均」年収を考慮してほしい場合は、明記する必要があります。

評価事項 実際の質問文 回答 正誤 備考 生成されたSQLクエリ
カラム指定なし merged_tableテーブルの情報は何件ありますか? Final answer: merged_tableテーブルの情報は100件あります。
カラム指定 merged_tableテーブルの一番salが大きい人のenameとそのsalは? Final answer: 一番salが大きい人のenameは中川 裕樹で、そのsalは797713です。
バリュー指定 merged_tableテーブルのTOP3の人の名前と、salを表示して。 Final answer: TOP3の人の名前は中川 裕樹、藤田 直子、岡本 真綾で、それぞれのsalは797713、793660、792546です。
カラム二つの&条件 merged_tableテーブルのlocationが東京の中で、salが720000以上のenameは誰か? 佐藤 充、前田 聡太郎、藤田 直子、中川 裕樹
期間絞り込み merged_tableテーブルのhiredateが2023年以降で最もsalが大きいenameは? 最もsalが大きいenameは「中村 明美」です。 SELECT ename FROM merged_table WHERE hiredate >= '2023-01-01' ORDER BY sal DESC LIMIT 1;
計算 merged_tableテーブルのjobごとの平均年収は? merged_tableテーブルのjobごとの平均年収は、以下の通りです:マネージャー: 495,213円 テクニカルサポート: 518,204円 セールス: 471,796円 エンジニア: 524,553円 SELECT job, AVG(sal) AS average_salary FROM merged_table GROUP BY job
比較 merged_tableテーブルのlocationが東京と福岡ではどちらが平均年収が高いか 東京の平均年収が高いです。 SELECT location, AVG(sal) AS average_salary FROM merged_table WHERE location IN ('東京', '福岡') GROUP BY location ORDER BY average_salary DESC LIMIT 1;
ケース4:期間の指定方法

次は集計対象の期間の指定方法を変更します。

  • 元の質問文:各質問ごとに異なる

  • 正解の回答:各質問ごとに異なる

  • 結果について:

    • 期間に「2023年」を指定した場合、2023-01-01しかとれてない→年月日をカラムで分けるか「以降」等の記載が必要。期間を指定する場合はより厳密に指定する必要がありそうです。
    • PostgreSQLにおいてMEDIANという関数が標準で存在しないが使用してしまっている。→プロンプトに中央値はpercentile_cont(0.5)を利用することを追記すると解消。
評価事項 実際の質問文 回答 正誤 備考 生成されたSQLクエリ
期間絞り込み merged_tableテーブルのhiredateが2023年以降で最もsalが大きいenameは? 最もsalが大きいenameは「中村 明美」です。 SELECT ename FROM merged_table WHERE hiredate >= '2023-01-01' ORDER BY sal DESC LIMIT 1;
期間絞り込み&バリューの呼び方1 merged_tableテーブルのhiredateが2023年で最もsalが大きいenameは? None × 2023年以降がないと、2023-01-01とイコールしかとれてない SELECT ename FROM merged_table WHERE hiredate = '2023-01-01' ORDER BY sal DESC LIMIT 1;
期間絞り込み&バリューの呼び方2 merged_tableテーブルのhiredateが2023年代で最もsalが大きいenameは? 中村 明美 2023年代あれば正解 SELECT ename FROM merged_table WHERE hiredate LIKE '2023%' ORDER BY sal DESC LIMIT 1;
期間絞り込み&計算 merged_tableテーブルのhiredateが2023年以降のsal平均値は? merged_tableテーブルのhiredateが2023年以降のsal平均値は551,592.50です。 年月は正規化されていなくても良い結果 SELECT AVG(sal) FROM merged_table WHERE hiredate >= '2023-01-01';
年月の期間指定 2023年以降1月~7月のsalの最大値は? 2023年以降1月~7月のsalの最大値は680,863です。 SELECT MAX(sal) AS max_sal FROM merged_table WHERE hiredate >= '2023-01-01' AND hiredate <= '2023-07-31'
年月の期間指定&計算 2023年以降1月~7月のsalの中央値は? エラー × PostgreSQLにおいてMEDIANという関数が標準で存在しない SELECT MEDIAN(sal) AS median_sal FROM merged_table WHERE hiredate >= '2023-01-01' AND hiredate <= '2023-07-31'
(参考)年月の期間指定&カラム削除 2023年以降1月~7月の最大値は? 2023年以降1月~7月の最大値は680,863です。 意図をくみ取っている SELECT MAX(sal) AS max_salary FROM merged_table WHERE hiredate >= '2023-01-01' AND hiredate <= '2023-07-31'
(参考)年月の期間指定&カラム削除&バリューの呼び方 2023年以降1月~7月で一番大きい数は? 2023年以降1月~7月で一番大きい数は680,863です。 SELECT MAX(sal) AS max_salary FROM merged_table WHERE hiredate >= '2023-01-01' AND hiredate <= '2023-07-31'
(参考)年月の期間指定&カラム削除&バリューの呼び方2 2023年以降1月~7月で一番大きいあたいは? 2023年以降1月~7月で一番大きい値は680,863です。 SELECT MAX(sal) FROM merged_table WHERE hiredate >= '2000-01-01' AND hiredate <= '2000-07-31'

曖昧さを軽減するアプローチ

今回の検証で確認された問題を参考に、曖昧さを軽減するアプローチを紹介します。

ユーザーインタラクションを活用したクエリ精度の向上

  • SQLクエリの確認とフィードバック:生成される中間成果物であるSQLクエリの内容をユーザーが確認し、期間やカラム名、計算式が意図通りか確認します。間違っている場合は、プロンプトや質問文に明記します (今回の場合だと、中央値の計算にpercentile_cont(0.5)を利用することを追記することで改善しました)。

自然言語処理技術による意図の明確化

  • 回答言語の指示: 内部のプロンプトが英語のためか、回答が日本語になりづらい →日本語の文章を書くようにプロンプトに追記。
  • モデルの変更:GPTGPT-3.5-turboでも十分精度がでましたが、GPT-4を使用することで、よりユーザーの意図を理解することが可能となります。

データベーススキーマとのマッチング技術の向上

  • テーブル名の指定:似たテーブルやデータが膨大になる場合、生成AIを補助するためにテーブル名は最低限指定する
  • カラム名の説明:ドメイン固有なデータベースについては、カラム名の説明を追加する

今後の展望

Text-to-SQL技術の将来性

自然言語でデータベースの連携を可能にするText-to-SQL技術の発展によって、データベース操作言語を知らない非技術者の方でもデータベースから情報を簡単に取得可能になることで、データ活用の民主化がより進むと考えられます。

しかし、生成AIを活用したText-to-SQLには、今回ご紹介した曖昧性等の課題に対処する必要があります。

未解決の課題と研究の方向性

曖昧さの問題以外にも、多言語対応、複雑なクエリの処理、非標準データベーススキーマへの適応、大規模データベース(複数テーブル、多数カラム、多数レコード)などが指摘されています。

今後の研究の方向性としては、実社会での応用性を考慮して、大規模データベースへの適用や、より複雑なクエリの処理を可能にする技術開発を目指していきたいと思います。
また、ユーザー体験の向上に向けては、より直感的なクエリの修正やインタラクティブなインターフェイスが必要となりそうです。

まとめ

この記事では、自然言語クエリをSQL文に変換するText-to-SQL技術について、特に生成AIを活用した場合の曖昧性の評価を行いました。Text-to-SQL技術は、データ活用の民主化を促進し、非技術者でもデータベースから必要な情報を簡単に取得できるようにすることで、ビジネスや研究における意思決定を大きく支援する可能性を持っています。

Text-to-SQLの重要性と可能性に対する最終的な考え

Text-to-SQLは、自然言語クエリをデータベース検索に変換する強力なツールですが、これらの自然言語の曖昧さとデータベーススキーマとの不一致は無視できない課題です。
今回の検証からは、一般の人間が判断できるレベルの曖昧な質問であれば回答が可能であることが確認できました。
基本的なユーザーからの質問への対応力が示されたものの、自然言語の曖昧さやデータベーススキーマとの不一致が引き起こす課題もあることが分かりました。解決策として、ユーザーインタラクションの強化、意図の明確化を目指した自然言語処理技術の改善、データベーススキーマとのより密な連携が考えられます。

AITCでは引き続き、生成AIを用いたText-to-SQLの技術検証を実施してまいります。

本テーマに関してやAITCについて問合せを希望される方は、お気軽にこちらのお問い合わせフォームからご連絡ください。

AITCお問い合わせフォーム

筆者
AIソリューショングループ
阿田木 勇八