T-CREATOR

GPT-5 失敗しないエージェント設計:プランニング/自己検証/停止規則のアーキテクチャ

GPT-5 失敗しないエージェント設計:プランニング/自己検証/停止規則のアーキテクチャ

GPT-5 をはじめとする大規模言語モデルを活用した AI エージェントは、複雑なタスクを自律的に実行できる可能性を秘めています。しかし、適切な設計なしでは無限ループに陥ったり、誤った判断を繰り返したりするリスクがあります。

本記事では、AI エージェントが確実にタスクを完了するための 3 つの重要な要素——プランニング自己検証停止規則——について、実装可能なアーキテクチャとともに解説していきます。エージェントの暴走を防ぎ、信頼性の高いシステムを構築するための具体的な手法を学べるでしょう。

背景

AI エージェントが求められる理由

近年、GPT-4 や Claude、そして今後登場する GPT-5 のような大規模言語モデルは、単なる対話システムを超えて「自律的に行動するエージェント」としての活用が進んでいます。

従来のチャットボットは単発の質問に答えるだけでしたが、AI エージェントは複数のステップを経て目標を達成する能力を持っています。たとえば、リサーチを行い、コードを書き、テストを実行し、結果を分析するといった一連のワークフローを自動化できます。

エージェントアーキテクチャの進化

AI エージェントの設計手法は急速に進化しています。初期のシンプルなプロンプトエンジニアリングから、現在では構造化されたアーキテクチャパターンが確立されつつあります。

以下の図は、AI エージェントの基本的な動作フローを示しています。

mermaidflowchart TD
    user["ユーザー"] -->|タスク指示| planning["プランニング<br/>フェーズ"]
    planning -->|実行計画| execution["実行<br/>フェーズ"]
    execution -->|結果| verification["自己検証<br/>フェーズ"]
    verification -->|OK| complete["タスク完了"]
    verification -->|NG| execution
    execution -.->|無限ループ防止| stoprule["停止規則<br/>チェック"]
    stoprule -->|上限到達| forcestop["強制停止"]

この図から分かるように、エージェントは計画 → 実行 → 検証のサイクルを回しながらタスクを進めます。しかし、適切な停止条件がなければ、このサイクルが永遠に続いてしまう可能性があるのです。

課題

エージェント設計における 3 つの主な問題

AI エージェントを実装する際、多くの開発者が直面する課題があります。これらの課題を理解することが、堅牢なシステム設計の第一歩となります。

課題 1:計画なき実行による非効率性

エージェントが事前の計画なしに行動すると、無駄な試行錯誤を繰り返し、リソースを浪費します。たとえば、「Web サイトから情報を収集して分析する」というタスクに対して、どのページから始めるべきか、どのような順序で処理するべきかを考えずに実行すると、関連性の低いページを延々と巡回し続けることになります。

API 呼び出しの回数制限やトークン使用量の上限がある環境では、この非効率性が直接的なコスト増加につながるでしょう。

課題 2:自己検証の欠如による誤り伝播

エージェントが自分の出力を検証しない場合、初期の誤りがそのまま後続のステップに影響を及ぼします。

たとえば、データ抽出の段階で誤った形式のデータを取得したまま次の処理に進むと、最終的な結果が完全に無意味なものになる可能性があります。人間であれば「これはおかしい」と気づける異常値も、検証機能のないエージェントは何の疑問も持たずに処理し続けてしまいます。

課題 3:停止条件の不在による無限ループ

最も深刻な問題は、エージェントがいつ停止すべきかを判断できないケースです。

以下のような状況で無限ループが発生します。

#状況発生原因影響
1目標達成の判定基準が曖昧「完璧な文章を書く」など定量化できない目標永遠に改善を試みる
2外部 API エラーのリトライエラーが継続的に発生する状態での無制限リトライリソース枯渇
3自己検証の基準が高すぎる到達不可能な品質基準の設定改善ループが終わらない
4ステップ数の上限がない実行回数に制限を設けていないコスト増大・タイムアウト

これらの問題を解決するには、適切な停止規則を設計に組み込む必要があります。次のセクションでは、これらの課題に対する具体的な解決策を見ていきましょう。

解決策

3 層アーキテクチャによるエージェント設計

課題を解決するために、プランニング・自己検証・停止規則の 3 つを統合したアーキテクチャを構築します。

以下の図は、これら 3 つの要素がどのように連携するかを示しています。

