自社構築したSQLデータベースをLangChainで活用する
背景
私は化学メーカーの原料購買部員として働く者です。日々の業務の中で、色々な市況(例、国産ナフサ、原油、その他個別の化学品)データを参照して資料作成したり何かしらのロジックを組むことがあります。その際、業務背景ごとに過去の任意の期間の市況データを参照するので、データを取得しにいくのが地味に面倒臭いです。また、化学メーカーの原料購買部員は文系出身が多く、IT音痴が多いので、対話的にデータを取得できる仕組みがあると便利な気がします。
やったこと
SQLiteで複数の市況データが管理されていることを仮定し、LangChainで必要なデータを取得できるかを試してみました。
手順
SQLデータベースを構築
概念検証なのでデータはなんでも良いのですが、以下の2種類のデータを格納したSQLを作成しました。
- 国産ナフサ価格
大景化学株式会社のナフサ価格推移表を参照しました。 - 化成品Aの価格
仮想的に800-1000USD/MTの間を推移する価格として乱数で作成しました。
Pythonコードを下記します。
pandas : 1.5.2
numpy : 1.26.4
sqlalchemy : 1.4.44
sqlite3 : 2.6.0
import pandas as pd
import numpy as np
from sqlalchemy import create_engine
import sqlalchemy
import sqlite3
# ★国産ナフサのデータ取得
df_list = pd.read_html("https://www.daikeikagaku.co.jp/naphtha/",header=None)
## データクレンジング
df_naphtha = pd.DataFrame([])
for df in df_list:
colnames = ["年","分類"]+[str(n)+"月" for n in range(1,13)]
df.columns = colnames
df_naphtha = pd.concat((df_naphtha, df))
df_naphtha = df_naphtha.melt(id_vars=["年","分類"])
df_naphtha.columns = ["年","分類","月","value"]
df_naphtha = df_naphtha.query("分類=='Q平均'")
df_naphtha["Date"] = pd.to_datetime(df_naphtha["年"]+df_naphtha["月"], format="%Y年%m月")
df_naphtha.drop(columns=["年","月","分類"], inplace=True)
df_naphtha["name"] = "国産ナフサ"
df_naphtha["unit"] = "JPY/KL"
df_naphtha = df_naphtha[[
"Date","name","unit","value"
]]
# 化成品A価格データの作成
date_range = pd.date_range(start="2015-01-01", end="2024-12-01", freq="MS")
name_column = ["化成品A"] * len(date_range)
unit_column = ["USD/MT"] * len(date_range)
value_column = np.random.choice(np.arange(800, 1001, 10), size=len(date_range))
df_a = pd.DataFrame({
"Date": date_range,
"name": name_column,
"unit": unit_column,
"value": value_column
})
# 結合
df_all = pd.concat((df_naphtha, df_a))
df_all.reset_index(drop=True, inplace=True)
# SQLiteに登録
sql_uri = "sqlite:///sample.db"
engine = create_engine(sql_uri, echo=False)
df_all.to_sql("market", con=engine, if_exists="replace", index=False)
SQLiteのスキーマを確認します。
import sqlite3
# SQLiteデータベースに接続
conn = sqlite3.connect("sample.db")
cursor = conn.cursor()
# スキーマを取得
cursor.execute("SELECT sql FROM sqlite_master WHERE type='table';")
schemas = cursor.fetchall()
# 結果を表示
for schema in schemas:
print(schema[0])
# 接続を閉じる
conn.close()
CREATE TABLE market (
"Date" DATETIME,
name TEXT,
unit TEXT,
value BIGINT
)
LangChainでSQLデータベースにアクセス
事前にOpenAIのAPIキーは取得しておきます。私の環境では、.env
にAPIキーを入れておき、load_dotenv
で読み込む仕様にしました。
openai : 1.61.1
langchain_community : 0.3.17
langchain : 0.3.18
from dotenv import load_dotenv
import os
from openai import OpenAI
from langchain_community.agent_toolkits import create_sql_agent
from langchain_openai import ChatOpenAI
from langchain_community.utilities.sql_database import SQLDatabase
# .envの内容を読み込む
load_dotenv()
# APIキーを参照してOpenAI APIを使える状態にする
client = OpenAI(
api_key = os.environ.get("OPENAI_API_KEY")
)
# sqlデータベースを読み込む
sql_uri = "sqlite:///sample.db"
db = SQLDatabase.from_uri(sql_uri)
# LLMのモデルを指定
llm = ChatOpenAI(
# model_name="gpt-3.5-turbo",
# model_name="gpt-4o-mini",
model_name="gpt-4o",
temperature=0.0
)
# basic
agent_executor = create_sql_agent(llm, db=db, agent_type="openai-tools", verbose=True)
result = agent_executor.invoke(
"marketテーブルを参照して国産ナフサ、および化成品Aの価格の2024年平均を算出し、日本語で出力してください"
)
出力result
の内容は以下のように得られました(一例)。
{'input': 'marketテーブルを参照して国産ナフサ、および化成品Aの価格の2024年平均を算出し、日本語で出力してください',
'output': '2024年の国産ナフサの平均価格は75,400円/KL、化成品Aの平均価格は895円です。'}
コスト
上記1回の試行において、入力が4020トークン、出力が226トークンでした。gpt-4o
モデルを使う場合、入力100万トークンにつき2.5USD、出力100万トークンにつき10USDかかります。これらの数字から、1試行あたりのコストを計算すると、0.01+0.002=0.012USDになります。為替150円/USD前提とすると、1.8円かかります。
所感
欲しい回答が返ってくるかどうか、およびその安定性は、プロンプト(指示)の詳細さとGPTモデルの性能に依存する印象でした。例えば、gpt-4o
を使うと、指示が短くても正確な回答が返ってきました。一方で、gpt-3.5-turbo
を使っても、指示や処理手順を細かく記載することで、欲しい回答が返ってくることもありました(が不安定でした)。
また、コスト計算すると、gpt-4o
で1.8円かかりました。ただの「平均価格」を尋ねるだけのボットだと費用対効果が低いかもしれません。これをベースとして包括的に化学業界のデータを集約できるシステムに繋げられると良さそうです。
参照URL
- https://zenn.dev/umi_mori/books/llm-rag-langchain-python/viewer/rag-method-sql
- https://zenn.dev/umi_mori/books/prompt-engineer/viewer/sql_langchain_chatgpt
- OpenAIのモデル一覧
以上
Discussion