🤖

AIエージェント開発で踏んだ5つの地雷(コード付き)

に公開

「AIエージェントは一度作れば回り続ける」と思われがちですが、実運用は逆です。
壊れる前提で設計しないと、ある日まとめて止まります。

この記事では、本プロジェクト の実装と運用ログをもとに、
実際に踏みやすい失敗を5つに分解して解説します。

対象読者:

  • AIエージェントを実運用している個人開発者
  • 複数エージェントの自走パイプラインを回したいエンジニア
  • 「動くけど不安定」な状態から抜けたい人

失敗事例① ハルシネーション連鎖(幻覚の地獄)

症状

  • 1つ目の誤情報を別エージェントが正しい前提として継承し、数ステップで全体判断が崩壊する。
  • 「それっぽいが根拠がない」説明がログに残り、後から原因追跡しづらい。

原因

  • 不確実情報に「わからない」を返すルールが弱い。
  • 出力の根拠確認(URL妥当性、許可ドメイン確認)が後段のみ。

再現

  1. 参照先URLが曖昧なまま要約を生成。
  2. 次段の評価エージェントが要約を事実として採用。
  3. 実行系が誤前提で投稿・通知まで進む。

対策

  • 安全レビューで URL 妥当性と許可ドメインを必須チェック。
  • 「根拠不明なら不確実と明示」のプロンプトを全戦略で統一。
// 根拠URLの検証例
function validateSources(content: string): { isValid: boolean; reason?: string } {
  const urls = extractUrls(content);
  const allowedDomains = ['note.com', 'github.com', 'zenn.dev', 'x.com'];

  for (const url of urls) {
    const domain = new URL(url).hostname;
    if (!allowedDomains.some(d => domain.includes(d))) {
      return { isValid: false, reason: `許可外ドメイン: ${domain}` };
    }
  }
  return { isValid: true };
}

再発防止

  • scripts/auto-review.ts の安全チェック(秘匿情報・URL検証)を通らない投稿は自動却下。
  • 根拠URLのない主張をそのまま外部出力しない運用ルールを固定。

失敗事例② 無限ループ(レビュー修正戦争)

症状

  • 修正→再レビュー→再修正が終わらず、タスクが完了しない。
  • API呼び出しと通知だけが増えて成果物が出ない。

原因

  • 終了条件(最大回数・承認条件)の不在。
  • 人間レビューと自動レビューの責務が曖昧。

再現

  1. レビュー基準を曖昧なまま自動チェックを有効化。
  2. 軽微な文言差分で毎回差し戻し。
  3. キュー滞留が増え、次タスクを押し出す。

対策

  • 2名投票(安全/品質)+ 承認条件を明文化。
  • レビューキューの滞留検知を監視対象に追加。
// レビューループ防止の実装例
const MAX_REVIEW_CYCLES = 3;

async function reviewWithLimit(content: string, cycle = 0): Promise<ReviewResult> {
  if (cycle >= MAX_REVIEW_CYCLES) {
    return { status: 'MANUAL_REQUIRED', reason: 'レビュー上限到達' };
  }

  const result = await autoReview(content);
  if (result.status === 'APPROVED') return result;

  const fixed = await applyFixes(content, result.issues);
  return reviewWithLimit(fixed, cycle + 1);
}

再発防止

  • scripts/self-run-check.ts でレビューキュー放置(6時間超)を DEGRADED として検知。
  • scripts/auto-review.ts を定期実行して再審査を自動化。

失敗事例③ コンテキスト溢れ(長い会話の健忘症)

症状

  • 最初の制約(件数、言語、禁止事項)が後半で抜ける。
  • 出力フォーマットが途中で崩れ、下流の処理が失敗する。

原因

  • 長い会話をそのまま渡し続ける設計。
  • 入力の要約層がなく、重要制約が埋もれる。

再現

  1. 連続で追記指示を与える。
  2. 過去の重要制約が文脈後方へ追いやられる。
  3. 形式違反や件数超過が発生。

対策

  • コンテキストは「要約JSON + 重要制約」を固定フォーマットで渡す。
  • 実行前にスキーマ検証し、違反時は実行しない。
// コンテキスト圧縮と制約保持の例
interface StrategyContext {
  constraints: {
    maxCount: number;
    language: string;
    forbidden: string[];
  };
  history: string; // 要約済み過去文脈
  currentTask: string;
}

function compressContext(fullHistory: Message[]): StrategyContext {
  return {
    constraints: extractConstraints(fullHistory[0]), // 最初の指示を固定
    history: summarize(fullHistory.slice(1, -5)),
    currentTask: fullHistory[fullHistory.length - 1].content,
  };
}

再発防止

  • 戦略 execute() のメタデータ契約を共通化(action / blockingReasons / artifacts)。
  • 形式違反はレビュー段階で自動的に弾く。

**


詳細版は note の有料版で公開しています: https://note.com/agent_workshop

GitHubで編集を提案

Discussion