LangChain

【Python】LangChain: Agentを使って実行を自動化する

【Python】LangChain Agentを使って実行を自動化する

Pythonで大規模言語モデル(LLM: Large Language Model)を活用する際に使用できるLangChainでAgent機能を使って実行を自動化する方法を解説します。

LangChainのAgent機能

LangChainは、大規模言語モデル(LLM: Large Language Model)を活用するためのオープンソースPythonライブラリです。LangChainでは、LLMの複雑なタスクを実行することが可能です。

LangChainには、PromptTemplateやOutptParser、Chainといった一連のワークフローを簡単に実現できる枠組みを提供してくれています。また、LangChainにはAgent機能があり、複数の言語モデル、ツール、データベース、外部API等を統合して動的にタスク処理を行うことができるようになります。ここで動的にというのは、タスクを遂行するためにLLMが最適な手段を選び、ステップごとに判断を行うプロセスのことを指します。

LangChainのAgent機能では、特定タスクに特化したスクリプトや従来のプログラムとは異なりより柔軟で適応的なアプローチを提供できる点が非常に強力です。

この記事では、LangChainでAgent機能を使って実行を自動化する方法を紹介していきます。例としてReActで思考しながらツールを選択するAgentの実装例を紹介します。

Agent機能

LangChainのAgent機能では、ユーザーの質問に対して、どう行動するべきかをLLMが考えて行動を実行します。Agentに関する公式ドキュメントはこちらを参照してください。

ここで行動と言っているのは、インターネットサイトから情報を検索する、企業や個人固有のDBへアクセスする、外部で定義されているツールを使用するといった外部データソースへのアクセスを含んでいます。

Agent機能を構成する要素としては以下のようなものがあります。

構成要素概要
Toolツールという名の通り、Agent機能が呼び出す処理とその説明を保有するもので、LangChainは多くのToolを提供しています。もちろん、独自のToolを作成することも可能です。
AgentExecutorAgent機能が動作するための実行環境で、Agentが選択した処理の呼び出しを繰り返しつつ実行を行います。

LangChainによるAgentの実装

ここでは、Agent実装例を紹介します。AIに思考をさせてToolの中から必要な機能を呼び出し、実行するようなReAct Agentを作成してみましょう。まずは、ReActの考え方を簡単に説明した後に、Pythonプログラムの実装例を紹介していきます。

なお、以降で紹介するAgentExecutorによるAgent実装方法は広く使用されている方法ですが、より柔軟性や制御のしやすさを必要とするような場合は、LangGraph Agentの使用を検討することもできます。

LangGraphは、複雑なワークフローや高度な制御が必要な場合に特に有用です。また、LangGraphについては別途まとめる予定です。

この記事では各種パッケージを使用していますが、以下バージョンでの動作を確認しています。生成AIの分野は進歩が速いため、API等の変更により最新バージョンではプログラムがそのままでは動作しない可能性もありますので注意してください。

  • Python: 3.11.5
  • langchain: 0.2.11
  • langchain-core: 0.2.26
  • langchain-community: 0.2.10
  • langchain-openai: 0.1.16

ReAct Agentの実装

生成AIでは、プロンプトエンジニアリングと言ってAIにどのように指示を与えるのかという技術が非常に重要になります。プロンプトエンジニアリングの技術の中にはAIに思考させるReAct(Reasoning and Acting)という手法があります。

ReActでは、モデルが思考を行い、結果に基づいて必要なツールを選択、アクションを実行します。ここで思考と言っているのは、LLMがどのツールを使うかを決定する過程(プロセス)のことを言っています。このReActにより、複雑なタスクや段階的なプロセスを含むタスクにおいて効果的な結果を返す機能を実現できます。

概念概要
推論(Reasoning)モデルが与えられた情報や文脈に基づいて推論を行うステップです。この段階ではモデルがタスクに対する理解を示し、適切な結果を導くための情報を生成します。
行動(Acting)推論結果に基づいて、具体的なアクションを取る段階です。これは実際のタスクを実行するための指示や操作を含みます。

Agent機能を利用したReActの実装

今回は、指定した地名の現在の天候と座標を確認するようなことをしてみましょう。なお、現在の天気予報や座標を取得する機能は「OpenWeatherのAPI使用方法」で紹介したOpenWeatherのAPIを使ったプログラムを用意してToolとして使うことにしてみます。

まずは、今回用意した各プログラムの実装例を紹介して、以降で詳細の説明をしていきます。

今回作成しているプログラムは参考例としてのプログラムのため細かな例外処理などを十分な考慮できていない点についてはご注意ください。また、ReActの思考の流れによってはうまくToolを実行できず例外が発生する場合もあります。

config.ini:各種APIのキー等を保存するコンフィグファイル (xxxxには必要なキーを記載)

[OPENAI]
key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

