langchianlogo

LangChain入門 – 7)Toolkit ツールキット

LangChain入門7回目です。Toolkitとは各種ツール・Agentを含むライブラリです。ここではそれらの使い方を見てゆきましょう。

本記事はFuture Coders独自教材からの抜粋です。2024年1月現在モジュールのバージョンの不整合などの原因で以下の内容は動作しないものがあります。動作確認ができ次第更新させていただきます。ご了承ください。

Azure Cognitive Services

AzureではCognitive Serviceとして、画像認識、文字認識、翻訳、テキストとスピーチの変換などさまざまなサービスを提供しています。AzureCognitiveServicesToolkitを使用すると、それらのサービスとスムーズに連携することができます。

このToolkitはazure-ai-visionパッケージを内部で使用しますが、このパッケージは2023年9月時点ではLinuxとWindowsにしか対応していないそうです

Azureにログインして、適切なリソース(Cognitive Service)を作成してAPIキーを作成する必要があります。

https://portal.azure.com/#home

Cognitive Serviceのリソースを作成します。

screen3
LangChain入門 – 7)Toolkit ツールキット 10

サービス作成に必要なパラメタを適宜設定します。最新のサービスはUSから開始されることが多いようです。日本ではまだ利用できないサービスも少なくないので、RegionはEast USにしました。

screen4
LangChain入門 – 7)Toolkit ツールキット 11

キーとEndpointを取得してメモしておきます。

screen5
LangChain入門 – 7)Toolkit ツールキット 12

Computer VisionのWebAPIを直接実行してみる

LangChainでアクセスするまえに、直接AzureのWebAPIを呼び出してみましょう。サンプルコード中のAPIキーは自分で取得した値に置き換えてください。以下の画像(https://cdn.vuetifyjs.com/images/carousel/sky.jpg) を認識させてみます。

sky
LangChain入門 – 7)Toolkit ツールキット 13

プログラムは以下の通りです。

import requests, json

headers = {
    "Ocp-Apim-Subscription-Key":"16d97xxxxxxxxxxxxxxxx2f11dac",
    "Content-Type": "application/json"
}
params = {
    "features":"caption,read",
    "model-version":"latest",
    "language":"en",
    "api-version":"2023-02-01-preview"
}
data = json.dumps({"url":"https://cdn.vuetifyjs.com/images/carousel/sky.jpg"})
r = requests.post("https://langchain-azure.cognitiveservices.azure.com/computervision/imageanalysis:analyze", 
    headers=headers,  params=params, data=data)
print(r.json())

Pythonのrequestsモジュールを使ってAzureのCognitive SericeのWebAPIを呼び出しています。headersがHTTPヘッダ、paramsがクエリ文字列です。WebAPIの詳しい使い方は以下のURLを参考にしてください。

https://learn.microsoft.com/en-us/azure/ai-services/computer-vision/

requests.postでサーバにリクエストを送信します。サーバからの応答はJSON形式で得られます。戻り値のResponseオブジェクト(変数r)のjsonメソッドで応答を取得しています。

出力は以下の通りです。’a rocky shore with snow covered mountains in the background’と画像を正しく認識していることがわかります。

{'captionResult': {'text': 'a rocky shore with snow covered mountains in the background', 'confidence': 0.6585934162139893}, 'readResult': {'stringIndexType': 'TextElements', 'content': '', 'pages': [{'height': 768.0, 'width': 1280.0, 'angle': 0.0, 'pageNumber': 1, 'words': [], 'spans': [{'offset': 0, 'length': 0}], 'lines': []}], 'styles': [], 'modelVersion': '2022-04-30'}, 'modelVersion': '2023-02-01-preview', 'metadata': {'width': 1280, 'height': 768}}

AzureCognitiveServicesToolkit

このAPIをLangChainから使用してみます。
以下のコマンドを実行してモジュールをインストールします。

pip install --upgrade azure-ai-formrecognizer
pip install --upgrade azure-cognitiveservices-speech
pip install --upgrade azure-ai-vision

