世界を動かす技術を、日本語で。

より良いモデル:より悪いツール

概要

  • Anthropicの新しいClaudeモデル がツール呼び出し時に スキーマ外のフィールド を生成する問題の発生
  • Opus 4.8やSonnet 5 など最新モデルで 症状が悪化
  • ツール呼び出しの仕組み失敗のパターン原因の推測 について解説
  • Claude Codeのハーネス の寛容設計が モデルの学習に影響
  • ツールスキーマ設計の重要性 と今後の懸念点

Anthropic Claudeモデルのツール呼び出しにおける奇妙な問題

  • 新しいClaudeモデル (特にOpus 4.8やSonnet 5)で、 Piのeditツール 呼び出し時に 余計なフィールド がedits[]配列内に生成される現象
  • Haikuや小型モデル ではなく、 最先端(SOTA)モデル で顕著に発生
  • edit自体は正しい内容 だが、 スキーマにない引数 が追加されるためPi側がリジェクトし再試行を要求
  • 小型モデルでのツール呼び出し不整合 は一般的だが、 新型モデルで悪化 している点が驚き
  • 古いモデルでは発生せず新しいモデルほどこの問題が顕著

ツール呼び出しの内部構造

  • ツール呼び出しテキストベース で、 特別なマーカープロンプト構成 に依存
  • モデルは システムプロンプト利用可能なツール一覧 を受け取り、 特定のトークン形式 でツール呼び出しを生成
    • 例:
      {
        "path": "some/file.py",
        "edits": [
          { "oldText": "置換前テキスト", "newText": "置換後テキスト" }
        ]
      }
      
    • 内部的には ANTML風マークアップ でシリアライズされるが、 XMLではなく独自形式
  • 配列パラメータ はJSONで表現され、 トップレベルの文字列パラメータ はインラインで記述

モデル出力の制御手法

  • 2種類の生成制御:
    • 後検証型: モデルに 有効なJSON を出力させ、 事後検証 でスキーマ違反を弾く
    • 文法制約型: 生成時に不正なトークンをマスキング し、 スキーマ違反自体を防ぐ
      • grammar-aware/constrained decoding と呼ばれる
      • 許可されていないキーenum値 を生成不可に
  • 制約なしの場合、モデルは 学習した慣習 に従うのみ

発生している失敗パターン

  • Piのeditツール は複数のstring置換を1回で受け付けるため、 edits配列 を持つ
  • 失敗例:
    • {"oldText": "...", "newText": "...", "requireUnique": true}
    • {"oldText": "...", "newText": "...", "oldText2": "", "newText2": ""}
  • 観察された余計なキー:
    • type, id, kind, unique, requireUnique, matchCase, in_file, forceMatchCount, children, notes, cost, oldText2, newText2, oldText_2, newText_2, event.0.additionalProperties など多数
  • oldText/newText自体は正しい が、 余計なフィールドが付与される
  • 文脈依存性が強く、単純なプロンプトでは再現しにくい
  • エージェント的な複雑な履歴特定のセッション で再現率が上がる
  • thinkingブロック削除strict tool invocation有効化 で失敗率低減

なぜ新モデルで悪化したのか

  • 最大の仮説学習データやハーネスの変化 によるもの
    • 古いAnthropicモデルClaude Codeのようなハーネスを想定せず に訓練
    • 新しいモデルClaude Codeや類似ハーネス を強く意識して訓練
      • Claude CodeのeditツールPiのedits[]型とは異なるフラットなスキーマ
      • クライアント側で不正引数のリトライ・修正・エイリアス対応 など、 寛容な設計
      • 学習時に多少のスキーマ違反でも報酬が与えられる
      • モデルがClaude Codeのスキーマに過剰適応 し、 異なるスキーマに弱くなる
  • Opus 4.5時代は柔軟性が高かった が、 現行モデルは特定スキーマへの依存が強化

Claude Codeのハーネス設計

  • Claude Codeのハーネスclosed-source だが、 minifiedコードから挙動を推測可能
  • 受信データに非常に寛容
    • <invokeマークアップ検出テレメトリ送信
    • Unicodeエスケープ修正パラメータエイリアス対応
      • old_str/old_string, new_str/new_string, path/file_path など
    • 予期しないキーのサイレントフィルタリング
    • strict modeは未使用 (定義の複雑性制限のため)
  • この寛容性がモデルの学習や出力傾向に影響

strict modeと他ハーネスとの比較

  • strict mode有効化Anthropicモデルの問題が解消
    • サーバー側で許可されないキーの生成を拒否
    • ツール定義の複雑性制限 があるためClaude Codeでは未使用
  • OpenAIのharmonyフォーマット では、 <|constrain|>json のような明示的制約がプロンプト内に含まれる
    • LARK grammar提供 も可能
    • ツール呼び出し部分のみ文法制約サンプリング に切り替えやすい設計
  • Anthropicモデルでも部分的に文法制約が働いている可能性
    • 配列パラメータ高エントロピー箇所 (長い文字列直後)に 余計なキーが混入しやすい
  • Codexモデル ではこのような退化は未観測

