AI エージェント入門 (LangGraph)
IBM社[1]が「2025年はAIエージェントの年だ」とした記事を出したり、Mckinsey社が2025年アウトルック[2]で生成AIと並んでAIエージェントを主要なトレンドとしたり、AIエージェントが注目を集め始めています。
AI エージェントってなに?
ではそもそも、AIエージェントとは、何を意味するのか?
AIエージェントはまだ発展中の分野で、開発や評価のための明確な理論は確立されていません。確立された定義がないため、LangChainの記事にあったものをここでは紹介しておきます。
LLMはあくまでも言語モデルでテキストを確率的に生成することが主な役割です。そのため、LLM単体では複雑な数式、物理演算などの演算結果が確率的生成ゆえに保証できません。
このような不得意な分野を外部のツールを利用することでLLMの機能を拡張させようという試みの一つです。
ハードコーディングして、LLM前後の各ステップを制御することもできますが、AIエージェントはユーザーのリクエストを受け、「LLMが判断」して、外部のツールを利用するか決めるというポイントがAIエージェントの肝になります。
チャットボットとの違い
AIエージェントとチャットボット、確かに似ている言葉で混同されやすい。
チャットボットは事前に決められたシナリオやルールに沿って回答をします。一方、AIエージェントは、チャットボットと同じで会話をしますが、会話は「インターフェース」であり、真の目的は、タスクの遂行です。
例を考えてみます。
-
チャットボット:「明日の天気は?」→「晴れの予報です」と答えます。これは、あらかじめ、知識として与えられているものを答えています。
-
AIエージェント:「明日の天気は?」→LLMが「天気API を呼ぶべきと判断」→APIにアクセスしてデータを取得→結果を返答。
AIエージェントは、能動的(agency)に必要なアクションを判断して行動する点がチャットボットとの違いです。
ReActエージェントとは
理論的な解説が中心のため、興味ない方は読み飛ばしても問題ありません。
ReActエージェントは2023年にReAct: Synergizing Reasoning and Acting in Language Modelsで発表された、厳格なワークフローではなく人間ののような柔軟な問題解決を反映しようとしたアプローチです。
ReActの中心にあるのはReasoning(推論)とActing(行動)を組み合わせることで、最終的なアウトプットをより正確で柔軟なものにしようとしています。
ReActプロンプトのコア要素:
- Observation(観察):現在の状況に対する認識
- Thought(推論):与えられたタスクをこなすために、次に何をすべきかを思考
- Action(行動):推論で得られた指針を基に行動(ツールの呼び出し、計算する、回答をまとめるetc)
- Observation(観察):行動の結果を観察し、次の推論に活かす
このサイクルを繰り返すことで複雑なタスクを段階的に解決し最良の回答を導き出します。
例題を見てみます。
【質問】 「30℃を華氏に変換するといくらですか?」
【ReActプロンプト】
-
Observation:温度は30℃
-
Thought:華氏に変換するには F = C × 9/5 + 32 の式を使うべき
-
Action:calculator_tool: 30 × 9/5 + 32
-
Observation:86(ツールが計算した結果)
-
Final Answer:30℃は86℉に相当します。
このようにReasoning(推論)とActing(行動)を組み合わせて進めていくことで、最終的なアウトプットはより正確で柔軟なものになります。
LangGraphでAIエージェントを作成する
ReActの仕組みがわかったところで、次はこれを実際にAIエージェントとして動かしてみましょう。ここで役立つのが LangGraph です。LangGraphは「エージェントが考えて動く流れ」をフローチャートのように整理できるフレームワークで、途中の思考を記録しながら進められるのが特徴です。これはまさに、ReActが「考える → 行動する → 結果を見る」を繰り返す流れと相性がぴったりです。そのため、LangGraphでは create_react_agent
という便利な機能が用意されており、ReActの考え方をすぐに実装できます。
パッケージのインストール
uv add langchain langchain_google_genai langgraph
or
pip install langchain langchain_google_genai langgraph
必要なパッケージをインポートします。
from langgraph.prebuilt import create_react_agent
from langchain.tools import tool
from langchain_core.messages import HumanMessage
from langchain_google_genai import ChatGoogleGenerativeAI
import os
from dotenv import load_dotenv
load_dotenv()
モデルの定義
使用したいモデルを用意します。ここでは、gemini-2.5-flash
を使用します。
llm = ChatGoogleGenerativeAI(
model = 'gemini-2.5-flash',
api_key=os.getenv('GEMINI_API_KEY')
)
ツールの定義
次に、モデルが使用するツールを定義します。通常の関数に@tool
デコレーターを付けるだけで定義できます。@tool
は関数をモデルが呼び出して実行できるインターフェースを提供します。関数名、関数の記述、引数の型ヒントはモデルが関数を呼び出す判断基準として使用するので、シンプルでわかりやすく記述してください。
@tool
def add (a: int, b: int)-> int:
"add two values"
return a + b
@tool
def multiply (a: int, b: int) -> int:
"multiply two values"
return a * b
定義したツールの一覧を出力して確認します
for i, t in enumerate([add, multiply]):
print(f'Tool_{i+1}:')
print(f"Name: {t.name} \nDescription: {t.description}")
print('-'*50)
# 出力
Tool_1:
Name: add
Description: add two values
--------------------------------------------------
Tool_2:
Name: multiply
Description: multiply two values
--------------------------------------------------
エージェントの構築
AIエージェントを構築します。create_react_agent
を使用して、モデルとモデルが使用できるツールを登録します。
agent = create_react_agent(llm, [add, multiply]) # ツールはリストで渡す
これで、AIエージェントが完成しました。
実際に試してみます。
user_input = 'Add 2 and 4'
response = agent.invoke({ "messages": [HumanMessage(content=user_input)]})
print(response['messages'][-1].content)
agent.invokeの呼び出し形式
以下のようなカスタム辞書形式で呼び出すことも可能です。使用するモデルによってはエラーで受け付けないこともあります。
response = agent.invoke({'messages': ('human', user_input)})
上記のコードを実行すると以下の内容が表示されます。
The sum of 2 and 4 is 6.
正しく計算されていますが、実際にツールを使用して計算されたものかを確認してみます。
messages = agent.invoke({'messages': [HumanMessage(content=user_input)]})
for msg in messages['messages']:
msg.pretty_print()
実行すると、このようにメッセージが表示されます。
================================ Human Message =================================
Add 2 and 4
================================== Ai Message ==================================
Tool Calls:
add (4b21f234-2899-4f1b-be17-209d7145efe6)
Call ID: 4b21f234-2899-4f1b-be17-209d7145efe6
Args:
a: 2.0
b: 4.0
================================= Tool Message =================================
Name: add
6
================================== Ai Message ==================================
The sum of 2 and 4 is 6.
メッセージを見ていくと、Ai Messageの所に、Tool Calls
とあり、モデルがadd
関数を呼び出して、計算するように判断したのがわかります。また、それぞれのargumentに渡されたvalueも表示されています。Tool Messageとして、add
関数が6
と計算結果を返しています。その後、モデルがツールの結果を受け取り、最終結果を出力しています
モデルが能動的にツールを使用するか判断して与えられたタスクを遂行するプロセスがよくわかります。
タスクを少し複雑にしてみます。加算をしてその結果をさらに乗算した結果を求めてみます。
user_input = 'Add 2 and 4. Then multiply that by 5'
messages = agent.invoke({'messages': [HumanMessage(content=user_input)]})
for msg in messages['messages']:
msg.pretty_print()
================================ Human Message =================================
Add 2 and 4. Then multiply that by 5
================================== Ai Message ==================================
Tool Calls:
add (3c3b9eb2-3926-4dbf-8ad8-9b5c97f000a8)
Call ID: 3c3b9eb2-3926-4dbf-8ad8-9b5c97f000a8
Args:
a: 2.0
b: 4.0
================================= Tool Message =================================
Name: add
6
================================== Ai Message ==================================
Tool Calls:
multiply (85b0832c-fe4c-43c0-88f2-4eef1b63689f)
Call ID: 85b0832c-fe4c-43c0-88f2-4eef1b63689f
Args:
a: 6.0
b: 5.0
================================= Tool Message =================================
Name: multiply
30
================================== Ai Message ==================================
The answer is 30.
出力を見ると、複数回ツールの呼び出しが行われています。このように、段階的に複数のツールを使い分けて結果を求める事ができます。
メモリ機能:Checkpointer
最後にLangGraphのメモリ機能について解説していきます。
まず、会話を分割して計算しようとするとどのような結果になるか見ていきます。
# first input
user_input1 = 'Add 2 and 4'
response = agent.invoke({ "messages": [HumanMessage(content=user_input1)]})
print('Answer 1: ', response['messages'][-1].content)
# second input
user_input2 = 'Multiply that by 5'
response = agent.invoke({ "messages": [HumanMessage(content=user_input2)]})
print('Answer 2: ', response['messages'][-1].content)
出力を見てみると、
# 出力
Answer 1: The sum of 2 and 4 is 6.
Answer 2: I need to know what "that" refers to. Can you please provide the number you would like to multiply by 5?
that
が理解できないため、計算できませんと返してきました。前のセクションで見た、1つの質問としてuser_input = 'Add 2 and 4. Then multiply that by 5'
入力すると答えを求めることができたのに、会話を分割した場合は、求めれませんでした。
AIエージェントの呼び出しはそれぞれ独立しているため、記憶の保持や受け渡しをすることができません。そのため、最初の計算結果が2番目の呼び出しのときには保持されず、計算できませんでした。
複数のやり取りを記憶するための機能がcheckpointer
です。
共通のthread_id
を使用することで、次の会話は最初の会話の続きとして自動的に記録されます。そのため、以前の計算結果や質問内容についても正しく答えることができるようになります。もし会話を切り離したいときは、thread_id
を変更することで、それぞれの会話ツリーは独立した存在となります。
from langgraph.checkpoint.memory import InMemorySaver
checkpointer = InMemorySaver()
agent_with_memory = create_react_agent(
model= llm,
tools= [add, multiply],
checkpointer=checkpointer)
session_id = 'test_session'
config = {'configurable': {'thread_id': session_id}}
# first input
user_input1 = 'Add 2 and 4'
response = agent_with_memory.invoke({ "messages": [HumanMessage(content=user_input1)]}, config=config)
print('Answer 1: ', response['messages'][-1].content)
# second input
user_input2 = 'Multiply that by 5'
response = agent_with_memory.invoke({ "messages": [HumanMessage(content=user_input2)]},config=config)
print('Answer 2: ', response['messages'][-1].content)
# third input
user_input3 = 'What was the first question?'
response = agent_with_memory.invoke({ "messages": [HumanMessage(content=user_input3)]},config=config)
print('Answer 3: ', response['messages'][-1].content)
# 出力
Answer 1: The sum of 2 and 4 is 6.
Answer 2: 6 multiplied by 5 is 30.
Answer 3: The first question was "Add 2 and 4".
結論
今回はLangGraphのcreate_react_agent
を使用してAIエージェントを構築する方法を見ていきました。簡単にAIエージェントを構築できると感じた読者も多いと思います。様々なツールと組み合わせて、AIエージェントを試してみてください。
最後に、今回の内容は入門編として基本的なAIエージェントについて解説していきました。LangGraphにはStateGraph
を使用して独自のワークフローを設定したAIエージェントや複数のAIエージェント
を管理するsupervisor
AIエージェントを構築したり、MCPサーバーをツールとして連携したりと様々な拡張を行うことができます。下記にリンクを置いておくので興味がありましたら、読んでみてください。
もし、これらの解説記事がほしいなどの要望がありましたら、コメントで教えて下さい。コメントを参考に解説記事を出すかもしれません。
Discussion