Claude Media
Claude Fable 5のsend_to_userツールで非同期タスクの進捗を届ける

Claude Fable 5のsend_to_userツールで非同期タスクの進捗を届ける

Claude Fable 5の非同期エージェントに中間報告を届けるsend_to_userツールの設計を、JSON定義・システムプロンプト・ツール結果の返し方・reasoning_extractionの落とし穴まで解説します。

Claude Fable 5にsend_to_userツールが必要になる場面

Claude Fable 5は長時間・非同期のタスクに強いモデルですが、ターンを終えるまでユーザーは基本的にモデルの出力を見られません。数十分から数時間かかるエージェントの途中で、生成済みのコードスニペットや下書きメッセージ、実行中にユーザーが投げた質問への直接回答を、そのまま届けたい場面が出てきます。こうした逐語のまま見せる必要がある内容を、ターンを終えずに表示する仕組みがsend_to_userツールです。

進捗のナレーション用ではなく、ユーザーがその文言のまま読む必要があるコンテンツ専用の窓口です。ツールの入力は表示するメッセージそのもので、Claudeが呼んだらUI側が入力をそのまま描画し、ツール結果として簡単な受領確認を返します。ツール入力は要約されないため、内容は欠けも丸めもなく届きます。

手順

send_to_userを動かすまでの3手順

  1. 1

    ツールを定義する

    message文字列を1つだけ受け取るJSON定義をリクエストのtoolsに加えます。

  2. 2

    システムプロンプトで呼ぶ場面を指示する

    定義だけではほとんど呼ばれません。「ツール呼び出しの合間に、ユーザーが逐語で読む内容があれば呼ぶ」と書いて初めて動きます。

  3. 3

    UIに描画して受領確認を返す

    ハーネス側でmessageをそのまま表示し、tool_resultに短い確認文を入れて次のターンへ進めます。

ツール定義のJSONスキーマ(JSON tool schema)

プロンプティングガイドに載っているツール定義は次のとおりです。

{
  "name": "send_to_user",
  "description": "Display a message directly to the user. Use this for progress updates, partial results, or content the user must see exactly as written before the task finishes.",
  "input_schema": {
    "type": "object",
    "properties": {
      "message": {
        "type": "string",
        "description": "The content to display to the user."
      }
    },
    "required": ["message"]
  }
}

messageという文字列プロパティが1つだけの、小さな定義です。Claudeが呼び出すたびに、その中身をアプリのUIにそのまま出す実装をハーネス側に書きます。

このツールを足す基準は、ユーザー体験が「タスク完了前の逐語コンテンツ配信」や「タスク途中のユーザーとの直接のやり取り」に依存するかどうかです。定型的な進捗報告しかしないエージェントなら、モデル自身の要約で通常は足ります。

ツール定義だけでは呼ばれない — システムプロンプトでの誘導が必須

実装でつまずきやすい点がここです。ツールを定義しただけでは、Claude Fable 5はほとんど呼びません。システムプロンプトに明示的な指示が要ります。プロンプティングガイドが挙げる誘導文言の趣旨は次のとおりです。

誘導文言(原文の要約)

ツール呼び出しの合間で、ユーザーが逐語で読む必要のあるコンテンツ(部分的な成果物、ユーザーの質問への直接回答)を持っているときは、その内容でsend_to_userツールを呼ぶ。send_to_userはユーザー向けコンテンツにのみ使い、ナレーションや推論の出力には使わない。

文言には2つの役割があります。「ツール呼び出しの合間」というタイミングの明示と、ナレーションや内部の推論をこのツールに流さないという用途の限定です。ガイドは、ユーザー向けでない内容で多用するとツールを用意した意味が失われる、とも述べています。

使いどころと使いすぎの境界線

線引きは「要約されると困る情報かどうか」です。

状況send_to_userの要否理由
数値入りの途中経過を伝えるsend_to_userの要否必要理由具体的な数値を含む進捗は、ガイドが挙げる用途の1つ
実行中にユーザーが投げた質問への直接回答send_to_userの要否必要理由回答がそのままの文言で届く必要がある
生成済みコードや下書きメッセージの中間共有send_to_userの要否必要理由成果物は要約されると内容が欠ける
「〜を実行中です」という定型のナレーションsend_to_userの要否不要理由モデル自身の要約で通常は足りる
内部の推論過程の逐次表示send_to_userの要否使わない理由用途の対象外で、次節のとおり拒否の原因にもなる