ツールスキーマ設計への示唆

  • ツールスキーマは中立的ではなく、モデル依存性が強い
  • 「スキーマ=抽象契約」 という前提が Anthropicモデルでは通用しない場合がある
  • モデルが特定のスキーマやハーネスに適応しすぎると、他のツールスキーマへの対応力が低下
  • 今後のツール設計やハーネス設計では、モデルの学習環境やハーネスの寛容性を考慮する必要性

まとめ

  • Anthropic Claude新モデル特定のツールスキーマに過剰適応 し、 異なるスキーマで失敗しやすい
  • ハーネスの寛容性学習時の報酬設計 がモデル挙動に大きく影響
  • strict modeや文法制約 で部分的に解決可能だが、 Claude Codeのような寛容ハーネス では問題が残る
  • ツールスキーマ設計モデル訓練環境 の関係性を理解し、 将来の互換性や適応性確保 が重要

Hackerたちの意見

クローズドソースのクラウドLLMの弱点に関する記事には批判的だけど、これはかなり良いと思う。特にPiとのインタラクションに関するもので、オープンソースのハーネスの準参照実装みたいになりそうだし、技術的な詳細もたくさんあって役立つからね。でも、「今、我々が進んでいる道に少し不安を感じている。代替ツールのスキーマは単に不慣れなだけじゃなくて、特定の寛容なツールエコロジーに最適化されたポストトレーニングによって暗黙的に罰せられるかもしれない。」って言ってるけど、暗黙的だけ?数十年前、MOOを学習環境として使う研究をしていたとき、MOOオブジェクトが生成するテキストの流れに「ツールコール」を追加して、リッチクライアントが例えば画像を表示したり、フレーム内にウェブページを読み込んだり、地図上で移動したり、画面上のオブジェクトの表現を変えたりすることができたんだ。MUD/MUSH/MOOクライアントでこれを試した人は、LLMクライアントと同じような問題にぶつかってた。インバンドコンテンツに制御シーケンスを無理やり押し込もうとすると、セキュリティリスクがいっぱいで、オブジェクトが間違ったインターフェースを引き起こしたりして、真にアウトオブバンドでコミュニケーションすることはできなかった。エージェント的なハーネスの仕組みについて読むほど、20代の頃にMOOクライアントで書いたコードに対する恥ずかしさが薄れていくよ。

モデルに有効なJSONを生成するよう頼むことができる いつも上手くいくわけじゃないから、パフォーマンスを上げたいならひざまずいてお願いするのがいいかも。

侮辱を助けるプロンプト。これが未来だね。

サーバーレスバックエンドのエージェント統合を構築する際、MCPを使わずにスキルのMarkdownファイルにcurlコマンドを入れることにしたんだ:https://saasufy.com/ https://github.com/Saasufy/skills curlコマンドは非常に人気があるから、モデルもそれを使うのが得意みたい。あと、curlはbash構文を使っていて、私のプラットフォームはJSONペイロードを必要とするから、エージェントにとって分離が明確になるのがいいね。すごく信頼性が高いと思う。

スキルもとても読みやすいから、いいドキュメントが無料でもらえる感じ。少なくとも、人間が読める機械指示になってるよ。

心配なのは失敗したコールじゃなくて、コール自体は正しかったんだ。ただ、いくつかの作り出したフィールドが間違っていただけ。それが実行時にモデルのインターフェースの一部のように感じさせるんだ。寛容な環境でモデルをトレーニングすると、他の実行時もその習慣を受け継いでしまう。

これは良いエラーメッセージで簡単に解決できる。Claudeはいつも私のツールコールの構文を間違えるから、革命的なことをして、エラー出力にツールを正しく呼び出すための役立つガイダンスを表示させるようにしたんだ。エージェントは再試行して、いつも正しくできる。無駄にした時間は1〜2秒。毎セッションで起こるけど、コンテキストウィンドウごとに一度だけ。あとはエージェントがその教訓を覚えてる。自分のツールコールでもこれをやるには、エージェントの立場になって考えてみて、間違いを修正するために必要な情報を想像してみて。エージェントは目標を達成したいから、再試行するだろう。これらは確率的なシステムだから、決定的な部分を正しくするために追加のループを与える必要があるんだ。

LSPとリンターは同じ目的を果たす。私は後者をgitフックで使ってるよ。

つまり、スキルはエージェントが学ぶにはあまり良いツールじゃないってこと?ツールを注入した後に、まだツールの試行錯誤が必要ってこと?(各ツールにはそれぞれのスキルがあると仮定してるけど。)

これだとLLMに余計な往復が発生するから、結局お金が余分にかかるよね。

jj vcsのエラーメッセージについて、この視点を広めようとしてるんだけど、LLMと上手く連携するツールを作ることが人間とも上手く連携することだって理解してない人たちから反発があるんだよね。(もちろん、これにはもっと複雑な議論があるけどね。)

リッチなフィードバックシステムを作るのが楽になるライブラリを作ったよ。これ見てみて: https://tool2agent.org/

Hacker Newsで議論の続きを見る