AI Engineering Academy · Oppitunti

Tokenien suoratoiston ymmärtäminen

Ymmärrä, miten streaming API lähettää osittaisia täydennyksiä niiden muodostuessa, miten OpenAI:n stream=True-parametri toimii ja milloin suoratoisto parantaa käyttökokemusta.

Oppitunti 1/413 vaihetta

Tokenien suoratoiston ymmärtäminen on ilmainen AI Engineering Academy-oppitunti CoddyKitissä. Tämä on oppitunti 1/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu AI Engineering Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. AI Engineering Academy-kurssilla on yhteensä 4 oppituntia.

Miksi suoratoisto on tärkeää käyttökokemuksen kannalta

Ilman suoratoistoa sovelluksesi on odotettava, että LLM luo koko vastauksen, ennen kuin se näyttää mitään — pitkissä vastauksissa usein 5–30 sekuntia. Suoratoiston ansiosta ensimmäinen token näkyy 200–500 ms:n kuluessa pyynnön lähettämisestä, ja seuraavat tokenit saapuvat suoratoistona heti niiden luonnin jälkeen. Tämä muuttaa käyttäjän kokeman kokemuksen odottamisesta kiinnostavaksi reaaliaikaiseksi luonniksi ja parantaa koettua reagointinopeutta huomattavasti, vaikka kokonaisluontiaika pysyy samana.

Miten LLM:t luovat tokeneita

LLM:t ovat autoregressiivisia: ne luovat tekstiä yhden tokenin kerrallaan siten, että jokainen uusi token perustuu kaikkiin aiempiin tokeneihin. Kun API vastaanottaa pyynnön, GPU aloittaa ensimmäisen tokenin otannan heti kehotteen käsittelyn jälkeen. Jokaisen seuraavan tokenin luominen kestää suunnilleen saman ajan. Suoratoisto lähettää jokaisen tokenin asiakkaalle heti otannan jälkeen sen sijaan, että kaikki tokenit puskuroitaisiin ja lähetettäisiin lopuksi yhtenä kokonaisena merkkijonona.

# Conceptual model of autoregressive generation
prompt = 'The capital of France is'

# Step 1: process full prompt, predict next token
# token_1 = sample(logits) → ' Paris'

# Step 2: append token_1 to context, predict next
# token_2 = sample(logits) → '.'

# Step 3: append token_2 to context, predict next
# token_3 = sample(logits) → '<|end|>'

# Total time: time_to_process_prompt + n_tokens * time_per_token
# With streaming: first token arrives after time_to_process_prompt (TTFT)
# Without streaming: everything arrives after TTFT + n_tokens * time_per_token

TTFT ja TPOT: kaksi viivemittaria

Suoratoisto tuo mukanaan kaksi erillistä viivekäsitettä. TTFT (Time to First Token) on pyynnön lähettämisestä ensimmäisen tokenin vastaanottamiseen kuluva aika, johon kehotteen käsittely vaikuttaa eniten. TPOT (Time Per Output Token) on peräkkäisten tokenien välinen aika, jonka määrittävät mallin koko ja laitteisto. TTFT vaikuttaa siihen, kuinka nopeasti käyttöliittymä reagoi; TPOT vaikuttaa tekstin suoratoiston sujuvuuteen. Molempia tulee seurata erikseen havainnointijärjestelmässä.

import time
from openai import OpenAI

client = OpenAI()

def measure_streaming_latency(prompt: str):
    t_start = time.perf_counter()
    t_first_token = None
    token_times = []

    stream = client.chat.completions.create(
        model='gpt-4o-mini',
        messages=[{'role': 'user', 'content': prompt}],
        stream=True,
    )
    for chunk in stream:
        if chunk.choices[0].delta.content:
            t_now = time.perf_counter()
            if t_first_token is None:
                t_first_token = t_now
                print(f'TTFT: {(t_first_token - t_start) * 1000:.0f}ms')
            else:
                token_times.append(t_now - token_times[-1] if token_times else t_now - t_first_token)
            token_times.append(t_now)
    print(f'TPOT avg: {1000 * (token_times[-1] - t_first_token) / max(len(token_times)-1, 1):.1f}ms')

stream=True-parametri

Suoratoiston käyttöönotto OpenAI SDK:ssa edellyttää asetuksen stream=True määrittämistä kutsussa chat.completions.create. Vastauksen tyyppi muuttuu ChatCompletion-objektista Stream[ChatCompletionChunk]-iteraattoriksi. Jokainen osa sisältää delta-kentän, jossa on joko content-merkkijonon katkelma tai None, jos token on työkalukutsu tai suoratoisto on päättymässä.

from openai import OpenAI

client = OpenAI()

# Non-streaming: wait for complete response
response = client.chat.completions.create(
    model='gpt-4o-mini',
    messages=[{'role': 'user', 'content': 'Explain RAG in one paragraph.'}],
)
full_text = response.choices[0].message.content

# Streaming: receive tokens incrementally
stream = client.chat.completions.create(
    model='gpt-4o-mini',
    messages=[{'role': 'user', 'content': 'Explain RAG in one paragraph.'}],
    stream=True,
)
for chunk in stream:
    delta = chunk.choices[0].delta.content
    if delta:  # delta can be None for non-content chunks
        print(delta, end='', flush=True)
