取り組みの背景
AIエージェント(AI Agent)技術が急速に進化する中、「どのような設計が最適か」という問いは多くの開発チームの悩みの種となっています。エージェントの設計方法は1つではなく、タスクの複雑性・信頼性要件・レイテンシ制約によって大きく異なります。
AIエージェント設計の全体像
エージェント設計を理解するには、まず「エージェントとは何か」を明確に定義する必要があります。
エージェント vs チャットボット vs LLM
| 特性 | LLM | チャットボット | エージェント |
|---|---|---|---|
| 自律性 | なし | 低い(スクリプト依存) | 高い(自己決定) |
| 外部ツール利用 | 不可 | 限定的 | 完全対応 |
| ステート管理 | なし | 簡易的 | 複雑なステート機構 |
| 推論ループ | 単一ターン | シーケンシャル | 反復的・適応的 |
| 本番環境適性 | 低 | 中 | 高 |
エージェントの本質は、外部環境(API・DB・ユーザー入力)と相互作用しながら、目的達成に向けて自律的に行動を選択する能力にあります。
パターン1: Reactive パターン
最もシンプルな設計で、「入力 → 思考 → 行動」の単一ループを実行します。
特徴
入力 → LLMが状況を分析 → 最適な行動を選択 → 実行結果を返す
ユーザーの質問に対して、LLMが即座に適切なツール(検索・計算・DB照会等)を選択・実行する方式です。複雑な推論は不要ですが、「多ステップのタスク」には不適切です。
実装例
class ReactiveAgent:
def __init__(self, llm, tools):
self.llm = llm
self.tools = {t.name: t for t in tools}
def run(self, user_input):
# Step 1: ツール選択を指示
prompt = f"""
利用可能なツール: {list(self.tools.keys())}
ユーザー入力: {user_input}
最適なツールを1つ選択し、その理由を述べてください。
形式: TOOL: [tool_name], REASON: [reason]
"""
response = self.llm.generate(prompt)
tool_name = self._extract_tool(response)
# Step 2: ツール実行
if tool_name in self.tools:
result = self.tools[tool_name].execute(user_input)
return result
else:
return "Tool not found"
def _extract_tool(self, response):
# レスポンスからツール名を抽出
import re
match = re.search(r"TOOL:\s*(\w+)", response)
return match.group(1) if match else None長所
- シンプルで実装が容易
- レイテンシが低い(単一LLM呼び出し)
- デバッグが簡単
短所
- 複雑なマルチステップタスクに対応不可
- エラー時の自動リカバリなし
- 文脈の保持が弱い
使用例
✓ 単純な情報検索(「〇〇の天気は?」) ✓ 簡易的な計算・変換 ✗ 顧客対応チケットの解決 ✗ 複雑なレポート生成
パターン2: Planning パターン
複雑なタスクを事前に複数ステップに分解し、順序立てて実行する設計です。
特徴
入力 → タスク分解 → 実行計画作成 → ステップバイステップ実行 → 結果統合
このパターンではLLMが「やるべきことのリスト」を最初に作成し、それに従って順序立てて行動を実行していきます。
実装例
class PlanningAgent:
def __init__(self, llm, tools):
self.llm = llm
self.tools = {t.name: t for t in tools}
def run(self, user_input):
# Step 1: タスク分解
decompose_prompt = f"""
ユーザーのタスク: {user_input}
このタスクを実行するための最小ステップを列挙してください。
形式:
1. [ステップ1の説明]
2. [ステップ2の説明]
...
"""
decomposed = self.llm.generate(decompose_prompt)
steps = self._parse_steps(decomposed)
# Step 2: 各ステップを順序立てて実行
context = {}
for i, step in enumerate(steps):
plan_prompt = f"""
現在のタスク: {step}
これまでの結果: {context}
利用可能なツール: {list(self.tools.keys())}
使用するツールを選択し、パラメータを指定してください。
形式: TOOL: [name], PARAMS: [params]
"""
response = self.llm.generate(plan_prompt)
tool_name, params = self._parse_tool_call(response)
if tool_name in self.tools:
result = self.tools[tool_name].execute(**params)
context[f"step_{i}"] = result
return context
def _parse_steps(self, decomposed):
# タスク分解テキストからステップを抽出
import re
matches = re.findall(r"^\d+\.\s*(.+)$", decomposed, re.MULTILINE)
return matches
def _parse_tool_call(self, response):
# レスポンスからツール名とパラメータを抽出
import re, json
tool_match = re.search(r"TOOL:\s*(\w+)", response)
params_match = re.search(r"PARAMS:\s*({.*?})", response)
tool_name = tool_match.group(1) if tool_match else None
params = json.loads(params_match.group(1)) if params_match else {}
return tool_name, params長所
- マルチステップタスクに対応
- 実行計画が明示的で検証可能
- エラー箇所の特定が容易
短所
- 計画作成に余計な時間とコスト(LLM呼び出し)
- 動的な状況変化に対応しづらい
- 「実行計画に誤りがあった場合」の修正が複雑
使用例
✓ レポート生成(複数データソースの集約) ✓ 顧客情報の複合検索・統合 ✗ リアルタイム会話応答 ✗ 予測不可能な環境での行動
パターン3: Learning パターン
エージェントが過去の成功・失敗から学習し、時間とともに意思決定精度を向上させるパターンです。
特徴
実行 → 結果評価 → フィードバック記録 → 次回の意思決定に反映
各行動の成功度を測定し、メモリに蓄積することで、エージェントの意思決定が改善していきます。
実装例
class LearningAgent:
def __init__(self, llm, tools, memory_store):
self.llm = llm
self.tools = {t.name: t for t in tools}
self.memory = memory_store # {task_pattern -> [success_examples]}
self.feedback_history = []
def run(self, user_input, learn=True):
# Step 1: 過去の成功例から類似パターンを検索
similar_cases = self.memory.find_similar(user_input)
# Step 2: 類似例をコンテキストに組み込んでLLMに提示
few_shot_examples = self._format_examples(similar_cases)
prompt = f"""
過去の成功例:
{few_shot_examples}
新しいタスク: {user_input}
成功例を参考にして、このタスクに対して推奨行動を述べてください。
"""
response = self.llm.generate(prompt)
action = self._parse_action(response)
# Step 3: 行動実行
result = self._execute_action(action)
# Step 4: フィードバック記録(学習)
if learn:
feedback = self._evaluate_result(result)
self.feedback_history.append({
'task': user_input,
'action': action,
'result': result,
'feedback': feedback
})
# メモリに成功例を保存
if feedback['success']:
self.memory.add(user_input, action)
return result
def _format_examples(self, examples):
# 例を構造化フォーマットで整形
formatted = []
for ex in examples[:3]: # 最大3例
formatted.append(f"""
タスク: {ex['task']}
推奨行動: {ex['action']}
結果: {ex['result']}
""")
return "\n".join(formatted)
def _evaluate_result(self, result):
# 結果から成功度を判定
# 実装は業務ドメインに依存
return {
'success': result.get('error') is None,
'quality_score': result.get('quality', 0),
'execution_time': result.get('duration')
}長所
- 時間とともに精度向上
- ドメイン固有の最適解を学習可能
- エラーケースから自動改善
短所
- 初期段階で誤ったパターンを学習するリスク
- メモリ管理が複雑
- 過去の学習が新しい状況に対応しない可能性
使用例
✓ カスタマーサポートチャットボット ✓ 反復的な意思決定タスク ✗ 一度きりのタスク ✗ ドメイン知識が急速に変わる業務
パターン4: Agentic Loop パターン
最も高度な設計で、エージェントが**「思考→行動→観察→次の思考」を反復**し、目標達成まで自律的に進める方式です。
特徴
思考 ↓
観察 ← 行動実行
↑←←←←←
(目標達成まで反復)
このパターンはClaudeの「extended thinking」や「agentic loop」などの最新技術に対応しており、複雑なプロブレムソルビングに向いています。
実装例
class AgenticLoopAgent:
def __init__(self, llm, tools, max_iterations=10):
self.llm = llm
self.tools = {t.name: t for t in tools}
self.max_iterations = max_iterations
self.conversation_history = []
def run(self, user_input):
goal = user_input
iteration = 0
while iteration < self.max_iterations:
# Step 1: 現在の状況を分析・次のアクションを思考
thinking_prompt = f"""
最終目標: {goal}
これまでの観察と試行:
{self._format_history()}
現在のステータス: {self._assess_progress()}
目標達成に向けて、次のアクションを詳細に計画してください。
形式:
[思考プロセス]
NEXT_ACTION: [action]
RATIONALE: [理由]
EXPECTED_OUTCOME: [期待される結果]
"""
response = self.llm.generate(thinking_prompt)
action = self._parse_action(response)
# Step 2: アクション実行
observation = self._execute_action(action)
# Step 3: 観察を記録
self.conversation_history.append({
'iteration': iteration,
'thought': response,
'action': action,
'observation': observation
})
# Step 4: ゴール達成判定
if self._check_goal_achieved(goal, observation):
return {
'success': True,
'final_result': observation,
'iterations': iteration + 1
}
iteration += 1
return {
'success': False,
'reason': 'Max iterations reached',
'iterations': iteration
}
def _format_history(self):
# 過去の試行履歴をテキスト化
lines = []
for entry in self.conversation_history[-5:]: # 最新5件
lines.append(f"""
Iteration {entry['iteration']}:
- Action: {entry['action']}
- Observation: {entry['observation']}
""")
return "\n".join(lines)
def _assess_progress(self):
# 現在の進捗を評価
if not self.conversation_history:
return "タスク開始前"
last_obs = self.conversation_history[-1]['observation']
return f"直前の試行結果: {last_obs}"
def _check_goal_achieved(self, goal, observation):
# ゴール達成判定(実装はドメイン依存)
# 簡易版: キーワード検索
goal_keywords = goal.lower().split()
obs_text = str(observation).lower()
return all(kw in obs_text for kw in goal_keywords[:2])長所
- 最も高い自律性と適応性
- 予測不可能な状況に対応可能
- 複雑なプロブレムソルビングに最適
短所
- 最も多くのLLM呼び出し(コスト&レイテンシ)
- デバッグが困難(思考プロセスが複雑)
- 目標達成判定が難しい場合がある
使用例
✓ 複雑な調査・分析タスク ✓ 予測困難な環境での意思決定 ✓ 創造的問題解決
4パターンの選択基準
| 条件 | 推奨パターン |
|---|---|
| タスク複雑度 低 | Reactive |
| マルチステップ・計画可能 | Planning |
| 反復的・学習効果あり | Learning |
| 複雑・予測不可能 | Agentic Loop |
| 高速レスポンス必須 | Reactive / Planning |
| 信頼性最優先 | Planning / Agentic Loop |
| コスト最優先 | Reactive |
本番環境ベストプラクティス
エラーハンドリング
class RobustAgent:
def run_with_fallback(self, user_input):
try:
# Agentic Loop を試行
return self._agentic_loop(user_input)
except ToolExecutionError as e:
# Planning パターンにフォールバック
logger.warning(f"Agentic loop failed: {e}, falling back to planning")
return self._planning_agent(user_input)
except Exception as e:
# さらに簡易な Reactive にフォールバック
logger.error(f"Planning failed: {e}, falling back to reactive")
return self._reactive_agent(user_input)メモリ管理
長時間運用するエージェントは、会話履歴がメモリを圧迫します。
class MemoryEfficientAgent:
def __init__(self, max_history=50):
self.conversation_history = []
self.max_history = max_history
def add_to_history(self, entry):
self.conversation_history.append(entry)
# 古い履歴を削除
if len(self.conversation_history) > self.max_history:
# 重要な情報(フィードバック・エラー)は保持
important = [e for e in self.conversation_history
if e.get('importance', 0) > 0.5]
# 最新データも保持
recent = self.conversation_history[-10:]
# 統合
self.conversation_history = important + recentコスト最適化
class CostOptimizedAgent:
def decide_llm_provider(self, task_complexity):
if task_complexity < 3:
return "claude-3-haiku" # 最安値モデル
elif task_complexity < 7:
return "claude-3-sonnet" # バランス型
else:
return "claude-3-opus" # 高精度まとめ
AIエージェント設計は「最適なパターン」を選ぶことが成功の鍵です。
Key Takeaways
- Reactive: シンプル・高速だが機能限定
- Planning: マルチステップに対応、計画可視化
- Learning: 反復的タスクで精度向上
- Agentic Loop: 最高の自律性、複雑度最高
各パターンに優劣なく、あなたのタスク要件に最適なパターンを選択する点が肝心です。多くの実務では、複数パターンの「ハイブリッド」設計(例:Planning + Learning)で最大の効果を得ます。
本番環境での運用では、エラーハンドリング・メモリ管理・コスト最適化を常に念頭に置き、継続的に改善することを忘れずに。