以下のプログラムを実行すると、このツールキットで使用できるエージェント一覧がリスト形式で出力されます。APIキーやエンドポイントなどは自身で取得した値に置き換えてください。

以下の3つは自分で取得した値を設定してください。

  • AZURE_COGS_KEY = Azure Cognitive ServiceのAPIキー
  • AZURE_COGS_ENDPOINT = Azure Cognitive ServiceのAPIのエンドポイント
  • AZURE_COGS_REGION = Azure Cognitive Serviceのリージョン
import os
os.environ["AZURE_COGS_KEY"] = "caxxxxxxxxxxxxxxxxxxxxxxxa68c"
os.environ["AZURE_COGS_ENDPOINT"] = "https://japaneast.api.cognitive.microsoft.com/vision/v3.2/analyze"
os.environ["AZURE_COGS_REGION"] = "eastus"
from langchain.agents.agent_toolkits import AzureCognitiveServicesToolkit
toolkit = AzureCognitiveServicesToolkit()
print([tool.name for tool in toolkit.get_tools()])

AzureCognitiveServicesToolkitのオブジェクトを作成して、get_tools()メソッドの戻り値を取得しているだけのプログラムです。

実行したところ以下のように出力されました。このツールキットの中にには4つのエージェントが含まれていることがわかります。

['azure_cognitive_services_form_recognizer', 'azure_cognitive_services_speech2text', 'azure_cognitive_services_text2speech', 'azure_cognitive_services_image_analysis']

では、Azureの画像認識エージェントを使って以下の画像からどんな料理が作れるか調べてみましょう。

ingredients
LangChain入門 – 7)Toolkit ツールキット 14
import os
os.environ["OPENAI_API_KEY"] = "sk-n2WTnxxxxxxxxxxxxxxxxxxxxxxd6qDX"
os.environ["AZURE_COGS_KEY"] = "16dxxxxxxxxxxxxxxxxxxxxxxxxdac"
os.environ["AZURE_COGS_ENDPOINT"] = "https://langchain-azure.cognitiveservices.azure.com/"
os.environ["AZURE_COGS_REGION"] = "eastus"

from langchain import OpenAI
from langchain.agents import initialize_agent, AgentType
from langchain.agents.agent_toolkits import AzureCognitiveServicesToolkit

toolkit = AzureCognitiveServicesToolkit()

llm = OpenAI(temperature=0)
agent = initialize_agent(
    tools=toolkit.get_tools(),
    llm=llm,
    agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True,
)

r = agent.run(
    "この材料で何がつくれますか?"
    "https://images.openai.com/blob/9ad5a2ab-041f-475f-ad6a-b51899c50182/ingredients.png"
)

print(r)

出力は以下のようになりました。

> Entering new  chain...

Action:

{
“action”: “azure_cognitive_services_image_analysis”,
“action_input”: “https://images.openai.com/blob/9ad5a2ab-041f-475f-ad6a-b51899c50182/ingredients.png”
}

Observation: Caption: a group of eggs and flour in bowls
Objects: Egg, Egg, Food
Tags: dairy, ingredient, indoor, thickening agent, food, mixing bowl, powder, flour, egg, bowl
Thought: I can use the objects and tags to determine what can be made with the ingredients.
Action:

{
“action”: “Final Answer”,
“action_input”: “You can make pancakes, omelettes, or other egg-based dishes with these ingredients.”
}

> Finished chain.
You can make pancakes, omelettes, or other egg-based dishes with these ingredients.

途中経過も出力されているので見づらいかもしれませんが、最終的に”pancake, omelettes(パンケーキ、オムレツ)など” それっぽい応答が得られています。途中経過を詳しく見てゆくと、写真の内容を理解して、そこから材料を抽出し、それを使った料理を考えていることがわかります。

このままでは応答が英語なので、最後の応答を日本語に翻訳するChainを追加してみましょう。

import os
os.environ["OPENAI_API_KEY"] = "sk-n2WTnxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx6qDX"
os.environ["AZURE_COGS_KEY"] = "16d97xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx1dac"
os.environ["AZURE_COGS_ENDPOINT"] = "https://langchain-azure.cognitiveservices.azure.com/"
os.environ["AZURE_COGS_REGION"] = "eastus"

