Claude Media
thinking display updatesで進捗更新だけをUIに出す方法

thinking display updatesで進捗更新だけをUIに出す方法

thinking.displayを"updates"にすると、ツール呼び出しの合間の進捗メモだけがテキストで返ります。描画ループ、ストリーミング、無言になる原因、催促の入れ方を示します。

thinking.displayを"updates"にすると、エージェントがツールを呼ぶ合間に書く「いま何を見つけて、次に何をするか」という一言だけがテキストで返ります。推論の本体は空のままです。エージェントUIに状況表示の行を出したいが、思考の要約までは見せたくない場合の設定です。

この記事は、その進捗メモを画面に出す側の実装を扱います。クライアントの描画ループ、ストリーミングでの拾い方、メモが出ないターンへの備え、催促の入れ方の順に進みます。displayの3つの値そのものの違いは、thinkingのdisplay: summarizedとはにあります。

進捗メモはどこに入って返るのか

進捗メモ(progress update)は、モデルがツール呼び出しの前に書く短いメモです。見つけたことと次の動作を、画面を見ている人に向けて書いたものです。推論としては書かれていません。

返り方には決まりがあります。

  • 推論とは別のthinkingブロックで返り、ブロックごとに独自のsignatureを持つ
  • 対応するtool_use(またはserver_tool_use)ブロックの直前に置かれる
  • 1回のツール呼び出しの前に付くのは最大1つで、モデルは省略できる
  • 対象モデルは、Claude Fable 5.1 / Mythos 5.1 / Opus 5.5 / Sonnet 5.5 / Fable 5

インターリーブ思考とは別物です。推論ブロックが間に挟まるかどうかに関係なく、進捗メモは現れます。1つの応答に両方が入ることもあります。

"updates"で返る応答の先頭は、次のような形です。最初のthinkingは推論で空のまま、2つ目が進捗メモです。

{
  "content": [
    { "type": "thinking", "thinking": "", "signature": "EqMBCkYICxIM..." },
    {
      "type": "thinking",
      "thinking": "Confirmed the retry path never refreshes the expired token. Editing auth.py to add the refresh call.",
      "signature": "Es8CCkYICxIM..."
    },
    { "type": "tool_use", "id": "toolu_01D7FLrfh4GYq7yT1ULFeyMV", "name": "edit_file", "input": { "path": "auth.py", "content": "..." } }
  ]
}

表示の判定が単純になるのが"updates"の利点です。"summarized"では推論も進捗メモも要約テキストで返るため、返ってきたブロックだけでは区別がつきません。"updates"なら、テキストが入ったthinkingブロックは全部進捗メモだと言い切れます。描画の条件は「thinkingが空でないこと」だけです。

リクエストの書き方

必要なのは、thinkingのdisplayと、ベータヘッダーthinking-display-updates-2026-08-18の2つです。

curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "anthropic-beta: thinking-display-updates-2026-08-18" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-fable-5-1",
    "max_tokens": 16000,
    "thinking": { "type": "adaptive", "display": "updates" },
    "messages": [
      { "role": "user", "content": "ログインのテストが1時間後に落ちる。原因を調べて直して。" }
    ]
  }'

displayはtype: "adaptive"とtype: "enabled"のどちらにも付けられます。ヘッダーを付けずに"updates"を送ると、未知のdisplay値と同じ400のinvalid_request_errorで拒否されます。Amazon Bedrock、Google Cloud、Microsoft Foundryでのベータ値の渡し方は、別ページの規則に従います。

displayが使えない組み合わせも押さえておきます。

設定結果
thinking.type: "disabled"結果displayは無効(表示するものがない)
thinking.type: "between_tools"結果displayを付けると400(Sonnet 5.5のみ)
adaptiveでモデルが考えないと判断結果displayに関係なくthinkingブロックが出ない

描画ループの組み方

非ストリーミングなら、応答のcontentを順に見るだけで済みます。thinkingブロックのうち中身が空でないものを、直後のtool_useの見出しとして出します。

def render_turn(content):
    """content は assistant の content ブロックの列(辞書形式)"""
    pending_note = None
    for block in content:
        if block["type"] == "thinking":
            if block["thinking"]:          # 空なら何も描かない
                pending_note = block["thinking"]
        elif block["type"] == "tool_use":
            show_status(pending_note or f"{block['name']} を実行中")
            pending_note = None
        elif block["type"] == "text":
            show_message(block["text"])

