ストーリー
エラーの分類
エージェントで発生するエラーの種類
| エラー種別 | 例 | 一般的な対策 |
|---|---|---|
| 一時的エラー | APIタイムアウト、レート制限 | リトライ |
| 永続的エラー | 認証エラー、権限不足 | フォールバック or エスカレーション |
| データエラー | レコード未発見、不正なデータ | 代替検索 or ユーザーに確認 |
| バリデーションエラー | LLMが不正なパラメータを生成 | エラー内容をLLMに返して再生成 |
| ビジネスロジックエラー | 在庫切れ、与信NG | ビジネスルールに従った対応 |
リトライ戦略
Exponential Backoff
一時的なエラーに対しては、間隔を広げながらリトライします。
async function executeWithRetry(
toolName: string,
args: Record<string, unknown>,
maxRetries: number = 3
): Promise<ToolResponse> {
let lastError: Error | null = null;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const result = await executeTool(toolName, args);
if (result.success) {
return result;
}
// リトライ不可能なエラーは即座に返す
if (!isRetryable(result.error?.code)) {
return result;
}
lastError = new Error(result.error?.message);
} catch (error) {
lastError = error as Error;
}
// Exponential Backoff: 1秒、2秒、4秒...
const delay = Math.pow(2, attempt) * 1000;
await sleep(delay);
}
return {
success: false,
error: {
code: "MAX_RETRIES_EXCEEDED",
message: `${maxRetries}回のリトライ後も失敗: ${lastError?.message}`,
suggestion: "しばらく時間をおいて再試行するか、別の方法を検討してください"
}
};
}
function isRetryable(errorCode?: string): boolean {
const retryableCodes = [
"TIMEOUT",
"RATE_LIMITED",
"SERVICE_UNAVAILABLE",
"CONNECTION_ERROR"
];
return retryableCodes.includes(errorCode ?? "");
}
リトライ判断のフローチャート
エラー発生
↓
一時的エラーか?
├── Yes → リトライ回数の上限内か?
│ ├── Yes → Exponential Backoff → リトライ
│ └── No → フォールバック戦略へ
└── No → フォールバック戦略へ
フォールバック戦略
リトライで解決しない場合の代替手段を設計します。
フォールバックパターン
| パターン | 説明 | 例 |
|---|---|---|
| 代替ツール | 別のツールで同等の結果を得る | メインDB不可 → キャッシュDB参照 |
| 縮退運転 | 一部の情報なしで処理を続行 | 配送追跡不可 → 注文情報のみで回答 |
| ユーザー確認 | 不足情報をユーザーに質問 | 注文番号不明 → ユーザーに確認 |
| エスカレーション | 人間のオペレーターに引き継ぐ | 重大エラー → サポートチームに通知 |
| キャッシュ利用 | 以前の結果をキャッシュから取得 | リアルタイムデータ不可 → 最終取得データ |
フォールバックの実装
async function executeWithFallback(
primaryTool: ToolCall,
fallbackTools: ToolCall[]
): Promise<ToolResponse> {
// プライマリツールを試行
const primaryResult = await executeWithRetry(
primaryTool.name,
primaryTool.args
);
if (primaryResult.success) {
return primaryResult;
}
// フォールバックツールを順番に試行
for (const fallback of fallbackTools) {
const result = await executeWithRetry(
fallback.name,
fallback.args,
1 // フォールバックは1回のみ試行
);
if (result.success) {
return {
...result,
metadata: {
usedFallback: true,
originalTool: primaryTool.name,
fallbackTool: fallback.name
}
};
}
}
// 全フォールバック失敗
return {
success: false,
error: {
code: "ALL_FALLBACKS_FAILED",
message: "すべての代替手段が失敗しました",
suggestion: "人間のオペレーターにエスカレーションしてください"
}
};
}
エラー情報のLLMへの伝達
LLMが適切に判断するためのエラーレスポンス設計
良いエラーレスポンス(LLMが次のアクションを判断できる):
{
"success": false,
"error": {
"code": "ORDER_NOT_FOUND",
"message": "注文番号 ORD-99999 は見つかりませんでした",
"suggestion": "注文番号を再確認するか、顧客IDや注文日で検索してください",
"available_alternatives": ["search_by_customer_id", "search_by_date"]
}
}
悪いエラーレスポンス(LLMが次のアクションを判断できない):
{
"error": "Not found"
}
タイムアウトの設計
const TOOL_TIMEOUTS: Record<string, number> = {
search_orders: 5000, // 5秒
track_shipment: 10000, // 10秒(外部API依存)
generate_report: 30000, // 30秒(重い処理)
send_email: 15000 // 15秒
};
async function executeWithTimeout(
toolName: string,
args: Record<string, unknown>
): Promise<ToolResponse> {
const timeout = TOOL_TIMEOUTS[toolName] ?? 10000;
try {
const result = await Promise.race([
executeTool(toolName, args),
new Promise<never>((_, reject) =>
setTimeout(() => reject(new Error("TIMEOUT")), timeout)
)
]);
return result;
} catch (error) {
return {
success: false,
error: {
code: "TIMEOUT",
message: `${toolName} が ${timeout / 1000}秒以内に応答しませんでした`,
suggestion: "再試行可能です。サービスが一時的に遅延している可能性があります",
retryable: true
}
};
}
}
まとめ
| ポイント | 内容 |
|---|---|
| エラー分類 | 一時的/永続的/データ/バリデーション/ビジネスロジック |
| リトライ戦略 | Exponential Backoffで一時的エラーに対応 |
| フォールバック | 代替ツール、縮退運転、エスカレーション |
| エラーレスポンス | LLMが次の判断をできる十分な情報を含める |
チェックリスト
- エージェントで発生するエラーの種類を分類できる
- Exponential Backoffによるリトライ戦略を理解した
- フォールバックパターンの使い分けを理解した
- LLMに伝えるエラーレスポンスの設計方法を把握した
次のステップへ
次は「演習:業務ツールを実装しよう」です。NetShop社の業務に必要なツールを設計・実装する演習に取り組みます。
推定読了時間: 30分