LangGraphを用いたAIエージェント"ReAct"の実装

# はじめに

この記事は「[LLM・LLM活用 Advent Calendar 2024](https://qiita.com/advent-calendar/2024/large-language-model)」4日目の記事になります。

生成AIについての話題を聞かない日はないくらい、生成AIが盛り上がりを見せていますね。そんな生成AIの中でも、私はAIエージェントに注目しています。  
AIエージェントは特定のタスクを**自律的に**実行し、環境と相互作用する能力を持つようなAIシステムのことで、2025年はAIエージェントが話題の中心になるとも言われています。AIエージェントにもさまざまな種類がありますが、大きく「シングル型」と「マルチ型」に分けることが出来ます。  
本記事では、シングルAIエージェントの中でも非常に有名な"ReAct"というAIエージェントの内容と実装方法を紹介します。

# ReActとは

"ReAct"は2022年に公開された「[ReAct: Synergizing Reasoning and Acting in Language Models](https://react-lm.github.io/)」という論文で提案されたAIエージェントです。  
これまで、AIによる推論 (Reasoning) と行動 (Acting) は別々のトピックとして研究されてきましたが、双方を合わせて行う形にすることで、「自分で考え、行動し、問題を解決する」ということが出来るようになりました。

具体的には、まずユーザがアイディアやタスクをAIエージェントに投げかけます。  
AIエージェントはツールを保持しており、渡されたタスクに対して保持しているツールを使うべきかを判断します。  
ツールを使う場合、ツールを実行して得られた結果をAIエージェントに返し、その内容を元にユーザへの回答を提示します。

![napkin-selection.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542065/6baaded9-cc7b-31c6-a85e-b16d7b1c07cb.png)

# 実装方法

実装においては、[LangGraphのReActのページ](https://langchain-ai.github.io/langgraph/how-tos/create-react-agent/)が参考になります。

まず最初に、必要なライブラリをインストールします。  
他の記事などではLangChainを利用しているものが多いですが、現在はLangGraphを利用することが推奨されています。

```shell
$ pip install -U langgraph langchain-openai
```

次に、使用するLLMのセッティングを行います。  
API KEYはスクリプトに直書きでも良いですが、安全性を考えるならば.envファイルに記述することをオススメします。

```python
import os
# OpenAI API KEYの例
os.environ["OPENAI_API_KEY"] = "{API KEY}"

model = ChatOpenAI(model="gpt-4o", temperature=0)
```

続いて、AIエージェントが必要に応じて使用するツールを作成します。  
今回は例として、東京と岩手の天気予報を提供するツールを作成します。

```python
@tool
def get_weather(city: Literal["tokyo", "iwate"]):
    """Use this to get weather information."""
    if city == "tokyo":
        return "It might be cloudy in tokyo"
    elif city == "iwate":
        return "It's always snow in iwate"
    else:
        raise AssertionError("Unknown city")

tools = [get_weather]
```

次にgraphの構築です。  
graphの構築はたった1行で出来ます。  
どのようなgraphが出来たか、確認することもでき、今回の場合、図のような非常にシンプルなgraphが構築されていることが分かります。

```python
# graphの構築
graph = create_react_agent(model, tools=tools)
# グラフの可視化
png_data = Image(graph.get_graph().draw_mermaid_png())
with open("output_graph.png", "wb") as f:
    f.write(png_data.data)
```

![output_graph.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/542065/2f091735-bcb6-c04e-427a-a0a4d01091ea.png)

最後に、プロンプトを設定して実行してみましょう！

```python
def print_stream(stream):
    for s in stream:
        message = s["messages"][-1]
        if isinstance(message, tuple):
            print(message)
        else:
            message.pretty_print()

inputs = {"messages": [("user", "what is the weather in iwate")]}
print_stream(graph.stream(inputs, stream_mode="values"))
```

こちらを実行すると、以下のような出力が得られるかと思います。  
まず最初に、ユーザが入力したプロンプトが"Human Message"で示されており、AIエージェントはToolが必要と判断してcity: iwateの情報を取得しています。  
取得した情報が"It's always snow in iwate"なので、その情報を元にAIエージェントがユーザに"The weather in Iwate is snowy"と回答しています。

```
================================ Human Message =================================

what is the weather in iwate
================================== Ai Message ==================================
Tool Calls:
  get_weather (call_PMPG39UHUqPrSQXWVaamDQL0)
 Call ID: call_PMPG39UHUqPrSQXWVaamDQL0
  Args:
    city: iwate
================================= Tool Message =================================
Name: get_weather

It's always snow in iwate
================================== Ai Message ==================================

The weather in Iwate is snowy.
```

一方、Toolを使っても判断できない情報が渡されると、以下のようにToolを使わずにLLMが持っている情報から回答を作成します。

```python
inputs = {"messages": [("user", "who built you?")]}
```
```
================================ Human Message =================================

who built you?
================================== Ai Message ==================================

I was developed by OpenAI, a research organization focused on creating and promoting friendly AI for the benefit of all humanity.
```

今回使用したスクリプトの全体は以下の通りです。

<details><summary>全体スクリプト</summary>

```python
from langchain_openai import ChatOpenAI
from typing import Literal
from langchain_core.tools import tool
from langgraph.prebuilt import create_react_agent
from IPython.display import Image
import os

os.environ["OPENAI_API_KEY"] = "{API KEY}"

# LLMモデル設定
model = ChatOpenAI(model="gpt-4o", temperature=0)

# toolで与えるデータの設定
@tool
def get_weather(city: Literal["tokyo", "iwate"]):
    """Use this to get weather information."""
    if city == "tokyo":
        return "It might be cloudy in tokyo"
    elif city == "iwate":
        return "It's always snow in iwate"
    else:
        raise AssertionError("Unknown city")

tools = [get_weather]

# graphの構築
graph = create_react_agent(model, tools=tools)

# グラフの可視化
png_data = Image(graph.get_graph().draw_mermaid_png())
with open("output_graph.png", "wb") as f:
    f.write(png_data.data)

def print_stream(stream):
    for s in stream:
        message = s["messages"][-1]
        if isinstance(message, tuple):
            print(message)
        else:
            message.pretty_print()

inputs = {"messages": [("user", "what is the weather in iwate")]}
print_stream(graph.stream(inputs, stream_mode="values"))
```
</details>


# まとめ

今回はAIエージェントの中でも有名なReActについてご紹介しました。  
ReActは[AutoGPT](https://github.com/Significant-Gravitas/AutoGPT)でも用いられていたりなど、非常に重要な技術となっています。  
AIエージェントについてのキャッチアップの一助になれば幸いです。