推論の書き起こしを求めると、フォールバックされず拒否が返る

モデルの内部推論をそのまま応答にエコーさせる、書き起こさせる、説明させる指示は、システムプロンプトにも、スキルにも、ハーネスの指示にも入れないでください。Claude Fable 5ではreasoning_extractionという拒否カテゴリに当たることがあります。推論の見せ方でも、send_to_userは進捗を表に出す代替経路になります。

この拒否は、Claude Opus 4.8への自動フォールバックでは救われません。ガイドによると、サーバー側フォールバック(fallbacks: "default")はreasoning_extractionで拒否されたリクエストを再試行せず、拒否がそのままアプリに返ります。拒否・フォールバックのページも、このカテゴリには推奨のフォールバック先モデルがないとして、リクエストを再試行せずプロンプトを直すよう求めています。出力が出る前の拒否でも課金される(bio・frontier_llmと並ぶ3カテゴリ)点は、コストの面で見逃せません。

くらべる

推論の見せ方、どちらを選ぶか

拒否の原因

避ける書き方

「推論過程をそのまま出力に書き写して」という指示や、<thinking>・<reasoning>のスクラッチパッド欄、JSON出力やツール入力のreasoning・thinking・traceフィールドが該当します。ツールの説明文に書いた場合も対象です。

正しい経路

代わりにやること

推論の見え方が必要なら、display: "summarized"にして構造化されたthinkingブロックを読みます。長時間の進捗はsend_to_userで表に出します。回答の短い説明や実行した操作の要約を頼むのは問題ありません。

send_to_userを自作するときは、messageのほかに「理由」「思考メモ」を入れるフィールドを足さないのが安全です。拒否ページの例にreasoning・thinking・traceフィールドがツール入力にある場合が挙がっているためで、既存のスキルや説明文を移行するときも、同じ語句がないかを洗い出す価値があります。

description欄はどこまで詳しく書くか

ツール定義のdescriptionの書き方は、カスタムツール全般の指針として別に示されています。ツール設計のページは、詳細な説明文を「ツール性能に最も影響する要素」とし、何をするか、いつ使うか、いつ使わないか、注意点まで書いて、少なくとも3〜4文を目安にするよう求めています。

前掲のJSON定義のdescriptionは1〜2文で簡潔です。そのままでも動きますが、複数のツールを併用するハーネスなら、通常の応答テキストとの使い分けをdescription側にも書いておくと、呼ぶ場面の判断がツール定義だけで読み取れます。

"description": "Display a message directly to the user, verbatim, without ending your turn. Use this for a partial deliverable, a progress update with concrete numbers, or a direct answer to a question the user asked mid-task. Do not use this for routine narration of what you are about to do next; your normal response text already covers that."

この書き足しは、ガイドの例を上のツール設計指針に沿って厚くした一案です。公式のJSON定義そのものではありません。

ツール結果(tool result)の返し方と設計

ツールの応答は、次の判断に必要な高シグナルの情報だけにする、というのも同じ設計ページの指針です。send_to_userの結果を短い受領確認にとどめるのは、この指針と合っています。表示はUI側の責務で、モデルに返す結果を膨らませても呼び出しの精度は上がりません。

ハーネスのループでは、tool_useブロックのidをtool_resultのtool_use_idに入れて返します。次のスケッチは、send_to_userだけを特別扱いしてUIに流す形です(MODELとrender_in_uiは各自のものに置き換えます)。

while True:
    resp = client.messages.create(
        model=MODEL, max_tokens=4096, tools=TOOLS, system=SYSTEM, messages=messages
    )
    messages.append({"role": "assistant", "content": resp.content})
    if resp.stop_reason != "tool_use":
        break
    results = []
    for block in resp.content:
        if block.type == "tool_use" and block.name == "send_to_user":
            render_in_ui(block.input["message"])  # 逐語のまま描画する
            results.append({
                "type": "tool_result",
                "tool_use_id": block.id,
                "content": "Displayed to the user.",
            })
        elif block.type == "tool_use":
            results.append(run_other_tool(block))  # 通常のツールの実行結果
    messages.append({"role": "user", "content": results})

