Przewodnik i wgląd

Rozliczanie tokenów strumieniowych w bramie API AI: ostateczne wykorzystanie, anulowanie i częściowe odpowiedzi

Przesyłanie strumieniowe poprawia postrzegane opóźnienia, ale może zakłócić analizę wykorzystania sztucznej inteligencji i rozliczenia, jeśli brama przesyła tylko bajty proxy. Oto praktyczny wzorzec maszyny stanu do przechwytywania końcowego użycia, przerwanych strumieni, błędów dostawcy i częściowych odpowiedzi.

Odpowiedzi LLM przesyłane strumieniowo są łatwe do proxy i trudne do prawidłowego wystawienia rachunku. Jeśli brama interfejsu API AI przekazuje zdarzenia wysyłane przez serwer do klienta, ale pierwsze fragmenty traktuje jako zapis użycia, analityka dzierżawy będzie się wahać. Dryf zwykle pojawia się w przypadku sporów, takich jak: „użytkownik zobaczył tylko połowę odpowiedzi”, „dostawca naliczył wyższą opłatę, niż pokazuje nasz pulpit nawigacyjny”, „przydział został zwolniony zbyt wcześnie” lub „przekroczenie limitu czasu wygenerowało tokeny, ale nie było wiersza faktury”.

Głównym problemem jest to, że połączenia przesyłane strumieniowo nie są jednym zdarzeniem. Są to następujące sekwencje: żądanie przyjęte, otwarty strumień nadrzędny, dostarczone bajty, raport końcowego użycia, zatrzymanie dostawcy, odłączenie klienta, przekroczenie limitu czasu bramy i uregulowanie płatności. Niezawodna brama powinna jawnie modelować te stany, zamiast zakładać, że wypełniona odpowiedź HTTP jest jedyną pomyślną ścieżką.

Tryb awarii: przesyłanie strumieniowe ukrywa granicę rozliczeniową

Zakończenia inne niż przesyłane strumieniowo zazwyczaj zwracają jeden obiekt odpowiedzi z metadanymi użycia. Brama może normalizować to użycie, zapisywać wiersze księgi, aktualizować limity i emitować analizy w jednym przebiegu.

Streaming zmienia granice. Doświadczenie użytkownika ma charakter przyrostowy, ale prawda dotycząca rozliczeń może zostać ujawniona na końcu, w zdarzeniu końcowym specyficznym dla dostawcy, w formie skumulowanej delty, poprzez zagregowaną odpowiedź pakietu SDK lub później za pośrednictwem interfejsów API raportowania dostawcy. Jeśli klient rozłączy się przed końcowym zdarzeniem użycia, brama mogła dostarczyć tylko część odpowiedzi, podczas gdy dostawca nadal wygenerował i obciążył większą liczbę tokenów.

Fakt: dokumenty OpenAI wskazują, że osoby wywołujące przesyłające strumieniowo, które chcą danych o użyciu, powinny ustawić stream_options za pomocą include_usage. OpenAI zapewnia również punkty końcowe dotyczące wykorzystania i kosztów na poziomie organizacji, pamiętając jednak, że wykorzystanie i koszty nie zawsze mogą idealnie się zgadzać z powodów finansowych.

Fakt: Przesyłanie strumieniowe antropiczne wykorzystuje zdarzenia wysyłane przez serwer, takie jak message_start, content_block_delta, message_delta i message_stop. Informacje o użyciu message_delta mają charakter zbiorczy, więc brama nie może sumować poszczególnych różnic użytkowania.

Fakt: interfejsy API przesyłania strumieniowego w stylu Gemini i Vertex mogą udostępniać przyrostowe fragmenty, podczas gdy zestawy SDK mogą również udostępniać zagregowany obiekt odpowiedzi. W przypadku bram ta zagregowana ścieżka może być lepszym źródłem pełnego wykorzystania niż same widoczne fragmenty.

