Claude Fable 5でAIエージェントを構築する方法
# Claude Fable 5でAIエージェントを構築する:実践ガイド
先月、カスタマーサポートの自動化プロジェクトで壁にぶつかりました。既存のセットアップではシンプルな問い合わせには問題なく対応できていましたが、注文履歴の検索、在庫確認、パーソナライズされた回答の作成など、複数ステップを要するタスクになると、すべてが崩れてしまいました。プロセスの途中でコンテキストを見失ったり、存在しないAPIレスポンスをハルシネーション(幻覚)したりしたのです。
そこで、単なるチャットボットではなく、本格的なエージェントの構築にClaude Fable 5を使えないか実験し始めました。数週間のテストを経て、何が機能し、何が機能しないのかを学んだので、ここで共有したいと思います。
## エージェント構築においてFable 5が異なる理由
Fable 5に切り替えて最初に気付いたのは、マルチステップの推論の処理方法です。単一のレスポンスを返してうまくいくことを祈るのではなく、実際にステップを計画し、順番に実行し、そして極めて重要なことですが、すでに実行した内容を記憶している点です。
あるシンプルなリサーチタスクでこれをテストしました。「あるSaaSプロダクトのトップ3の競合を調べ、その価格をリサーチし、比較表を作成する」というものです。以前のセットアップでは、もっともらしいけれど完全に捏造された数字で埋め尽くされた表が返されていました。Fable 5はアプローチが異なりました。タスクを分解し、特定の価格が見つからない場合はそれを正直に認め、利用可能な情報から推定するか、空白のままにするかを尋ねてきたのです。
この挙動だけで、何時間もの事実確認の時間を節約できました。
## 最初のマルチステップワークフローのセットアップ
実践的な例を見ていきましょう。私は顧客の返金リクエストを処理するエージェントを構築しました。これは複数のシステムを確認し、判断を下す必要があるタスクです。
最初に作成した基本構造は以下の通りです:
```python
import anthropic
import json
client = anthropic.Anthropic(api_key="your-api-key")
# Define the tools our agent can use
tools = [
{
"name": "lookup_order",
"description": "Look up order details by order ID or customer email",
"input_schema": {
"type": "object",
"properties": {
"order_id": {"type": "string", "description": "The order ID to look up"},
"email": {"type": "string", "description": "Customer email address"}
},
"required": ["order_id"]
}
},
{
"name": "check_refund_policy",
"description": "Check if an order is eligible for refund based on purchase date and item category",
"input_schema": {
"type": "object",
"properties": {
"order_id": {"type": "string"},
"reason": {"type": "string", "description": "Reason for refund request"}
},
"required": ["order_id", "reason"]
}
},
{
"name": "process_refund",
"description": "Process a refund for an eligible order",
"input_schema": {
"type": "object",
"properties": {
"order_id": {"type": "string"},
"amount": {"type": "number"},
"reason": {"type": "string"}
},
"required": ["order_id", "amount", "reason"]
}
}
]
```
ここでの重要な洞察は、Fable 5は単に1つのツールを選択して終わりにするのではなく、各ステップで学習した内容に基づいてツールをチェーン(連鎖)させるという点です。
## ツール利用のループ
ここで最初の間違いを犯しました。最初は、モデルが勝手に順序を理解してくれると思い込み、ツール呼び出しを1回のパスで処理しようとしました。うまくいきませんでした。エージェントは適格性を確認せずに`process_refund`を呼び出してしまったのです。
正しいアプローチは、エージェントが必要なすべての情報を得るまでループし続けることです:
```python
def run_agent(user_message, max_turns=10):
messages = [{"role": "user", "content": user_message}]
for turn in range(max_turns):
response = client.messages.create(
model="claude-3-5-sonnet-20241022", # Fable 5 model
max_tokens=4096,
tools=tools,
messages=messages
)
# Check if we're done
if response.stop_reason == "end_turn":
print("Agent completed the task")
print(response.content[0].text)
break
# Handle tool calls
if response.stop_reason == "tool_use":
# Add the assistant's response to conversation
messages.append({"role": "assistant", "content": response.content})
# Process each tool call
tool_results = []
for block in response.content:
if block.type == "tool_use":
result = execute_tool(block.name, block.input)
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": json.dumps(result)
})
# Add tool results back to conversation
messages.append({"role": "user", "content": tool_results})
return messages
def execute_tool(name, params):
"""Execute the actual tool logic"""
if name == "lookup_order":
# In real code, this hits your database
return {"order_id": params["order_id"], "total": 89.99, "date": "2024-01-15", "status": "delivered"}
elif name == "check_refund_policy":
# Your business logic here
return {"eligible": True, "reason": "Within 30-day window"}
elif name == "process_refund":
# Call your payment processor
return {"success": True, "refund_id": "REF-12345"}
```
驚いたのは、エージェントがエッジケース(例外的な状況)を推論していく様子でした。6ヶ月前の注文でテストしたところ、返金の対象外になる可能性があることを正しく特定し、黙って失敗するのではなく、どう進めるべきか尋ねてきました。
## 永続的なタスク実行
以前のセットアップで最も不満だったのは、長時間実行されるタスクでコンテキストが失われることでした。Fable 5は予想以上にうまくこれを処理してくれますが、会話履歴を適切に構造化する必要があります。
時間がかかる可能性のあるタスクに対して、私が落ち着いたパターンは以下の通りです:
```python
class PersistentAgent:
def __init__(self, task_id):
self.task_id = task_id
self.conversation_history = []
self.task_state = {
"status": "pending",
"steps_completed": [],
"current_step": None,
"errors": []
}
def save_state(self):
"""Save to your database or file system"""
state = {
"task_id": self.task_id,
"conversation": self.conversation_history,
"state": self.task_state
}
# In production, save to Redis, Postgres, etc.
with open(f"task_{self.task_id}.json", "w") as f:
json.dump(state, f)
def load_state(self):
"""Resume from previous state"""
try:
with open(f"task_{self.task_id}.json", "r") as f:
state = json.load(f)
self.conversation_history = state["conversation"]
self.task_state = state["state"]
return True
except FileNotFoundError:
return False
def execute_with_recovery(self, user_request):
# Try to resume previous state
if self.load_state():
print(f"Resuming task from step: {self.task_state['current_step']}")
# Continue execution
self.conversation_history.append({"role": "user", "content": user_request})
try:
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=4096,
tools=tools,
system=f"""You are working on task {self.task_id}.
Current state: {json.dumps(self.task_state)}
If this is a resumed task, continue from where you left off.""",
messages=self.conversation_history
)
# Process response and update state
self.task_state["current_step"] = "processing"
self.save_state()
return response
except Exception as e:
self.task_state["errors"].append(str(e))
self.save_state()
raise
```
このパターンは、APIのレート制限に引っかかったり、タスクの途中でサーバーが再起動したりした際に、何度も私を救ってくれました。
## 実際に機能するエラーリカバリー
エージェントシステムにおけるエラーハンドリングは、単なるtry-catchブロックのことだと思っていました。Fable 5は、それがモデルに自己修正するための十分なコンテキストを与えることだと教えてくれました。
現在、ツールの失敗を次のように処理しています:
```python
def execute_tool_with_retry(name, params, max_retries=2):
attempts = 0
while attempts <= max_retries:
try:
result = execute_tool(name, params)
return {"success": True, "data": result}
except Exception as e:
attempts += 1
if attempts > max_retries:
# Return structured error so agent can adapt
return {
"success": False,
"error": str(e),
"suggestion": "The API is currently unavailable. You could try an alternative approach or inform the user."
}
# Wait before retry
time.sleep(2 ** attempts)
# Updated tool result handling
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": json.dumps(execute_tool_with_retry(block.name, block.input))
})
```
違いは`suggestion`フィールドにあります。これを追加すると、エージェントは単に失敗するのではなく、キャッシュされたデータにフォールバックしたり、ユーザーに手動入力を求めたりするなど、より賢明な決定を下すようになりました。
## エージェント構築におけるFable 5とClaude Codeの比較
私は両方を広く使ってきましたが、それぞれ目的が異なります。私の正直な見解は以下の通りです:
**Claude Code**は、モデルにコードを直接記述して実行させたい場合に非常に優れています。モデルが即座にPythonスクリプトを生成する必要があるデータ分析エージェントなどに使用しています。トレードオフとして、サンドボックス化された実行環境が必要であり、生成されたコードのデバッグが苦痛になる場合がある点です。
**Fable 5**は、明確に定義されたツールやAPIがある場合に力を発揮します。関数呼び出し(ファンクションコーリング)がより予測可能で、どのツールをどの順序で使用するかについてモデルがより適切に推論できるようです。存在しない関数を捏造するインスタンスも大幅に減りました。
私の返金処理エージェントについては、Fable 5が明らかな勝者でした。ツールはすでに定義されており(データベース検索、決済処理)、コード実行環境のオーバーヘッドなしに確実な実行が必要だったからです。
一方で、任意のCSVファイルを変換する必要があったデータパイプラインエージェントについては、変換内容を事前に予測できなかったため、Claude Codeの方が適していました。
## 私の失敗から学ぶ実践的なヒント
**ツールは少なく始める。** 最初のバージョンでは15個のツールがあり、エージェントは間違ったツールばかり選択していました。5個の明確に定義されたツールに統合したところ、精度が劇的に向上しました。
**失敗モードについて明示する。** ツールが失敗したときの対処法をモデルに伝えてください。このガイダンスがないと、同じ失敗したアプローチをループで試し続けることがありました。
**すべてをログに記録する。** エージェントが特定の決定を下した理由をデバッグする必要が出てきます。私はすべてのツール呼び出し、推論、結果をログに記録しています。これにより数え切れないほどの時間を節約できました。
**「自律的」な期待値を現実的に設定する。** Fable 5は非常に優秀ですが、魔法ではありません。複雑なタスクには依然として人間のチェックポイントが必要です。データを変更したりコストがかかるアクションについては、承認ステップを追加しました。
## 注意すべき制限事項
私が直面した最大の制限は、非常に長い会話におけるコンテキストウィンドウの管理です。約20〜30回のツール呼び出しの後、履歴の要約や切り捨てが必要になり、重要な詳細が失われる可能性があります。
ツールの選択も完璧ではありません。曖昧な状況では、モデルが妥当だが間違ったツールを選ぶことがあります。ツールの説明を極めて具体的にし、システムプロンプトに例を含めるようにしました。
また、大量のリクエストを処理するエージェントの場合、レート制限がボトルネックになる可能性があります。各ターンがAPI呼び出しとなるため、複雑なタスクは割り当て容量をすぐに消費してしまいます。
## さらに学ぶために
ここでは表面をさらっただけです。より深いドキュメントや例については、https://aiclawhot.com/zh/agents/claude-fable-5/ をチェックしてください。この学習プロセスを通じて、私の頼れる参考資料でした。
信頼性の高いエージェントを構築するのは依然として難しいですが、Fable 5は以前の選択肢よりも大幅に取り組みやすくなっています。シンプルなワークフローから始め、徐々に複雑さを追加し、常に失敗モードを想定して計画してください。テクノロジーは有用であるレベルに達していますが、本番環境で確実に機能させるには、依然として思慮深いエンジニアリングが必要です。