from langchain import OpenAI
from langchain.agents import initialize_agent, AgentType
from langchain.agents.agent_toolkits import AzureCognitiveServicesToolkit
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain, SimpleSequentialChain

toolkit = AzureCognitiveServicesToolkit()

llm = OpenAI(temperature=0)
agent = initialize_agent(
    tools=toolkit.get_tools(),
    llm=llm,
    agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True,
)

prompt = PromptTemplate.from_template(
    "{sentence}を日本語に翻訳してください"
)
transchain = LLMChain(llm=llm, prompt=prompt)

sequential_chain = SimpleSequentialChain(chains=[agent, transchain], verbose=True)

r = sequential_chain.run(
    "この材料で何がつくれますか?"
    "https://images.openai.com/blob/9ad5a2ab-041f-475f-ad6a-b51899c50182/ingredients.png"
)

print(r)

さきほどのプログラムに翻訳するLLMChainを作成して、SimpleSequentialChainでつなげただけです。最終的な出力は以下のようになりました。

これらの材料でパンケーキ、オムレツ、または他の卵を使った料理を作ることができます。

Pandas DataFrame

PandasはPythonでデータを扱うためのライブラリです。データ処理では必須モジュールといっても過言ではありません。AIだけでなく、機械学習など幅広い範囲で利用されています。PandasではDataFrameというクラスで表形式のデータを処理します。LangChainではDataFrameを扱うためのモジュールが用意されています。

さっそく試してみましょう。まず必要なモジュールをインストールします。

pip install pandas
pip install seaborn
pip install tabulate

機械学習でよく利用されるTitanicのデータセットについて調べてみましょう。まずはそのデータが何行あるか聞いてみます。

import seaborn as sns

from langchain.agents import create_pandas_dataframe_agent
from langchain.llms import OpenAI

df = sns.load_dataset("titanic")

agent = create_pandas_dataframe_agent(OpenAI(temperature=0), df, verbose=True)

agent.run("全部で何行ありますか?")

使い方はとてもシンプルです。create_pandas_dataframe_agentでエージェントを作成して、runメソッドで質問を渡しているだけです。

出力は以下の通りです。verbose=Trueを指定しているので途中経過が出力されます。

> Entering new  chain...
Thought: df.shapeを使えば行数を取得できる
Action: python_repl_ast
Action Input: df.shape
Observation: (891, 15)
Thought: 結果の最初の数字が行数
Final Answer: 891行

> Finished chain.

「Thought: df.shapeを使えば行数を取得できる」というところから、データフレームのshapeプロパティから行と列を取得しようとしていることがわかります。ここから、内部的にPythonのコードを実行し、891行という結果を得ていることがわかります。

もうすこし複雑な質問をしてみましょう。

agent.run("生存率はどのくらいですか?男女別に教えてください")

と質問をかえてみました。出力は以下のようになりました。

> Entering new  chain...
Thought: 生存率を計算する必要がある
Action: python_repl_ast
Action Input: df.groupby('sex')['survived'].mean()
Observation: sex
female    0.742038
male      0.188908
Name: survived, dtype: float64
Thought: 男女別の生存率を確認した
Final Answer: 男性の生存率は0.188908、女性の生存率は0.742038です。

> Finished chain.

質問の意図を正しく理解して、df.groupby('sex')['survived'].mean()というPythonのコードを生成・実行していることがわかります。これは性別(sex)の列でグループ分けして、そこから生存者(survived)の列を取得し、meanメソッドで平均値を算出するというPythonのコードです。ここから正しい答えを導いていることがわかります。途中経過が必要ないときはverboseモードをFalseにします。

以下幾つか質問をするサンプルです。

import seaborn as sns

from langchain.agents import create_pandas_dataframe_agent
from langchain.llms import OpenAI

df = sns.load_dataset("titanic")

