DEV Community

Cover image for AIエージェントのお守りをやめる方法
Akira
Akira

Posted on • Originally published at apidog.com

AIエージェントのお守りをやめる方法

要点(TL;DR)

AIエージェントの絶え間ない監視をやめるには、以下の3つを構築します。ガードレール(壊滅的な障害を防ぐ制約)、可観測性(何が起こったかを知らせるログとメトリクス)、チェックポイント(人間が意思決定を検証するための自動一時停止)。これらを一度設定すれば、エージェントは数分ではなく数時間自律的に実行できます。Apidogのようなツールは、エージェントが違反できないAPI契約を定義できるようにすることで、APIレイヤーをセーフティネットに変えるのに役立ちます。

今すぐApidogを試す

はじめに

先週、ある開発者が、時間を節約してくれるはずのAIエージェントを4時間も監視しているのを目撃しました。数分ごとに中断し、間違いを修正し、再起動していました。結局、彼は自分でコードを書いた場合よりも多くの手作業をすることになりました。

これが「ベビーシッター問題」であり、AIエージェントがその約束を果たせない最大の理由です。ツールは機能し、モデルも高性能です。しかし、ほとんどのチームは絶え間ない監視の段階を乗り越えることができません。

何が起こっているかというと、ほとんどのAIエージェントの設定では、LLMをあらゆるタスクで手取り足取り指導する必要があるジュニア開発者のように扱っています。しかし、LLMはジュニアではありません。彼らは、境界を設定しないと、自信満々に間違ったことをする、非常に高速で時折幻覚を見るインターンのようなものです。

💡 APIを構築している、またはAPIを呼び出すAIエージェントと連携している場合、Apidogはその境界を定義するのに役立ちます。正確なリクエスト/レスポンススキーマを指定することで、エージェントが誤って違反できない契約を作成します。これは、エージェントをさまよわせるのではなく、地図を与えるようなものです。

AIエージェントが従うことができるAPI契約を定義する

このガイドの最後には、以下の内容を理解しているでしょう。

  • エージェントの自律性について考えるためのメンタルモデル
  • ガードレール、可観測性、チェックポイントの具体的なパターン
  • 今日からプロジェクトにコピーできるコード例
  • エージェントが監視なしで実行できる準備ができているか評価するためのチェックリスト

エージェントが絶え間ない監視を必要とする理由

AIエージェントは予測可能な方法で失敗します。これらの失敗モードを理解することが、問題を解決するための第一歩です。

失敗モード1:スコープクリープ

エージェントに「APIエンドポイントに認証を追加して」と依頼します。エージェントは認証を追加します。その後、レート制限を追加します。さらにデータベーススキーマをリファクタリングします。そして「未使用」だと思われるファイルを削除しますが、それが重要なファイルであることが判明します。

エージェントは誰も止めなかったため、作業を続けました。LLMには「完了」という生来の感覚がありません。トークン制限に達するか、ユーザーが中断するまで変更を加え続けます。

失敗モード2:間違った抽象化

「エラー処理を改善する」というタスクを与えられたエージェントは、あらゆる場所にtry-catchブロックを追加するかもしれません。技術的には正しいですが、実際にはひどいものです。コードは読みにくくなり、ロギングは一貫性がなく、実際のエラーケースは処理されません。

エージェントはリクエストを文字通りに理解しましたが、意図を見落としました。適切なエラー処理の例がなければ、最も明白な(そして最悪の)解釈に頼ってしまいます。

失敗モード3:カスケード障害

エージェントがステップ1で小さな間違いを犯します。ステップ10までに、その間違いは後続のすべての決定に波及しています。関数名のタイプミスから始まったものが、壊れたAPI、壊れたテスト、そして何が間違っていたのかを解明しようと混乱する開発者へとつながります。

これは最も危険な失敗モードです。なぜなら、エージェントは失敗したことを知らないからです。各ステップは単独では妥当に見えます。最終結果だけが問題を明らかにします。

失敗モード4:リソース枯渇

監視されていない場合、一部のエージェントは無限ループに陥ります。失敗したAPI呼び出しを無制限に再試行したり、制限なく新しいサブエージェントを生成したり、請求上限に達するまでコードを生成し続けたりします。

リソースの制約がなければ、エージェントはいつやめるべきかを知りません。


自律性フレームワーク:ガードレール、可観測性、チェックポイント

これらの問題は3つの層で解決します。これらをピラミッドとして考えてみてください。一番下がガードレール(障害の防止)、真ん中が可観測性(障害の検出)、一番上がチェックポイント(障害からの回復)です。

