langchianlogo

LangChain入門 – 6)Agent エージェント

LangChain入門の6回目です。Agentとは代理人という意味ですが、面倒な仕事を手伝ってくれる助っ人のようなもの考えてください。Agentは仕事をこなすためにツールを使用します。ツールには、検索したり、Pythonを実行したり、数学を解いたりといろいろなものが用意されています。AgentはLLMと協力しながら、どのツールを使ったらよいか考えながら作業を進めてゆきます。

agent1
LangChain入門 – 6)Agent エージェント 6

本記事はFuture Coders独自教材からの抜粋です。

Langchainではあらかじめ幾つかのAgentを用意しています。それぞれ得意なことが異なります。

現時点でサポートされているAgentの種類を確認してみましょう。

from langchain.agents import AgentType
for t in AgentType:
    print(t)

出力は以下のようになりました。わりと速いペースで増えているようです。

AgentType.ZERO_SHOT_REACT_DESCRIPTION
AgentType.REACT_DOCSTORE
AgentType.SELF_ASK_WITH_SEARCH
AgentType.CONVERSATIONAL_REACT_DESCRIPTION
AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION
AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION
AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION
AgentType.OPENAI_FUNCTIONS

主なエージェントは以下の通りです。

  • ZERO_SHOT_REACT_DESCRIPTION = 各ツールの説明文章からどのツールを使うか判断するエージェント
  • REACT_DOCSTORE = 文書の扱いを得意とするエージェント
  • SELF_ASK_WITH_SEARCH = 検索した結果に基づいて判断するエージェント
  • CONVERSATIONAL_REACT_DESCRIPTION = 会話の扱いが得意なエージェント

この中でZERO_SHOT_REACT_DESCRIPTION系のエージェントはツールの説明文章を読んでどのツールを使うか判断します。つまり、ツールは自分を説明する文章を含んでいる必要があります。その内容を確認してみましょう。

以下は、wikipedia, llm-math, open-meteo-apiといったツールの説明文書を確認するコードです。

from langchain.agents import load_tools
tools = load_tools(["wikipedia", "llm-math", "open-meteo-api"], llm=llm)
for t in tools:
    print(f"名前={t.name} 説明={t.description}")

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

名前=Wikipedia 説明=A wrapper around Wikipedia. Useful for when you need to answer general questions about people, places, companies, facts, historical events, or other subjects. Input should be a search query.
名前=Calculator 説明=Useful for when you need to answer questions about math.
名前=Open Meteo API 説明=Useful for when you want to get weather information from the OpenMeteo API. The input should be a question in natural language that this API can answer.

各ツールの説明内容は英語ですが、それぞれのツールの説明をしていることがわかります。エージェントはこの説明を参考に、計算するときはCalculatorを呼び出したり、天気を調べるときはOpen Meteo APIを呼び出したり、と判断できるようになります。

では、このエージェントとツールをためしてみましょう。

serpapi

serpapiはGoogleなどの検索エンジンを横断的に検索して、その結果を取得する検索用のAPIです。開発者で登録した場合、月5000回の検索までは無料です。利用するにはAPIキーを取得する必要があります。

https://serpapi.com/

アカウントがない場合はRegisterから登録します。

screen agent1
LangChain入門 – 6)Agent エージェント 7

ログインするとAPIキーが取得できます。

screen agent2
LangChain入門 – 6)Agent エージェント 8

serpapiを使用するために、PIPコマンドでモジュールをインストールします。

pip install google-search-results

まずは普通にserpapiをそのまま使ってみましょう。以下「ラーメン」を検索するサンプルです。

import json
from serpapi import GoogleSearch
search = GoogleSearch({
    "q":"ラーメン",
    "num":3,
    "location":"Yokohama",
    "api_key":"e8de952fd0643ff908ca7e02c5f4ac03ede1a2740b8215651834a3eb45c3960d"
    })

result = search.get_dict()
print(json.dumps(result, ensure_ascii=False, indent=4))

GoogleSearchオブジェクトを作成して、必要なパラメタを適宜設定して、get_dictと実行するだけでGoogle検索の結果が得られます。qが検索ワード、numが件数、locationは場所の指定となります。

以下のように膨大な情報が取得できることが確認できます。