mermaidflowchart LR
    task["タスク入力"] --> planner["プランナー"]
    planner -->|計画| executor["エグゼキュータ"]
    executor -->|実行結果| verifier["バリファイア"]
    verifier -->|検証OK| nextStep["次ステップへ"]
    verifier -->|検証NG| executor
    executor -.->|ステップカウント| stoprule["停止規則<br/>エンジン"]
    verifier -.->|品質チェック| stoprule
    stoprule -->|継続判定| executor
    stoprule -->|停止判定| result["結果出力"]

このアーキテクチャでは、各コンポーネントが明確な責任を持ち、相互にチェック機能を提供します。

解決策 1:プランニングフェーズの実装

プランニングフェーズでは、タスクを実行可能な小さなステップに分解し、実行順序を決定します。

プランナーの基本構造

プランナーは、ユーザーの入力を受け取り、実行計画を生成するコンポーネントです。以下のように実装できます。

typescript// プランナーの型定義
interface Task {
  id: string;
  description: string;
  dependencies: string[]; // 依存する他のタスクのID
  estimatedTokens: number; // 推定トークン使用量
  priority: number; // 優先度(1-10)
}

interface ExecutionPlan {
  tasks: Task[];
  totalEstimatedTokens: number;
  estimatedDuration: number; // 推定実行時間(秒)
}

プランニングプロンプトの設計

プランナーに適切な計画を生成させるためのプロンプト例です。

typescript// プランニング用のプロンプト生成関数
function createPlanningPrompt(userTask: string): string {
  return `
あなたは優秀なタスクプランナーです。以下のタスクを達成するための実行計画を作成してください。

【タスク】
${userTask}

【要件】
1. タスクを5-10個の具体的なステップに分解する
2. 各ステップは独立して検証可能にする
3. ステップ間の依存関係を明確にする
4. 各ステップの推定トークン数を記載する
5. 実行順序を最適化する

【出力形式】
JSON形式で以下の構造を返してください:
{
  "tasks": [
    {
      "id": "task_1",
      "description": "具体的なタスク内容",
      "dependencies": [],
      "estimatedTokens": 500,
      "priority": 10
    }
  ]
}
`;
}

このプロンプトにより、AI は構造化された実行計画を生成します。ステップを明確に定義することで、後の検証と停止判定が容易になるでしょう。

プランナーの実装例

プランニング機能を実装したクラスの例です。

typescript// プランナークラスの実装
class TaskPlanner {
  private llm: LanguageModel; // LLMインスタンス
  private maxTotalTokens: number; // トークン上限

  constructor(
    llm: LanguageModel,
    maxTotalTokens: number = 10000
  ) {
    this.llm = llm;
    this.maxTotalTokens = maxTotalTokens;
  }

  // 実行計画の生成
  async createPlan(
    userTask: string
  ): Promise<ExecutionPlan> {
    const prompt = createPlanningPrompt(userTask);
    const response = await this.llm.complete(prompt);

    // JSONパースとバリデーション
    const plan = JSON.parse(response);

    return this.validatePlan(plan);
  }

  // 計画の妥当性チェック
  private validatePlan(plan: ExecutionPlan): ExecutionPlan {
    // トークン数の合計チェック
    if (plan.totalEstimatedTokens > this.maxTotalTokens) {
      throw new Error(
        `計画のトークン数が上限を超えています: ${plan.totalEstimatedTokens}`
      );
    }

    // 循環依存のチェック
    this.checkCircularDependencies(plan.tasks);

    return plan;
  }
}

プランナーは計画を生成するだけでなく、その計画が実行可能かどうかも検証します。これにより、実行前に問題を検出できるのです。

解決策 2:自己検証メカニズムの構築

自己検証フェーズでは、各ステップの実行結果が期待通りかをチェックします。

検証器の型定義

検証器が扱うデータ構造を定義します。

typescript// 検証結果の型定義
interface VerificationResult {
  isValid: boolean; // 検証が成功したか
  confidence: number; // 信頼度スコア(0-1)
  issues: Issue[]; // 発見された問題のリスト
  suggestions: string[]; // 改善提案
}

interface Issue {
  severity: 'critical' | 'warning' | 'info';
  message: string;
  location?: string; // 問題の場所
}

検証プロンプトの設計

実行結果を検証するためのプロンプト例です。

typescript// 検証用プロンプト生成関数
function createVerificationPrompt(
  taskDescription: string,
  result: string,
  criteria: string[]
): string {
  return `
あなたは厳格な品質検証担当者です。以下のタスクの実行結果を検証してください。

【タスク内容】
${taskDescription}

【実行結果】
${result}

【検証基準】
${criteria.map((c, i) => `${i + 1}. ${c}`).join('\n')}

【検証項目】
- 結果がタスクの要件を満たしているか
- データ形式が正しいか
- 論理的な矛盾がないか
- エッジケースが考慮されているか

【出力形式】
以下のJSON形式で検証結果を返してください:
{
  "isValid": true/false,
  "confidence": 0.95,
  "issues": [
    {
      "severity": "warning",
      "message": "具体的な問題点",
      "location": "該当箇所"
    }
  ],
  "suggestions": ["改善提案1", "改善提案2"]
}
`;
}