print()  # newline at end

Koko vastauksen kokoaminen

Monissa sovelluksen työnkuluissa tarvitset sekä tokenien suoratoistoa käyttöliittymään reagointinopeuden vuoksi että koko vastaustekstin kokoamista jatkokäsittelyä, kuten lokitusta, välimuistia tai putken seuraavia vaiheita, varten. Menettely on yksinkertainen: käy suoratoisto läpi, tulosta tai välitä jokainen osa asiakkaalle ja yhdistä samalla sisältö kokonaiseksi merkkijonoksi.

def stream_and_accumulate(prompt: str) -> str:
    stream = client.chat.completions.create(
        model='gpt-4o-mini',
        messages=[{'role': 'user', 'content': prompt}],
        stream=True,
    )

    full_text = ''
    finish_reason = None

    for chunk in stream:
        choice = chunk.choices[0]
        delta = choice.delta.content
        if delta:
            print(delta, end='', flush=True)  # real-time display
            full_text += delta               # accumulate
        if choice.finish_reason:
            finish_reason = choice.finish_reason

    print()  # newline
    print(f'Finished: {finish_reason}, total chars: {len(full_text)}')
    return full_text

Suoratoisto käyttötietojen kanssa

Oletusarvoisesti suoratoistovastaus ei sisällä tokenien käyttötietoja (kehotteen tokenit, valmistuneet tokenit). Voit sisällyttää ne välittämällä asetuksen stream_options={'include_usage': True}. Käyttötiedot saapuvat viimeisessä osassa sisältövirran päätyttyä. Tämä on tärkeää tuotantosovellusten kustannusten seurannassa ja nopeusrajoitusten valvonnassa.

stream = client.chat.completions.create(
    model='gpt-4o-mini',
    messages=[{'role': 'user', 'content': 'What is a vector database?'}],
    stream=True,
    stream_options={'include_usage': True},  # include token counts
)

full_text = ''
usage = None

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        full_text += chunk.choices[0].delta.content
    if chunk.usage:  # arrives in the final chunk
        usage = chunk.usage

if usage:
    print(f'Prompt tokens: {usage.prompt_tokens}')
    print(f'Completion tokens: {usage.completion_tokens}')
    print(f'Total tokens: {usage.total_tokens}')

Milloin suoratoistoa ei pidä käyttää

Suoratoisto ei ole aina oikea valinta. Vältä suoratoistoa, kun: (1) tarvitset koko vastauksen ennen kuin voit tehdä sillä mitään, esimerkiksi JSON-jäsennystä tai työkalukutsun tunnistamista varten; (2) vastaus on erittäin lyhyt (alle 30 tokenia), jolloin suoratoiston aiheuttama lisäkäsittely lisää viivettä enemmän kuin säästää sitä; tai (3) käsittelet eräajona monia pyyntöjä, jolloin läpimeno on tärkeämpää kuin yksittäisen vastauksen viive. Näissä tapauksissa tavalliset suoratoistamattomat kutsut ovat yksinkertaisempia ja yhtä nopeita.

Suoratoisto Anthropic- ja Gemini-rajapinnoilla

Suoratoisto on saatavilla kaikissa tärkeimmissä LLM-palveluntarjoajien rajapinnoissa, ei vain OpenAI:ssa. Menettely on samankaltainen, mutta SDK-rajapinnat eroavat hieman toisistaan. Anthropicin Python SDK käyttää client.messages.stream()-kutsua kontekstinhallintana, kun taas Gemini käyttää kutsua generate_content(stream=True). Kun rakennat palveluntarjoajasta riippumattomia sovelluksia, sijoita suoratoistorajapinta yhteisen generaattorifunktion taakse.

import anthropic

ant_client = anthropic.Anthropic(api_key='YOUR_KEY')

# Anthropic streaming
with ant_client.messages.stream(
    model='claude-sonnet-4-5',
    max_tokens=1024,
    messages=[{'role': 'user', 'content': 'Explain hybrid search briefly.'}],
) as stream:
    for text in stream.text_stream:
        print(text, end='', flush=True)

# Final message with usage stats
final_msg = stream.get_final_message()
print(f'\nInput tokens: {final_msg.usage.input_tokens}')
print(f'Output tokens: {final_msg.usage.output_tokens}')

Generaattoriin perustuva suoratoistorajapinta

Selkeä arkkitehtuurimalli käärii suoratoiston Python-generaattorifunktioon, joka palauttaa token-merkkijonoja yield-lauseilla. Tämä irrottaa suoratoistologiikan kulutuslogiikasta — kutsuja voi iteroida generaattorin yli, kirjoittaa tiedostoon, välittää tuloksen WebSocketiin tai koota sen merkkijonoksi ilman, että suoratoistokoodi tietää, miten sen tulosta käytetään. Tämä muodostaa useimpien tuotantokäyttöön tarkoitettujen suoratoisto-API:en perustan.

from typing import Generator