レイヤー1:ガードレール(予防)

ガードレールとは、壊滅的な障害を防ぐ制約のことです。プロンプトではなく、コードによって強制される、エージェントが破ることのできないルールです。

コードによる厳格な制約:

# やらないこと:エージェントが指示に従うと信用する
agent.run("Only modify files in the src/ directory")

# やること:コードで制約を強制する
import os
from pathlib import Path

ALLOWED_DIRECTORIES = {"src", "tests", "docs"}

def validate_file_path(path: str) -> bool:
    """エージェントは許可されたディレクトリの外に書き込むことはできません。"""
    abs_path = Path(path).resolve()
    return any(
        str(abs_path).startswith(str(Path(d).resolve()))
        for d in ALLOWED_DIRECTORIES
    )

def agent_write_file(path: str, content: str):
    if not validate_file_path(path):
        raise ValueError(f"Cannot write to {path}: outside allowed directories")
    with open(path, 'w') as f:
        f.write(content)
Enter fullscreen mode Exit fullscreen mode

APIスキーマ制約:

エージェントがAPIを呼び出す際、不正なリクエストを防ぐためにスキーマを使用します。Apidogが真価を発揮するのはここです。API契約を一度定義すれば、エージェントが間違ったデータ形式を送信することはありません。

// apidog-schema.ts
export const CreateUserSchema = {
  type: 'object',
  required: ['email', 'name'],
  properties: {
    email: { type: 'string', format: 'email' },
    name: { type: 'string', minLength: 1, maxLength: 100 },
    role: { type: 'string', enum: ['user', 'admin', 'guest'] }
  },
  additionalProperties: false
}

// エージェントはAPI呼び出しの前に検証する必要があります
function validateRequest(schema: object, data: unknown): void {
  const valid = ajv.validate(schema, data)
  if (!valid) {
    throw new Error(`Invalid request: ${JSON.stringify(ajv.errors)}`)
  }
}
Enter fullscreen mode Exit fullscreen mode

予算制約:

import time
from dataclasses import dataclass

@dataclass
class AgentBudget:
    max_steps: int = 50
    max_tokens: int = 100000
    max_time_seconds: int = 600  # 10分
    max_api_calls: int = 100

class BudgetEnforcer:
    def __init__(self, budget: AgentBudget):
        self.budget = budget
        self.start_time = time.time()
        self.steps = 0
        self.tokens_used = 0
        self.api_calls = 0

    def check(self) -> bool:
        """予算を超過した場合にFalseを返します。"""
        elapsed = time.time() - self.start_time

        if self.steps >= self.budget.max_steps:
            raise RuntimeError(f"Step limit reached: {self.steps}")
        if self.tokens_used >= self.budget.max_tokens:
            raise RuntimeError(f"Token limit reached: {self.tokens_used}")
        if elapsed >= self.budget.max_time_seconds:
            raise RuntimeError(f"Time limit reached: {elapsed:.0f}s")
        if self.api_calls >= self.budget.max_api_calls:
            raise RuntimeError(f"API call limit reached: {self.api_calls}")

        return True

    def record_step(self, tokens: int, api_calls: int = 0):
        self.steps += 1
        self.tokens_used += tokens
        self.api_calls += api_calls
        self.check()
Enter fullscreen mode Exit fullscreen mode

レイヤー2:可観測性(検出)

エージェントが長時間実行される場合、すべてのステップを監視することなく、何をしているのかを知る必要があります。可観測性は、意思決定のタイムラインを提供します。

構造化ロギング:

import json
from datetime import datetime
from typing import Any

class AgentLogger:
    def __init__(self, log_file: str = "agent_trace.jsonl"):
        self.log_file = log_file
        self.entries = []

    def log(self, event: str, data: dict[str, Any] | None = None):
        entry = {
            "timestamp": datetime.utcnow().isoformat(),
            "event": event,
            "data": data or {}
        }
        self.entries.append(entry)
        with open(self.log_file, 'a') as f:
            f.write(json.dumps(entry) + '\n')

    def log_decision(self, decision: str, reasoning: str, confidence: float):
        self.log("decision", {
            "decision": decision,
            "reasoning": reasoning,
            "confidence": confidence
        })

    def log_action(self, action: str, params: dict, result: str):
        self.log("action", {
            "action": action,
            "params": params,
            "result": result[:200]
        })

    def log_error(self, error: str, context: dict):
        self.log("error", {
            "error": error,
            "context": context
        })

