初級
ReActパターンの実装
ReasoningとActing:LLMが外部ツールと対話することを可能にする基礎的なパターン
12分で読める
LangChainOpenAI
ReActパターンの実装#
ReAct(Reasoning and Acting)は、推論の過程とアクションの実行を交互に行うことで、LLMが複雑な問題を解決できるようにする基礎的なパターンです。
ReActとは?#
ReActは以下の要素を組み合わせます:
- 推論(Reasoning):モデルが問題を段階的に考え抜く
- 行動(Acting):モデルが外部ツールを使用してアクションを実行する
- 観察(Observation):モデルがツールの出力を推論に組み込む
このループは、モデルが最終的な答えに到達するまで続きます。
ReActループ#
Thought: Xについての情報を見つける必要がある
Action: search[X]
Observation: [検索結果]
Thought: 結果に基づいて、Yがわかった。Zを計算する必要がある。
Action: calculate[Zの式]
Observation: [計算結果]
Thought: 必要な情報はすべて揃った。
Action: finish[最終的な答え]基本的な実装#
ツールのセットアップ#
from typing import Callable, Dict
# 利用可能なツールを定義
tools: Dict[str, Callable] = {
"search": lambda query: search_web(query),
"calculate": lambda expr: eval_math(expr),
"lookup": lambda term: lookup_wikipedia(term),
}
def execute_action(action: str, input: str) -> str:
"""適切なツールを使用してアクションを実行する"""
if action in tools:
return tools[action](input)
return f"Unknown action: {action}"ReActプロンプト#
REACT_PROMPT = """利用可能なツールを使用して以下の質問に答えてください。
利用可能なツール:
- search[query]: ウェブで情報を検索する
- calculate[expression]: 数式を評価する
- lookup[term]: Wikipediaで用語を調べる
- finish[answer]: 最終的な答えを提供する
常に以下の形式を使用してください:
Thought: <次に何をするかについての推論>
Action: <tool_name>[<input>]
観察を受け取った後は、別のThought/Actionを続けるか、finishしてください。
質問: {question}
{history}
Thought:"""ReActエージェント#
from openai import OpenAI
import re
client = OpenAI()
def parse_action(response: str) -> tuple:
"""モデルの応答からアクションと入力を解析する"""
match = re.search(r'Action:\s*(\w+)\[(.*?)\]', response)
if match:
return match.group(1), match.group(2)
return None, None
def react_agent(question: str, max_steps: int = 10) -> str:
"""完了するまでReActループを実行する"""
history = ""
for step in range(max_steps):
# 次の思考とアクションを生成
prompt = REACT_PROMPT.format(question=question, history=history)
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}],
temperature=0,
stop=["Observation:"]
)
output = response.choices[0].message.content
history += output + "\n"
# アクションを解析して実行
action, action_input = parse_action(output)
if action == "finish":
return action_input
if action:
observation = execute_action(action, action_input)
history += f"Observation: {observation}\n"
else:
history += "Observation: Could not parse action. Try again.\n"
return "Max steps reached without conclusion"LangChainの使用#
LangChainは組み込みのReActサポートを提供します:
from langchain.agents import create_react_agent, AgentExecutor
from langchain.tools import Tool
from langchain_openai import ChatOpenAI
from langchain import hub
# ツールを定義
tools = [
Tool(
name="Search",
func=search_web,
description="最新情報をウェブで検索する"
),
Tool(
name="Calculator",
func=eval_math,
description="数学計算に役立つ"
),
]
# hubからReActプロンプトを取得
prompt = hub.pull("hwchase17/react")
# エージェントを作成
llm = ChatOpenAI(model="gpt-4", temperature=0)
agent = create_react_agent(llm, tools, prompt)
# エグゼキューターを作成
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True,
max_iterations=10,
handle_parsing_errors=True
)
# 実行
result = agent_executor.invoke({"input": "What is the population of Tokyo times 2?"})
print(result["output"])ReActの拡張#
メモリの追加#
from langchain.memory import ConversationBufferMemory
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True
)
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
memory=memory,
verbose=True
)エラー回復#
def react_with_recovery(question: str) -> str:
"""エラー回復機能付きReActエージェント"""
history = ""
errors = 0
max_errors = 3
for step in range(max_steps):
try:
# ... 通常のReActループ
pass
except Exception as e:
errors += 1
history += f"Observation: Error occurred: {str(e)}. Trying alternative approach.\n"
if errors >= max_errors:
return "Too many errors, unable to complete task"
return result構造化出力#
from pydantic import BaseModel
class ReActStep(BaseModel):
thought: str
action: str
action_input: str
observation: str | None = None
class ReActTrace(BaseModel):
question: str
steps: list[ReActStep]
final_answer: str一般的なパターン#
検索 → 要約#
Thought: AIに関する最近のニュースを見つける必要がある
Action: search[latest AI news 2024]
Observation: [複数の記事...]
Thought: いくつかの記事が見つかった。主要なポイントを要約しよう。
Action: finish[Summary: 1. OpenAI released... 2. Google announced...]計算 → 検証#
Thought: 複利を計算する必要がある
Action: calculate[1000 * (1 + 0.05) ** 10]
Observation: 1628.89
Thought: 段階的に計算して検証しよう
Action: calculate[1000 * 1.05 ** 10]
Observation: 1628.89
Thought: 両方の計算結果が一致する
Action: finish[The compound interest result is $1,628.89]ベストプラクティス#
- 明確なツール説明:ツールの目的を曖昧さなくする
- ツール数の制限:3〜5個のツールが最適。多すぎると混乱を招く
- エラーの適切な処理:ループにエラー回復機能を含める
- 最大反復回数の設定:無限ループを防ぐ
- トレースのログ記録:デバッグと改善のために完全なトレースを保持する
ReActを使用するタイミング#
適しているタスク:
- 多段階の調査タスク
- 参照を伴う計算
- リアルタイムデータを必要とするタスク
理想的でないタスク:
- 単純なQ&A
- 創造的な文章作成
- 外部ツールを必要としないタスク