Claude Agent SDK でエージェントに指示を渡す方法は2つあります。長く動き続けるセッションにメッセージを順に流し込むストリーミング入力と、1回の問い合わせで完結する単発メッセージ入力です。この記事では公式ドキュメント「Streaming Input」に沿って、2つのモードの違い、ストリーミング入力でできること、非同期ジェネレータを使った実装例、単発入力の使いどころと制限、エラー処理と会話の継続を整理します。
2つの入力モードの違い
公式ドキュメントは、Agent SDK の入力モードを次の2つに分けています。
- ストリーミング入力モード: 持続的で対話的なセッション。公式が推奨する使い方です。
- 単発メッセージ入力: セッションの状態と再開(resume)を使う、1回きりの問い合わせ。
どちらを使うかは query() の prompt に何を渡すかで決まります。TypeScript のリファレンスでは prompt の型が string | AsyncIterable<SDKUserMessage> と定義されており、文字列を渡せば単発入力、ユーザーメッセージを順に返す非同期イテラブル(非同期ジェネレータ)を渡せばストリーミング入力になります。Python では、ストリーミング入力の例に ClaudeSDKClient が使われています。
Python リファレンスにある query() と ClaudeSDKClient の比較表の要点は次のとおりです。
| 項目 | query() | ClaudeSDKClient |
|---|---|---|
| セッション | 既定で毎回新しいセッションを作る | 同じセッションを使い回す |
| 会話 | 1回のやり取り | 同じ文脈で複数回のやり取り |
| ストリーミング入力 | 対応 | 対応 |
| 割り込み(interrupt) | 非対応 | 対応 |
| 会話の継続 | continue_conversation または resume で手動 | 自動 |
| 向いている用途 | 単発のタスク | 続けて行う会話 |
ストリーミング入力モードでできること
ストリーミング入力モードでは、エージェントが長く動き続けるプロセスとして、ユーザーの入力を受け取り、割り込みを処理し、権限の確認を表に出し、セッションを管理します。公式が挙げる利点は次の5つです。
- 画像のアップロード: メッセージに画像を直接添付して、図や画面を解析させられる。
- メッセージのキュー: 複数のメッセージを送ると順番に処理され、途中で割り込むこともできる。
- ツール連携: セッション中、すべてのツールと独自の MCP サーバーを使える。
- リアルタイムのフィードバック: 最終結果だけでなく、生成されていく応答をその場で見られる。
- 文脈の維持: 複数ターンにわたって会話の文脈が自然に保たれる。
TypeScript の Query オブジェクトには、ストリーミング入力モードでだけ使えるメソッドがあります。リファレンスでは interrupt()(実行中の問い合わせを止める)、setPermissionMode()(権限モードの変更)、setModel()(モデルの変更)などが「ストリーミング入力モードでのみ利用可能」と明記されています。セッションの途中で止めたり設定を切り替えたりしたいなら、ストリーミング入力を選ぶ必要があります。権限確認の扱いは「Agent SDK の承認とユーザー入力」で扱っています。
非同期ジェネレータで送る実装例
公式の例は、最初にコードベースのセキュリティ確認を依頼し、2秒待ってから画像付きの2通目を送るものです。作業ディレクトリにある diagram.png を読み込むので、試すときは画像を用意するかファイル名を変えてください。TypeScript ではユーザーメッセージを yield する非同期ジェネレータを作り、そのまま prompt に渡します。
import { query, type SDKUserMessage } from "@anthropic-ai/claude-agent-sdk";
import { readFile } from "fs/promises";
async function* generateMessages(): AsyncGenerator<SDKUserMessage> {
// 1通目
yield {
type: "user",
message: {
role: "user",
content: "Analyze this codebase for security issues"
},
parent_tool_use_id: null
};
// 条件やユーザー入力を待つ
await new Promise((resolve) => setTimeout(resolve, 2000));
// 画像付きの2通目
yield {
type: "user",
message: {
role: "user",
content: [
{ type: "text", text: "Review this architecture diagram" },
{
type: "image",
source: {
type: "base64",
media_type: "image/png",
data: await readFile("diagram.png", "base64")
}
}
]
},
parent_tool_use_id: null
};
}
for await (const message of query({
prompt: generateMessages(),
options: {
maxTurns: 10,
allowedTools: ["Read", "Grep"]
}
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
Python では ClaudeSDKClient を async with で開き、client.query() にジェネレータを渡してから receive_response() で応答を読みます。
from claude_agent_sdk import (
ClaudeSDKClient,
ClaudeAgentOptions,
AssistantMessage,
TextBlock,
)
import asyncio
import base64
async def streaming_analysis():
async def message_generator():
# 1通目
yield {
"type": "user",
"message": {
"role": "user",
"content": "Analyze this codebase for security issues",
},
}
# 条件を待つ
await asyncio.sleep(2)
# 画像付きの2通目
with open("diagram.png", "rb") as f:
image_data = base64.b64encode(f.read()).decode()
yield {
"type": "user",
"message": {
"role": "user",
"content": [
{"type": "text", "text": "Review this architecture diagram"},
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": image_data,
},
},
],
},
}
options = ClaudeAgentOptions(max_turns=10, allowed_tools=["Read", "Grep"])
async with ClaudeSDKClient(options) as client:
await client.query(message_generator())
async for message in client.receive_response():
if isinstance(message, AssistantMessage):
for block in message.content:
if isinstance(block, TextBlock):
print(block.text)
asyncio.run(streaming_analysis())
実行結果には言語で差があります。TypeScript 版は応答が完了するたびに結果を表示します。Python 版の receive_response() は最初の結果メッセージ(ResultMessage)までで終わるため、表示されるのはセキュリティ分析だけです。両方の応答を読みたい場合は、メッセージごとに query() と receive_response() を1組ずつ使います。
画像ブロックの source が無い、またはオブジェクトでない場合、SDK はエラーを返しません。Claude Code は画像の代わりに [Image could not be processed: image block has no source object] のようなテキストを Claude に送り、セッションはそのまま続きます。画像が無視されているように見えるときは、まずブロックの形を確認してください。
ジェネレータ内で例外が起きたときの挙動にも注意が必要です。
- TypeScript: 読み込むファイルが無いなどでジェネレータが例外を投げると、ストリームは元のエラーではなく
Claude Code process aborted by userというエラーで終わります。このメッセージを見たら、まずジェネレータ内のコードを疑います。前にバンドルされた SDK の長い1行が出ることがあるので、出力の最後までエラー文を読みます。 - Python: ジェネレータの例外は debug レベルでログに出るだけで、例外は送出されずセッションが止まったままになります。出力が無いまま固まったら、debug ログを有効にしてジェネレータを確認します。
単発メッセージ入力の使いどころと制限
単発メッセージ入力は、より簡単ですが機能は限られます。公式が挙げる使いどころは次のとおりです。
- 1回きりの応答がほしいとき
- 画像の添付やセッション途中の制御メソッドが要らないとき
- Lambda 関数のようなステートレスな環境で動かすとき
一方、単発メッセージ入力では次の機能が使えません。
- メッセージへの画像の直接添付
- 動的なメッセージのキュー
- リアルタイムの割り込み
- 自然な複数ターンの会話
途中で追加の指示を送る、画像を見せる、止める、といった対話的な操作が必要なら、ストリーミング入力を選びます。逆に、バッチ処理やサーバーレス関数から決まった指示を1回投げるだけなら、単発入力のほうが構成は単純です。
エラー処理と会話の継続
単発メッセージの query() は、error_max_turns のようなエラーの結果で終わると、最後の結果メッセージを返したあとに失敗内容を含むエラーを送出します。処理を続けたいコードでは、ループを try で囲みます。続けて同じ会話を進めたいときは、TypeScript では continue: true、Python では continue_conversation=True を指定すると直前の会話の続きになります(どちらもリファレンスで「直近の会話を続ける」オプションと説明されています)。
import { query } from "@anthropic-ai/claude-agent-sdk";
// 1回きりの問い合わせ(error_max_turns などで終わると throw する)
try {
for await (const message of query({
prompt: "Explain the authentication flow",
options: {
maxTurns: 5,
allowedTools: ["Read", "Grep"]
}
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
} catch (error) {
console.error(`Query failed: ${error}`);
}
// 直前の会話を続ける
try {
for await (const message of query({
prompt: "Now explain the authorization process",
options: {
continue: true,
maxTurns: 5
}
})) {
if (message.type === "result" && message.subtype === "success") {
console.log(message.result);
}
}
} catch (error) {
console.error(`Query failed: ${error}`);
}
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
import asyncio
async def single_message_example():
try:
async for message in query(
prompt="Explain the authentication flow",
options=ClaudeAgentOptions(max_turns=5, allowed_tools=["Read", "Grep"]),
):
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
except Exception as e:
print(f"Query failed: {e}")
# 直前の会話を続ける
try:
async for message in query(
prompt="Now explain the authorization process",
options=ClaudeAgentOptions(continue_conversation=True, max_turns=5),
):
if isinstance(message, ResultMessage) and message.subtype == "success":
print(message.result)
except Exception as e:
print(f"Query failed: {e}")
asyncio.run(single_message_example())
実行すると、認証フローの説明、続いて認可処理の説明が順に表示されます。結果メッセージの subtype は success のほか error_during_execution・error_max_turns・error_max_budget_usd などがあり、詳しくは「Agent SDK のエージェントループ」で扱っています。特定のセッション ID を指定して再開する方法は「Agent SDK のセッション」を参照してください。
本ページは Streaming Input を中心に、Agent SDK reference - TypeScript(prompt の型、interrupt() などのストリーミング専用メソッド、continue)と Agent SDK reference - Python(query() と ClaudeSDKClient の比較、receive_response()、continue_conversation)を照合した非公式の日本語解説です(確認日 2026-11-01)。最新の仕様は公式ドキュメントを確認してください。