[WEATHER_API]
key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
url_current_weather_data = https://api.openweathermap.org/data/2.5/weather
url_direct_geocoding = https://api.openweathermap.org/geo/1.0/direct

get_current_weather.py:現在の天候を取得するクラスを含むプログラム

import configparser

import requests


class GetWeather:
    def __init__(self) -> None:
        # APIキーの読み込み
        config = configparser.ConfigParser()
        config.read("./config.ini")
        self.api_key = config["WEATHER_API"]["key"]
        self.url_current_weather_data = config["WEATHER_API"][
            "url_current_weather_data"
        ]
        self.url_direct_geocoding = config["WEATHER_API"]["url_direct_geocoding"]

    def get_coordinate(self, location: str) -> dict:
        """地名の情報をもとに座標情報を取得する。

        :param location: 地名
        :return: 座標情報
        """
        # 座標情報取得のためのパラメータ設定
        params = {"q": location, "appid": self.api_key}
        # データの取得
        response = requests.get(self.url_direct_geocoding, params=params)
        geocode_result = response.json()

        return geocode_result[0]

    def get_current_weather(self, location: str) -> dict:
        """地名の情報をもとに現在の天気の情報を取得する。

        :param location: 地名
        :return: 現在の天気情報
        """
        # 地名情報から座標情報を取得
        geo_data = self.get_coordinate(location)
        lat = geo_data["lat"]
        lon = geo_data["lon"]

        # 天気情報取得のためのパラメータ設定
        params = {
            "lat": lat,
            "lon": lon,
            "units": "metric",
            "appid": self.api_key,
            "lang": "ja",
        }
        # 天気データの取得
        response = requests.get(self.url_current_weather_data, params=params)
        weather_result = response.json()

        return weather_result


if __name__ == "__main__":
    pass

agent_basics.py:具体的にAgentを実行するプログラム

import configparser

from langchain import hub
from langchain.agents import AgentExecutor, Tool, create_react_agent
from langchain_openai import ChatOpenAI

from get_current_weather import GetWeather

# コンフィグファイルからOpenAI用のキーを取得する
config = configparser.ConfigParser()
config.read("config.ini")

# llmの設定
llm = ChatOpenAI(openai_api_key=config["OPENAI"]["key"])

# GetWeatherクラスの生成
gw = GetWeather()

# Toolの生成
weather_tool = Tool(
    name="天気",
    func=gw.get_current_weather,
    description="地名を入力すると現在の天気の情報を取得する",
)
coordinate_tool = Tool(
    name="座標",
    func=gw.get_coordinate,
    description="地名を入力すると座標の情報を取得する",
)
tools = [weather_tool, coordinate_tool]

# プロンプトテンプレートを準備する。以下リンク先から取得。
# https://smith.langchain.com/hub/hwchase17/react
prompt = hub.pull("hwchase17/react")
print(prompt)

# Agentの生成
agent = create_react_agent(llm, tools, prompt)
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,
    max_iterations=3,
)


result = agent_executor.invoke({"input": "名古屋の現在の天候と座標を教えてください。"})
print(result)
【実行結果例】※読みやすいように適宜改行追加等しているため実出力とは異なります。
input_variables=['agent_scratchpad', 'input', 'tool_names', 'tools'] metadata={'lc_hub_owner': 'hwchase17', 'lc_hub_repo': 'react', 'lc_hub_commit_hash': 'd15fe3c426f1c4b3f37c9198853e4a86e20c425ca7f4752ec0c9b0e97ca7ea4d'}
template='Answer the following questions as best you can. You have access to the following tools:

{tools}

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

Begin!\

Question: {input}
Thought:{agent_scratchpad}'


