Streaming, Tool Use, and Structured Output

AI Navigate Original / 3/24/2026

💬 OpinionDeveloper Stack & Infrastructure
共有:

Key Points

  • SSE streaming improves perceived speed for chat/long-text UIs; use final confirmed text (not fragments) for DB/audit, and keep strict structured output out of streaming.
  • Tool Use: control on the app side which tool and how far it's allowed; add confirmation for side effects, strict JSON Schema args, and separate search/reference/update tools.
  • JSON structured output: fix the schema, demand "JSON only," keep temperature 0–0.2, validate app-side, and separate explanation vs. structured APIs.
  • Prompt Caching cuts cost for long fixed prompts; Batch API suits large async jobs. Adopt in order: structured output → streaming → tool use → caching/batch.

Mastering "Production-Like" Features With the Claude API

The Claude API has not only text generation but also practical features such as streaming responses, Tool Use (function calling), structured JSON output, Prompt Caching, and the Batch API. As of 2025, rather than turning a prompt tried in a chat UI into an API as-is, an implementation that designs response format, speed, cost, and reproducibility is important.

In this article, with Python-centered code examples, we organize "how to integrate it so it is actually easy to use." Since fine differences in the SDK are updated, always read the official documentation together when adopting.

SSE Streaming: First, Create an Experience That "Doesn't Make Users Wait"

For longish answers and summary generation, displaying progressively with Server-Sent Events (SSE) gives better UX than waiting for the full text to complete. It is especially effective for chat, review support, and minutes generation.

The idea of a basic implementation

  • Receive the Claude API stream on the backend
  • Relay to the frontend as-is, or format and send
  • Handle not just fragment text but also completion events and error events
from anthropic import Anthropic

client = Anthropic(api_key="YOUR_API_KEY")

with client.messages.stream(
    model="claude-sonnet-4-5",
    max_tokens=1200,
    temperature=0.2,
    messages=[
        {"role": "user", "content": "Tell me 3 key points for implementing SSE streaming"}
    ]
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)

    final_message = stream.get_final_message()

The point at implementation time is not to store fragments as-is. Since intermediate output mixes in restatements and unfinished sentences, it is safer to use the final confirmed text for DB storage and audit logs.

Cases streaming suits and doesn't suit

CaseSuitabilityReason
Chat UISuitsPerceived speed improves greatly
Long-text summarizationSuitsUsers can start reading partway
JSON structured outputSlight cautionIntermediate fragments tend to be incomplete JSON
Batch aggregationDoesn't suitThe merit of progressive display is thin

Especially for processing that uses structured output strictly, the practical separation is: streaming display is UI-only, and business processing is done after the final response is confirmed.

Tool Use: "Don't Leave External Processing Too Much to the Model"

Tool Use is a mechanism where Claude "chooses the needed function and builds the arguments." Common uses are internal search, inventory lookup, weather retrieval, RAG search, DB queries. However, what matters is not letting the AI execute freely but controlling on the app side which tool, under what conditions, and how far it is allowed.

Example of a tool definition

tools = [
    {
        "name": "get_weather",
        "description": "Get the current weather of a specified city",
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "City name. e.g., Tokyo"}
            },
            "required": ["city"]
        }
    }
]
response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=800,
    tools=tools,
    messages=[
        {"role": "user", "content": "Check the weather in Tokyo and also tell me a clothing guideline"}
    ]
)

If a tool use request is included in the response, the app side executes the actual function and returns its result to Claude again.

def get_weather(city: str):
    return {"city": city, "weather": "sunny", "temp_c": 27}

# Pseudo-code: extract tool_use from response
tool_result = get_weather("Tokyo")

followup = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=800,
    messages=[
        {"role": "user", "content": "Check the weather in Tokyo and also tell me a clothing guideline"},
        {"role": "assistant", "content": response.content},
        {
            "role": "user",
            "content": [{
                "type": "tool_result",
                "tool_use_id": "toolu_123",
                "content": str(tool_result)
            }]
        }
    ]
)

Practical points of Tool Use design

  • Insert a confirmation step for processing with side effects (send/update/delete)
  • Strictly define tool arguments with JSON Schema
  • Don't pass search results as-is; format to the necessary minimum
  • On failure, "returning the reason for failure" is more stable than "retrying"

What beginners especially tend to do is cram everything into one all-purpose tool. In reality, separating a search tool, a reference tool, and an update tool reduces misinvocation and permission accidents.

JSON Structured Output: Receive It in a Form Easy to Post-Process

For business-system integration, stable JSON is more important than natural prose. For example, inquiry classification, FAQ extraction, and ticket priority judgment make downstream work easier if you fix the output schema from the start.

Prompt example

You are an inquiry classification API.
You must return only JSON.
Schema:
{
  "category": "billing | technical | sales | other",
  "priority": 1,
  "summary": "string"
}

Inquiry body:
"The invoice amount differs from last month. Please check."

In practice, state "do not return anything other than JSON" and specify enumerated values, numeric ranges, and required fields. Further, also perform validation on the app side.

import json
from pydantic import BaseModel, ValidationError

class TicketResult(BaseModel):
    category: str
    priority: int
    summary: str

resp = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=300,
    temperature=0,
    messages=[{"role": "user", "content": prompt}]
)

text = "".join(
    block.text for block in resp.content if getattr(block, "type", "") == "text"
)

data = json.loads(text)
result = TicketResult(**data)
print(result)

Keep temperature low, ideally 0–0.2, to suppress format drift. Note that returning UI-facing explanatory text and JSON at the same time tends to break, so separating the explanation API and the structured API is the standard approach.

Prompt Caching: Lower the Cost of Long Common Prompts

Sending a system prompt, long business rules, or a product spec every time increases latency and cost. That is where Prompt Caching is effective. Making the long common premise text a cache target and structuring it to be called repeatedly tends to produce effects for high-frequency APIs.

  • Suited to: long guidelines, common knowledge, fixed templates
  • Doesn't suit: prompts whose content changes almost entirely every time

In implementation, "how much to make the fixed part" matters. For example, fixing internal rules, output rules, classification criteria and making only user input variable raises the cache reuse rate.

Batch API: Think of Large-Volume Processing Separately From the Synchronous API

For asynchronous processing of hundreds to tens of thousands of items—summarizing review comments, auto-classifying inquiries, generating product descriptions—the Batch API is suited. Real-time responsiveness drops, but it is easy to operate and rate control is easier, which are advantages.

Rough guide for dividing use

MethodSuited useCharacteristics
Normal APIChat, instant responseFor real-time
StreamingLong-text generation UIHigh perceived speed
Batch APINightly processing, bulk classificationSuited to large-scale processing

In the field, dividing as Streaming for processing where the user waits and Batch for internal back-office processing makes design easier.

A Recommended Configuration at Implementation Time

  • UI layer: streaming display, cancel operation
  • API layer: Claude calls, retry, rate control
  • Tool layer: separate search/reference/update
  • Validation layer: JSON validation, audit logs, permission checks

With this separation, even if you later change the model or improve prompts, you can keep the impact on the whole app small. The Claude API has many advanced features, but you don't need to put them all in from the start. The recommendation is phased adoption in the order 1) JSON structured output then 2) Streaming then 3) Tool Use then 4) Caching/Batch. This is the least failure-prone and most visibly effective way to proceed.