def stream_completion(
    messages: list[dict],
    model: str = 'gpt-4o-mini',
    **kwargs,
) -> Generator[str, None, None]:
    stream = client.chat.completions.create(
        model=model,
        messages=messages,
        stream=True,
        **kwargs,
    )
    for chunk in stream:
        delta = chunk.choices[0].delta.content
        if delta:
            yield delta

# Usage: pipe to stdout
for token in stream_completion([{'role': 'user', 'content': 'Hello!'}]):
    print(token, end='', flush=True)

# Usage: accumulate
full = ''.join(stream_completion([{'role': 'user', 'content': 'Hello!'}]))

Suoratoisto pääte- ja CLI-sovelluksissa

Päätesovelluksissa suoratoistettu tuloste näyttää samalta kuin kirjoittaminen — jokainen merkki ilmestyy heti sen luonnin jälkeen. Keskeinen vaatimus on käyttää asetusta flush=True jokaisessa print-kutsussa. Ilman tyhjennystä Python puskuroi tulosteen rivinvaihtoon asti, mikä kumoaa suoratoiston hyödyn. Voit myös käyttää kutsua sys.stdout.write(token) ja sen jälkeen kutsua sys.stdout.flush(), jos haluat hallita tulosteen muotoilua tarkemmin.

import sys

def stream_to_terminal(messages: list[dict]):
    stream = client.chat.completions.create(
        model='gpt-4o-mini',
        messages=messages,
        stream=True,
    )
    token_count = 0
    for chunk in stream:
        delta = chunk.choices[0].delta.content
        if delta:
            sys.stdout.write(delta)  # no newline added
            sys.stdout.flush()       # MUST flush or output buffers
            token_count += 1
    print()  # final newline
    print(f'({token_count} tokens generated)')

Suoratoisto ja virheistä palautuminen

Suoratoisto vaikeuttaa virheenkäsittelyä, koska virhe voi tapahtua suoratoiston puolivälissä sen jälkeen, kun osa tokeneista on jo lähetetty asiakkaalle. Suositeltava toimintamalli on ympäröidä suoratoiston iteraatio try/except-lohkolla ja virheen sattuessa joko lähettää asiakkaalle virhesentinelli tai sulkea suoratoisto hallitusti. Määritä aina koko suoratoistolle aikakatkaisu, jotta tilanteet, joissa palvelin aloittaa suoratoiston mutta pysähtyy kesken luonnin, voidaan käsitellä.

import signal

def stream_with_timeout(messages, timeout_seconds=30):
    def timeout_handler(signum, frame):
        raise TimeoutError('LLM stream timed out')

    signal.signal(signal.SIGALRM, timeout_handler)
    signal.alarm(timeout_seconds)

    try:
        stream = client.chat.completions.create(
            model='gpt-4o-mini',
            messages=messages,
            stream=True,
        )
        for chunk in stream:
            delta = chunk.choices[0].delta.content
            if delta:
                yield delta
    except TimeoutError:
        yield '\n[Response timed out]'
    except Exception as e:
        yield f'\n[Error: {str(e)}]'
    finally:
        signal.alarm(0)  # cancel timeout

Pikatarkistus

Testaa tämän oppitunnin avulla, ymmärrätkö LLM-tokenien suoratoistoa.

Oppitunnin yhteenveto

Tässä oppitunnissa opitte, että streaming lähettää jokaisen generoidun tokenin asiakkaalle heti sen näytteenoton jälkeen, mikä parantaa merkittävästi koettua reagointinopeutta, TTFT ja TPOT ovat kaksi keskeistä viivemittaria, joita kannattaa seurata erikseen, ja stream=True muuttaa OpenAI SDK:n vastauksen iteraattoriksi, jonka osia käsittelette for-silmukalla. Kapseloikaa streamit generaattorifunktioihin, jotta rajapinnasta tulee selkeä ja uudelleenkäytettävä. Seuraavaksi toteutamme asynkronisen streamauksen Python SDK:lla.

Aloita maksutta

Opi Python tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
30
Oppitunnit
120

Usein kysytyt kysymykset

Onko oppitunti ”Tokenien suoratoiston ymmärtäminen” ilmainen?

Kyllä – oppitunnin ”Tokenien suoratoiston ymmärtäminen” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko AI Engineering Academy-kurssin, päivitä CoddyKit PROhon. AI Engineering Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Tokenien suoratoiston ymmärtäminen”?

Ymmärrä, miten streaming API lähettää osittaisia täydennyksiä niiden muodostuessa, miten OpenAI:n stream=True-parametri toimii ja milloin suoratoisto parantaa käyttökokemusta. Harjoittelet AI Engineering Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni AI Engineering Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin AI Engineering Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 1/4.

Kuinka kauan ”Tokenien suoratoiston ymmärtäminen”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä AI Engineering Academy-oppitunnilla?

Kyllä. Jokainen AI Engineering Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Tokenien suoratoiston ymmärtäminen
  2. Suoratoistojen käsittely Python SDK:lla
  3. Suoratoisto FastAPIssa Server-Sent Events -tapahtumilla
  4. Työkalukutsujen käsittely suoratoistovastauksissa
← Takaisin: AI Engineering Academy