このプロンプトにより、AI は客観的な視点で自分の出力を評価できます。

検証器の実装例

検証機能を実装したクラスです。

typescript// 検証器クラスの実装
class ResultVerifier {
  private llm: LanguageModel;
  private minConfidence: number; // 最低信頼度閾値

  constructor(
    llm: LanguageModel,
    minConfidence: number = 0.8
  ) {
    this.llm = llm;
    this.minConfidence = minConfidence;
  }

  // 結果の検証
  async verify(
    task: Task,
    result: string,
    criteria: string[]
  ): Promise<VerificationResult> {
    const prompt = createVerificationPrompt(
      task.description,
      result,
      criteria
    );

    const response = await this.llm.complete(prompt);
    const verification = JSON.parse(response);

    return this.enrichVerification(verification);
  }

  // 検証結果の補強
  private enrichVerification(
    verification: VerificationResult
  ): VerificationResult {
    // 重大な問題がある場合は無効とする
    const hasCriticalIssue = verification.issues.some(
      (issue) => issue.severity === 'critical'
    );

    if (hasCriticalIssue) {
      verification.isValid = false;
    }

    // 信頼度が閾値未満の場合も無効とする
    if (verification.confidence < this.minConfidence) {
      verification.isValid = false;
      verification.issues.push({
        severity: 'warning',
        message: `信頼度が閾値${this.minConfidence}未満です: ${verification.confidence}`,
      });
    }

    return verification;
  }
}

検証器は、AI による検証に加えて、プログラマティックなルールも適用します。これにより、より確実な品質保証が実現できるでしょう。

多段階検証の実装

より堅牢な検証のために、複数の検証レイヤーを設けることができます。

typescript// 多段階検証器の実装
class MultiLayerVerifier {
  private syntaxVerifier: SyntaxVerifier; // 構文検証
  private semanticVerifier: SemanticVerifier; // 意味検証
  private llmVerifier: ResultVerifier; // LLM検証

  // 3層の検証を実行
  async verifyWithLayers(
    task: Task,
    result: string
  ): Promise<VerificationResult> {
    // レイヤー1: 構文検証(形式チェック)
    const syntaxResult = await this.syntaxVerifier.verify(
      result
    );
    if (!syntaxResult.isValid) {
      return syntaxResult; // 構文エラーがあれば即座に返す
    }

    // レイヤー2: 意味検証(論理チェック)
    const semanticResult =
      await this.semanticVerifier.verify(task, result);
    if (!semanticResult.isValid) {
      return semanticResult;
    }

    // レイヤー3: LLMによる総合検証
    const llmResult = await this.llmVerifier.verify(
      task,
      result,
      this.generateCriteria(task)
    );

    return llmResult;
  }
}

3 層の検証により、単純なエラーは早期に検出し、複雑な判断は LLM に任せるという効率的な設計になっています。

解決策 3:停止規則エンジンの実装

停止規則エンジンは、エージェントがいつ停止すべきかを判断する重要なコンポーネントです。

停止条件の型定義

様々な停止条件を表現するための型を定義します。

typescript// 停止条件の型定義
interface StopRule {
  name: string;
  check: (context: ExecutionContext) => boolean;
  message: string; // 停止理由のメッセージ
}

interface ExecutionContext {
  stepCount: number; // 実行ステップ数
  totalTokensUsed: number; // 使用トークン数
  elapsedTime: number; // 経過時間(ミリ秒)
  successCount: number; // 成功したステップ数
  failureCount: number; // 失敗したステップ数
  lastVerification?: VerificationResult; // 最後の検証結果
}

標準的な停止規則の実装

一般的に使用される停止規則を実装します。

typescript// 停止規則の実装例
class StopRuleEngine {
  private rules: StopRule[] = [];

