44
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

いつも使っている Claude Code の裏側を覗いてみた

44
Last updated at Posted at 2026-05-13

SapeetでSWEをやっている林です。
毎日 Claude Code を使っていると、「このリポジトリの差分を確認してほしい」と一言打つだけで、いつの間にかコミット内容がまとめられて返ってくる。便利すぎて当たり前になっているのですが、ふと「裏側で何が起きているんだろう?」と気になりました。

調べてみると、1回の発言に対して内部では 6回以上の API 呼び出し が走っていて、Claude 自身はツールを一切実行していないことがわかりました。この記事では、その仕組みを順を追って解説します。

この記事でわかること

  • なぜ Claude 自身はコマンドを実行できないのか
  • ツールの呼び出しがどういう仕組みで動いているか
  • なぜ1回の質問で複数のターンが必要になるのか
  • Claude がいつ「もう終わり」と判断しているのか
  • プロンプトキャッシュがなぜ効いてくるのか

まず claude-tap を入れると「見える」ようになる

説明に入る前、まず内部の動きを可視化するツールを紹介します。

claude-tap は、Claude Code と Anthropic API の間に入るローカルプロキシです。Claude Code が送っているリクエストとレスポンスをすべて傍受して、ブラウザのビューアーで確認できるようにしてくれます。

Claude Code → claude-tap(ローカルプロキシ) → Anthropic API
                    ↓
              .traces/ にログ保存
              ブラウザで確認可能

image.png

system prompt の全文・ツール定義・各ターンの messages・トークン数・キャッシュの状況など、通常は見えないものがすべて確認できます。

インストール

Python 3.11 以上が必要です。

# uv を使う場合(推奨)
uv tool install claude-tap

# pip を使う場合
pip install claude-tap

使い方

# Claude Code を claude-tap 経由で起動するだけ
claude-tap

# リアルタイムでブラウザに表示しながら起動
claude-tap --tap-live

claude-tap が Claude Code を自動で起動してくれます。セッション終了後、.traces/ ディレクトリにログが保存されます。

.traces/
└── 2026-05-10/
    ├── trace_134932.jsonl   # 完全なリクエスト/レスポンス(JSON Lines)
    └── trace_134932.log     # 人間が読みやすいサマリー

大前提:Claude はコマンドを「実行」できない

まず、大事な前提から。

Claude はテキストを受け取ってテキストを返す推論エンジンです。ファイルの読み書きも、シェルコマンドの実行も、Claude 自身には一切できません

では「Bash を実行して」という要求をどう処理しているのか。

答えは、「実行してほしいツールの名前と引数を JSON で出力するだけ」 です。実際に実行するのは Claude ではなく、ローカルで動いている Claude Code(クライアント) です。

ポイント: Claude は「何をすべきか」を判断して JSON を出力する係。Claude Code は「その JSON を受け取って実際に動かす」係。この分業がすべての出発点です。


ツール呼び出しの仕組み

リクエストの中に「使えるツールの一覧」が入っている

Claude Code が API にリクエストを送るとき、tools という形でツールの定義を一緒に渡しています。今回のセッションでは 30 個のツールが定義されていました(Bash、Read、Write、Edit など)。

Gemini_Generated_Image_wehq9vwehq9vwehq.png

⚠️ ここが重要: Claude は tool_use ブロックを出力した時点で応答を止めます。クライアントがツールを実行して結果を返すまで、Claude は次の推論を行いません。これが Turn が複数必要になる根本的な理由です。

実際のログを見てみると

image.png

Turn 3 の Claude レスポンス(.jsonl から抜粋)

{
  "content": [
    {
      "type": "text",
      "text": "リポジトリの差分を確認します。"
    },
    {
      "type": "tool_use",
      "id": "toolu_01TY2z...",
      "name": "Bash",
      "input": {
        "command": "git status && git diff HEAD"
      }
    }
  ],
  "stop_reason": "tool_use"
}