同じ設計ページには、create_pr・review_pr・merge_prのような関連操作を、actionパラメーターを持つ1つのツールにまとめる指針もあります。入力がmessage1つのsend_to_userは、この指針の想定する「複数操作の統合」とは別の話です。役割が「ユーザーへの逐語表示」の1つだけなので、システムプロンプトで呼ぶ場面を具体的に書きやすい形でもあります。

Managed Agentsで使うときのイベントの流れ

Claude Managed Agentsでは、send_to_userはカスタムツールとして定義します。カスタムツールはMessages APIのユーザー定義クライアントツールに相当し、モデルは実行を要求するだけで、実際の処理は自分のコードが担います。

呼ばれると、セッションはagent.custom_tool_useイベントを出し、session.status_idle(stop_reason: requires_action)で一時停止します。UIにmessageを描画したら、custom_tool_use_idに元のイベントIDを入れたuser.custom_tool_resultを返します。ブロックしているイベントがすべて解決されると、セッションはrunningに戻ります。

つまり描画のあとに結果を返さない限り、エージェントは先へ進みません。UIの描画に失敗しても結果は必ず返す実装にしておくと、画面側の不具合でエージェントが止まる事態を避けられます。

進捗報告の中身を裏付けと結びつける

send_to_userで届ける進捗は、中身の正確さが前提です。長時間の自律実行では、実際には終わっていない作業を完了と報告する虚偽の進捗が起こりえます。ガイドは、報告の前に各主張をそのセッション内のツール実行結果と突き合わせるよう指示することを勧め、Anthropicのテストではこれで虚偽の状況報告がほぼなくなったと述べています。テストが失敗していれば出力とともに失敗と報告し、未検証の項目は未検証と明示させる運用です。数値入りの進捗を届ける設計なら、この指示を同じシステムプロンプトに入れておくと、届く内容の信頼性が上がります。

作業が終わったあとの最終まとめにも配慮が要る

send_to_userは完了前の中間報告用ですが、プロンプティングガイドの隣の節は、長い非同期セッションを終えた最終まとめを扱っています。ツール呼び出しを多数重ねると、Claude Fable 5は矢印つなぎの省略表記や、ユーザーが見ていない思考への言及、専門的すぎる言い回しを含む、追いにくい文章を書くことがあります。

夜間や長時間、会話を見ていなかったユーザーにとって、最終メッセージはそのタスクの最初の接点です。ガイドの追補プロンプトは、これを作業の続きではなく仕切り直しとして書かせます。結果から書き始め、完全な文で、作業中に作った略語や矢印つなぎを使わず、ファイル名・コミット・フラグといった識別子にはそれぞれ平易な説明を添えます。

作業途中の省略表現のままmessageに詰め込むと、逐語で表示される分だけ読みにくさがユーザーに届きます。中間報告でも、ユーザーが初めて読む前提で完結した文にしておくのが安全です。

並列サブエージェントとの組み合わせ

Claude Fable 5は並列のサブエージェントへの委譲を、従来のモデルより積極的に行います。長時間の実行では、新しいコンテキストで動く別のサブエージェントに検証を任せるほうが、自己批評より成績が良い傾向があるともされています。send_to_userは、こうした裏側の並列処理や自己検証をユーザーに見せないまま、届けるべき情報だけを表に出す役割を担います。

Claude Agent SDKでカスタムツールを定義する具体的な手順はAgent SDKカスタムツールの作り方にあります。サブエージェントへの委譲を制御する仕組みはAgent SDKのサブエージェント上限で扱っています。

まとめ

send_to_userは、要約されると困る内容(数値入りの進捗・直接回答・成果物)だけを逐語で届ける専用の窓口にします。定義だけでは呼ばれないので、呼ぶ場面と用途の限定をシステムプロンプトに書くこと、そしてmessage以外に推論を書かせるフィールドや指示を足さないことが、設計の勘所です。

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