  // 標準的な停止規則を追加
  addStandardRules(config: {
    maxSteps?: number;
    maxTokens?: number;
    maxTime?: number;
    maxConsecutiveFailures?: number;
  }): void {
    // 規則1: 最大ステップ数
    if (config.maxSteps) {
      this.rules.push({
        name: 'max_steps',
        check: (ctx) => ctx.stepCount >= config.maxSteps!,
        message: `最大ステップ数 ${config.maxSteps} に到達しました`,
      });
    }

    // 規則2: 最大トークン数
    if (config.maxTokens) {
      this.rules.push({
        name: 'max_tokens',
        check: (ctx) =>
          ctx.totalTokensUsed >= config.maxTokens!,
        message: `最大トークン数 ${config.maxTokens} に到達しました`,
      });
    }

    // 規則3: 最大実行時間
    if (config.maxTime) {
      this.rules.push({
        name: 'max_time',
        check: (ctx) => ctx.elapsedTime >= config.maxTime!,
        message: `最大実行時間 ${config.maxTime}ms を超過しました`,
      });
    }

    // 規則4: 連続失敗回数
    if (config.maxConsecutiveFailures) {
      this.rules.push({
        name: 'consecutive_failures',
        check: (ctx) => {
          // 直近N回のステップがすべて失敗している場合
          return (
            ctx.failureCount >=
            config.maxConsecutiveFailures!
          );
        },
        message: `連続 ${config.maxConsecutiveFailures} 回失敗しました`,
      });
    }
  }
}

これらの規則により、様々な異常状態を検出できます。

動的停止条件の実装

実行状況に応じて停止条件を動的に調整する機能を実装します。

typescript// 動的停止条件の実装
class AdaptiveStopRuleEngine extends StopRuleEngine {
  private baseMaxSteps: number;

  constructor(baseMaxSteps: number) {
    super();
    this.baseMaxSteps = baseMaxSteps;
  }

  // 進捗率に応じた停止判定
  shouldStop(
    context: ExecutionContext,
    plan: ExecutionPlan
  ): {
    shouldStop: boolean;
    reason?: string;
  } {
    // 標準的な停止規則をチェック
    for (const rule of this.rules) {
      if (rule.check(context)) {
        return { shouldStop: true, reason: rule.message };
      }
    }

    // 進捗率による動的判定
    const progress =
      context.successCount / plan.tasks.length;
    const efficiency =
      context.successCount / context.stepCount;

    // 効率が低い場合は早期停止
    if (context.stepCount > 10 && efficiency < 0.3) {
      return {
        shouldStop: true,
        reason: `実行効率が低すぎます(成功率: ${(
          efficiency * 100
        ).toFixed(1)}%)`,
      };
    }

    // 進捗が停滞している場合
    if (
      context.stepCount > this.baseMaxSteps * 0.5 &&
      progress < 0.2
    ) {
      return {
        shouldStop: true,
        reason: '進捗が停滞しています(進捗率: 20%未満)',
      };
    }

    return { shouldStop: false };
  }
}

動的な停止条件により、無駄な処理を早期に打ち切ることができます。効率が悪いと判断された時点で停止することで、リソースを節約できるでしょう。

3 つの要素を統合したエージェント実装

プランニング、検証、停止規則を統合した完全なエージェントを実装します。

メインエージェントクラスの定義

すべてのコンポーネントを統合するメインクラスです。

typescript// 統合エージェントクラス
class RobustAgent {
  private planner: TaskPlanner;
  private verifier: MultiLayerVerifier;
  private stopEngine: AdaptiveStopRuleEngine;
  private executor: TaskExecutor;

  constructor(config: AgentConfig) {
    this.planner = new TaskPlanner(
      config.llm,
      config.maxTokens
    );
    this.verifier = new MultiLayerVerifier();
    this.stopEngine = new AdaptiveStopRuleEngine(
      config.maxSteps
    );
    this.executor = new TaskExecutor(config.llm);

    // 停止規則の設定
    this.stopEngine.addStandardRules({
      maxSteps: config.maxSteps,
      maxTokens: config.maxTokens,
      maxTime: config.maxTime,
      maxConsecutiveFailures: config.maxConsecutiveFailures,
    });
  }
}

エージェントの実行ループ

プランニング → 実行 → 検証 → 停止判定のループを実装します。