Turn 4 でクライアントが送る tool_result

{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01TY2z...",
      "content": "On branch main\nYour branch is ahead of 'origin/main' by 1 commit..."
    }
  ]
}

image.png

tool_use → ツール実行 → tool_result → 次の推論、この1サイクルが「1ターン」に相当します。


Claude はツール説明から「どのツールを使うか」を判断している

疑問に思った方もいると思います。Claude はなぜ「git status を使おう」と判断できるのか。

答えはシンプルで、毎回のリクエストにツールの説明文(description)が含まれているから です。Claude はその説明をテキストとして読んで判断しているだけです。特別な訓練や記憶があるわけではありません。

{
  "name": "Bash",
  "description": "Executes a given bash command and returns its output...(詳細な説明)",
  "input_schema": {
    "type": "object",
    "properties": {
      "command": {"type": "string"},
      "timeout": {"type": "number"}
    },
    "required": ["command"]
  }
}

image.png

💡 ポイント: description の質がそのままツール選択の精度に直結します。カスタムツールを作るときに説明文が重要な理由はここにあります。


なぜ Turn 4・5・6 がそれぞれ必要なのか

「差分を確認して」という一言に対して、なぜ 4 ターンも必要なのか。

理由は「前のツールの結果を見てから次のツールを選ぶ」ためです。Claude は1回の応答で実行結果を受け取ることができないので、確認 → 判断 → 確認 → 判断 のサイクルが必然的に発生します。

Turn 3: Claude が初回推論
        → tool_use: Bash("git status && git diff HEAD")
        ↓ Claude Code が実行・結果を取得

Turn 4: Claude が「origin/main より 1 コミット進んでいる」を読んで判断
        → tool_use: Bash("git log origin/main..HEAD --stat")
        ↓ Claude Code が実行・結果を取得

Turn 5: Claude がコミットハッシュを確認して判断
        → tool_use: Bash("git show HEAD")
        ↓ Claude Code が実行・結果を取得

Turn 6: Claude がコミット全内容を受け取って最終回答を生成
        → text: "現在の差分の概要です..."
        stop_reason: "end_turn" → ループ終了

Turn が進むごとに messages が積み上がっていきます。

Turn messages 数 追加されたもの
3 1 ユーザーの発言のみ
4 3 + Turn3 の assistant 応答 + git status 結果
5 5 + Turn4 の assistant 応答 + git log 結果
6 7 + Turn5 の assistant 応答 + git show 結果

ちなみに: Claude は1回の応答に複数の tool_use を含めることもできます(並列呼び出し)。ただし今回は「git log の結果に含まれるコミットハッシュを確認してから git show を呼ぶ」という順序の依存があったため、ターンが分かれています。


ループを終了させるのも Claude

「いつ終わるか」を決めているのは Claude Code ではなく、Claude 自身 です。

Claude のレスポンスには必ず stop_reason が含まれていて、Claude Code はその値を見てループを続けるか終わるかを判断しています。

stop_reason 意味 Claude Code の動作
"tool_use" まだツールを呼びたい ツールを実行して結果を返す → 次ターンへ
"end_turn" 回答が完成した ユーザーに表示してループ終了
# エージェントループの本質(擬似コード)
while True:
    response = client.messages.create(...)
    messages.append({"role": "assistant", "content": response.content})

    if response.stop_reason == "end_turn":
        break  # ← Claude が「終わり」と判断した

    tool_results = [execute(block) for block in response.content if block.type == "tool_use"]
    messages.append({"role": "user", "content": tool_results})

今回のセッションでは git show HEAD の結果でコミットの全内容が把握できたため、Claude が Turn 6 で end_turn を返してループが終わっています。


補足:client-side tools と server-side tools の違い

ここまで「Claude Code がローカルでツールを実行する」という話をしてきましたが、実はツールにはもう一種類あります。

