7月4日、Toward Data Scienceが「AI Agents Explained: What Is a ReAct Loop and How Does It Work?」と題した記事を公開した。最大の見どころは、条件分岐を含む質問に対してAIモデルがツール呼び出しを自律的にスキップできる点だ。雨が降らなかった場合には通貨変換ツールを一度も呼ばずに終了する、その挙動がReActループの本質を端的に示している。
ReActループとは何か
ReActとは「Reason(推論)+ Act(行動)」を組み合わせた造語で、AIモデルがツールを繰り返し呼び出しながら段階的に問題を解いていく仕組みだ。2022年にYao et al.が発表した論文「ReAct: Synergizing Reasoning and Acting in Language Models」で提唱されたアーキテクチャが原型となっている。ループの構造はシンプルで、以下の3ステップを繰り返す。
- Reason:モデルが現在持っている情報と不足している情報を評価する
- Act:不足情報を補うため、適切なツールを呼び出す
- Observe:ツールの実行結果をコンテキストに追加する
このループは、モデルが「回答に十分な情報が揃った」と判断した時点で終了し、テキストで最終回答を返す。

なぜ並列ツール呼び出しでは不十分なのか
記事が例として挙げるのが、次の問いだ。
「アテネで今日雨が降ったら友人と100EURの賭けをした。もし勝ったなら、それは何USDになる?」
この質問には条件分岐が含まれている。「雨が降ったかどうか」を確認しなければ、通貨変換(convert_currency)を呼び出すべきかどうか自体が決まらない。
一方、従来の並列ツール呼び出しは「天気を教えて、かつ100USDは何EUR?」のような独立した2つの問いには有効だが、片方の結果がもう片方の実行条件になるケースには対応できない。ReActループはまさにこの「依存関係のある逐次処理」を解決するために存在する。
コードで見るReActループ
記事ではOpen-Meteoで天気を、Frankfurterで為替レートを取得するサンプルを使用している(いずれもAPIキー不要)。なお、このサンプルは同シリーズの前回記事で導入したツール定義を踏まえており、今回はprecipitation_mmフィールドを追加して賭けの条件(雨が降ったか)を評価できるよう拡張している。
天気取得関数の要点をまとめると、都市名からジオコーディングAPIで緯度・経度を取得し、その座標で気温と降水量(precipitation_mm)を取得して辞書で返す構造だ。前回からの変更はprecipitation_mmの追加のみで、これがモデルの条件判断を可能にするキーフィールドとなる。
ループ本体は以下のように実装する。会話履歴(messages)を引き回し、モデルがツール呼び出しを要求する限りループを継続、テキスト回答が返ってきた時点で終了する。
# モデルの応答を見ながらツール呼び出しを繰り返し、テキスト回答が出たら終了するメインループ
messages = [{"role": "user", "content": "I bet my friend 100 EUR that it would rain in Athens today. If I won, how many USD is that?"}]
max_iterations = 5
for i in range(max_iterations):
response = client.chat.completions.create(
model="gpt-4o-mini", messages=messages, tools=tools
)
message = response.choices[0].message
messages.append(message)
if not message.tool_calls:
print("Final answer:", message.content)
break
for tool_call in message.tool_calls:
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
function_response = available_functions[function_name](**function_args)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(function_response)
})
max_iterationsの上限設定が重要なのは、モデルの呼び出しごとにコストが発生するためだ。無限ループへの防御として必ず入れるべき実装パターンである。
実行結果:早期終了がコスト削減に直結する
雨が降っていた場合(3ステップ):
Step 1: get_current_weather → precipitation_mm: 3.2
Step 2: convert_currency → 108.50 USD
Step 3: Final answer — "It did rain in Athens today. Your 100 EUR comes out to 108.50 USD."
雨が降っていなかった場合(2ステップで終了):
Step 1: get_current_weather → precipitation_mm: 0.0
Step 2: Final answer — "It did not rain in Athens today. No currency conversion needed!"
後者のシナリオでは、モデルはconvert_currencyを一度も呼び出さずに終了している。誰かが明示的にスキップを指示したわけではなく、モデルが観測結果から自律的に判断した結果だ。並列ツール呼び出しなら両ツールが問答無用で実行されるところを、ReActループは必要なツールだけを必要な回数だけ実行できる。
ReActループが並列呼び出しより優れる3つのケース
記事では、ReActループが有効なシナリオを以下のように整理している。
- あるツールの結果が、別のツールを呼び出すべきかどうかの条件になるとき(賭けの例がまさにこれ)
- 後のツール呼び出しの引数が、先のツールの返り値に依存するとき(例:都市から使用通貨を調べてから変換を実行する)
- 先のツールの結果が予期しない内容を返し、次のアクションを変更する必要があるとき
逆に、問いが独立した複数のサブタスクに分解できるなら、並列ツール呼び出しの方がシンプルで高速だ。
詳細はAI Agents Explained: What Is a ReAct Loop and How Does It Work?を参照していただきたい。