{
    "search_metadata": {
        "id": "6502663d359e9971c9fe88f7",
        "status": "Success",
        "json_endpoint": "https://serpapi.com/searches/71b693778593d1dc/6502663d359e9971c9fe88f7.json",
        "created_at": "2023-09-14 01:47:41 UTC",
        "processed_at": "2023-09-14 01:47:41 UTC",
        ....

詳しいAPIの使い方は以下のリンクに記載されています。

このようにserpapiは強力な検索機能を提供してくれますが、このままではLLMと組み合わせることは簡単ではありません。LLMと組み合わせられるように、serpapiのAgentが用意されています。

serpapiとOpenAIを組み合わせたサンプルです。昨日の気温を聞いています。OpenAIだけではこの問いに答えられないはずです。serpapiと組み合わされることで、このような問いにも答えられるようになっていることに注目してください。

import os
os.environ["SERPAPI_API_KEY"] = "e8de952fd0643ff908ca7e02c5f4ac03ede1a2740b8215651834a3eb45c3960d"
from langchain.agents import load_tools, initialize_agent, AgentType
from langchain.llms import OpenAI

llm = OpenAI(temperature=0.7)
tools = load_tools(["serpapi", "llm-math"], llm=llm)
agent = initialize_agent(tools, llm, AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
r = agent.run("昨日の東京の最高気温は?摂氏と華氏の両方で教えてください。")
print(r)

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

> Entering new  chain...
 I should search for the answer to this question.
Action: Search
Action Input: Tokyo yesterday temperature celsius fahrenheit
Observation: See weather overview. Tokyo Temperature Yesterday. Maximum temperature yesterday: 91 °F (at 1:00 pm) Minimum temperature yesterday: 74 °F (at 3:00 am) Average ...
Thought: I should convert the temperature from Fahrenheit to Celsius.
Action: Calculator
Action Input: 91F to Celsius
Observation: Answer: 32.77777777777778
Thought: I should convert the temperature from Celsius to Fahrenheit
Action: Calculator
Action Input: 74C to Fahrenheit
Observation: Answer: 23.333333333333332
Thought: I now know the final answer
Final Answer: 昨日の東京の最高気温は 32.77777777777778 度 (摂氏)、23.333333333333332 度 (華氏)です。

> Finished chain.
昨日の東京の最高気温は 32.77777777777778 度 (摂氏)、23.333333333333332 度 (華氏)です。

最初に「質問に答えるには検索しなくては」と判断して、「Action: Search」を実行しています。これはserpapiを実行していることにほかなりません。検索結果には、最高気温が91度(華氏)、最低気温が74度(華氏)という結果が含まれていることがわかります。華氏は米国で使われている単位です。次に、「これを摂氏に変換しなくては」と判断して、91度を摂氏に変換して32度という気温を得ています。ここまではよかったのですが、74度を摂氏と勘違いして、華氏に変換して23度という値を得ていることがわかります。回答が正確ではありませんでしたが、検索や計算を実行していることが確認できます。

news-api

news-apiツールを使って最新のニュースを検索してみましょう。このツールを使用するにはユーザ登録を行い、APIキーを取得する必要があります。

以下のサイトからユーザー登録を行って、APIキーを取得してください。

https://newsapi.org/

今回はツールとしてnews-apiとserpapiの2つを用意してみました。質問内容に応じてエージェントがどのようにツールを使い分けるか見てみましょう。

import os
os.environ["SERPAPI_API_KEY"] = "e8de952fd06xxxxxxxxxxxxxxxxxx1834a3eb45c3960d"
from langchain.agents import load_tools, initialize_agent, AgentType
from langchain.llms import OpenAI

llm = OpenAI(temperature=0.7)
tools = load_tools(["news-api","serpapi"], llm=llm, news_api_key="8e8a45685b2543978213a4d2b5e91f65")
agent = initialize_agent(tools, llm, AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
r = agent.run("昨日のKIRINカップのサッカーの結果を教えてください")
print(r)
r = agent.run("昨日日本で話題になった芸能ニュースを1つ教えてください")
print(r)

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

> Entering new  chain...
 今日のニュースを検索する
Action: Search
Action Input: 昨日のKIRINカップサッカー
Observation: 日本, トルコ. GK, 1, 中村 航輔, GK, 23, ウールジャン・チャクル. DF, 3, 谷口 彰悟, DF, 4, チャーラル・ソユンジュ (Cap.).
Thought: 検索結果から、結果を確認する
Action: Search
Action Input: 昨日のKIRINカップサッカーの結果
Observation: キリンチャレンジカップ 10/17 · 日本代表男子. チュニジア代表男子. 10/17(火) 03:10. 試合結果. キリンチャレンジカップ 9/12 · 日本代表男子4. トルコ代表男子2.
Thought: 結果を確認した
Final Answer: 昨日のKIRINカップのサッカーの結果は、日本代表男子が4-2でトルコ代表男子を破り勝利 したことです。

> Finished chain.
昨日のKIRINカップのサッカーの結果は、日本代表男子が4-2でトルコ代表男子を破り勝利したことです。 


> Entering new  chain...
 I should get the latest news about celebrities in Japan
Action: News API
Action Input: Japanese celebrity news
Observation:  The new anime series "FIST OF THE NORTH STAR" is being produced to celebrate the 40th anniversary of the action manga "FIST OF THE NORTH STAR". The series will feature new staff and cast with the latest video technology to accurately portray the manga world.
Thought: I now know the final answer
Final Answer: 新作アニメ「北斗の拳」が制作され、『北斗の拳』の40周年記念を祝うため新スタッフや キャストを起用し、最新の映像技術で漫画の世界を正確に再現する。

> Finished chain.
新作アニメ「北斗の拳」が制作され、『北斗の拳』の40周年記念を祝うため新スタッフやキャストを起用 し、最新の映像技術で漫画の世界を正確に再現する。

ActionとAction Inputのところに注目してください。Actionがツール、Action Inputがそのツールへの入力です。

最初のサッカーの結果に関しては、Searchとあるようにserpapiを使用していることがわかります。検索も1回ではなく2回実行して適切な結果を取得しようとしている様子が伺えます。後者の芸能ニュースに関してはNews APIを呼び出していることがわかります。

以下はStreamlitと組み合わせた簡単なニュースアプリのサンプルです。

import streamlit as st
from langchain.agents import load_tools, initialize_agent, AgentType
from langchain.llms import OpenAI

llm = OpenAI(temperature=0.7)
tools = load_tools(["news-api"], llm=llm, news_api_key="8e8a45685b2543978213a4d2b5e91f65")
agent = initialize_agent(tools, llm, AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)

st.header("最新ニュース")

with st.sidebar:
    genre = st.radio("ジャンル", ["時事", "経済", "スポーツ", "芸能"])

if st.button("更新"):
    st.subheader(f"{genre}の最新ニュース")
    r = agent.run(f"{genre}の分野で話題になっているニュースを出力してください")
    st.write(r)

ジャンルを選択して、更新ボタンを押下すると最新のニュースが表示されます。

screen agent3
LangChain入門 – 6)Agent エージェント 9

wikipedia

Wikipedia用のツールも用意されています。単にツール名にwikipediaと指定するだけです。APIキーも不要で簡単に利用できます。

from langchain.agents import load_tools, initialize_agent, AgentType
from langchain.llms import OpenAI

llm = OpenAI(temperature=0)
tools = load_tools(["wikipedia"], llm=llm)
agent = initialize_agent(tools, llm, AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
r = agent.run("男はつらいよは全部で何シリーズある?")
print(r)

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

> Entering new  chain...
 I need to find out how many series of the manga "Otoko wa Tsurai yo" there are.
Action: Wikipedia
Action Input: "Otoko wa Tsurai yo"
Observation: Page: Otoko wa Tsurai yo
Summary: Otoko wa Tsurai yo (男はつらいよ, "It's Tough Being a Man") is a Japanese film series starring Kiyoshi Atsumi as Tora-san (寅さん), a kind-hearted vagabond who is always unlucky in love. The series 

<中略>

series, and he appeared in Akira Kurosawa's "Rhapsody in August" and "Madadayo". He won the Japan Academy Award Best Actor in 2006 for "Always - Sunset on Third Street".He was married to actress Yuki Uchida from 2002 to 2005.
Thought: I now know the final answer
Final Answer: 男はつらいよは全部で50シリーズあります。

> Finished chain.
男はつらいよは全部で50シリーズあります。

出力が長いので途中で省略してありますが、正しい結果が得られています(日本語のWikipediaを確認しました)。
ActionにWikipedia、Action Inputに”Otoko wa Turai yo”と入力されていることも確認できます。

llm-math

llm-mathは数学に関する問題を解決するツールです。以下のサンプルではビルゲイツの元奥さんの年齢と、その平方根を求めています。

import os
os.environ["SERPAPI_API_KEY"] = "e8de952fd0643ff908ca7e02c5f4ac03ede1a2740b8215651834a3eb45c3960d"
from langchain.agents import load_tools, initialize_agent, AgentType
from langchain.llms import OpenAI

llm = OpenAI(temperature=0)
tools = load_tools(["serpapi","llm-math"], llm=llm)
agent = initialize_agent(tools, llm, AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
r = agent.run("ビルゲイツの元奥さんの年齢は?その年数の平方根を教えてください")
print(r)

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

> Entering new AgentExecutor chain...
 I need to find out the age of Bill Gates' ex-wife and then calculate the square root of that number
Action: Search
Action Input: Bill Gates' ex-wife age
Observation: 59 years
Thought: I need to calculate the square root of 59
Action: Calculator
Action Input: sqrt(59)
Observation: Answer: 7.681145747868608
Thought: I now know the final answer
Final Answer: The square root of Bill Gates' ex-wife's age is 7.681145747868608.

> Finished chain.
The square root of Bill Gates' ex-wife's age is 7.681145747868608.

最初のActionでSearchを使って年齢を検索し、次のActionでCalculatorを使っていることが分かります。

pal-math

pal-mathも数学に関する問題を解決するツールです。複雑な言葉の問題を解くのに適しています。

from langchain.agents import load_tools, initialize_agent, AgentType
from langchain.llms import OpenAI

llm = OpenAI(temperature=0)
tools = load_tools(["pal-math"], llm=llm)
agent = initialize_agent(tools, llm, AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
r = agent.run("鶴と亀が何匹かいます。頭の数の合計は4で、足の数の合計は10です。それぞれ何匹いますか?答えは日本語でお願いします")
print(r)

出力は以下のようになりました。Path-Mathを使って答えを求めていることがわかります。

> Entering new  chain...
 I need to solve a math problem
Action: PAL-MATH
Action Input: How many cranes and turtles are there if the total number of heads is 4 and the total number of legs is 10?
Observation: (2, 2)
Thought: I now know the final answer
Final Answer: 鶴が2匹、亀が2匹です。

> Finished chain.
鶴が2匹、亀が2匹です。

ActionとしてPAL-MATHが使用されていることが分かります。PAL-MATHもその内部でLLMを使用するため、load_toolsでllmを引数で指定しています。

演習

agent-ex1.py

serpapiを使って、この一週間のドル円相場を求めてください。

以下は出力例です。

> Entering new  chain...
 現在の為替レートを検索する
Action: Search
Action Input: 今週の為替レート
Observation: 今週のレンジ予想[毎週月曜日 更新]. 今週のレンジ予想. 9月11日 ~ 9月17日. ドル ... お取引した通貨にて、相場の変動による価格変動やスワップポイントの変動により ...
Thought: ドルのレートを見つける
Action: Search
Action Input: ドルの為替レート
Observation: 通貨(通貨単位), 為替レート(円). 外貨→円貨(TTB), 円貨→外貨(TTS). 米ドル(1 USD), 146.69, 147.69. ユーロ(1 EUR), 157.46, 158.86.
Thought: ドルの為替レートを計算する
Action:  None
Final Answer: 1 USD = 146.69 - 147.69円

> Finished chain.
1 USD = 146.69 - 147.69円

agent-ex2.py

wikipediaを使って、ジブリ作品の代表例を出力してください。

出力例

スタジオジブリが作品として発表した作品には、『となりのトトロ』『千と千尋の神隠し』『ハウルの動く城』 『崖の上のポニョ』などがあります。また、ジブリ美術館などもあります。

agent-ex3.py

以下の問題をLLMを使って解いてください。

A国とB国の間は49kmあります。A国の王子が、B国に向かって時速3kmで、B国の王女が、A国に向かって時速4kmで、同時に歩き始めました。2人は出発してから何時間後に出会うでしょう。

出力例

2人は7時間後に出会うでしょう。

解答例

agent-ex1.py

import os
os.environ["SERPAPI_API_KEY"] = "e8de952fd0643ff908ca7e02c5f4ac03ede1a2740b8215651834a3eb45c3960d"
from langchain.agents import load_tools, initialize_agent, AgentType
from langchain.llms import OpenAI

llm = OpenAI()
tools = load_tools(["serpapi"])
agent = initialize_agent(tools, llm, AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
r = agent.run("直近1週間において、1ドルが何円だったかを教えてください")
print(r)

agent-ex2.py

from langchain.agents import load_tools, initialize_agent, AgentType
from langchain.llms import OpenAI

llm = OpenAI()
tools = load_tools(["wikipedia"])
agent = initialize_agent(tools, llm, AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
r = agent.run("ジブリ作品の代表例を教えてください")
print(r)

agent-ex3.py

from langchain.agents import load_tools, initialize_agent, AgentType
from langchain.llms import OpenAI

llm = OpenAI()
tools = load_tools(["pal-math"], llm=llm)
agent = initialize_agent(tools, llm, AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
r = agent.run("""A国とB国の間は49kmあります。
    A国の王子がB国に向かって時速3kmで、B国の王女がA国に向かって時速4kmで、同時に歩き始めました。
    2人は出発してから何時間後に出会うでしょう。日本語で答えてください。""")
print(r)