Użyj maszyny stanu strumienia, a nie logicznej flagi sukcesu

Żądanie przesyłane strumieniowo powinno mieć trwały zapis użycia przed rozpoczęciem wywołania nadrzędnego. Ten rekord powinien przechodzić przez jawne stany. Praktyczne minimum to:

  • zaakceptowano: brama uwierzytelniła klucz, przypisała dzierżawcę i utworzyła otwarty wiersz księgi.
  • first_byte_sent: co najmniej jedno zdarzenie wyjściowe dotarło do klienta końcowego.
  • provider_completed: dostawca wyższego szczebla wyemitował normalny sygnał stopu lub ukończył obiekt odpowiedzi.
  • client_aborted: gniazdo downstream zostało zamknięte przed normalnym zakończeniem bramy.
  • provider_error: dostawca wyższego szczebla zwrócił błąd po rozpoczęciu transmisji lub przed ostatecznym użyciem.
  • gateway_timeout: brama wykorzystała swój budżet opóźnień i zakończyła żądanie.
  • rozliczone: brama przekonwertowała użycie na koszt najemcy i wykorzystanie przydziału.
  • uzgodnione: późniejsze dane dotyczące wykorzystania lub kosztów dostawcy potwierdziły lub skorygowały wiersz.

Ten model zapobiega typowemu błędowi analitycznemu: oznaczaniu każdego strumienia, który wygenerował tekst, jako „udanego i dokładnego”. Strumień może być przydatny dla użytkownika, niekompletny od dostawcy, szacowany do celów rozliczeniowych i jednocześnie oczekujący na uzgodnienie.

Zalecane pola księgi

Trzymaj wiersz czasu żądania mały, ale wyraźny:

