手っ取り早い答え: sgcWebSocketsを使ってDelphiでLLMレスポンスをストリーミングするには、コンポーネントの OnHTTPAPISSE イベントを割り当ててから、ストリーミングメソッドを呼び出します — OpenAIには Stream := True 付きの _CreateChatCompletion、Anthropic ClaudeとOllamaには _CreateMessageStream、Geminiには _CreateContentStream です。各デルタはServer-Sent Eventとしてハンドラーに届きます。aData を Memo に追加すれば、モデルが生成するにつれて回答が自動的に打ち出されていきます。
ストリーミングしないLLM呼び出しは、回答全体が用意できるまでブロックします。1段落の返答ならそれで構いませんが、長い補完では、ユーザーは何のフィードバックもないまま、フリーズしたウィンドウを数秒間見つめることになります。ストリーミングはそれを解決します。モデルは出力を1本のHTTP接続上で小さなチャンクの連続として返し、あなたは各チャンクが届いた瞬間にそれを描画します。その結果、ChatGPTで見られるおなじみの「タイピング」効果が得られ、回答全体に時間がかかる場合でも応答性の高いUIになります。
ストリーミングの仕組み: OnHTTPAPISSE
内部では、すべてのsgcWebSockets AIコンポーネントがServer-Sent Events(SSE)を使ってストリーミングします。プロバイダーはレスポンスを開いたまま保ち、トークンが生成されるたびにイベントをプッシュします。コンポーネントはそれらのイベントを解析し、すべてのコンポーネントで同じシグネチャを持つ単一のハンドラーを通じて、チャンクごとに1つのイベントを発生させます。
procedure TForm1.HandleSSE(Sender: TObject;
const aEvent, aData: string; var Cancel: Boolean);
begin
// aEvent -> the SSE event name (provider-specific)
// aData -> the payload for this chunk (the token delta)
// Cancel -> set True to abort the stream early
Memo1.Lines.Add(aData);
end;
これが契約のすべてです。aData は増分のペイロードを運び、aEvent はそれがどの種類のイベントかを教え(複数のイベントタイプを発するプロバイダーで便利です)、Cancel := True を設定するとストリームが停止します。たとえばユーザーが停止ボタンをクリックしたときなどです。同じハンドラーの形がOpenAI、Anthropic、Gemini、Ollamaで機能するので、一度書けばプロバイダー間で再利用できます。
OpenAI: Stream := True
TsgcHTTP_API_OpenAI では、リクエストでストリーミングを有効にし、イベントをフックします。OpenAIOptions.ApiKey を設定し、OnHTTPAPISSE を割り当てれば、チャット補完が1ブロックではなくチャンクごとに配信されます。
uses
sgcHTTP_API_OpenAI;
var
OpenAI: TsgcHTTP_API_OpenAI;
begin
OpenAI := TsgcHTTP_API_OpenAI.Create(nil);
OpenAI.OpenAIOptions.ApiKey := 'sk-...';
// Each token delta arrives in HandleSSE
OpenAI.OnHTTPAPISSE := HandleSSE;
// Build a typed request, set Stream := True, then call
OpenAI._CreateChatCompletion('gpt-4o-mini', 'Explain WebSockets in detail.');
end;
型付きリクエストは Stream プロパティを公開しているので、リクエストオブジェクトを自分で構築する場合は、送信する前に Stream := True を設定します。トークンは生成されるにつれて OnHTTPAPISSE を通じて表面化し、あなたはそれぞれを Memo に追加します。
Anthropic Claude: _CreateMessageStream
Claudeは専用のストリーミングヘルパーを公開しているので、切り替える別個のフラグはありません。TsgcHTTP_API_Anthropic の _CreateMessageStream を呼び出すと、そのリクエストでSSEが有効になります。APIキーとバージョンを設定し、ハンドラーを割り当てて呼び出します。
uses
sgcHTTP_API_Anthropic;
var
Anthropic: TsgcHTTP_API_Anthropic;
begin
Anthropic := TsgcHTTP_API_Anthropic.Create(nil);
Anthropic.AnthropicOptions.ApiKey := 'sk-ant-...';
Anthropic.AnthropicOptions.AnthropicVersion := '2023-06-01';
Anthropic.OnHTTPAPISSE := HandleSSE;
Anthropic._CreateMessageStream(
'claude-3-5-sonnet-latest',
'Summarise RFC 6455',
1024);
end;
Claudeはストリーミング中にいくつかのSSEイベントタイプを発します(コンテンツブロックの開始、デルタ、そして停止)。必要であれば aEvent 引数でそれらを区別できます。「届いたテキストを表示する」だけのシンプルなUIなら、aData を追加するだけで十分です。
GeminiとOllama: 同じ形
Google Geminiは、独自のストリーミングメソッド TsgcHTTP_API_Gemini の _CreateContentStream で、まったく同じパターンに従います。
Gemini.OnHTTPAPISSE := HandleSSE;
Gemini._CreateContentStream(
'gemini-2.0-flash',
'Explain quantum entanglement',
1024);
ローカルモデルもまったく同じように動作します。TsgcHTTP_API_Ollama はAPIキーを必要としません — OllamaOptions.BaseUrl を http://localhost:11434/api に向けて _CreateMessageStream を呼び出せば、あなた自身のハードウェア上のオープンモデルが、同じ OnHTTPAPISSE ハンドラーを通じてストリーミングで返ってきます。
Ollama.OllamaOptions.BaseUrl := 'http://localhost:11434/api';
Ollama.OnHTTPAPISSE := HandleSSE;
Ollama._CreateMessageStream('llama3', 'Summarise RFC 6455');
4つのプロバイダー、1つのイベント、それぞれ1回のメソッド呼び出し。ストリーミングバックエンドの切り替えは、書き直しではなく局所的な編集です。
UIを安全に更新する
ハンドラーに関する実践的な注記をいくつか。まず、OnHTTPAPISSE の中での作業は小さく保ちましょう — デルタを追加して戻るだけです。トークンごとの重い処理はストリームを途切れがちに感じさせるので、テキストを蓄積し、ストリームが終了してから高コストなフォーマット処理を一度だけ行います。次に、スレッドのコンテキストに注意してください。バックグラウンドスレッドからリクエストを開始すると、SSEイベントはそのスレッドで発生し、メインスレッド以外でVCLやFMXのコントロールに触れるのは安全ではありません。その場合は TThread.Synchronize(またはブロックしない追加なら TThread.Queue)で更新を戻します。
procedure TForm1.HandleSSE(Sender: TObject;
const aEvent, aData: string; var Cancel: Boolean);
begin
TThread.Queue(nil,
procedure
begin
Memo1.SelStart := Length(Memo1.Text);
Memo1.SelText := aData; // append at the caret, no full repaint
end);
end;
Lines.Add ではなく SelText で追加すると、トークンごとに Memo 全体を再フローさせずに済み、長いストリームをスムーズに保てます。メインスレッドからAPIを呼び出す場合は、TThread.Queue のラッパーを外して、コントロールを直接更新できます。
はじめに
これらのコンポーネントはすべてsgcWebSocketsに付属しています。無料トライアルを入手し、使いたいプロバイダーのコンポーネントを配置し、OnHTTPAPISSE を割り当ててストリーミングメソッドを呼び出します — 数行でトークン単位のUIが手に入ります。メソッドの完全なリファレンスについてはOpenAIコンポーネントのページとAnthropicコンポーネントのページをご覧いただくか、AI & LLM コンポーネントハブですべてのモデルを閲覧してください。
ご質問やフィードバックは? お問い合わせください — コードを書いた本人たちから返信が届きます。