agent = create_pandas_dataframe_agent(OpenAI(temperature=0), df=df)
questions = [
    "最高齢の生存者は何歳ですか?",
    "一番高い乗船料金はいくらでしたか?",
    "名前がわかった人と不明な人の比をおしえてください",
]
for q in questions:
    r = agent.run(q)
    print(f"問:{q} -> 答:{r}")

出力は以下のようになりました。

問:最高齢の生存者は何歳ですか? -> 答:生存者の最高齢は80歳です。
問:一番高い乗船料金はいくらでしたか? -> 答:一番高い乗船料金は512.3292でした。
問:名前がわかった人と不明な人の比をおしえてください -> 答:男性が64.76%、女性が35.24%です。

最後の問に対する答えが不自然です。もともとのデータに名前の情報を含んでいないため、この質問には答えられないはずです。Verbose=Trueにすると以下のような処理をおこなっていることがわかりました。

> Entering new  chain...
Thought: 名前がわかった人と不明な人をカウントする必要がある
Action: python_repl_ast
Action Input: df['name'].count()
Observation: KeyError: 'name'
Thought: 名前がわかった人と不明な人をカウントするためには、'name'列が必要
Action: python_repl_ast
Action Input: df.columns
Observation: Index(['survived', 'pclass', 'sex', 'age', 'sibsp', 'parch', 'fare',
       'embarked', 'class', 'who', 'adult_male', 'deck', 'embark_town',
       'alive', 'alone'],
      dtype='object')
Thought: 'name'列がないことがわかった
Action: python_repl_ast
Action Input: df['sex'].value_counts()
Observation: sex
male      577
female    314
Name: count, dtype: int64
Thought: 名前がわかった人と不明な人の比を知るためには、'sex'列を使う必要がある
Action: python_repl_ast
Action Input: df['sex'].value_counts(normalize=True)
Observation: sex
male      0.647587
female    0.352413
Name: proportion, dtype: float64
Thought: 男性と女性の比を知ることができた
Final Answer: 男性が64.76%、女性が35.24%です。

> Finished chain.
問:名前がわかった人と不明な人の比をおしえてください -> 答:男性が64.76%、女性が35.24%です。

最初はname列をさがしたものの、その列がなかったために性別で代用していることがわかります。ときには間違った返答を返すこともあるので、最初はVerboseモードをTrueにして何を行っているか確認するとよいでしょう。

seabornにはサンプルデータセットがいくつか含まれています。そのデータに対して質問をすると回答を返す、そんなアプリをつくってみましょう。

import streamlit as st
import seaborn as sns
from langchain.agents import create_pandas_dataframe_agent
from langchain.llms import OpenAI

st.set_page_config(layout="wide")
st.title("各種データ分析ツール")
dataset = st.sidebar.radio("データセット", sns.get_dataset_names())

st.subheader("データフレーム")
df = sns.load_dataset(dataset)
st.dataframe(df, height=200)

st.subheader("分析")
query = st.text_input("質問", value="行数は?")
if st.button("質問"):
    agent = create_pandas_dataframe_agent(OpenAI(temperature=0), df, verbose=True)
    r = agent.run(query)
    st.write(r)

実際に動かしていろいろ質問を入力してみてください。コンソールには途中経過が表示されているのでLangChainがどのように答えを求めようとしているかがわかります。

screen6
LangChain入門 – 7)Toolkit ツールキット 15

create_python_agent

Pythonの実行に特化したツールも用意されています。

先ずツールを確認してみましょう。

from langchain.tools.python.tool import PythonREPLTool
tool = PythonREPLTool()
print(f"名前={tool.name}, 説明={tool.description}")

出力は以下のようになりました。

名前=Python_REPL, 説明=A Python shell. Use this to execute python commands. Input should be a valid python command. If you want to see the output of a value, you should print it out with `print(...)`.

ツールを実行するエージェントはcreate_python_agent関数で作成します。

from langchain.llms import OpenAI
from langchain.agents.agent_toolkits import create_python_agent
from langchain.tools.python.tool import PythonREPLTool