typescript// エージェントの実行メソッド
async execute(userTask: string): Promise<AgentResult> {
  // フェーズ1: プランニング
  console.log('[プランニング開始]');
  const plan = await this.planner.createPlan(userTask);
  console.log(`計画完了: ${plan.tasks.length}個のタスク`);

  // 実行コンテキストの初期化
  const context: ExecutionContext = {
    stepCount: 0,
    totalTokensUsed: 0,
    elapsedTime: 0,
    successCount: 0,
    failureCount: 0
  };

  const startTime = Date.now();
  const results: TaskResult[] = [];

  // フェーズ2: 実行ループ
  for (const task of plan.tasks) {
    console.log(`[ステップ ${context.stepCount + 1}] ${task.description}`);

    // 停止規則チェック
    const stopCheck = this.stopEngine.shouldStop(context, plan);
    if (stopCheck.shouldStop) {
      console.log(`[停止] ${stopCheck.reason}`);
      break;
    }

    // タスク実行
    context.stepCount++;
    const result = await this.executor.execute(task);

    // トークン使用量を記録
    context.totalTokensUsed += result.tokensUsed;
    context.elapsedTime = Date.now() - startTime;

    // フェーズ3: 検証
    const verification = await this.verifier.verifyWithLayers(
      task,
      result.output
    );
    context.lastVerification = verification;

    if (verification.isValid) {
      context.successCount++;
      results.push({ task, result, verification });
      console.log('[検証成功]');
    } else {
      context.failureCount++;
      console.log('[検証失敗]', verification.issues);

      // リトライロジック
      const shouldRetry = this.shouldRetry(context, verification);
      if (shouldRetry) {
        console.log('[リトライ]');
        // タスクを再実行(実装は省略)
      }
    }
  }

  return this.buildResult(results, context);
}

このメインループが、エージェントの核となる処理です。各フェーズが明確に分離され、それぞれが独立して機能する設計になっています。

リトライと回復戦略

失敗時の対処方法を実装します。

typescript// リトライ判定メソッド
private shouldRetry(
  context: ExecutionContext,
  verification: VerificationResult
): boolean {
  // 致命的エラーの場合はリトライしない
  const hasCritical = verification.issues.some(
    issue => issue.severity === 'critical'
  );
  if (hasCritical) {
    return false;
  }

  // リトライ回数の上限チェック
  const maxRetries = 2;
  if (context.failureCount >= maxRetries) {
    return false;
  }

  // 信頼度が一定以上ならリトライする価値あり
  return verification.confidence > 0.5;
}

// リトライ時の戦略調整
private adjustStrategyForRetry(
  task: Task,
  verification: VerificationResult
): Task {
  // 検証結果の提案を反映
  const enhancedDescription = `
${task.description}

【前回の問題点】
${verification.issues.map(i => `- ${i.message}`).join('\n')}

【改善提案】
${verification.suggestions.map(s => `- ${s}`).join('\n')}
`;

  return {
    ...task,
    description: enhancedDescription
  };
}

リトライ戦略により、一時的なエラーから回復する能力をエージェントに与えることができます。

具体例

実例 1:Web スクレイピングエージェントの構築

Web サイトから情報を収集し、構造化データとして出力するエージェントを実装します。このエージェントは、プランニング・検証・停止規則のすべてを活用します。

タスクの定義

まず、ユーザーが指定するタスクを定義します。

typescript// スクレイピングタスクの例
const scrapingTask = `
以下のWebサイトから技術記事の情報を収集してください:
- URL: https://example.com/tech-blog
- 収集項目: タイトル、著者、公開日、タグ、本文の要約
- 対象記事数: 最新10件
- 出力形式: JSON配列
`;

プランニングフェーズの実行結果

エージェントがこのタスクから生成する実行計画の例です。

typescript// プランナーが生成する計画
const generatedPlan: ExecutionPlan = {
  tasks: [
    {
      id: 'task_1',
      description:
        'Webページのトップページにアクセスし、HTMLを取得する',
      dependencies: [],
      estimatedTokens: 200,
      priority: 10,
    },
    {
      id: 'task_2',
      description: '記事一覧ページのURLを抽出する',
      dependencies: ['task_1'],
      estimatedTokens: 300,
      priority: 9,
    },
    {
      id: 'task_3',
      description: '最新10件の記事URLを取得する',
      dependencies: ['task_2'],
      estimatedTokens: 400,
      priority: 8,
    },
    {
      id: 'task_4',
      description:
        '各記事ページから情報を抽出する(バッチ処理)',
      dependencies: ['task_3'],
      estimatedTokens: 2000,
      priority: 7,
    },
    {
      id: 'task_5',
      description: '抽出データをJSON形式に整形する',
      dependencies: ['task_4'],
      estimatedTokens: 500,
      priority: 6,
    },
    {
      id: 'task_6',
      description: 'データの妥当性を検証する',
      dependencies: ['task_5'],
      estimatedTokens: 300,
      priority: 5,
    },
  ],
  totalEstimatedTokens: 3700,
  estimatedDuration: 120, //
};

計画が明確に構造化されており、依存関係も定義されています。これにより、効率的な実行が可能になるでしょう。

検証基準の設定

各ステップの検証基準を定義します。

