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回流して閉じます。
進捗メモのイベント列は次の順です。
content_block_start(type: "thinking")thinking_delta(テキスト入り)signature_deltacontent_block_stop- 続けて、対応する
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
displayが既定のままになっていないか
対象モデルの既定は
"omitted"です。このとき進捗メモのブロックは存在しても空で返るので、textブロックだけを描画するクライアントは、長いエージェント実行の間ずっと無言に見えます。 - 2
Opus 5からの移行コードではないか
Opus 5では、ツール呼び出しの間の文章が
textブロックで返っていました。Opus 5.5とFable 5.1では、これがthinkingブロックに移っています。リクエストは失敗せず、画面だけが静かになります。 - 3
プロンプトが語りを抑えていないか
「結果は最終回答までまとめて出す」といった指示が、システムプロンプトに残っていないかを見ます。
3つ目は、displayを直しても解決しません。Fable 5.1は、Fable 5より長いツール呼び出しの間にユーザー向けの更新を書く回数が少なく、effortが高いほど、ツールの連鎖が長いほど顕著になります。ツール呼び出しが長く続くと、進捗メモは減ります。画面の設計は、メモが出ないターンがあっても崩れないようにしておきます。
進捗メモを増やす3つの手段
displayを直しても静かなときは、プロンプトとハーネスの側で調整します。Opus 5.5のプロンプトガイドは、段階的な手段を挙げています。
- 語りを抑える旧来の指示(「発見は最終回答まで保留する」など)を、まず消す
- 更新してほしい場所をシステムプロンプトで指定する
- ハーネスが、無言のステップを数えて催促する
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 |
| effort | display: "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のエージェントが途中で止まるのを防ぐ設定で別に扱っています。