今回使ってきた Bash や Read のように クライアントが実行するもの(client-side tools) と、Anthropic のサーバー側で処理される server-side tools です。Claude Code を使う分には意識する機会は少ないですが、API を直接触るようになると知っておくと便利です。

client-side tools(今回使ったもの)

[Claude] → tool_use を出力
[Claude Code] → ローカルで実行
[Claude Code] → tool_result を API に送信
[Claude] → 結果を受け取って次の推論

Claude Code の 30 ツール(Bash, Read, Write, Edit, WebFetch...)はすべてこちらです。

server-side tools(Anthropic が提供)

[Claude] → ツールを内部的に呼び出し
[Anthropic サーバー] → 実行して Claude に結果を返す
[Claude] → 結果を受け取って次の推論

クライアントは tool_result を用意する必要がなく、Claude が自律的に結果を得られます。

代表的な server-side tools

ツール 概要
web_search Web 検索を実行して結果を返す
Computer Use スクリーンショット・クリック・キーボード操作
Code execution Python をサンドボックスで実行(claude.ai Artifacts)

どちらを使うか

操作の種類 推奨
ローカルファイルの読み書き client-side(Read, Write, Edit)
シェルコマンドの実行 client-side(Bash)
Web の情報取得 server-side(web_search)または client-side(WebFetch)
スクリーン操作 server-side(Computer Use)

Claude Code のアーキテクチャ全体像

ここまでの内容をまとめると、Claude Code は次のような構造で動いています。
Gemini_Generated_Image_8irt6m8irt6m8irt.png

Claude(クラウド)が担うこと

  • ツールの description を読んで「何をどう呼ぶか」を判断する
  • tool_result を読んで次のアクションを決める
  • stop_reason: "end_turn" でループ終了を宣言する

Claude Code(ローカル)が担うこと

  • stop_reason を見てループを回し続ける
  • ツールを実際に実行する
  • 会話履歴とキャッシュを管理する
  • Haiku で軽量な事前処理(クォータ確認・タスク分類)を行う

💡 一言で言うと: Claude は「考える人」、Claude Code は「手と足を持つ実行者」です。


プロンプトキャッシュがターンをまたいで効いてくる

claude-tap のトークン数を見ると、Turn 3 以降で cache_read が増え続けているのが確認できます。

Turn cache_create cache_read 効果
3 27,529 0 初回:system prompt をキャッシュ
4 229 27,529 Turn3 のキャッシュを再利用
5 206 27,758 Turn3+4 のキャッシュを再利用
6 424 27,964 Turn3+4+5 のキャッシュを再利用

Turn 3 で CLAUDE.md を含む system prompt(27,529 トークン)を一度キャッシュしてしまえば、Turn 4〜6 はそれを cache_read で再利用できます。cache_read の料金は通常の 90% オフ なので、ターンが増えるほどコスト削減効果が大きくなります。


おわりに:すっきりした

claude-tap でログを眺めていたら、「なんとなく動いている」だったものが一気に解像度が上がりました。

一言打つたびに、Haiku がクォータを確認して、タスクを分類して、Sonnet が description を読んでツールを選んで、結果を解釈して、「もういい」と判断するまでループを回し続けている——そういう仕組みが裏で動いていたんですね。

特に「Claude はコマンドを実行していない」という事実は、使い始めた頃に知りたかったと思いました。Claude はあくまで「次に何をするかを考える係」で、実際に手を動かしているのは Claude Code 側。この分業を理解してからは、プロンプトの書き方やカスタムツールの設計に対する考え方も少し変わりました。

毎日お世話になっているツールの裏側を知ると、なんとなく愛着が増す気がします。みなさんもぜひ一度 claude-tap で覗いてみてください。

一緒に「裏側」を作りませんか

ここまで読んでいただきありがとうございました。
「自分もこういう仕組みを掘り下げたい」と思った方、Sapeet ではまさにそういったエンジニアを探しています。弊社について少しでも気になっていただけた方は、ぜひこちらも覗いてみてください。

44
8
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
44
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?