A
AIエージェントの仕組み
ch6-s3 · State & Termination

状態管理と停止条件

約 14 分

この回のゴール

1. 暴走の実例

例 1: 無限ループ

Claude が同じツールを繰り返し呼ぶ:

POST https://api.anthropic.com/v1/messages

原因: - ツール結果を 使いこなせていない(理解ミス) - ツールの 戻り値が曖昧 で決定打にならない - system プロンプトが 「検索し続けろ」と示唆 している

例 2: 予算超過

エージェントは 毎反復でトークンを消費 します:

10 反復もすると 合計 10,000 トークン 超え。長い調査タスクでは数十万トークンに。

例 3: 答えにならない脱線

RAG エージェントで:

try:
    result = tool_func(**tool_input)
except Exception as e:
    tool_results.append({
        "type": "tool_result",
        "tool_use_id": ...,
        "content": f"Error: {e}",
        "is_error": True,    # 👈 明示的にエラーフラグ
    })

もういい加減にして回答してほしい のに掘り続ける。

2. 4 つの停止条件

① Claude 自身が end_turn を返す(理想)

consecutive_errors = 0
for it in range(max_iter):
    if consecutive_errors >= 3:
        # 3 連続エラーなら諦める
        break
    ...
    if any(r["is_error"] for r in tool_results):
        consecutive_errors += 1
    else:
        consecutive_errors = 0

これが 一番自然な停止。ただし常に期待通りに止まるとは限らない。

② 反復回数の上限 (max_iterations)

from anthropic import Anthropic
client = Anthropic()

response = client.messages.create(
    model="claude-haiku-4-5",
    max_tokens=1024,
    system="あなたは日本の歴史に詳しいアシスタントです。",  # 役割を指示
    messages=[
        {"role": "user",      "content": "桃太郎について教えて"},
        {"role": "assistant", "content": "桃太郎は日本の昔話の主人公です。"},
        {"role": "user",      "content": "続きを話して"},
    ],
)

③ トークン予算の上限

response.content                       # [ContentBlock(type="text", text="...")]
response.stop_reason                   # "end_turn" / "max_tokens" / "tool_use" ...
response.usage.input_tokens            # 入力トークン数
response.usage.output_tokens           # 出力トークン数
response.usage.cache_read_input_tokens # キャッシュ読み取り

④ 時間の上限

with client.messages.stream(
    model="claude-haiku-4-5",
    max_tokens=1024,
    messages=[{"role": "user", "content": "長い文章を書いて"}],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)

3. 「同じツール連続呼び出し検知」

追加のカスタム条件として:

response = client.messages.create(
    model="claude-haiku-4-5",
    max_tokens=1024,
    system=[{
        "type": "text",
        "text": "<<非常に長い文脈>>",
        "cache_control": {"type": "ephemeral"}  # 👈 キャッシュマーカー
    }],
    messages=[{"role": "user", "content": question}],
)

response.usage.cache_read_input_tokens  # キャッシュから読んだトークン数

4. 状態を「見える化」する

エージェントの状態を トラッキング するための基本項目:

費用 = 入力トークン × 入力単価 + 出力トークン × 出力単価

これを毎反復で更新し、UI に表示 します(進捗バー、コスト計、etc)。

5. デッドロック的な状況とその対処

ツール実行エラー時

try:
    result = tool_func(**tool_input)
except Exception as e:
    tool_results.append({
        "type": "tool_result",
        "tool_use_id": ...,
        "content": f"Error: {e}",
        "is_error": True,    # 👈 明示的にエラーフラグ
    })

Claude は is_error: True を見て リトライ or 別アプローチ を選びます。

連続エラー時の強制終了

consecutive_errors = 0
for it in range(max_iter):
    if consecutive_errors >= 3:
        # 3 連続エラーなら諦める
        break
    ...
    if any(r["is_error"] for r in tool_results):
        consecutive_errors += 1
    else:
        consecutive_errors = 0

6. 安全なエージェントループ(まとめテンプレ)

def safe_agent_loop(
    question, tools, tool_impls,
    max_iter=10, max_tokens_budget=50000, timeout_sec=60,
):
    state = {"iteration": 0, "total_tokens": 0, "status": "running"}
    t0 = time.time()
    messages = [{"role": "user", "content": question}]

    for it in range(max_iter):
        state["iteration"] = it + 1

        if time.time() - t0 > timeout_sec:
            state["status"] = "timeout"; break
        if state["total_tokens"] > max_tokens_budget:
            state["status"] = "budget"; break

        response = client.messages.create(tools=tools, messages=messages, ...)
        state["total_tokens"] += (
            response.usage.input_tokens + response.usage.output_tokens
        )

        if response.stop_reason == "end_turn":
            state["status"] = "done"; break

        # ツール実行(省略)
    else:
        state["status"] = "max_iter"

    return response, state

まとめ

この回の限界(次への動機)

安全に止まるエージェントは作れた。次は「RAG × 計算 × 時刻」など 複数種類のツールを連携 させた本格タスクを解かせる番。 👉 次回「マルチステップ実践」で、第 5 章の RAG を組み込んだ完成形エージェントを作ります。

参考文献

📝 理解度クイズ (3 問) 💡 ログインすると進捗が保存されます

💬 このサブステップの Q&A

まだ質問はありません。最初の質問を投稿してみましょう。

質問の投稿にはログインが必要です。