LangChain

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

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

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 として使います。

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

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 と連携した機能開発を効率化できる機能ですので、是非皆さんも色々と調べて実装してみてもらえればと思います。

あわせて読みたい
【Python Tech】プログラミングガイド
【Python Tech】プログラミングガイド

ABOUT ME
ホッシー
ホッシー
システムエンジニア
はじめまして。当サイトをご覧いただきありがとうございます。 私は製造業のメーカーで、DX推進や業務システムの設計・開発・導入を担当しているシステムエンジニアです。これまでに転職も経験しており、以前は大手電機メーカーでシステム開発に携わっていました。

プログラミング言語はこれまでC、C++、JAVA等を扱ってきましたが、最近では特に機械学習等の分析でも注目されているPythonについてとても興味をもって取り組んでいます。これまでの経験をもとに、Pythonに興味を持つ方のお役に立てるような情報を発信していきたいと思います。どうぞよろしくお願いいたします。

※キャラクターデザイン:ゼイルン様
記事URLをコピーしました