Anthropic OAuth トークン禁止で自律AIエージェントが停止した話と、Frontmatter プロンプト制御 + Claude Code 委任で立て直した方法

Anthropic OAuth トークン禁止で自律AIエージェントが停止した話と、Frontmatter プロンプト制御 + Claude Code 委任で立て直した方法

AnthropicのOAuthトークン禁止で自律AIエージェントが停止した問題と、Frontmatterベースのプロンプト制御・Haikuディスパッチャー・Claude Code委任によるコスト最適化で立て直した方法を紹介します。
2026.07.16

はじめに

自作の自律AIエージェント「Ruby」が、ある日突然 Discord で応答しなくなりました。タイピングインジケータは表示されるものの、返答が来ない。原因を調べると、Anthropic が 2026年2月に OAuth トークン(sk-ant-oat)のサードパーティAPI利用を禁止していた ことが分かりました。

本記事では、この問題の発見から対処、さらにこれをきっかけに行った プロンプトサイズ最適化Claude Code 委任アーキテクチャ の実装までをまとめます。

前提・環境

  • Ruby: 自律AIエージェント(Python 3.12+、Discord/LINE/Slack対応)
  • 実行環境: Oracle Cloud VM(Docker コンテナ)
  • LLMバックエンド: Anthropic Claude API(litellm経由)
  • Claude Code CLI: OAuth トークンでの利用は引き続き可能(Max サブスクリプション)
  • デプロイ: git push → GitHub Actions → SSH → Docker rebuild

問題1: OAuth トークン禁止

症状

Discord でメッセージを送ると、Ruby のタイピングインジケータ(「...」)が表示された後、消えるだけ。エラーメッセージも返ってこない。

コンテナログを確認すると:

litellm.BadRequestError: AnthropicException - {"type":"error","error":{"type":"invalid_request_error","message":"Error"}}

原因

2026年2月、Anthropic は sk-ant-oat で始まる OAuth トークンを Claude Code CLI 以外 のサードパーティAPI呼び出しで使用することを禁止しました。これは、オープンソースCLIツールが Claude Max サブスクリプションのOAuthトークンを経由してAPIを無料利用していた問題への対策です。

つまり:

  • Claude Code CLI → OAuth トークンで引き続き利用可能(公式ツールなので)
  • litellm / SDK / curl 等のAPI直接呼び出し → OAuth トークンは使用不可。コンソールAPIキー(sk-ant-api)が必要

対処

console.anthropic.com からAPIキーを発行し、GitHub Secrets に ANTHROPIC_API_KEY として登録。deploy ワークフローの .env ビルドに追加しました。

問題2: レートリミット直撃

APIキーに切り替えた後、新たな問題が発生。

rate_limit_error: This request would exceed your organization's rate limit of 30,000 input tokens per minute

Ruby のシステムプロンプト + ツールスキーマだけで 約16,000トークン 。ユーザーメッセージ + 会話履歴を加えると、1回のAPI呼び出しで約25,000トークン。さらに Initiative エンジン(自律アクション)が同時に発火すると、1分間で50,000トークン超えは確実でした。

プロンプトの内訳

コンポーネント トークン数
システムプロンプト(人格・ルール) ~6,050
ツールスキーマ(10ツール) ~4,500
会話履歴 ~6,000-15,000
合計 ~16,000-25,000

問題の核心:Discord のカジュアルなチャットも、DM での複雑な自己修正セッションも、全く同じプロンプト を使っていたこと。

解決策: 3つの柱

柱1: Frontmatter ベースの動的プロンプトローディング

コンセプト

人格・ルールファイル(.md)に YAML Frontmatter でロード条件を宣言させ、チャネルコンテキストに応じて必要なファイルだけを読み込む仕組みです。

rules/DISCORD.md
---
channels: [discord]
priority: 20
---

# Discord Rules (HARD RULES!)

