Szybka odpowiedź: aby strumieniować odpowiedź LLM w Delphi z sgcWebSockets, przypisz zdarzenie komponentu OnHTTPAPISSE, a następnie wywołaj metodę strumieniującą — _CreateChatCompletion z Stream := True dla OpenAI, _CreateMessageStream dla Anthropic Claude i Ollama, _CreateContentStream dla Gemini. Każda delta trafia do procedury obsługi jako Server-Sent Event; dołącz aData do komponentu Memo, a odpowiedź sama się wypisuje, w miarę jak model ją generuje.
Wywołanie LLM bez strumieniowania blokuje aż do momentu, gdy cała odpowiedź jest gotowa. Dla jednoakapitowej odpowiedzi jest to w porządku, ale przy długim uzupełnieniu użytkownik wpatruje się w zamarznięte okno przez kilka sekund bez żadnej informacji zwrotnej. Strumieniowanie to naprawia: model zwraca swoje wyjście jako sekwencję małych porcji przez pojedyncze połączenie HTTP, a Ty renderujesz każdą porcję w chwili, gdy się pojawia. Efektem jest znany efekt „pisania”, który widać w ChatGPT, oraz interfejs, który wydaje się responsywny, nawet gdy pełna odpowiedź zajmuje chwilę.
Jak działa strumieniowanie: OnHTTPAPISSE
Pod maską każdy komponent AI sgcWebSockets strumieniuje za pomocą Server-Sent Events (SSE). Dostawca utrzymuje otwartą odpowiedź i wysyła zdarzenia w miarę generowania tokenów. Komponent parsuje te zdarzenia i zgłasza jedno zdarzenie na porcję przez pojedynczą procedurę obsługi o tej samej sygnaturze w każdym komponencie:
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;
To cały kontrakt. aData niesie przyrostowy ładunek, aEvent mówi, jakiego rodzaju jest to zdarzenie (przydatne u dostawców, którzy emitują kilka typów zdarzeń), a ustawienie Cancel := True zatrzymuje strumień, na przykład gdy użytkownik kliknie przycisk Stop. Ten sam kształt procedury obsługi działa dla OpenAI, Anthropic, Gemini i Ollama, więc gdy raz ją napiszesz, możesz ją wykorzystać ponownie u różnych dostawców.
OpenAI: Stream := True
Z TsgcHTTP_API_OpenAI włączasz strumieniowanie w żądaniu i podpinasz zdarzenie. Ustaw OpenAIOptions.ApiKey, przypisz OnHTTPAPISSE, a uzupełnienie czatu jest dostarczane porcja po porcji zamiast w jednym bloku:
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;
Typowane żądanie udostępnia właściwość Stream, więc gdy sam budujesz obiekt żądania, ustawiasz Stream := True przed wysłaniem. Tokeny wypływają wtedy przez OnHTTPAPISSE w miarę ich generowania, a Ty dołączasz każdy z nich do swojego komponentu Memo.
Anthropic Claude: _CreateMessageStream
Claude udostępnia dedykowaną metodę pomocniczą do strumieniowania, więc nie ma osobnej flagi do przełączenia: wywołanie _CreateMessageStream na TsgcHTTP_API_Anthropic włącza SSE dla tego żądania. Ustaw klucz API i wersję, przypisz procedurę obsługi i wywołaj ją:
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 emituje kilka typów zdarzeń SSE podczas strumieniowania (start bloków treści, delty, a potem stop). Argument aEvent pozwala je rozróżnić, jeśli potrzebujesz; dla prostego interfejsu „pokaż tekst w miarę napływania” wystarczy dołączać aData.
Gemini i Ollama: ten sam kształt
Google Gemini stosuje identyczny wzorzec z własną metodą strumieniującą, _CreateContentStream na TsgcHTTP_API_Gemini:
Gemini.OnHTTPAPISSE := HandleSSE;
Gemini._CreateContentStream(
'gemini-2.0-flash',
'Explain quantum entanglement',
1024);
Modele lokalne działają dokładnie tak samo. TsgcHTTP_API_Ollama nie potrzebuje klucza API — wskaż OllamaOptions.BaseUrl na http://localhost:11434/api i wywołaj _CreateMessageStream, a otwarty model na Twoim własnym sprzęcie strumieniuje z powrotem przez tę samą procedurę obsługi OnHTTPAPISSE:
Ollama.OllamaOptions.BaseUrl := 'http://localhost:11434/api';
Ollama.OnHTTPAPISSE := HandleSSE;
Ollama._CreateMessageStream('llama3', 'Summarise RFC 6455');
Czterej dostawcy, jedno zdarzenie, po jednym wywołaniu metody każdy. Zmiana zaplecza strumieniowania to lokalna edycja, a nie przepisywanie od nowa.
Bezpieczna aktualizacja interfejsu
Kilka praktycznych uwag dotyczących procedury obsługi. Po pierwsze, utrzymuj pracę wewnątrz OnHTTPAPISSE jako niewielką — dołącz deltę i wyjdź. Ciężkie przetwarzanie na każdy token sprawi, że strumień będzie odczuwany jako poszarpany, więc gromadź tekst i wykonuj kosztowne formatowanie raz, gdy strumień się zakończy. Po drugie, miej na uwadze kontekst wątku. Jeśli uruchamiasz żądanie z wątku w tle, zdarzenie SSE wyzwala się w tym wątku, a dotykanie kontrolek VCL lub FMX poza głównym wątkiem nie jest bezpieczne. W takim przypadku przekaż aktualizację z powrotem za pomocą TThread.Synchronize (lub TThread.Queue dla nieblokującego dołączania):
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;
Dołączanie za pomocą SelText zamiast Lines.Add unika ponownego rozkładania całego komponentu Memo przy każdym tokenie, co utrzymuje długi strumień płynnym. Jeśli wywołujesz API z głównego wątku, możesz pominąć opakowanie TThread.Queue i aktualizować kontrolkę bezpośrednio.
Jak zacząć
Wszystkie te komponenty są dostarczane w sgcWebSockets. Pobierz bezpłatną wersję próbną, upuść komponent dla wybranego dostawcy, przypisz OnHTTPAPISSE i wywołaj metodę strumieniującą — będziesz mieć interfejs działający token po tokenie w kilku liniach. Zobacz stronę komponentu OpenAI i stronę komponentu Anthropic po pełną dokumentację metod albo przejrzyj każdy model w centrum komponentów AI i LLM.
Pytania lub uwagi? Skontaktuj się z nami — odpowiedź otrzymasz od ludzi, którzy napisali ten kod.