typescript// ステップごとの検証基準
const verificationCriteria = {
  task_1: [
    'HTTPステータスコードが200である',
    'HTMLが取得できている',
    'HTML構造が正しい(<!DOCTYPE html>で始まる)',
  ],
  task_2: [
    '記事一覧ページのURLが1つ以上抽出されている',
    'URL形式が正しい(http/httpsで始まる)',
    '同一ドメインのURLである',
  ],
  task_3: [
    '正確に10件の記事URLが取得されている',
    '重複URLがない',
    'すべてのURLがアクセス可能である',
  ],
  task_4: [
    '各記事から必須項目(タイトル、著者、公開日)が抽出されている',
    '日付形式が統一されている',
    '本文要約が100-300文字の範囲である',
  ],
  task_5: [
    'JSON形式が正しい(パース可能)',
    '配列の要素数が10である',
    'すべての要素が同じスキーマを持つ',
  ],
  task_6: [
    'すべてのフィールドに値が入っている(nullやundefinedがない)',
    'タグが配列形式である',
    '公開日が現在日時より過去である',
  ],
};

具体的な検証基準により、各ステップの品質を保証できます。

停止規則の設定

このエージェント専用の停止規則を設定します。

typescript// スクレイピングエージェントの設定
const agentConfig: AgentConfig = {
  llm: gpt5Model,
  maxSteps: 20, // 最大20ステップ
  maxTokens: 5000, // 計画より余裕を持たせる
  maxTime: 180000, // 3分
  maxConsecutiveFailures: 3, // 3回連続失敗で停止
};

const agent = new RobustAgent(agentConfig);

実行ログの例

実際に実行した際のログを見てみましょう。

typescript// 実行ログ(成功例)
/*
[プランニング開始]
計画完了: 6個のタスク

[ステップ 1] Webページのトップページにアクセスし、HTMLを取得する
実行中...
[検証成功] 信頼度: 0.98

[ステップ 2] 記事一覧ページのURLを抽出する
実行中...
[検証成功] 信頼度: 0.95

[ステップ 3] 最新10件の記事URLを取得する
実行中...
[検証成功] 信頼度: 1.00

[ステップ 4] 各記事ページから情報を抽出する(バッチ処理)
実行中...
[検証失敗] 問題: 1件の記事でタイトルが抽出できませんでした
[リトライ]
実行中...
[検証成功] 信頼度: 0.92

[ステップ 5] 抽出データをJSON形式に整形する
実行中...
[検証成功] 信頼度: 1.00

[ステップ 6] データの妥当性を検証する
実行中...
[検証成功] 信頼度: 0.96

[完了]
総ステップ数: 7(リトライ1回含む)
使用トークン数: 4,120
実行時間: 98秒
成功率: 100%
*/

検証により問題が検出され、リトライによって最終的に成功しています。停止規則の範囲内で完了できました。

実例 2:コード生成・テスト・修正エージェント

次に、より複雑な例として、コードを生成し、テストを実行し、エラーがあれば修正するエージェントを見ていきます。

タスクの定義

typescript// コード生成タスク
const codingTask = `
TypeScriptでユーザー認証機能を実装してください:

【要件】
- JWT認証を使用
- ログイン、ログアウト、トークン更新のAPIを実装
- Expressを使用
- 単体テストも作成
- ESLintのルールに準拠
- すべてのテストが通ること
`;

動的プランニングの例

コード生成タスクでは、テスト結果に応じて計画を調整する必要があります。

typescript// 初期計画
const initialPlan: ExecutionPlan = {
  tasks: [
    {
      id: 'task_1',
      description: 'プロジェクト構造を設計する',
      dependencies: [],
      estimatedTokens: 300,
      priority: 10,
    },
    {
      id: 'task_2',
      description: '認証ミドルウェアを実装する',
      dependencies: ['task_1'],
      estimatedTokens: 800,
      priority: 9,
    },
    {
      id: 'task_3',
      description: 'ログインAPIエンドポイントを実装する',
      dependencies: ['task_2'],
      estimatedTokens: 600,
      priority: 8,
    },
    {
      id: 'task_4',
      description: 'ログアウトAPIエンドポイントを実装する',
      dependencies: ['task_2'],
      estimatedTokens: 400,
      priority: 7,
    },
    {
      id: 'task_5',
      description:
        'トークン更新APIエンドポイントを実装する',
      dependencies: ['task_2'],
      estimatedTokens: 500,
      priority: 6,
    },
    {
      id: 'task_6',
      description: '単体テストを作成する',
      dependencies: ['task_3', 'task_4', 'task_5'],
      estimatedTokens: 1000,
      priority: 5,
    },
    {
      id: 'task_7',
      description: 'テストを実行する',
      dependencies: ['task_6'],
      estimatedTokens: 200,
      priority: 4,
    },
    {
      id: 'task_8',
      description: 'ESLintでコード品質をチェックする',
      dependencies: ['task_7'],
      estimatedTokens: 200,
      priority: 3,
    },
  ],
  totalEstimatedTokens: 4000,
  estimatedDuration: 180,
};