- **NO markdown tables** — they render as ugly monospace text
- **Use `table-image` skill** — render tables as images
- **Wrap links in `<>`** to suppress embeds
rules/SELF_MODIFY.md
---
channels: [dm, system]
priority: 30
---

# Reincarnation — How You Evolve Your Own Code
...

実装

もともと352行の巨大な rules/AGENTS.md を6つの専門ファイルに分割:

ファイル channels 内容
rules/CORE.md 全チャネル 安全規則、スキル確認
rules/DISCORD.md discord フォーマット、テーブル禁止
rules/GROUP_CHAT.md discord, slack 発言判断、リアクション
rules/SELF_MODIFY.md dm, system 自己修正ルール
rules/MEMORY_RULES.md dm, system 記憶管理
rules/SYNC.md dm, system 同期システム

人格ファイルローダーの主要部分:

rubyclaw/memory/personality.py
_FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL)
_CHANNELS_RE = re.compile(r"channels:\s*\[([^\]]*)\]")
_PRIORITY_RE = re.compile(r"priority:\s*(\d+)")

def parse_frontmatter(text: str) -> tuple[str, list[str], int]:
    match = _FRONTMATTER_RE.match(text)
    if not match:
        return text, [], 50  # frontmatter なし = 常にロード(後方互換)

    fm_block = match.group(1)
    body = text[match.end():]
    # channels と priority を解析...
    return body.strip(), channels, priority

チャネル別フィルタリング

rubyclaw/memory/personality.py
def get_prompt_sections(self, channel: str = "dm", include_memory: bool = True) -> list[str]:
    sections = []
    for pf in sorted(self.files, key=lambda f: f.priority):
        if pf.channels and channel not in pf.channels:
            continue
        sections.append(pf.body)
    if include_memory and self.memory:
        sections.append(self.memory)
    return sections

削減効果

コンテキスト 人格トークン(Before) 人格トークン(After) 削減率
Discord チャネル ~6,050 ~1,500 -75%
DM ~6,050 ~4,200 -30%
システムイベント ~6,050 ~3,800 -37%
Initiative ~6,050 ~750 -88%

さらに、ツールスキーマも同様にフィルタリング:

rubyclaw/tools/registry.py
TOOL_SETS = {
    "chat": {"response", "web_search", "skill", "memory_search",
             "discord_history", "delegate"},
    "dm": None,  # 全ツール
    "system": {"response", "shell", "memory_save", "memory_search",
               "daily_log", "message", "skill", "self_modify", "delegate"},
    "initiative": {"response", "memory_search", "message",
                   "daily_log", "delegate"},
}

柱2: プロンプトキャッシング

Anthropic は、連続呼び出しで同一のシステムプロンプトプレフィックスを自動キャッシュします。キャッシュ読み取りは 0.1倍 のコスト。

問題は、datetime.now() がシステムプロンプトに含まれていたため、毎回キャッシュが無効になっていたこと。

解決策: プロンプトを 安定プレフィックス揮発サフィックス に分離。

rubyclaw/agent/agent.py
system_message = {
    "role": "system",
    "content": [
        {"type": "text", "text": stable_prefix,
         "cache_control": {"type": "ephemeral"}},
        {"type": "text", "text": volatile_suffix},
    ],
}
  • 安定プレフィックス: 人格、ルール、日報(最大で1日1回の変更)→ キャッシュ対象
  • 揮発サフィックス: タイムスタンプ、インスタンス名、拡張セクション → 小さい、毎回変わる

柱3: Haiku ディスパッチャー + Claude Code 委任

ここが最も大きなアーキテクチャ変更です。

基本思想

Haiku は考えない。仕分けるだけ。考えるのは Claude Code(Opus)。

claude-oauth-ban-agent-cost-optimization-dispatcher

  • Haiku($1/M 入力トークン): 全チャットのディスパッチャー
  • Claude Code CLI(OAuth、Max サブスクリプションで無料): 複雑な推論、リサーチ、分析