# エージェントでの使用例
logger = AgentLogger()
logger.log_decision(
    decision="Add rate limiting to API",
    reasoning="Current endpoint has no protection against abuse",
    confidence=0.85
)
logger.log_action(
    action="write_file",
    params={"path": "src/middleware/rate-limit.ts"},
    result="Successfully wrote 45 lines"
)
Enter fullscreen mode Exit fullscreen mode

メトリクスダッシュボード:

from collections import Counter
from dataclasses import dataclass, field

@dataclass
class AgentMetrics:
    actions_taken: Counter = field(default_factory=Counter)
    files_modified: list[str] = field(default_factory=list)
    api_calls: dict[str, int] = field(default_factory=dict)
    errors: list[str] = field(default_factory=list)
    decisions_by_confidence: dict[str, int] = field(default_factory=lambda: {
        "high (>0.9)": 0,
        "medium (0.7-0.9)": 0,
        "low (<0.7)": 0
    })

    def record_action(self, action: str):
        self.actions_taken[action] += 1

    def record_file_modification(self, path: str):
        if path not in self.files_modified:
            self.files_modified.append(path)

    def record_api_call(self, endpoint: str):
        self.api_calls[endpoint] = self.api_calls.get(endpoint, 0) + 1

    def record_error(self, error: str):
        self.errors.append(error)

    def record_decision(self, confidence: float):
        if confidence > 0.9:
            self.decisions_by_confidence["high (>0.9)"] += 1
        elif confidence >= 0.7:
            self.decisions_by_confidence["medium (0.7-0.9)"] += 1
        else:
            self.decisions_by_confidence["low (<0.7)"] += 1

    def summary(self) -> str:
        return f"""
エージェントメトリクス概要
=====================
アクション: {dict(self.actions_taken)}
変更されたファイル: {len(self.files_modified)}
API呼び出し: {self.api_calls}
エラー: {len(self.errors)}
信頼度別決定: {self.decisions_by_confidence}
"""
Enter fullscreen mode Exit fullscreen mode

レイヤー3:チェックポイント(回復)

チェックポイントとは、エージェントが人間の検証を待つ自動一時停止のことです。これにより、絶え間ない監視なしで問題を早期に発見できます。

自動チェックポイント:

from enum import Enum
from typing import Callable
from dataclasses import dataclass

class CheckpointTrigger(Enum):
    BEFORE_FILE_WRITE = "before_file_write"
    BEFORE_API_CALL = "before_api_call"
    BEFORE_GIT_COMMIT = "before_git_commit"
    BEFORE_DELETE = "before_delete"
    AFTER_N_STEPS = "after_n_steps"

@dataclass
class Checkpoint:
    trigger: CheckpointTrigger
    description: str
    data: dict
    requires_approval: bool = True

class CheckpointManager:
    def __init__(self, auto_approve: set[CheckpointTrigger] | None = None):
        self.auto_approve = auto_approve or set()
        self.pending: list[Checkpoint] = []

    def create_checkpoint(
        self, 
        trigger: CheckpointTrigger, 
        description: str, 
        data: dict
    ) -> bool:
        if trigger in self.auto_approve:
            return True

        checkpoint = Checkpoint(
            trigger=trigger,
            description=description,
            data=data
        )
        self.pending.append(checkpoint)
        return False

    def approve(self, checkpoint_id: int) -> None:
        if 0 <= checkpoint_id < len(self.pending):
            self.pending.pop(checkpoint_id)

    def reject(self, checkpoint_id: int) -> None:
        raise RuntimeError(f"Checkpoint rejected: {self.pending[checkpoint_id]}")

# エージェントでの使用例
checkpoints = CheckpointManager(
    auto_approve={CheckpointTrigger.BEFORE_FILE_WRITE}
)

if not checkpoints.create_checkpoint(
    trigger=CheckpointTrigger.BEFORE_DELETE,
    description="About to delete src/legacy/ directory",
    data={"path": "src/legacy/", "files": ["old_handler.ts", "deprecated.ts"]}
):
    agent.pause("Waiting for approval to delete files")
Enter fullscreen mode Exit fullscreen mode

Apidogで自律型エージェントを構築する

AIエージェントがAPIと連携する際、最大のリスクは、後続の障害を引き起こす不正な形式のリクエストです。Apidogは、エージェントが従うべき正確なAPIスキーマを定義できるようにすることで、これを支援します。

API契約のセットアップ:

  1. ApidogでOpenAPI仕様をインポートまたは定義する
  2. 組み込みの検証機能付きクライアントコードを生成する
  3. エージェントに生HTTPの代わりに検証済みクライアントを与える