進捗メモが付かなかったツール呼び出しには、ツール名ベースの定型文を出しています。メモが省略されるのは正常動作なので、表示の穴にしないための保険です。

もう1つ、描画とは別に守ることがあります。進捗メモのブロックも、他のthinkingブロックと同じく、assistantのターンの一部として変更せずに送り返します。返ってくるthinkingのテキストは要約です。送り返したブロックは、モデルに要約でなく本来の全文を渡します。表示用に文面を整形してmessagesに書き戻すと、この前提が崩れます。表示用の文字列は別の変数に持つのが安全です。

ストリーミングで拾うときの要点

ストリーミングでは、進捗メモのブロックだけが、テキスト付きのthinking_deltaを流します。推論ブロックは"omitted"と同じく、空のthinking_deltaとsignature_deltaを1回流して閉じます。

進捗メモのイベント列は次の順です。

  1. content_block_start(type: "thinking")
  2. thinking_delta(テキスト入り)
  3. signature_delta
  4. content_block_stop
  5. 続けて、対応するtool_useのcontent_block_start

判定は「ブロックを開いた時点」ではなく、thinking_deltaに空でないテキストが載った時点で行います。開いた時点では、推論ブロックと進捗メモのブロックは同じ形だからです。

import anthropic
 
client = anthropic.Anthropic()
 
stream = client.beta.messages.create(
    model="claude-fable-5-1",
    max_tokens=16000,
    thinking={"type": "adaptive", "display": "updates"},
    betas=["thinking-display-updates-2026-08-18"],
    tools=TOOLS,
    messages=messages,
    stream=True,
)
 
notes = {}   # index -> 進捗メモの途中経過
for event in stream:
    if event.type == "content_block_delta" and event.delta.type == "thinking_delta":
        if event.delta.thinking:               # 空の delta は推論ブロック
            notes[event.index] = notes.get(event.index, "") + event.delta.thinking
            show_status(notes[event.index])

上のコードは、公式のイベント列に沿って組んだ描画の骨格です。実運用では、content_block_stopでメモを確定させて履歴に積む処理や、tool_useの入力JSONを組み立てる処理が別に要ります。

注意したいのは待ち時間です。進捗メモのブロックが開くまでに、数秒の間が空くことがあります。ストリームが止まったと判断してタイムアウトにしないでください。ドキュメントはこの間を正常としています。

無言になる原因を順に疑う

「進捗が一切出ない」という症状は、原因が3つに分かれます。上から確かめると早く切り分けられます。

手順

進捗が出ないときの確認順

  1. 1

    displayが既定のままになっていないか

    対象モデルの既定は"omitted"です。このとき進捗メモのブロックは存在しても空で返るので、textブロックだけを描画するクライアントは、長いエージェント実行の間ずっと無言に見えます。

  2. 2

    Opus 5からの移行コードではないか

    Opus 5では、ツール呼び出しの間の文章がtextブロックで返っていました。Opus 5.5とFable 5.1では、これがthinkingブロックに移っています。リクエストは失敗せず、画面だけが静かになります。

  3. 3

    プロンプトが語りを抑えていないか

    「結果は最終回答までまとめて出す」といった指示が、システムプロンプトに残っていないかを見ます。

3つ目は、displayを直しても解決しません。Fable 5.1は、Fable 5より長いツール呼び出しの間にユーザー向けの更新を書く回数が少なく、effortが高いほど、ツールの連鎖が長いほど顕著になります。ツール呼び出しが長く続くと、進捗メモは減ります。画面の設計は、メモが出ないターンがあっても崩れないようにしておきます。

進捗メモを増やす3つの手段

displayを直しても静かなときは、プロンプトとハーネスの側で調整します。Opus 5.5のプロンプトガイドは、段階的な手段を挙げています。

  1. 語りを抑える旧来の指示(「発見は最終回答まで保留する」など)を、まず消す
  2. 更新してほしい場所をシステムプロンプトで指定する
  3. ハーネスが、無言のステップを数えて催促する

2の例として、Fable 5.1のガイドは次の文を示しています。

Before you start, say in a line what you're about to do; brief updates while you work help the user follow along. Close with a short recap that stands on its own — what you found, what you did, and what's next — so a reader who only sees the last message has the full picture.

この文は、更新の場所と中身を決めています。ペアプログラミングのように人が関わる作業で、更新の効果が大きいとされています。

