CLIエージェントでのストリーミング出力
ターミナルインターフェースで、ストリーミングされるトークンを1文字ずつ表示します。
「CLIエージェントでのストリーミング出力」はCoddyKit上の無料AI Agentsレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAI Agents学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 AI Agentsコースには全4レッスンが含まれています。
CLIエージェントでストリーミングが重要な理由
ストリーミングを使わない場合、CLIエージェントはLLMの完全な応答が準備できるまで何も出力しません。これには5~30秒かかることがあり、ユーザーはプログラムがクラッシュしたのかと思いながら、何も表示されないターミナルを見つめることになります。
ストリーミングを使うと、生成されたトークンがすぐに表示されるため、即座にフィードバックが得られ、体験が大幅に向上します。
OpenAI SDKでストリーミングを有効にする
chat.completions.create()にstream=Trueを渡します。この呼び出しは、完全なレスポンスオブジェクトではなくジェネレーターを返します。ジェネレーターを反復処理して、チャンクが届くたびに処理します。
import openai
client = openai.OpenAI(api_key='YOUR_API_KEY')
stream = client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': 'Explain Python generators in 3 sentences.'}],
stream=True # <-- enable streaming
)
# Each chunk arrives as it is generated
for chunk in stream:
delta = chunk.choices[0].delta
if delta.content:
print(delta.content, end='', flush=True)
print() # newline after the response is completeprint()とsys.stdout.write()の比較
ストリーミング時は、print(text, end='', flush=True)を使うか、sys.stdout.write(text)の後にsys.stdout.flush()を呼び出します。flush=Trueがないと、Pythonが出力をバッファーにためて一度に表示することがあり、ストリーミングの目的が失われます。
import sys
import openai
client = openai.OpenAI(api_key='YOUR_API_KEY')
def stream_to_terminal(messages: list):
stream = client.chat.completions.create(
model='gpt-4o-mini',
messages=messages,
stream=True
)
full_response = ''
for chunk in stream:
token = chunk.choices[0].delta.content or ''
full_response += token
# Option 1: print with flush
print(token, end='', flush=True)
# Option 2: sys.stdout.write + flush
# sys.stdout.write(token)
# sys.stdout.flush()
print() # final newline
return full_responseストリーミング中に完全な応答を収集する
ストリーミングの完了後に、保存、追加処理、表示のために完全な応答テキストが必要になることはよくあります。出力しながらトークンを文字列に蓄積します。
import openai
client = openai.OpenAI(api_key='YOUR_API_KEY')
def stream_and_collect(messages: list) -> str:
full_text = ''
print('Agent: ', end='', flush=True)
stream = client.chat.completions.create(
model='gpt-4o-mini',
messages=messages,
stream=True
)
for chunk in stream:
token = chunk.choices[0].delta.content or ''
full_text += token
print(token, end='', flush=True)
print() # newline
return full_text
# The return value contains the complete response for storage
# response_text = stream_and_collect(history)
# history.append({'role': 'assistant', 'content': response_text})AsyncOpenAIによる非同期ストリーミング
非同期のエージェントアーキテクチャでは、AsyncOpenAIとasync forを使ってストリーミングチャンクを反復処理し、イベントループをブロックしないようにします。
import asyncio
import openai
async def async_stream_agent(query: str) -> str:
client = openai.AsyncOpenAI(api_key='YOUR_API_KEY')
stream = await client.chat.completions.create(
model='gpt-4o-mini',
messages=[{'role': 'user', 'content': query}],
stream=True
)
full_text = ''
print('Agent: ', end='', flush=True)
async for chunk in stream:
token = chunk.choices[0].delta.content or ''
full_text += token
print(token, end='', flush=True)
print()
return full_text
# asyncio.run(async_stream_agent('What is asyncio?'))finish_reasonでストリームの終了を検知する
ストリームの最後のチャンクには、nullではないfinish_reasonが含まれます。これを確認すると、ストリームが終了した理由を把握できます。'stop'は正常終了、'length'は途中で切り詰められたこと、'tool_calls'は関数呼び出しが必要なことを示します。
import openai
client = openai.OpenAI(api_key='YOUR_API_KEY')
def stream_with_finish_detection(messages: list):
stream = client.chat.completions.create(
model='gpt-4o-mini',
messages=messages,
stream=True
)
finish_reason = None
for chunk in stream:
delta = chunk.choices[0].delta
if delta.content:
print(delta.content, end='', flush=True)
if chunk.choices[0].finish_reason:
finish_reason = chunk.choices[0].finish_reason
print()
if finish_reason == 'length':
print('[WARNING: Response was truncated. Try increasing max_tokens.]')
elif finish_reason == 'stop':
pass # normal completion
return finish_reasonターミナル出力のANSIカラー
ANSIエスケープコードを使うと、ターミナル出力に色を付けられます。エージェントのプレフィックス、ユーザー入力のプロンプト、警告を視覚的に区別するために使用します。coloramaライブラリは、Windowsを含むクロスプラットフォーム環境をサポートします。
# pip install colorama
from colorama import Fore, Style, init
init(autoreset=True) # reset color after each print
def print_colored_stream(messages: list, client):
# Print agent prefix in cyan
print(Fore.CYAN + 'Agent: ' + Style.RESET_ALL, end='', flush=True)
stream = client.chat.completions.create(
model='gpt-4o-mini',
messages=messages,
stream=True
)
for chunk in stream:
token = chunk.choices[0].delta.content or ''
print(token, end='', flush=True)
print()
# Also useful:
# print(Fore.GREEN + 'Success!') — green
# print(Fore.RED + 'Error!') — red
# print(Fore.YELLOW + 'Warning') — yellowより豊かなターミナル出力のためのRichライブラリ
richライブラリは、ターミナル上でのMarkdownレンダリング、構文ハイライト付きコードブロック、テーブル、スピナーを提供します。ストリーミングするエージェント出力との相性も優れています。
# pip install rich
from rich.console import Console
from rich.live import Live
from rich.markdown import Markdown
console = Console()
def stream_with_rich(messages: list, client):
full_text = ''
with Live(console=console, refresh_per_second=10) as live:
stream = client.chat.completions.create(
model='gpt-4o-mini',
messages=messages,
stream=True
)
for chunk in stream:
token = chunk.choices[0].delta.content or ''
full_text += token
# Render accumulated text as Markdown in real time
live.update(Markdown(full_text))
return full_textツール呼び出しとのストリーミング
ストリーミングと関数呼び出しを組み合わせると、tool_callsフィールドも分割された形でストリーミングされます。解析する前に、チャンクをまたいでJSON文字列を蓄積します。
import json
import openai
client = openai.OpenAI(api_key='YOUR_API_KEY')
def stream_with_tools(messages: list, tools: list) -> dict:
stream = client.chat.completions.create(
model='gpt-4o-mini',
messages=messages,
tools=tools,
stream=True
)
tool_call_chunks = {}
for chunk in stream:
delta = chunk.choices[0].delta
if delta.tool_calls:
for tc in delta.tool_calls:
idx = tc.index
if idx not in tool_call_chunks:
tool_call_chunks[idx] = {'name': '', 'args': ''}
if tc.function.name:
tool_call_chunks[idx]['name'] += tc.function.name
if tc.function.arguments:
tool_call_chunks[idx]['args'] += tc.function.arguments
# Parse accumulated tool calls
return {v['name']: json.loads(v['args']) for v in tool_call_chunks.values()}トークンカウンターの表示
ストリーミング中にトークン数をリアルタイムで表示すると、ユーザーが使用量を把握し、コストを理解するのに役立ちます。stream_options={'include_usage': True}を設定すると、OpenAIのストリームの最後のチャンクに使用量データが含まれます。
import openai
client = openai.OpenAI(api_key='YOUR_API_KEY')
def stream_with_token_count(messages: list):
stream = client.chat.completions.create(
model='gpt-4o-mini',
messages=messages,
stream=True,
stream_options={'include_usage': True}
)
for chunk in stream:
delta = chunk.choices[0].delta
if delta.content:
print(delta.content, end='', flush=True)
# Last chunk includes usage
if chunk.usage:
print(f'\n[Tokens: prompt={chunk.usage.prompt_tokens}, '
f'completion={chunk.usage.completion_tokens}, '
f'total={chunk.usage.total_tokens}]')ストリーミングのベストプラクティス
CLIエージェントでストリーミング出力を扱う際のベストプラクティスをまとめます。
- バッファリングを防ぐため、必ず
flush=Trueまたはsys.stdout.flush()を使用します - ストリーミング後に保存できるよう、トークンを文字列に蓄積します
- 途中で切り詰められたことを検知するため、
finish_reasonを確認します - 視認性を高めるため、ANSIカラーまたは
richを使用します - ツール呼び出しのストリーミングでは、JSON引数のチャンクを蓄積して処理します
理解度チェック:ストリーミング出力
CLIエージェントのストリーミング出力について、理解度を確認します。
振り返り:CLIエージェントでのストリーミング出力
これで、応答性が高く現代的な印象のストリーミング対応CLIエージェントを構築できるようになりました。
- OpenAI SDKからのストリーミングを有効にするには、
stream=Trueを渡します - トークンをすぐに表示するには、
print(token, end='', flush=True)を使用します - ストリーム終了後の処理に備えて、トークンを文字列に蓄積します
- 切り詰めが発生したかどうかを検出するには、最後のチャンクで
finish_reasonを確認します - 非同期エージェントでは、
AsyncOpenAIとasync forを使用します - 洗練されたターミナル体験を実現するには、ANSIカラーや
richを追加します
よくある質問
「CLIエージェントでのストリーミング出力」レッスンは無料ですか?
はい。「CLIエージェントでのストリーミング出力」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Agentsコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Agentsコースには全4レッスンが含まれています。
「CLIエージェントでのストリーミング出力」で何を学びますか?
ターミナルインターフェースで、ストリーミングされるトークンを1文字ずつ表示します。 ブラウザで直接実行するハンズオンコードでAI Agentsを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
AI Agentsを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのAI Agentsは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「CLIエージェントでのストリーミング出力」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このAI Agentsレッスンでコードを書いて実行できますか?
はい。すべてのAI Agentsレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- コマンドラインエージェントインターフェースの構築
- 対話型REPLスタイルエージェント
- 引数解析とヘルプテキスト
- CLIエージェントでのストリーミング出力