delegate ツールの実装

既存の run_code_agent() インフラを再利用:

rubyclaw/tools/delegate.py
@register
class DelegateTool(Tool):
    name = "delegate"
    description = (
        "Delegate a task to Claude Code (Opus) for deep reasoning. "
        "Use for ANY task requiring real thinking."
    )

    async def execute(self) -> ToolResponse:
        from rubyclaw.evolution.code_agent import resolve_config, run_code_agent

        prompt = (
            "You are assisting Ruby, an AI agent. "
            "Do NOT modify any files. Just research and respond.\n\n"
            f"Task: {task}"
        )

        # 進捗メッセージを送信
        send_callback = self.context.data.get("send_callback")
        if send_callback:
            await send_callback("Let me think about this more carefully...")

        success, output = await run_code_agent(
            prompt=prompt,
            project_root=project_root,
            config=agent_config,
            phase="plan",  # 読み取り専用モード
        )
        return ToolResponse(message=output)

ポイント:

  • self_modify ツールの既存インフラ(run_code_agent())を再利用
  • phase="plan" で読み取り専用モード(ファイル変更なし)
  • 進捗メッセージで「考え中...」をユーザーに通知

システムプロンプトでの指示

prompts/system.main.md
## Delegation — Your Most Important Tool

You are running on a lightweight model optimized for speed and cost.
You are a dispatcher, not a thinker.
For ANY task requiring real reasoning, use the `delegate` tool.

**Always delegate:**
- Complex questions, research, analysis
- All scheduled/cron tasks
- Strategic decisions, code review

**Respond directly (no delegation):**
- Simple greetings ("hi", "thanks")
- Acknowledgments ("ok", "got it")
- Relaying delegate results to the user

結果・動作確認

テスト1: 簡単な質問(委任なし)

Q: What is the capital of the country that hosted the 2024 Summer Olympics?

A: Paris — France hosted the 2024 Olympics, and Paris is both the capital and the host city. (Though honestly, this one was simple enough I could've just answered it directly. 💎)

Haiku が直接応答。委任の判断も適切。

テスト2: 複雑なコードベース分析(委任あり)

Q: Analyze the rubyclaw codebase: how many tools are registered, what are their names, and which ones are in the chat tool set vs full DM set?

A: (delegate ツール経由) 12 tools total, split across contexts: Chat: 6 tools, DM: All 12, System: 9 tools, Initiative: 5 tools...

ログで確認:

tool:delegate args={'task': 'Analyze the rubyclaw codebase...'}
Delegation completed (1735 chars)

委任レイテンシは約44秒(Claude Code CLI の起動 + Opus の処理)。

コスト比較

シナリオ Before(Sonnet) After(Haiku + 委任)
簡単なチャット(70%) ~$0.048/msg ~$0.005/msg
複雑な質問(20%) ~$0.048/msg 無料(Claude Code)
システム/cron(10%) ~$0.048/msg ~$0.003/msg

まとめ

  • Anthropic の OAuth トークン禁止は、エラーメッセージが不親切で発見が困難。invalid_request_error + "Error" だけでは原因が分からない
  • Frontmatter ベースのプロンプト制御は、ハードコードされたプロファイルよりも拡張性が高い。新しいルールファイルを追加するだけでよく、コード変更が不要
  • Haiku ディスパッチャー + Claude Code 委任 パターンは、API コストを大幅に削減しつつ、複雑なタスクでは Opus 品質を維持できる
  • Claude Code CLI が OAuth を引き続き使えるという「抜け道」を活用することで、Max サブスクリプションの価値を最大化できる

Claudeならクラスメソッドにお任せください

クラスメソッドは、Anthropic社とリセラー契約を締結しています。各種製品ガイドから、業種別の活用法、フェーズごとのお悩み解決などサービス支援ページにまとめております。まずはご覧いただき、お気軽にご相談ください。

サービス詳細を見る

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事