<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Streaming on KbWen Blog</title>
    <link>https://www.kbwen.com/tags/streaming/</link>
    <description>KbWen is a practical technology blog about AI systems, machine learning, Python, data engineering, and software development.</description>
    <generator>Hugo</generator>
    <language>zh-tw</language>
    <image>
      <url>https://www.kbwen.com/images/og-default.png</url>
      <title>KbWen Blog</title>
      <link>https://www.kbwen.com/</link>
    </image>
    
    <lastBuildDate>Mon, 05 Oct 2026 09:55:00 +0800</lastBuildDate><atom:link href="https://www.kbwen.com/tags/streaming/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>How tool call arguments stream in the Claude API</title>
      <link>https://www.kbwen.com/claude-api-streaming-tool-call-arguments/</link>
      <pubDate>Mon, 05 Oct 2026 09:55:00 +0800</pubDate><dc:creator>KbWen</dc:creator>
      <guid>https://www.kbwen.com/claude-api-streaming-tool-call-arguments/</guid>
      <description>A look at the input_json_delta events that carry a tool call&amp;#39;s arguments in a streamed Claude API response, and what changes when a tool streams them eagerly.</description>
      <content:encoded><![CDATA[<blockquote>
<p><strong>TL;DR:</strong> Keep one string per tool-use block, append each <code>partial_json</code> to it, and parse it at <code>content_block_stop</code>, ready for that parse to fail, because a <code>max_tokens</code> stop can cut a parameter off in either mode. With <code>eager_input_streaming</code> on, the input also arrives unchecked, so it may not be valid JSON.</p>
</blockquote>
<p>When a streamed response from the Claude API calls a tool, the tool&rsquo;s arguments arrive as a run of <code>input_json_delta</code> events, each carrying a piece of a JSON string. The <a href="https://platform.claude.com/docs/en/build-with-claude/streaming">streaming documentation</a> shows one such call in full, for a <code>get_weather</code> tool asked about San Francisco. This is the tool-use block from that example, start to stop:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">event: content_block_start
</span></span><span class="line"><span class="cl">data: {&#34;type&#34;:&#34;content_block_start&#34;,&#34;index&#34;:1,&#34;content_block&#34;:{&#34;type&#34;:&#34;tool_use&#34;,&#34;id&#34;:&#34;toolu_01T1x1fJ34qAmk2tNTrN7Up6&#34;,&#34;name&#34;:&#34;get_weather&#34;,&#34;input&#34;:{}}}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">event: content_block_delta
</span></span><span class="line"><span class="cl">data: {&#34;type&#34;:&#34;content_block_delta&#34;,&#34;index&#34;:1,&#34;delta&#34;:{&#34;type&#34;:&#34;input_json_delta&#34;,&#34;partial_json&#34;:&#34;&#34;}}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">event: content_block_delta
</span></span><span class="line"><span class="cl">data: {&#34;type&#34;:&#34;content_block_delta&#34;,&#34;index&#34;:1,&#34;delta&#34;:{&#34;type&#34;:&#34;input_json_delta&#34;,&#34;partial_json&#34;:&#34;{\&#34;location\&#34;:&#34;}}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">event: content_block_delta
</span></span><span class="line"><span class="cl">data: {&#34;type&#34;:&#34;content_block_delta&#34;,&#34;index&#34;:1,&#34;delta&#34;:{&#34;type&#34;:&#34;input_json_delta&#34;,&#34;partial_json&#34;:&#34; \&#34;San&#34;}}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">event: content_block_delta
</span></span><span class="line"><span class="cl">data: {&#34;type&#34;:&#34;content_block_delta&#34;,&#34;index&#34;:1,&#34;delta&#34;:{&#34;type&#34;:&#34;input_json_delta&#34;,&#34;partial_json&#34;:&#34; Francisc&#34;}}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">event: content_block_delta
</span></span><span class="line"><span class="cl">data: {&#34;type&#34;:&#34;content_block_delta&#34;,&#34;index&#34;:1,&#34;delta&#34;:{&#34;type&#34;:&#34;input_json_delta&#34;,&#34;partial_json&#34;:&#34;o,&#34;}}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">event: content_block_delta
</span></span><span class="line"><span class="cl">data: {&#34;type&#34;:&#34;content_block_delta&#34;,&#34;index&#34;:1,&#34;delta&#34;:{&#34;type&#34;:&#34;input_json_delta&#34;,&#34;partial_json&#34;:&#34; CA\&#34;}&#34;}}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">event: content_block_stop
</span></span><span class="line"><span class="cl">data: {&#34;type&#34;:&#34;content_block_stop&#34;,&#34;index&#34;:1}
</span></span></code></pre></div><p>Appended in order, the pieces in that block make <code>{&quot;location&quot;:</code> after the second delta and <code>{&quot;location&quot;: &quot;San Francisc</code> after the fourth, and nothing parses as JSON until the last fragment closes the quote and the brace. That is why the parse belongs at <code>content_block_stop</code>, for this call and for every other tool call.</p>
<p>Current models, the streaming page says, emit one complete key and value from the input at a time, so there can be delays between events while the model works. Once a key and value are complete, they go out as several deltas of chunked partial JSON, &ldquo;so that the format can automatically support finer granularity in future models.&rdquo; So for a tool that doesn&rsquo;t set the <code>eager_input_streaming</code> flag, a fragment arriving is not a sign of how far the model has got through the value. A client that prints each fragment the moment it arrives is printing pieces of a value that is already done.</p>
<h2 id="turning-on-eager_input_streaming">Turning on eager_input_streaming</h2>
<p>Without the flag, the <a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/fine-grained-tool-streaming">fine-grained tool streaming page</a> says, the API buffers and validates each parameter value before streaming it back, so nothing prints for a large parameter until Claude has finished generating it. Its example is a <code>make_file</code> tool asked to write a long poem to <code>poem.txt</code>, whose <code>lines_of_text</code> parameter is an array of lines.</p>
<p>Setting <code>&quot;eager_input_streaming&quot;: true</code> on a user-defined tool turns that buffering off. One thing to remember is that the request itself needs streaming turned on as well. Fragments start arriving as soon as Claude begins the parameter, which is how the poem can fill a terminal while it is being written. The page says these fragments are typically longer, too, with fewer breaks in the middle of a word. The <code>get_weather</code> trace above comes from a tool that sets no flag, and it has one of those breaks: <code> Francisc</code> and <code>o,</code> arrive as two separate fragments.</p>
<p>Turning the flag on also means the API no longer checks a tool&rsquo;s input before sending it, so the accumulated string may be partial or invalid JSON. The events themselves don&rsquo;t change, though: they are the same <code>input_json_delta</code> events, and a client appends them the same way, so the code that collects them can stay as it is.</p>
<h2 id="when-the-input-doesnt-parse">When the input doesn&rsquo;t parse</h2>
<p>A response can stop at <code>max_tokens</code> in the middle of a parameter, with the flag or without it. The string then ends partway through, much like the halfway points in the trace above, and it won&rsquo;t parse, so the parse at the stop event should be ready to fail in both modes. The stop reason shows when that has happened, so it&rsquo;s worth checking before deciding what to do next: retry the request with a higher <code>max_tokens</code>, or repair the partial input. With the flag on, the input can also fail to parse because nothing checked it. The tool can&rsquo;t run on that, and the fine-grained page describes how to report it back to Claude as an error result.</p>
<p>The flag is set per tool, so it can be on for a tool like <code>make_file</code> that writes out long text and off for a tool like <code>get_weather</code> that takes one short string. This is only one small part of the API, the moment when a tool&rsquo;s arguments come in as pieces, and if you have handled those pieces differently in your own client, I&rsquo;d be glad to hear how.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Claude API 串流時的工具呼叫與不完整的 JSON</title>
      <link>https://www.kbwen.com/claude-api-streaming-tool-call-partial-json/</link>
      <pubDate>Mon, 05 Oct 2026 09:50:00 +0800</pubDate><dc:creator>KbWen</dc:creator>
      <guid>https://www.kbwen.com/claude-api-streaming-tool-call-partial-json/</guid>
      <description>用 Claude API 開串流的時候，參數會是一段段字串。這篇簡單的看這些片段長什麼樣子，以及程式該怎麼接手處理。</description>
      <content:encoded><![CDATA[<p>用 Claude API 的時候把串流打開（<code>&quot;stream&quot;: true</code>），回覆會變成一連串 server-sent events，文字會一小段一小段送過來，可以邊收取並同時印出文字。Claude 決定呼叫工具的時候，工具的參數也是用同樣的方式分段送來，只是這些片段是 JSON 的一部分，處理方式不太一樣。</p>
<p><a href="https://platform.claude.com/docs/en/build-with-claude/streaming">官方的串流文件</a>裡有個查天氣的例子：使用者問舊金山的天氣，Claude 先回了一句話（index 0 的文字區塊），接著開了 index 1 的工具區塊，呼叫 <code>get_weather</code>。下面是事件：</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">event: content_block_start
</span></span><span class="line"><span class="cl">data: {&#34;type&#34;:&#34;content_block_start&#34;,&#34;index&#34;:1,&#34;content_block&#34;:{&#34;type&#34;:&#34;tool_use&#34;,&#34;id&#34;:&#34;toolu_01T1x1fJ34qAmk2tNTrN7Up6&#34;,&#34;name&#34;:&#34;get_weather&#34;,&#34;input&#34;:{}}}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">event: content_block_delta
</span></span><span class="line"><span class="cl">data: {&#34;type&#34;:&#34;content_block_delta&#34;,&#34;index&#34;:1,&#34;delta&#34;:{&#34;type&#34;:&#34;input_json_delta&#34;,&#34;partial_json&#34;:&#34;&#34;}}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">event: content_block_delta
</span></span><span class="line"><span class="cl">data: {&#34;type&#34;:&#34;content_block_delta&#34;,&#34;index&#34;:1,&#34;delta&#34;:{&#34;type&#34;:&#34;input_json_delta&#34;,&#34;partial_json&#34;:&#34;{\&#34;location\&#34;:&#34;}}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">event: content_block_delta
</span></span><span class="line"><span class="cl">data: {&#34;type&#34;:&#34;content_block_delta&#34;,&#34;index&#34;:1,&#34;delta&#34;:{&#34;type&#34;:&#34;input_json_delta&#34;,&#34;partial_json&#34;:&#34; \&#34;San&#34;}}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">event: content_block_delta
</span></span><span class="line"><span class="cl">data: {&#34;type&#34;:&#34;content_block_delta&#34;,&#34;index&#34;:1,&#34;delta&#34;:{&#34;type&#34;:&#34;input_json_delta&#34;,&#34;partial_json&#34;:&#34; Francisc&#34;}}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">event: content_block_delta
</span></span><span class="line"><span class="cl">data: {&#34;type&#34;:&#34;content_block_delta&#34;,&#34;index&#34;:1,&#34;delta&#34;:{&#34;type&#34;:&#34;input_json_delta&#34;,&#34;partial_json&#34;:&#34;o,&#34;}}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">event: content_block_delta
</span></span><span class="line"><span class="cl">data: {&#34;type&#34;:&#34;content_block_delta&#34;,&#34;index&#34;:1,&#34;delta&#34;:{&#34;type&#34;:&#34;input_json_delta&#34;,&#34;partial_json&#34;:&#34; CA\&#34;}&#34;}}
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">event: content_block_stop
</span></span><span class="line"><span class="cl">data: {&#34;type&#34;:&#34;content_block_stop&#34;,&#34;index&#34;:1}
</span></span></code></pre></div><p>仔細看這六段字串，第一段是空的，第二段是 <code>{&quot;location&quot;:</code>，只有左大括號和一個 key；<code> &quot;San</code> 開了引號沒有關，<code> Francisc</code> 則斷在單字的中間。每段都不能單獨拿去給 JSON 解析器，那肯定會錯，因為它們只是同一串文字切開後的段落。要等到 <code> CA&quot;}</code> 送來，頭尾符號都有了，接著 <code>content_block_stop</code> 表示這個區塊結束；這時把六段照順序接起來，才會是 <code>{&quot;location&quot;: &quot;San Francisco, CA&quot;}</code>。</p>
<p>所以程式在收參數的時候，要做的事情其實很簡單。<a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/fine-grained-tool-streaming">fine-grained tool streaming 文件</a>把它寫成三步：收到 tool_use 的 <code>content_block_start</code> 時準備一個空字串，每個 <code>input_json_delta</code> 來就把 <code>partial_json</code> 接到後面，等到 <code>content_block_stop</code> 再解析，解析要包在 try 裡。如果用的是 Python、TypeScript 或 Go 的 SDK，裡面已經有 helper 會把片段接好；但如果是直接處理事件，或是想自己決定怎麼處理的時候，才需要照這三步驟。</p>
<h2 id="eager_input_streaming">eager_input_streaming</h2>
<p>查天氣的參數只有一個城市名，很短也很快就送完了。如果參數很長，像是整份文件或整段程式碼，預設的做法就會讓人等比較久：API 會先把每個參數值緩衝起來、驗證過之後才送出。</p>
<p>在自己定義的工具上把 <code>eager_input_streaming</code> 設成 <code>true</code>，請求本身也記得要設成串流，這個參數就不經過伺服器端的緩衝跟 JSON 驗證，Claude 開始的同時，片段也就跟著跑出來。fine-grained tool streaming 文件的範例就打開了這個設定，片段一到就印出來，讓人看到參數寫到哪裡；不過要知道此時印出來的是還沒接完的字串，還不能當參數用。接的方式跟前面一樣，但是伺服器沒有先驗證，所以到了 <code>content_block_stop</code>，接完的字串不保證是合法的 JSON。</p>
<p>因此，我們在使用時，要不要替某個工具打開 <code>eager_input_streaming</code>，可以確認情境，有時候很長再空等待那也許是個可以考慮的方向。
總之這篇提供個 Claude 的不同小用法，有任何討論和想法歡迎提供。</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