// エージェントにAPIを直接呼び出させる代わりに
const response = await fetch('/api/users', {
  method: 'POST',
  body: JSON.stringify(data)  // 検証なし
})

// エージェントに検証済みクライアントを与える
import { UsersApi } from './generated/apidog-client'

const usersApi = new UsersApi()
// エージェントは有効なリクエストのみ送信可能 - スキーマ強制
const response = await usersApi.createUser({
  email: 'user@example.com',
  name: 'Test User',
  role: 'user'  // 有効な列挙値である必要がある
})
Enter fullscreen mode Exit fullscreen mode

これにより、APIレイヤーがガードレールになります。クライアントがリクエストが送信される前に不正なデータを拒否するため、エージェントは文字通り無効なデータを送信できません。

AIエージェント向けの検証済みAPIクライアントを生成する


実証済みのパターンとよくある間違い

パターン1:承認サンドイッチ

リスクの高い操作では、実行前と実行後に承認を必須とします。

def risky_operation(agent, operation):
    # 事前承認
    if not agent.checkpoint(f"About to: {operation.description}"):
        return "Cancelled by user"

    # 操作を実行する
    result = operation.execute()

    # 事後承認(結果を検証する)
    if not agent.checkpoint(f"Verify result of: {operation.description}"):
        operation.rollback()
        return "Rolled back by user"

    return result
Enter fullscreen mode Exit fullscreen mode

パターン2:信頼度しきい値

信頼度の低い決定に基づいてエージェントに行動させないでください。

MIN_CONFIDENCE = 0.75

def agent_decide(options: list[dict]) -> dict:
    best = max(options, key=lambda x: x.get('confidence', 0))

    if best['confidence'] < MIN_CONFIDENCE:
        return {
            'action': 'escalate',
            'reason': f"Best option has confidence {best['confidence']:.2f} < {MIN_CONFIDENCE}",
            'options': options
        }

    return best
Enter fullscreen mode Exit fullscreen mode

パターン3:冪等操作

エージェントのアクションを、副作用なしで繰り返し実行できるように設計します。

import hashlib
import os

def idempotent_write(path: str, content: str) -> bool:
    """コンテンツが変更された場合のみ書き込みます。"""
    content_hash = hashlib.sha256(content.encode()).hexdigest()

    existing_hash = None
    if os.path.exists(path):
        with open(path, 'r') as f:
            existing_hash = hashlib.sha256(f.read().encode()).hexdigest()

    if content_hash == existing_hash:
        logger.log_action("write_file", {"path": path}, "Skipped - no changes")
        return False

    with open(path, 'w') as f:
        f.write(content)
    logger.log_action("write_file", {"path": path}, f"Wrote {len(content)} bytes")
    return True
Enter fullscreen mode Exit fullscreen mode

避けるべきよくある間違い

  • プロンプトを制約として信頼する。 プロンプトの「ファイルを削除しない」は制約ではありません。ファイルパーミッションが制約です。
  • ロールバック計画がない。 エージェントが間違いを犯した場合、それを元に戻す必要があります。gitやバックアップを使用していない場合、エージェントに回復不可能なアクションを信頼していることになります。
  • 信頼度スコアを無視する。 ほとんどのLLMは信頼度を出力できます。信頼度が低い場合は一時停止して人間に尋ねるべきです。
  • 過剰な監視。 すべてのステップを監視しているなら、それは自律システムではなく遅い手動システムです。
  • 成功条件の指定不足。 エージェントはいつ完了したかを知る必要があります。「バグを修正する」には終了条件がありません。「バグを修正し、すべてのテストがパスする」にはあります。

代替案と比較

アプローチ 自律性 リスク 最適な用途
手動コーディング なし 複雑で重要な作業
AIとのペアプログラミング 学習、探索
監視付きエージェント 定型タスク
ガードレール付き自律型エージェント 制御可能 一括操作、移行
完全自律型エージェント 非常に高 信頼できる、十分にテストされたワークフロー

ほとんどのチームは「ガードレール付き自律型」を目指すべきです。これは、10%のリスクで80%の時間を節約できるスイートスポットです。


現実世界のユースケース

コードベースの移行。

あるチームは、RESTからGraphQLへの200のAPIエンドポイントを移行するために自律型エージェントを使用しました。ガードレールはスキーマの変更を防ぎました。チェックポイントは古いエンドポイントを削除する前に承認を必要としました。移行は3週間ではなく3日で完了し、本番環境でのインシデントはゼロでした。

ドキュメント生成。

