ツール状態としての自然言語

2026-05-22#agent-runtime#tool-use#bash#self-prompting#tool-output

背景

この観点は Claude の bash コマンドスタイルに関するスクリーンショットから来ている。中核の主張は:

  • Claude は bash コマンドを生成するとき、まずコメントを一行書き、それから実際のコマンドを書くことが多い。
  • echo&& echo "success" || echo "failed" のような形で、exit code を自然言語に翻訳することも多い。
  • こうした一見余計なテキストは、実はモデルが最も消費しやすい状態表現としての自然言語を活用している。
  • agent の tool use を設計するとき、tool input に description / intent を先に持たせ、tool output に裸のデータだけでなく自然言語の状態を返させてよい。

検証の結論

この観点の方向は正しいが、三つの層に分けて見る必要がある。

第一層:bash のコメントは確かに shell には無視されるが、モデルが既に生成したコンテキストには残る。GNU Bash マニュアルは、非対話 shell では # で始まる語がその語と行の残りの文字を無視させると説明している。つまりモデルが先にコメントを一行生成してからコマンドを生成すると、コメントは shell の実行を変えないが、モデル自身の生成軌跡の中で意図を先に固定する。

第二層:自然言語の中間状態がモデルの行動を助けることには、論文とエンジニアリングドキュメントの裏付けがある。Chain-of-Thought は中間推論ステップが複雑な推論を改善することを示し、ReAct はさらに reasoning trace と action を交互に織り込み、推論軌跡がモデルの計画の追跡・更新と例外処理を助けるようにした。「まず一文の自然言語で何をするかを言い切る」ことが、その後の tool call をしばしば安定させる理由はこれで説明できる。

第三層:tool description と tool output の自然言語の品質は、実際にモデルの性能に影響する。Anthropic のドキュメントは、ツールの description はツールが何をするか・いつ使うか・パラメータの意味と制限を説明すべきだと明言し、詳細な description が tool performance を左右する重要な要素だとしている。OpenAI の function calling ドキュメントも、ツール定義はモデルのコンテキストに注入されること、function output は通常文字列で返され、形式は JSON・error code・plain text のいずれでもよく、モデルが必要に応じて解釈することを説明している。

したがって、より正確な言い方は「自然言語は構造化データより高級」ではなく:

LLM にとって、構造化された状態は実行可能性を担い、自然言語の状態は説明可能性と次の行動の事前分布を担う。

訂正が必要だった点

スクリーンショットには OpenCode の bash tool に description パラメータがあるとあった。この例は訂正が要る。

opencode-ai/opencode の現行ソース(2026-05-22、commit 73ee493)と README を確認した:組み込み bash tool のパラメータは commandtimeout で、呼び出しごとの独立した description フィールドはない。ただし非常に長いツールレベルの description があり、command パラメータ自体にも description があり、さらにシステムプロンプトが、非自明な bash コマンドを実行する前にそのコマンドが何をするか・なぜ実行するかを説明するようモデルに求めている。

つまり OpenCode の実際の設計はこれに近い:

  • ツールの schema / tool description に意味情報を前もって詰め込む。
  • assistant の本文で非自明なコマンドの目的をモデルに説明させる。
  • bash tool の input 自体は command を主に保つ。

自分たちで custom bash tool を設計するなら description / intent フィールドを加えてよいが、それを現行の OpenCode 組み込み bash tool の既存事実として語ってはいけない。

設計の提案

Tool input

高リスク・長時間実行・システム状態を変更しうるツールには、tool input に明示的な intent または description フィールドを入れることを勧める:

{
  "intent": "检查 dev server 是否被 3000 端口占用,并找出占用进程",
  "command": "lsof -i :3000"
}

このフィールドは shell に見せるためのものではない。モデルに command を生成する前へ、行動の意図を一文の安定した自然言語へ圧縮させるためのものだ。tool-call レベルのミニ ReAct thought に相当するが、runtime による記録・監査・承認により適している。

Tool output

これだけを返してはいけない:

{"exit_code": 1}

より良い返り値は、構造化された状態に自然言語の要約を添えたものだ:

{
  "status": "failed",
  "exit_code": 1,
  "summary": "部署失败:3000 端口已被 nginx(pid 8432) 占用。",
  "suggested_next_steps": ["改用其他端口", "确认后停止占用进程"]
}

こうすれば runtime は引き続き status / exit_code で決定論的な制御ができ、モデルは summarysuggested_next_steps から次の行動の高確率パスを直接得られる。

bash echo パターンの罠

cmd && echo "success" || echo "failed" はモデルには優しいが、runtime に優しいとは限らない。最後に実行されるのは echo なので、shell コマンド全体の最終 exit code が 0 になり、元のコマンドの失敗を覆い隠しうる。

より堅実なのは、tool wrapper が元の exit code を捕捉した上で、同時に返すことだ:

  • 元の stdout / stderr
  • 元の exit_code
  • 正規化された status
  • モデル向けの自然言語 summary
  • 任意の suggested_next_steps

つまり、echo に状態機械の代わりをさせてはいけない。wrapper に、機械の状態を人間もモデルも読める状態へ翻訳させるのだ。

発展させうる記事の切り口

  • タイトル方向:Agent の Tool Output が exit_code だけを返すべきでない理由
  • 主線の問い:ツールの結果は機械の状態なのか、それともモデルが生成を続けるためのコンテキストなのか?
  • 鍵となる対比:裸のステータスコード・自然言語要約・構造化 diagnosis はそれぞれ誰に仕えるのか
  • 既存の方向と接続:tool call role design、Trace Log、multi-agent veto boundary
  • 結びの着地点:良い tool result は、プログラムが判定でき、人間が監査でき、モデルが行動を続けられる——この三つを同時に満たすべきだ

参考文献