> Entering new AgentExecutor chain...
We need to get the current weather and coordinates for Nagoya.
Action: 天気
Action Input: "Nagoya"
{'coord': {'lon': 136.8998, 'lat': 35.1851}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': '厚い雲', 'icon': '04d'}], 'base': 'stations', 'main': {'temp': 30.82, 'feels_like': 37.71, 'temp_min': 28.9, 'temp_max': 31.14, 'pressure': 1012, 'humidity': 72, 'sea_level': 1012, 'grnd_level': 1010}, 'visibility': 10000, 'wind': {'speed': 0.35, 'deg': 33, 'gust': 1.07}, 'clouds': {'all': 88}, 'dt': 1724544537, 'sys': {'type': 2, 'id': 2001167, 'country': 'JP', 'sunrise': 1724530788, 'sunset': 1724578179}, 'timezone': 32400, 'id': 1856057, 'name': '名古屋市', 'cod': 200}

Now I need to get the coordinates for Nagoya.
Action: 座標
Action Input: "Nagoya"{'name': 'Nagoya', 'local_names': {'ca': 'Nagoya', 'fr': 'Nagoya', 'uk': 'Наґоя', 'sv': 'Nagoya', 'de': 'Nagoya', 'lt': 'Nagoja', 'sl': 'Nagoja', 'ko': '나고야 시', 'pl': 'Nagoja', 'oc': 'Nagoya', 'io': 'Nagoya', 'kn': 'ನಗೋಯ', 'km': 'ក្រុងណាហ្គោយ៉ា', 'eo': 'Nagojo', 'cs': 'Nagoja', 'nl': 'Nagoya', 'en': 'Nagoya', 'ja': '名古屋市', 'mr': 'नागोया', 'sr': 'Нагоја', 'he': 'נגויה', 'zh': '名古屋市', 'pt': 'Nagoia', 'ia': 'Nagoya', 'es': 'Nagoya', 'th': 'นาโงยะ', 'ru': 'Нагоя', 'et': 'Nagoya'}, 'lat': 35.1851045, 'lon': 136.8998438, 'country': 'JP'}I now know the final answer.
Final Answer: 名古屋市の現在の天候は、厚い雲で気温は30.82度です。座標は、緯度35.1851045、経度136.8998438です。

> Finished chain.
{'input': 'Nagoyaの現在の天候と座標を教えてください。', 'output': '名古屋市の現在の天候は、厚い雲で気温は30.82度です。座標は、緯度35.1851045、経度136.8998438です。'}

プログラム実装内容の詳細説明

ここからは上記で紹介したプログラムの実装内容をポイントを絞って説明します。なお、この記事ではAgent機能の説明を中心にするため、OpenWeather APIやOpenAI APIに関する説明は省略します。必要に応じて以下のような記事も参考にしてください。

必要モジュールのインポートとLLMの準備

Agent機能については、langchain.agentsモジュールからAgentExecutor, Tool, create_react_agentをインポートしておきます。また、llmとしてOpenAIのChatOpenAIを用意しておきます。

Toolの用意
# GetWeatherクラスの生成
gw = GetWeather()

# Toolの生成
weather_tool = Tool(
    name="天気",
    func=gw.get_current_weather,
    description="地名を入力すると現在の天気の情報を取得する",
)
coordinate_tool = Tool(
    name="座標",
    func=gw.get_coordinate,
    description="地名を入力すると座標の情報を取得する",
)
tools = [weather_tool, coordinate_tool]

LLMは上記部分で準備したToolを使用します。今回は自分で独自に作ったGetWeatherというクラスの中のメソッドをToolとして登録しています。

  • weather_tool:地名から天候の情報を返却するツール
  • coordinate_tool:地名から座標を返却するツール

Toolには、該当する関数やメソッドの情報を指定します。引数の内容については以下の通りです。

引数
nameツール名を示します。
funcツールが実行する関数やメソッドを示します。
descriptionツールで実行される関数やメソッドが何をするかといったToolの詳細を示します。
AgentがどのToolを選択するかの判断に重要な役割を果たすので詳細かつ正確に記述する必要があります

用意したツールは「tools=[weather_tool, coordinate_tool]」のようにツールリストとしてまとめておきます。

プロンプトの準備

ReActのためのプロンプトテンプレートを用意します。プロンプトテンプレートを使用して自分で用意もできますが、LangChainではHubで様々なプロンプトテンプレートが提供されています。今回は「hwchase17/react」というReAct用のプロンプトテンプレートを取得して使用させてもらうことにします。

# プロンプトテンプレートを準備する。以下リンク先から取得。
# https://smith.langchain.com/hub/hwchase17/react
prompt = hub.pull("hwchase17/react")
print(prompt)

「hwchase17/react」のプロンプトテンプレートは、実行結果例でも表示していますが以下のようになっています。こちらを参考にしてください。

Answer the following questions as best you can. You have access to the following tools:

{tools}

Use the following format:

Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

Begin!

Question: {input}
Thought:{agent_scratchpad}

このプロンプトテンプレートはReActを実装するためにQuestion、Thought、Action、Action Input、Observation、Thought、Final Answerというような思考の流れが記載されています。上記で用意したToolの情報は{tools}に、呼び出す際の質問は{input}に埋め込まれて実行がされます。

Agentの準備と実行
# Agentの生成
agent = create_react_agent(llm, tools, prompt)
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,
    max_iterations=3,
)


result = agent_executor.invoke({"input": "名古屋の現在の天候と座標を教えてください。"})
print(result)

create_react_agentを使ってReAct Agentを生成しています。このAgentは、LLMやツール、プロンプトの情報を引数に取り、ReActの考え方に基づいたAgentを構築します。

Agentを実行するのがAgentExecutorです。生成時にcreate_react_agentで生成したagentや、Toolリストであるtoolsを指定します。

verbose=TrueにしておくとReActの実行の流れを出力することが可能であるので設定しておくと動作の確認ができます。また、max_iterationsは最大の思考回数を指定しています。