llm = OpenAI(model_name="text-davinci-003")
agent_executor = create_python_agent(
    llm=llm,
    tool=PythonREPLTool(),
    verbose=True,
)
r = agent_executor.run("SeabornのデータセットからIrisのデータフレームを作成して、PetalWidthの平均値を算出してください")
print(r)

出力は以下の通りです。

> Entering new  chain...
 I need to import seaborn and use its dataset to create a dataframe, then calculate the mean of the PetalWidth
Action: Python_REPL
Action Input: import seaborn as sns
               df = sns.load_dataset('iris')
               print(df['PetalWidth'].mean())
Observation: IndentationError('unexpected indent', ('<string>', 2, 15, "               df = sns.load_dataset('iris')\n", 2, -1))
Thought: I need to put the second line at the start of the line
Action: Python_REPL
Action Input: import seaborn as sns
df = sns.load_dataset('iris')
print(df['PetalWidth'].mean())
Observation: KeyError('PetalWidth')
Thought: I need to check the column names of the dataframe
Action: Python_REPL
Action Input: print(df.columns)
Observation: Index(['sepal_length', 'sepal_width', 'petal_length', 'petal_width',
       'species'],
      dtype='object')

Thought: I now know that the column name is petal_width
Action: Python_REPL
Action Input: print(df['petal_width'].mean())
Observation: 1.1993333333333336

Thought: I now know the final answer
Final Answer: The mean of PetalWidth is 1.1993333333333336

> Finished chain.
The mean of PetalWidth is 1.1993333333333336

Action: Python_REPL とあるのが、Pythonのツールキットを実行している箇所です。
途中経過をみると、インデントを間違えたり、列名を間違えたりしていますが、最終的にprint(df['petal_width'].mean())という命令にたどり着き、それを実行することで答えを求めていることがわかります。

演習

toolkit-ex1.py

AzureのCognitive Service Toolkitを使用して、入力されたURLにある画像がどのようなものか抽出するアプリを作成してください。アプリが難しい場合は、単にURLから分析結果を出力するだけで構いません。

screen azure2
LangChain入門 – 7)Toolkit ツールキット 16

toolkit-ex2.py

コロナの国別統計情報 country_wise_latest.csv というファイルを読み込んで、いろいろな質問に答えるアプリを作成してください。

CSVファイルは以下のURLからダウンロード可能です。

https://www.kaggle.com/datasets/imdevskp/corona-virus-report

screen toolkit1
LangChain入門 – 7)Toolkit ツールキット 17

解答例{.answer}

toolkit-ex1.py

import streamlit as st
import os
os.environ["OPENAI_API_KEY"] = "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"
os.environ["AZURE_COGS_KEY"] = "16d97a57f7264c55813737fec2f11dac"
os.environ["AZURE_COGS_ENDPOINT"] = "https://langchain-azure.cognitiveservices.azure.com/"
os.environ["AZURE_COGS_REGION"] = "eastus"

from langchain import OpenAI
from langchain.agents import initialize_agent, AgentType
from langchain.agents.agent_toolkits import AzureCognitiveServicesToolkit
from langchain.prompts import PromptTemplate
from langchain import PromptTemplate

toolkit = AzureCognitiveServicesToolkit()

llm = OpenAI()
agent = initialize_agent(
    tools=toolkit.get_tools(),
    llm=llm,
    agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True,
)
prompt = PromptTemplate.from_template(
    """以下のURLにある画像はなんですか?
        URL:{url}   
    """)

st.title("画像分析アプリ")
url = st.text_input("画像ファイルへのURL")
if st.button("分析"):
    st.image(url)
    with st.spinner(text="In progress..."):
        r = agent.run(prompt.format(url=url))
        st.write(r)

toolkit-ex2.py

import pandas as pd
import streamlit as st

from langchain.agents import create_pandas_dataframe_agent
from langchain.llms import OpenAI

df = pd.read_csv("country_wise_latest.csv")
agent = create_pandas_dataframe_agent(OpenAI(temperature=0), df, verbose=True)

st.title("COVID分析ツール")

st.dataframe(df, height=200)
query = st.text_input("質問")
if st.button("分析"):
    r = agent.run(query)
    st.write(r)