AIエージェント活用実践編 / 単一エージェント実装 — ReAct ループの基本
最小 QA エージェント完走 — 実装と動作確認
無料公開レッスン / 読了目安 13 分
search_wikipedia ツールとメインループの構造
まず、search_wikipedia ツールの定義を考えましょう。これは、第2レッスンで学んだ Tool 定義の原則に従います。
{
"name": "search_wikipedia",
"description": "Wikipedia で特定のキーワードに関する情報を検索します。事実確認や一般的な知識の取得に役立ちます。",
"input_schema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "検索したいキーワード。"
}
},
"required": ["query"]
}
}
次に、このツールと LLM を連携させるメインループの基本的な構造を考えます。
ここでは、Anthropic Claude の tool_use 機能を利用することを想定します。
import anthropic
import json
# Claude APIクライアントの初期化 (ch01で設定済みを想定)
# client = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
def execute_tool(tool_name: str, tool_input: dict) -> str:
"""
ツール実行をシミュレートする関数。
実際にはここでWikipedia APIなどを呼び出す。
"""
if tool_name == "search_wikipedia":
query = tool_input.get("query")
# ここで実際のWikipedia API呼び出しを行う
# 例: wikipedia-apiライブラリを使う
# import wikipedia
# try:
# page = wikipedia.page(query, auto_suggest=False)
# return page.summary
# except wikipedia.exceptions.PageError:
# return "Wikipediaで該当するページが見つかりませんでした。"
# 簡単のため、ここではダミーの応答を返す
if "ReAct" in query:
return "ReActは、LLMが推論(Reasoning)と行動(Acting)を組み合わせることで、複雑なタスクをより効果的に解決するためのフレームワークです。Thought-Action-Observationのループを繰り返します。"
elif "東京タワー" in query:
return "東京タワーは、東京都港区芝公園にある総合電波塔です。1958年に竣工し、高さは333メートルです。主にテレビやラジオの電波を送信しています。"
else:
return "検索結果はありません。"
else:
raise ValueError(f"Unknown tool: {tool_name}")
def run_qa_agent(user_question: str, max_iterations: int = 5):
conversation_history = []
tools = [
{
"name": "search_wikipedia",
"description": "Wikipedia で特定のキーワードに関する情報を検索します。事実確認や一般的な知識の取得に役立ちます。",
"input_schema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "検索したいキーワード。"
}
},
"required": ["query"]
}
}
]
for i in range(max_iterations):
messages = conversation_history + [{"role": "user", "content": user_question}] if not conversation_history else conversation_history
response = client.messages.create(
model="claude-3-opus-20240229", # または Sonnet, Haiku
max_tokens=1000,
messages=messages,
tools=tools,
tool_choice={"type": "auto"}
)
if response.stop_reason == "tool_use":
tool_use = response.content[0]
tool_name = tool_use.name
tool_input = tool_use.input
print(f"Thought: LLM decided to use tool '{tool_name}' with input {tool_input}")
# 会話履歴にLLMのツール使用の意図を追加
conversation_history.append({"role": "assistant", "content": response.content})
try:
observation = execute_tool(tool_name, tool_input)
print(f"Observation: Tool returned '{observation}'")
# 会話履歴にツールの実行結果を追加
conversation_history.append({"role": "user", "content": [{"type": "tool_result", "tool_use_id": tool_use.id, "content": observation}]})
except Exception as e:
print(f"Observation: Tool execution failed with error: {e}")
conversation_history.append({"role": "user", "content": [{"type": "tool_result", "tool_use_id": tool_use.id, "content": f"Error: {e}"}]})
# エラー発生時はエージェントを停止するか、ユーザーに報告する
return f"エラーが発生しました: {e}"
elif response.stop_reason == "end_turn":
# LLMが最終回答を生成したと判断
final_answer = response.content[0].text
print(f"Final Answer: {final_answer}")
return final_answer
else:
print(f"Unexpected stop reason: {response.stop_reason}")
print(f"LLM content: {response.content}")
return "申し訳ありませんが、質問に答えることができませんでした。"
return "最大イテレーション数に達しました。途中までの情報しか提供できません。"
# 実際の使い方
# print(run_qa_agent("ReActとは何ですか?"))
# print(run_qa_agent("東京タワーの高さは何メートルですか?"))
上記のコードは、client.messages.create が tool_use を返した場合、そのツールを実行し、結果を tool_result として LLM に返すという ReAct ループの基本的な流れを示しています。
max_iterations は、無限ループを防ぐための重要な安全装置です。
正常系の動作確認
このエージェントが意図した通りに動作するかを確認するには、いくつかの質問を投げかけてみましょう。
- 「ReActとは何ですか?」
search_wikipediaが呼び出され、その結果に基づいて回答が生成されることを期待します。
- 「東京タワーの高さは何メートルですか?」
- 同様に
search_wikipediaが呼び出され、回答が生成されることを期待します。
- 同様に
- 「存在しない概念について教えてください」
search_wikipediaが呼び出されるが、適切な結果が得られず、LLM がその状況を認識して「見つかりませんでした」と回答するか、別の対応をとることを期待します。
これらのテストを通じて、エージェントがツールを適切に選択し、その結果を解釈してユーザーに分かりやすい形で回答できるかを確認します。
まとめ
本レッスンでは、ReAct ループの知識を統合し、Wikipedia 検索と要約を行う最小限の QA エージェントの設計と実装の概略を学びました。
search_wikipedia のような外部ツールと LLM の推論能力を組み合わせることで、エージェントは自律的に情報を収集し、ユーザーの質問に答えることができます。
この経験は、より複雑なエージェントを構築するための重要な第一歩となるでしょう。