テスト失敗時の対応フロー

テストが失敗した場合、エージェントがどのように対応するかを示します。

typescript// テスト失敗検出と修正タスクの追加
class CodingAgent extends RobustAgent {
  async execute(userTask: string): Promise<AgentResult> {
    const plan = await this.planner.createPlan(userTask);
    const context = this.initializeContext();
    const results: TaskResult[] = [];

    for (const task of plan.tasks) {
      // 通常の実行フロー
      const result = await this.executor.execute(task);
      const verification =
        await this.verifier.verifyWithLayers(
          task,
          result.output
        );

      // テスト実行ステップの特別処理
      if (task.id === 'task_7' && !verification.isValid) {
        console.log('[テスト失敗検出]');

        // エラー内容を解析
        const errors = this.parseTestErrors(result.output);

        // 修正タスクを動的に追加
        const fixTasks = this.createFixTasks(errors);
        plan.tasks.splice(
          plan.tasks.indexOf(task) + 1,
          0,
          ...fixTasks
        );

        console.log(
          `[計画更新] ${fixTasks.length}個の修正タスクを追加`
        );
      }

      results.push({ task, result, verification });

      // 停止規則チェック
      const stopCheck = this.stopEngine.shouldStop(
        context,
        plan
      );
      if (stopCheck.shouldStop) {
        break;
      }
    }

    return this.buildResult(results, context);
  }

  // テストエラーの解析
  private parseTestErrors(testOutput: string): TestError[] {
    // テスト出力からエラー情報を抽出
    const errors: TestError[] = [];

    // Jest形式のエラーを解析する例
    const errorPattern =
      /● (.+?)\n\s+(.+?)\n\s+at (.+?):(\d+):(\d+)/g;
    let match;

    while (
      (match = errorPattern.exec(testOutput)) !== null
    ) {
      errors.push({
        testName: match[1],
        message: match[2],
        file: match[3],
        line: parseInt(match[4]),
        column: parseInt(match[5]),
      });
    }

    return errors;
  }

  // 修正タスクの生成
  private createFixTasks(errors: TestError[]): Task[] {
    return errors.map((error, index) => ({
      id: `fix_task_${index + 1}`,
      description: `テストエラーを修正: ${error.testName}\nエラー: ${error.message}\n場所: ${error.file}:${error.line}`,
      dependencies: [],
      estimatedTokens: 600,
      priority: 10 - index,
    }));
  }
}

このように、実行中に計画を動的に調整することで、柔軟に問題に対応できます。

エラー修正の実行例

実際のエラー修正フローを見てみます。

typescript// 実行ログ(テスト失敗からの回復例)
/*
[プランニング開始]
計画完了: 8個のタスク

[ステップ 1-6] ... (省略) ...

[ステップ 7] テストを実行する
実行中...
テスト結果:
  ✓ ログインAPIが正しいトークンを返す
  ✗ 無効な認証情報でエラーを返す
    - Expected: 401
    - Received: 500
  ✗ トークン更新が正しく動作する
    - Error: Cannot read property 'refreshToken' of undefined

[検証失敗] 2件のテスト失敗
[計画更新] 2個の修正タスクを追加

[ステップ 8] テストエラーを修正: 無効な認証情報でエラーを返す
実行中...
修正内容: エラーハンドリングを追加し、適切な401ステータスを返すように修正
[検証成功] 信頼度: 0.89

[ステップ 9] テストエラーを修正: トークン更新が正しく動作する
実行中...
修正内容: リフレッシュトークンのnullチェックを追加
[検証成功] 信頼度: 0.91

[ステップ 10] テストを再実行する(自動追加)
実行中...
テスト結果:
  ✓ ログインAPIが正しいトークンを返す
  ✓ 無効な認証情報でエラーを返す
  ✓ トークン更新が正しく動作する

[検証成功] 信頼度: 1.00
すべてのテストが通過しました

[ステップ 11] ESLintでコード品質をチェックする
実行中...
[検証成功] 信頼度: 0.95

[完了]
総ステップ数: 11
使用トークン数: 5,890
実行時間: 156秒
成功率: 100%(修正2回含む)
*/

テスト失敗を検出し、修正タスクを追加し、再テストによって成功を確認するという一連のフローが自動化されています。停止規則により、無限ループに陥ることなく完了できました。

実例 3:停止規則による異常検出

最後に、停止規則が実際に異常を検出して停止する例を見ていきます。

問題のあるタスク例

意図的に問題のあるタスクを実行してみます。