3の催促は、ハーネス側のカウンタで組みます。ツール呼び出しのたびに、textブロックも進捗メモのテキストも出なかったステップを数えます。連続して数回(例として5回)続いたら、直近のtool_resultの後ろに次の1文を追加します。

The user hasn't heard from you in a while — say in a few words what you're doing, then continue.

この1文は、ターン限定のsystemメッセージ(clear_at: "next_user_message")として送ります。ベータヘッダーはmid-conversation-system-clear-at-2026-08-21です。ドキュメントによると、催促を挿して後から消す方式ではなく、追記して残す方式なので、プロンプトキャッシュの一致と、後続のthinkingブロックの有効性が保たれます。Anthropicのエージェント的コーディングタスクでの検証では、長い無言区間があるタスクの割合が約半分になり、コストの測定可能な変化はなかったと説明されています。

催促には上限を設けます。それでも静かなターンには、2〜3回で催促をやめます。tool_resultの後ろにハーネスの文面を繰り返し差し込むと、モデルがプロンプトインジェクションを疑う場合があるためです。仕様の詳細はmid-conversation system messageの配置制約とclear_atにあります。

UI側でツール出力を折りたたむ場合は、そのことをモデルに伝えます。伝えないと、ユーザーに「見せる」ために、画面に出ない出力を再度コマンドで出しにいくことがあります。Fable 5.1のガイドは、ターン限定のsystemメッセージで「コマンド出力はあなたにしか見えない。ユーザーが読む必要があるなら返信に書く」と伝える形を示しています。

途中で打ち切られたターンの最後のブロック

max_tokens、model_context_window_exceeded、stop_sequenceのいずれかで、ツール呼び出しやtool_resultの直後に止まると、最後のブロックが進捗メモになることがあります。終えられなかった作業の代わりです。

このとき"updates"と"summarized"では、テキストがThis part of the response was interrupted before it finished.になります。"omitted"では空です。状況表示の行にそのまま出してかまいません。続けるときは、assistantのターンを変更せずに戻し、新しいuserメッセージを足します。そのメッセージには、ターン内の各tool_useブロックに対するtool_resultを入れます。

Sonnet 5.5のbetween_toolsとの違い

Sonnet 5.5には、thinking: {type: "between_tools"}という最低設定があります。最初の推論を止め、進捗メモだけをテキスト付きで返します。display: "updates"相当の結果になりますが、条件が異なります。

項目display: "updates"between_tools(Sonnet 5.5のみ)
ベータヘッダーdisplay: "updates"必要between_tools(Sonnet 5.5のみ)不要
推論ブロックdisplay: "updates"出るが空between_tools(Sonnet 5.5のみ)出ない(最初の推論を止める)
併用できるフィールドdisplay: "updates"typeと併用between_tools(Sonnet 5.5のみ)display・budget_tokens・block_bindingは400
effortdisplay: "updates"この項目に制限の記載なしbetween_tools(Sonnet 5.5のみ)high以下のみ(xhigh・maxは400)

Sonnet 5.5はthinkingが既定で有効で、disabledを送ると400で拒否されます。推論を止めつつ進捗だけを出したいなら、between_toolsが選択肢です。一方で、xhighやmaxのeffortを使うなら、between_toolsは使えません。"updates"のほうを選びます。

課金とトークンの扱い

進捗メモのテキストは要約で、通常は1〜2文です。長さには頼れません。usage.output_tokensには、要約でなく進捗メモ本来の長さが計上されます。画面に短い一行しか出ていなくても、請求されるトークン数はそれより大きくなります。なお"omitted"は、遅延を減らす設定で、思考トークンの課金は減りません。

実装時の確認項目

  • ベータヘッダーthinking-display-updates-2026-08-18を付けている
  • 描画条件は「thinkingが空でない」だけにしている
  • 進捗メモのブロックを、他のブロックと同様に変更せず履歴へ戻している
  • メモのないツール呼び出しにも、定型の状況表示を出す
  • ストリームの数秒の空白を異常扱いしていない
  • 無言ステップの催促に、上限回数を設けている

破壊的変更の全体像は、Opus 5.5がClaude Opus 5.5とは、Fable 5.1がClaude Fable 5.1とはにまとまっています。エージェントが途中で止まる問題は、Claude Opus 5.5のエージェントが途中で止まるのを防ぐ設定で別に扱っています。

この記事を共有:XはてブLinkedIn