エージェントはコードからAPIドキュメントを自動生成します。ガードレールにより、特定のディレクトリからのみ読み取ることが保証されます。チェックポイントは公開前に一時停止します。チームは手動でドキュメントを作成する代わりに、週に一度レビューします。

テストカバレッジ。

エージェントはコードを分析し、不足しているテストを作成します。予算制約により、テストの暴走生成が防止されます。信頼度しきい値により、不確実なテストは人間によるレビューのためにフラグが立てられます。カバレッジは1ヶ月で60%から85%に向上しました。


まとめ

以下に、あなたが学んだことをまとめます。

  • AIエージェントは予測可能な方法で失敗します:スコープクリープ、間違った抽象化、カスケード障害、リソース枯渇
  • 3つの層がほとんどの問題を解決します:ガードレール(予防)、可観測性(検出)、チェックポイント(回復)
  • ガードレールはプロンプトではなくコードです。プログラムによって制約を強制します。
  • 可観測性とは、すべてのステップを監視するのではなく、構造化されたログとメトリクスを意味します
  • チェックポイントは、絶え間ない監視なしで人間が決定を検証することを可能にします
  • ApidogのAPIスキーマは、APIレイヤーをガードレールに変えます

次のステップ:

  1. 最も反復的なAI支援タスクを特定する
  2. ガードレールを定義する:エージェントが絶対にやってはいけないことは何か?
  3. 何が起こっているかを確認するために構造化ロギングを追加する
  4. リスクの高い操作に対してチェックポイントを作成する
  5. 30分間実行させ、ログを確認する

目標は、人間をループから排除することではありません。人間をループ内の適切な位置に配置することです。つまり、低レベルの間違いを修正するのではなく、高レベルの決定を下すことです。

AIエージェントのAPIガードレールを構築する - 無料


よくある質問(FAQ)

AIエージェントとAIアシスタントの違いは何ですか?

アシスタントはあなたの要求に応答し、次の指示を待ちます。エージェントは目標を受け取り、それを達成するためのステップを自律的に計画・実行します。アシスタントはあらゆるループであなたを必要とします。エージェントはチェックポイントに到達するか、完了するまで実行されます。

エージェントが自律的に実行できる準備ができているか、どうすればわかりますか?

監視モードで10セッション実行します。介入しなければならなかったすべての回数を記録します。介入が1セッションあたり2回未満で、すべて軽微なもの(修正ではなく明確化)であれば、準備ができています。介入が頻繁であるか、作業を元に戻す必要がある場合は、より多くのガードレールを追加してください。

自律型エージェントの最大のリスクは何ですか?

エージェントが認識しないカスケード障害です。初期の小さな間違いが後で大きな問題となり、エージェントは各ステップが単独では妥当に見えるため、作業を続行します。チェックポイントは、検証を強制することでこれらのカスケードを阻止します。

これらのパターンはどのLLMでも使用できますか?

はい。これらのパターン(ガードレール、可観測性、チェックポイント)はモデルに依存しません。Claude、GPT-4、Gemini、その他どのモデルでも機能します。具体的な実装の詳細は異なるかもしれませんが、概念は共通しています。

可観測性はエージェントの速度をどの程度低下させますか?

ごくわずかです。ログファイルへの書き込みはマイクロ秒単位で済みます。速度低下は、人間からの入力を待つチェックポイントによるものです。真に自律的な実行の場合、すべてのステップではなく、リスクの高い瞬間にのみチェックポイントを設定します。

エージェントが私の意見に反する決定を下したらどうなりますか?

それがチェックポイントの目的です。意見が合わない決定を見た場合、チェックポイントを却下します。エージェントはロールバックするか、別のアプローチを試みます。より良い方法:あなたの好みをエージェントの指示に含めることで、時間をかけてあなたのスタイルを学習させることができます。

監視型エージェントと自律型エージェントのどちらから始めるべきですか?

常に監視型から始めてください。信頼できるようになるまで、すべての重要なアクションにチェックポイントを設定してエージェントを実行します。リスクの低いアクションについては、徐々にチェックポイントを削除します。これにより、最初の自律実行で壊滅的な失敗を招くリスクを冒すのではなく、段階的に信頼を構築できます。

ApidogはAIエージェントに具体的にどのように役立ちますか?

Apidogはあなたのスキーマから検証済みのAPIクライアントを生成します。エージェントがこれらのクライアントを使用すると、不正な形式のリクエストがバックエンドに到達する前に拒否されます。これにより、エージェントが間違ったデータ形式や無効な値を送信することによって発生するあらゆる種類の障害を防ぐことができます。

Top comments (0)