{
  "request_id": "gw_req_...",
  "ident_dzierżawcy": "dzierżawa_123",
  "api_key_id": "klucz_456",
  "dostawca": "openai|antropiczny|gemini|...",
  "provider_request_id": null,
  "model": "identyfikator-modelu dostawcy",
  "stan": "zaakceptowano",
  "strumień": prawda,
  „input_tokens”: null,
  "output_tokens_billed": null,
  "output_tokens_delivered_estimate": 0,
  „provider_usage_source”: null,
  "billing_status": "pending_reconciliation",
  „client_abort_at”: null,
  "provider_completed_at": null,
  "settled_at": null,
  „klasa_błędu”: null

Ważny podział to output_tokens_billed i output_tokens_delivered_estimate. Użytkownikom zależy na tym, co dotarło do ich aplikacji. Finanse dbają o to, co naliczył dostawca. Liczby te mogą się różnić po rozłączeniach, strumieniach wywołań narzędzi, ukrytych tokenach rozumowania, tokenach w pamięci podręcznej, przystankach bezpieczeństwa lub przekroczeniu limitu czasu bramy.

Reguły przechwytywania specyficzne dla dostawcy

Niezależny od dostawcy interfejs API zgodny z OpenAI jest przydatny dla twórców aplikacji, ale adapter bramy nadal wymaga reguł rozliczania specyficznych dla dostawcy.

Przesyłanie strumieniowe zgodne z OpenAI

W przypadku tras OpenAI udostępnij opcję bramy, która umożliwia raportowanie wykorzystania nadrzędnego, jeśli jest to obsługiwane. Typowym wzorcem jest akceptowanie wartości domyślnych na poziomie bramy, takich jak:

{
  "strumień": prawda,
  „opcje_strumienia”: {
    „include_usage”: prawda
  }

Jeśli obiekt wywołujący na dalszym etapie go pominie, brama może zdecydować, czy wstrzyknąć go dla tras, gdzie jest to zgodne. Udokumentuj to zachowanie, ponieważ niektórzy klienci oczekują dokładnej kompatybilności przewodów, a niektóre modele lub starsze wersje mogą nie obsługiwać końcowego użycia w ten sam sposób.

Zalecenie: nie rozliczaj kosztów najemcy z wczesnych fragmentów. Pozostaw wiersz księgi otwarty do czasu przechwycenia końcowego zdarzenia użycia, odpowiedzi dostawcy nie zakończy się bez użycia lub strumień wejdzie w błąd lub ścieżkę anulowania.

Przesyłanie strumieniowe antropiczne

Łączne wykorzystanie Anthropic wymaga innej reguły. Jeśli brama widzi trzy zdarzenia message_delta z liczbą tokenów wyjściowych 10, 25 i 40, liczba wyjściowa wynosi 40, a nie 75.

let lastUsage = null;
for Wait (zdarzenie stałe anthropicStream) {
  if (event.type === "message_delta" && event.usage) {
    if (latestUsage && event.usage.output_tokens < najnowszeUsage.output_tokens) {
      emit("cumulative_usage_regressed", identyfikator żądania);
    }
    najnowszeUsage = zdarzenie.usage;
  }
  forwardToClient(zdarzenie);
}
rozliczaćFromLatestCumulativeUsage(latestUsage);

Zalecenie: zapisz najnowszą skumulowaną wartość użytkowania i wyemituj zdarzenie obserwowalności, jeśli się cofnie. Regresja może wskazywać błędy analizatora składni, zduplikowane zdarzenia, zmiany dostawcy lub strumienie mieszane.

Przesyłanie strumieniowe w stylu Gemini i Vertex

Gemini obsługuje strumieniowe przesyłanie fragmentów, aby zmniejszyć postrzegane opóźnienia. W zestawach SDK w stylu Vertex przesyłanie strumieniowe może udostępniać zarówno strumień asynchroniczny, jak i zagregowany obiekt odpowiedzi. Brama powinna zachować tę zagregowaną ścieżkę, jeśli jest dostępna.

const streamingResult = czekaj na model.generateContentStream(żądanie);
for Wait (stała część streamingResult.stream) {
  forwardChunk(fragment);
  countDeliveredBytesOrText(fragment);
}
const zagregowane = czekaj na streamingResult.response;
rozliczaćFromAggregatedUsage(zagregowane);

Zalecenie: unikaj budowania całego rozliczenia na podstawie widocznych fragmentów, jeśli pakiet SDK podaje kompletny rekord odpowiedzi. Kawałki służą do opóźnienia. Ostateczny obiekt jest często lepszy do celów rozliczeniowych i analitycznych.

Traktuj rozłączenia klientów jako pierwszorzędne zdarzenia księgowe

Odłączanie klientów ma miejsce wtedy, gdy wiele bramek traci pieniądze lub obciąża klientów nadmiernymi kosztami. Karta przeglądarki zamyka się, sieć komórkowa przestaje działać lub aplikacja anuluje żądanie. Brama zauważa, że gniazdo odbiorcze jest zamknięte, ale dostawca nadrzędny może nadal generować.

Brama powinna wyraźnie wybierać zasady:

  • Natychmiast anuluj przesyłanie strumieniowe: zmniejsza straty związane z generowaniem i kosztami dostawcy, ale może przerwać przepływy pracy, w których backend nadal potrzebuje wyniku po rozłączeniu interfejsu użytkownika.
  • Kontynuuj przesyłanie danych w tle: może zachować pracę klientów po stronie serwera, ale użytkownik może nie widzieć wszystkich wygenerowanych i rozliczonych tokenów.
  • Zachowanie zależne od trasy: anuluj dla czatu interaktywnego, kontynuuj dla przepływów pracy przypominających pracę i udostępnij ustawienie jako widoczne dla najemców.

Praktycznym ustawieniem domyślnym w przypadku interaktywnego przesyłania strumieniowego jest anulowanie przesyłania danych w górę, gdy klient podrzędny rozłączy się, a następnie oznaczenie wiersza księgi jako client_aborted. Jeśli ostateczne wykorzystanie nastąpi w trakcie anulowania, rozlicz się na podstawie tego autorytatywnego wykorzystania. Jeśli nie, zaznacz wiersz szacowany lub pending_reconciliation, zamiast udawać, że jest dokładny.

downstream.on("close", async () => {
  if (!providerCompleted) {
    ledger.markClientAborted(identyfikator żądania);
    czekaj na upstream.abort().catch(() => {
      ledger.emit("upstream_cancel_failed", identyfikator żądania);
    });
  }
});

Zalecenie: eksponuj przejrzyste etykiety rozliczeniowe, takie jak final, provider_reconciled, szacowany, zwolniony lub pending_reconciliation. Można to lepiej obronić niż pokazywać, że każde przesyłane strumieniowo połączenie jest bezpośrednio dokładne.

Egzekwowanie limitów podczas transmisji

Dokładne rozliczenia zwykle zależą od końcowego wykorzystania usług przez dostawcę, ale egzekwowanie limitów nie zawsze może czekać do końca. Najemca z ograniczonym budżetem nie powinien mieć możliwości strumieniowego przesyłania strumieniowego w nieskończoność, ponieważ dokładne informacje o wykorzystaniu nie są dostępne w trakcie transmisji.

Użyj jednocześnie dwóch mechanizmów:

  1. Rezerwacja wstępna: zarezerwuj szacunkową kwotę maksymalną na podstawie modelu, żądanej maksymalnej liczby tokenów, zasad najemcy i bieżącego salda.
  2. Kontrola ciśnienia przesyłania strumieniowego: oszacuj dostarczony wynik podczas strumienia i zatrzymaj, jeśli żądanie przekroczy skonfigurowaną granicę bezpieczeństwa.

To jest mechanizm kontrolny, a nie ostateczny rachunek. Dostawcy mogą liczyć tokeny przechowywane w pamięci podręcznej, tokeny wnioskowania, tokeny multimodalne lub tokeny ukryte w inny sposób niż estymator bramy.

Kompromis: szacunki w czasie rzeczywistym pomagają egzekwować budżety, ale mogą odbiegać od tokenów rozliczanych przez dostawcę. W ostatecznym rozliczeniu należy użyć wiarygodnego dostawcy, jeśli jest dostępny, a w ramach uzgodnienia należy później skorygować szacunki.

Zdarzenia obserwowalności, które wyłapują błędy księgowe

Błędy rozliczeń podczas przesyłania strumieniowego są łatwiejsze do debugowania, gdy brama emituje ukierunkowane zdarzenia, a nie tylko ogólne dzienniki żądań. Dodaj zdarzenia takie jak:

  • final_usage_missing: strumień zakończył się bez autorytatywnego użycia.
  • cumulative_usage_regressed: skumulowana liczba tokenów przesunięta do tyłu.
  • stream_ended_without_stop_event: nie zaobserwowano normalnego znacznika zatrzymania dostawcy.
  • aborted_after_provider_completion: dostawca zakończył działanie, ale klient podrzędny został zamknięty, zanim brama zakończyła przekazywanie.
  • settled_from_estimate: księga najemców użyła szacunków, ponieważ ostateczne wykorzystanie było niedostępne.
  • reconciliation_justed_usage: dostawca zgłaszający później zmienił ten wiersz.

Fakt: Konwencje semantyczne OpenTelemetry GenAI zalecają używanie informacji o użytkowaniu zwracanych przez dostawcę do przesyłania strumieniowego odpowiedzi, jeśli są dostępne, i ostrzegają przed raportowaniem wskaźników użytkowania, jeśli nie można wydajnie i dokładnie uzyskać liczby tokenów.

W przypadku analityki wykorzystania sztucznej inteligencji oznacza to, że pulpity nawigacyjne powinny obsługiwać poziomy ufności. Wykres zawierający wartości ostateczne, szacunkowe i uzgodnione bez etykiet może wyglądać czysto, ale wprowadzać w błąd zespoły finansowe i wsparcie.

Testy zgodności dotyczące rozliczania strumieniowego

Nie polegaj na ręcznych testach za pomocą komunikatu na czacie Happy-Path. Każdy adapter dostawcy powinien mieć testy zgodności dla przypadków, które łamią księgi:

  • Normalny strumień: nadchodzi ostateczne wykorzystanie, zaobserwowano zdarzenie zatrzymujące, księga zostaje uznana za końcową.
  • Strumień wywołań narzędzi: delta wywołań narzędzi jest przekazywana dalej, rejestrowane jest użycie, uporządkowane metadane nie zakłócają zliczania tokenów.
  • Zatrzymanie ze względów bezpieczeństwa lub odmowa: dostawca zatrzymuje się wcześniej, wykorzystanie nadal jest ustalane prawidłowo.
  • Wymuszone odłączenie klienta: downstream zostaje zamknięty po częściowym wyjściu; upstream został anulowany lub kontynuowany zgodnie z polityką.
  • Przesyłanie 5xx po częściowym wyjściu: brama rejestruje częściowe dostarczenie i nie oznacza żądania jako całkowitego powodzenia.
  • Upłynął limit czasu bramy przed ostatecznym użyciem: wiersz jest szacunkowy lub oczekuje na uzgodnienie.
  • Brak zdarzenia końcowego: adapter emituje final_usage_missing i unika dokładnych etykiet rozliczeniowych.

Testy te powinny potwierdzać zmiany stanów, pola księgi, wyemitowane zdarzenia obserwowalności i dalsze zachowanie. Zgodność strumienia bajt po bajcie nie wystarczy; skutki uboczne księgowe są częścią umowy.

Praktyczna lista kontrolna wdrożenia

  • Utwórz wiersz księgi użytkowania przed wysłaniem żądania nadrzędnego.
  • Przechowuj identyfikatory dzierżawcy, klucza, użytkownika, modelu, trasy, dostawcy i żądań w momencie żądania.
  • Włącz raportowanie końcowego wykorzystania dostawcy, jeśli jest obsługiwane, na przykład stream_options.include_usage zgodne z OpenAI.
  • W przypadku dostawców zbiorczych przechowuj najnowszą wartość użycia zamiast sumować zdarzenia.
  • Zachowaj zagregowane obiekty odpowiedzi, gdy udostępniają je pakiety SDK.
  • Śledź dostarczone wyniki niezależnie od wykorzystania rozliczanego przez dostawcę.
  • Po rozłączeniu anuluj przesyłanie dalej zgodnie z zasadami trasy i zaznacz client_aborted.
  • Używaj przejrzystych statusów rozliczeń: ostateczny, szacunkowy, oczekujący na uzgodnienie, dostawca uzgodniony lub zniesiony.
  • Emituj zdarzenia obserwowalne specyficzne dla księgowości.
  • Uzgodnij później z raportami dotyczącymi wykorzystania dostawcy lub kosztami, jeśli są dostępne, zachowując jednocześnie przypisanie najemców w czasie żądania.

Co pokazać najemcom

Najemcy nie potrzebują każdego wydarzenia wewnętrznego, ale potrzebują uczciwych etykiet. Przydatna tabela użycia może zawierać:

  • Stan: ostateczny, szacunkowy lub uzgodniony.
  • Wynik żądania: ukończony, klient przerwany, błąd dostawcy lub przekroczono limit czasu bramy.
  • Dostarczone dane wyjściowe: przybliżony tekst lub bajty wysłane do klienta.
  • Tokeny płatne: użycie znormalizowane przez dostawcę używane do obliczania kosztów.
  • Korekta: jakakolwiek późniejsza delta uzgodnienia.

Taki projekt zmniejsza niejednoznaczność wsparcia. Jeśli użytkownik zobaczył tylko część odpowiedzi, pulpit nawigacyjny może wyjaśnić, czy dostawca już zakończył współpracę, czy bramka została anulowana wcześniej i czy opłata jest ostateczna czy szacunkowa.

Zalecenia a przewidywania

Zalecenia: traktuj żądania przesyłane strumieniowo jak maszyny stanu, poczekaj na wiarygodne ostateczne wykorzystanie przed dokładnym rozliczeniem, oddziel dostarczone dane wyjściowe od naliczonego użycia i uczciwie oznacz szacunkowe wiersze. Adaptery dostawców powinny kodować semantykę użycia specyficzną dla dostawcy, zamiast spłaszczać każdy strumień do ogólnego bajtowego serwera proxy.

Przewidywanie: księgowanie strumieniowe stanie się ważniejsze, gdy modele ujawnią więcej ukrytej pracy: tokeny wnioskowania, rabaty za tokeny w pamięci podręcznej, przetwarzanie multimodalne, ślady użycia narzędzi i przystanki bezpieczeństwa. Bramy, które już oddzielają wykorzystanie rozliczane przez dostawcę od wyników widocznych dla klienta, dostosują się łatwiej niż bramy, które zliczają tylko przesyłany strumieniowo tekst.

Wniosek, który można zastosować

Jeśli Twoja brama obsługuje przesyłanie strumieniowe, przeprowadź już dziś audyt jednej ścieżki: wymuś rozłączenie klienta po kilku pierwszych fragmentach i sprawdź wiersz księgi. Jeśli jest napisane „sukces” z dokładnie wyglądającą liczbą tokenów, Twoje analizy prawdopodobnie kłamią.

Poprawką nie jest rezygnacja z przesyłania strumieniowego. Zachowaj wygodę użytkownika, ale wyraźnie zaznacz stany rozliczeniowe dotyczące zakończenia strumienia, anulowania, błędów dostawcy, braku końcowego użycia i uzgodnienia. Daje to zespołom odpowiedzialnym za produkt elastyczne wyniki, zespołom finansowym możliwe do obrony koszty, a zespołom wsparcia wystarczające dowody, aby wyjaśnić częściowe odpowiedzi bez zgadywania.

Powiązane lektury

FAQ

Często zadawane pytania

Czy rachunek za bramkę powinien przesyłać strumieniowo odpowiedzi na podstawie szacunkowej liczby tokenów?
W razie potrzeby korzystaj z szacunków w celu ochrony przydziałów w czasie rzeczywistym, ale rozliczaj dokładne koszty dzierżawy na podstawie wykorzystania zwróconego przez dostawcę, jeśli są dostępne. Jeżeli brakuje ostatecznego wykorzystania, oznacz wiersz jako szacunkowy lub oczekujący na uzgodnienie.
Dlaczego dostarczone tokeny wyjściowe mogą różnić się od tokenów płatnych?
Klient może się rozłączyć, brama może przekroczyć limit czasu, dostawca może zliczyć ukryte tokeny rozumowania lub multimodalne lub dostawca może zakończyć generowanie, gdy użytkownik przestanie odbierać bajty. Śledź dostarczone dane wyjściowe niezależnie od wykorzystania rozliczanego przez dostawcę.
Jaki jest najczęstszy błąd związany z rozliczaniem przesyłania strumieniowego w Anthropic?
Sumowanie skumulowanych zdarzeń użytkowania. Liczniki użycia Anthropic Message_delta są kumulowane, dlatego brama powinna przechowywać najnowszą wartość, a nie dodawać każde zdarzenie.
Co powinno się stać, gdy przeglądarka rozłączy się podczas transmisji?
W przypadku tras interaktywnych praktycznym rozwiązaniem domyślnym jest anulowanie żądania nadrzędnego, oznaczenie wiersza księgi jako klient_przerwany i rozliczenie tylko na podstawie użycia autorytatywnego dostawcy, jeśli nadejdzie. W przeciwnym razie zaznacz wiersz szacunkowy lub oczekujący na uzgodnienie.