Agent実行時には、invokeメソッドを使用して実行します。その際にプロンプトの{input}に質問内容が渡せるように指定します。

実行結果の内容
> Entering new AgentExecutor chain...
We need to get the current weather and coordinates for Nagoya.
Action: 天気
Action Input: "Nagoya"
{'coord': {'lon': 136.8998, 'lat': 35.1851}, 'weather': [{'id': 804, 'main': 'Clouds', 'description': '厚い雲', 'icon': '04d'}], 'base': 'stations', 'main': {'temp': 30.82, 'feels_like': 37.71, 'temp_min': 28.9, 'temp_max': 31.14, 'pressure': 1012, 'humidity': 72, 'sea_level': 1012, 'grnd_level': 1010}, 'visibility': 10000, 'wind': {'speed': 0.35, 'deg': 33, 'gust': 1.07}, 'clouds': {'all': 88}, 'dt': 1724544537, 'sys': {'type': 2, 'id': 2001167, 'country': 'JP', 'sunrise': 1724530788, 'sunset': 1724578179}, 'timezone': 32400, 'id': 1856057, 'name': '名古屋市', 'cod': 200}

Now I need to get the coordinates for Nagoya.
Action: 座標
Action Input: "Nagoya"
{'name': 'Nagoya', 'local_names': {'ca': 'Nagoya', 'fr': 'Nagoya', 'uk': 'Наґоя', 'sv': 'Nagoya', 'de': 'Nagoya', 'lt': 'Nagoja', 'sl': 'Nagoja', 'ko': '나고야 시', 'pl': 'Nagoja', 'oc': 'Nagoya', 'io': 'Nagoya', 'kn': 'ನಗೋಯ', 'km': 'ក្រុងណាហ្គោយ៉ា', 'eo': 'Nagojo', 'cs': 'Nagoja', 'nl': 'Nagoya', 'en': 'Nagoya', 'ja': '名古屋市', 'mr': 'नागोया', 'sr': 'Нагоја', 'he': 'נגויה', 'zh': '名古屋市', 'pt': 'Nagoia', 'ia': 'Nagoya', 'es': 'Nagoya', 'th': 'นาโงยะ', 'ru': 'Нагоя', 'et': 'Nagoya'}, 'lat': 35.1851045, 'lon': 136.8998438, 'country': 'JP'}I now know the final answer.
Final Answer: 名古屋市の現在の天候は、厚い雲で気温は30.82度です。座標は、緯度35.1851045、経度136.8998438です。

> Finished chain.
{'input': 'Nagoyaの現在の天候と座標を教えてください。', 'output': '名古屋市の現在の天候は、厚い雲で気温は30.82度です。座標は、緯度35.1851045、経度136.8998438です。'}

実行結果を見てみましょう。上記を見てもらうと分かるように「We need to get the current weather and coordinates for Nagoya.」から始まり、まず天気ツールが使用されてインプットに"Nagoya"が入力されて結果が返ってきています。「{‘coord’: {‘lon’: 136.8998, ‘lat’: 35.1851}, ‘weather’: [{‘id’: 804, ‘main’: ‘Clouds’, ‘description’: ‘厚い雲’, ‘icon’: ’04d’}],…」という部分は、weather_toolに設定したgw.get_current_weatherからの返却値です。

次に、「Now I need to get the coordinates for Nagoya.」というように座標ツールが使われます。こちらも同様に"Nagoya"が入力されて、「{‘name’: ‘Nagoya’, ‘local_names’: {‘ca’: ‘Nagoya’,…」という結果が返ってきます。これは、coordinate_toolに設定したgw.get_coordinateからの返却値です。

そして最後にFinished Answerで、各ツールから取得した結果をまとめた回答文が作られてFinished Chainとなっています。このようにAIが自動的に思考し、ツールを選択し実行するような一連の流れをReAct Agentで実装できました。

今回はReAct Agentの一例として紹介しましたが、それ以外にもAgent機能を使用、実装することでLLMの活用の幅を大きく広げることができます。

まとめ

Pythonで大規模言語モデル(LLM: Large Language Model)を活用する際に使用できるLangChainでAgent機能を使って実行を自動化する方法を解説しました。

Agent機能とは、複数の言語モデル、ツール、データベース、外部API等を統合して動的にタスク処理を行うことができるようにするための機能です。

この記事では、Agentの実装例の一つとしてReAct Agentの実装例を紹介し、自分で作成した天候や座標の取得機能をツールとしてLLMと連携する例を紹介しました。今回のReAct Agent以外にももちろん色々なAgent機能があります。

LangChainのAgent機能は、LLMと連携した機能開発を効率化できる機能ですので、是非皆さんも色々と調べて実装してみてもらえると良いかと思っています。