typescript// 曖昧で達成不可能なタスク
const problematicTask = `
完璧な文章を書いてください。
- 誰が読んでも理解できる
- 専門家から見ても高度
- 短くかつ詳細
- すべての角度から完璧
`;

このタスクは矛盾する要件を含んでおり、完了条件が曖昧です。

停止規則による早期検出

エージェントの実行ログを見てみましょう。

typescript// 実行ログ(停止規則による中断例)
/*
[プランニング開始]
警告: タスクの完了条件が曖昧です
計画完了: 5個のタスク

[ステップ 1] 初稿を作成する
実行中...
[検証失敗] 「専門家から見て高度」の基準が満たされていない
[リトライ]

[ステップ 2] 専門性を高めた版を作成する
実行中...
[検証失敗] 「誰が読んでも理解できる」の基準が満たされていない
[リトライ]

[ステップ 3] バランスを調整した版を作成する
実行中...
[検証失敗] 「すべての角度から完璧」の基準が満たされていない
[リトライ]

[ステップ 4] さらに改善した版を作成する
実行中...
[検証失敗] 複数の基準が満たされていない

[停止規則発動]
理由: 実行効率が低すぎます(成功率: 0%)
総ステップ数: 4
使用トークン数: 3,200
実行時間: 67秒

[分析]
問題点:
- タスクの完了条件が矛盾している
- 定量的な成功基準がない
- 無限に改善を続ける可能性がある

推奨対応:
- タスクの要件を明確化する
- 定量的な評価基準を設定する
- 優先順位を明確にする
*/

停止規則により、無駄な試行を続ける前に問題を検出できました。エージェントは失敗した理由と改善提案を提供しています。

改善されたタスク定義

停止規則の分析を基に、タスクを改善します。

typescript// 改善されたタスク定義
const improvedTask = `
技術記事を作成してください:

【要件】(優先順位順)
1. 文字数: 1000-1500文字
2. 対象読者: プログラミング経験1年以上
3. 必須要素: コード例を3つ以上含む
4. 構成: 導入→説明→具体例→まとめ

【評価基準】
- 文字数が範囲内である(必須)
- コード例が3つ以上ある(必須)
- 段落が4つ以上ある(推奨)
- 専門用語に説明がある(推奨)

【完了条件】
必須要件をすべて満たし、推奨要件の50%以上を満たすこと
`;

明確な基準により、エージェントは適切に動作できるようになります。

まとめ

本記事では、GPT-5 などの大規模言語モデルを活用した AI エージェントを設計する際の重要な 3 つの要素——プランニング自己検証停止規則——について解説しました。

重要ポイントの振り返り

以下の表で、各要素の役割と実装のポイントをまとめます。

#要素役割実装のポイント
1プランニングタスクを実行可能なステップに分解依存関係を明確化、リソース見積もり、構造化された出力
2自己検証各ステップの結果を評価多段階検証、定量的基準、改善提案の生成
3停止規則異常状態を検出して停止複数の停止条件、動的調整、効率性の監視
4統合アーキテクチャ3 要素を連携させる明確な責任分離、フィードバックループ、柔軟な計画調整

設計における重要な原則

堅牢なエージェントを構築するための原則をまとめます。

明確性を優先する: タスクの要件、完了条件、評価基準は常に明確に定義しましょう。曖昧さは無限ループやリソース浪費の原因になります。

多層防御を実装する: 単一の検証や停止条件に頼らず、複数の層でチェックを行うことで、より確実に異常を検出できます。構文検証 → 意味検証 →LLM 検証という段階的アプローチが効果的です。

動的適応を取り入れる: 実行状況に応じて計画や基準を調整できるようにすることで、予期しない状況にも対応できるエージェントになります。テスト失敗時に修正タスクを追加する例のように、柔軟性が重要です。

観測可能性を確保する: ログ、メトリクス、検証結果を記録し、エージェントの動作を追跡できるようにしましょう。問題が発生した際の分析や改善に不可欠です。

今後の展望

AI エージェントの技術は急速に進化しています。GPT-5 のようなより強力なモデルが登場することで、エージェントの能力は飛躍的に向上するでしょう。

しかし、能力が向上するほど、適切な制御メカニズムの重要性も増します。本記事で紹介したプランニング・検証・停止規則のパターンは、今後も基本的な設計原則として有効であり続けるでしょう。

マルチエージェント協調、長期記憶の統合、外部ツールとの連携など、さらに複雑なシステムを構築する際も、これらの基本要素をしっかりと実装することが成功の鍵となります。ぜひ本記事の内容を参考に、信頼性の高い AI エージェントシステムを構築してください。

関連リンク