Routing wielomodelowy w agentach programistycznych
Routing między modelami niszczy cache promptu i zwiększa koszty operacyjne. Sprawdź, kiedy przypisanie modelu na sesję jest bardziej opłacalne niż dynamiczne przełączanie.
Wpływ routingu wielomodelowego na agenta programistycznego
Routing wielomodelowy kieruje każde żądanie do najtańszego modelu zdolnego do jego obsłużenia. W przypadku ruchu czatowego rozwiązanie to sprawdza się dobrze. W agencie programistycznym zazwyczaj generuje wyższe koszty, niż pozwala zaoszczędzić, ponieważ rachunek za agenta jest zdominowany przez prefiks promptu, który jest buforowany dla każdego modelu, a przełączanie modeli powoduje utratę tego bufora.
Zasada, której broni ten wpis: kieruj ruch między dostawcami w celu zapewnienia dostępności, kieruj ruch między poziomami cenowymi w celu optymalizacji kosztów tylko na granicach zadań i przypisuj jeden model na sesję dla wszelkich działań agentowych. Poniżej przedstawiono uzasadnienie.
Cztery terminy, zdefiniowane raz. Router wybiera model dla każdego żądania. Gateway to proxy, przez które przechodzi żądanie i które może, ale nie musi, pełnić funkcję routingu. Prompt cache to mechanizm dostawcy przechowujący przetworzony prefiks promptu, dzięki czemu późniejsze żądanie powtarzające ten prefiks jest rozliczane jako ułamek ceny wejściowej. KV cache (key value cache) to ta sama koncepcja wewnątrz serwera uruchamianego samodzielnie.
Dlaczego ruch czatu jest poprawnie trasowany, a ruch agentów nie
Żądanie czatu to pojedyncza wymiana informacji. Przychodzi, jest klasyfikowane, trafia do modelu i wraca. Żadne dane nie są przenoszone do kolejnego żądania. Router może wysłać to pytanie do małego modelu, a kolejne do dużego, przy czym żadne z żądań nie "wie" o istnieniu poprzedniego. Jest to obciążenie, które mierzy niemal każdy benchmark trasowania, a dobre routery radzą sobie z tym zadaniem bardzo dobrze.
Tura agenta nie jest pojedynczym żądaniem. Jedna instrukcja, taka jak "napraw niedziałający test", przekłada się na od dwudziestu do sześćdziesięciu wywołań API. Każde wywołanie ponownie przesyła całą konwersację: prompt systemowy, definicje wszystkich narzędzi, każdy plik odczytany przez agenta oraz dane wyjściowe wszystkich wykonanych poleceń. Kontekst tylko rośnie. Przy trzydziestym wywołaniu powtarzany prefiks może liczyć dziesiątki tysięcy tokenów, podczas gdy faktycznie nowa treść w każdym wywołaniu to zaledwie kilkaset tokenów.
Taki kształt danych zmienia znaczenie słowa "kosztowny". W przypadku czatu koszt jest w przybliżeniu iloczynem ceny modelu i liczby żądań. W pętli agenta kosztem jest prefiks, za który płaci się przy każdym pojedynczym wywołaniu. Dalsza część tego wpisu wynika bezpośrednio z tego jednego faktu.
Pamięć podręczna promptu jest przypisana do modelu, a agent znajduje się w jejewnątrz
Anthropic wycenia odczyt z pamięci podręcznej na 0.1 ceny bazowej wejścia, a zapis trwający pięć minut na 1.25 ceny bazowej. Są to oficjalne ceny katalogowe, aktualne na sierpień 2026 roku.
The data behind this chart
[
{
"label": "Opus 5",
"uncached_input_usd": "5.00",
"cache_read_usd": "0.50"
},
{
"label": "Sonnet 5",
"uncached_input_usd": "2.00",
"cache_read_usd": "0.20"
},
{
"label": "Haiku 4.5",
"uncached_input_usd": "1.00",
"cache_read_usd": "0.10"
}
]Należy porównać drugą serię z pierwszą, analizując wiersze, a nie kolumny. Odczyt z pamięci podręcznej w modelu Opus 5 kosztuje 0.50 dolarów za milion tokenów. Wejście bez użycia pamięci podręcznej w modelu Haiku 4.5, najtańszym z wymienionych, kosztuje 1.00 dolarów. Oznacza to, że ponowne odczytanie rozgrzanego prefiksu w najdroższym modelu kosztuje mniej za token wejściowy niż odczytanie tego samego prefiksu w stanie zimnym w modelu najtańszym.
To porównanie podważa większość planów routingu. Router przenoszący zadania do niższej warstwy porównuje ceny katalogowe. Jednak agent w trakcie sesji nie płaci ceny katalogowej za model, którego już używa. Płaci cenę odczytu z pamięci podręcznej, która jest już niższa niż stawka za wejście bez pamięci podręcznej w tanim modelu.
Pamięci podręczne są indeksowane na podstawie hasza prefiksu promptu i są przypisane do konkretnego modelu. Żądanie skierowane do innego modelu jest haszowane względem magazynu, który go nie zawiera, więc nie znajduje dopasowania i wymaga zapłaty pełnej ceny. Pamięć podręczna ma również strukturę hierarchiczną: najpierw narzędzia, potem system, a na końcu wiadomości. Zmiana na dowolnym poziomie unieważnia ten poziom oraz wszystko, co po nim następuje. Oznacza to, że edycja definicji jednego narzędzia powoduje odrzucenie pamięci podręcznej promptu systemowego znajdującej się za nim. Agenci rejestrujący narzędzia w czasie wykonywania napotykają ten problem bez konieczności korzystania z routera.
Rzeczywisty koszt pojedynczego przełączenia w trakcie sesji
Przyjmijmy sesję ze stałym prefiksem o długości 40,000 tokenów – jest to typowy rozmiar po przeczytaniu przez agenta kilku plików. Poniżej przedstawiono koszt prefiksu dla pojedynczej tury, obliczony na podstawie powyższych cen katalogowych.
The data behind this chart
[
{
"label": "Opus 5, cache warm",
"prefix_cost_usd": "0.020"
},
{
"label": "Sonnet 5, turn after switch",
"prefix_cost_usd": "0.100"
},
{
"label": "Opus 5, cache re-warmed",
"prefix_cost_usd": "0.250"
}
]Pozostanie przy Opus 5 z rozgrzaną pamięcią podręczną kosztuje 0.020 dolara za prefiks w tej turze. Pierwsza tura po przekierowaniu do Sonnet 5 kosztuje 0.100 dolara, ponieważ Sonnet nie posiada wpisu dla tego prefiksu i musi go zapisać. Powrót do Opus 5 kosztuje 0.250 dolara, ponieważ pierwotny wpis wygasł podczas nieobecności sesji.
W rezultacie pełny cykl wiąże się z dwoma zapisami do pamięci podręcznej, aby uniknąć dwóch odczytów. W zamian przełączenie pozwoliło uzyskać jedną turę wyjściową w cenie wyjściowej Sonnet zamiast Opus. Blok szczegółów przedstawia pełny przebieg operacji: oszczędności wynoszą ułamki centa, podczas gdy kara za pamięć podręczną liczona jest w dziesiątkach centów. Kara jest o ponad rząd wielkości wyższa i rośnie wraz z długością prefiksu, podczas gdy oszczędności pozostają stałe.
Jak obliczono te wartości
Każda liczba tutaj jest wynikiem działań arytmetycznych na opublikowanych cenach katalogowych z pierwszej tabeli. Jest to model kosztów, a nie benchmark; do jego przygotowania nie wysłano żadnych zapytań. Zmiana rozmiaru prefiksu zmienia również proporcje.
Prefiks: 40,000 tokenów, utrzymywany jako stała wartość w trakcie tury.
Opus 5, warm read 40,000 x $0.50 / 1e6 = $0.020
Sonnet 5, cache write 40,000 x $2.50 / 1e6 = $0.100 (1.25 x $2 base)
Opus 5, cache write 40,000 x $6.25 / 1e6 = $0.250 (1.25 x $5 base)Pełny cykl w obie strony: $0.100 + $0.250 = $0.350. Koszt dwóch tur z rozgrzanym Opus, które zostały zastąpione: $0.040. Dodatkowy koszt zmiany modelu: $0.310.
Oszczędność przy jednej turze z 800 tokenami wyjściowymi wynika z różnicy w cenie wyjściowej między Opus 5 ($25 za milion) a Sonnet 5 ($10 za milion):
800 x ($25 - $10) / 1e6 = $0.012Wydanie $0.310 w celu zaoszczędzenia $0.012 jest około dwudziestopięciokrotnie nieopłacalne. Oszczędności skalują się wraz z liczbą tokenów wyjściowych, które są niewielkie i w przybliżeniu stałe dla każdej tury. Kara skaluje się wraz z rozmiarem prefiksu, który rośnie przez całą sesję. Dłuższe sesje pogarszają tę sytuację, nigdy jej nie poprawiając.
Formaty wywołań narzędzi różnią się w zależności od dostawcy
Agent jest pętlą wywołań narzędzi, dlatego format wywołania ma znaczenie, jakiego nie posiada w przypadku zwykłego czatu. API Messages firmy Anthropic zwraca blok zawartości tool_use i oczekuje zwrotnego bloku tool_result. API kompatybilne z OpenAI zwracają tablicę tool_calls, w której function.arguments jest ciągiem znaków zakodowanym w formacie JSON, a nie zagnieżdżonym obiektem. Bramka tłumaczy te formaty między sobą, a w przypadku standardowych wywołań translacja przebiega bezproblemowo.
Problemy pojawiają się na granicach specyfikacji. Równoległe wywołania narzędzi, w których model generuje kilka wywołań w jednej odpowiedzi, są reprezentowane w różny sposób i nie są wszędzie obsługiwane identycznie. Ścisłe wymuszanie schematu jest funkcją zależną od dostawcy, więc model gwarantujący poprawność argumentów względem schematu w jednym punkcie końcowym, w innym może jedynie dążyć do poprawności. Agent interpretuje różnicę jako wynik narzędzia zawierający błąd parsowania, który następnie próbuje naprawić w kolejnej turze. Te tury naprawcze są rozliczane według pełnej ceny za prefiks, więc niedopasowanie formatu jest widoczne zarówno w transkrypcji, jak i na fakturze.
Punkty końcowe hostowane samodzielnie wymagają jawnej konfiguracji. Serwer vLLM kompatybilny z OpenAI wymaga parametru --enable-auto-tool-choice wraz z ustawieniem --tool-call-parser dopasowanym do rodziny modelu (hermes, mistral, llama3_json i inne), a także szablonu czatu obsługującego komunikaty o roli narzędzia. Dokumentacja vLLM jasno określa ograniczenia tej ścieżki: przy użyciu tool_choice="auto" i braku ścisłego ograniczenia schematu, vLLM wyodrębnia wywołania narzędzi z surowego tekstu, więc argumenty mogą czasami być błędne lub naruszać schemat parametrów funkcji. Wybór niewłaściwego parsera dla danego modelu jest błędem konfiguracyjnym, który objawia się jako agent niezdolny do wywoływania narzędzi, o czym warto wiedzieć przed skierowaniem do niego ruchu. W tym kontekście istotna jest różnica między Ollama a vLLM w samodzielnym serwowaniu modeli, ponieważ oba rozwiązania udostępniają wywoływanie narzędzi na różnych zasadach.
Zmiana zachowania w trakcie zadania przy użyciu mechanizmu fallback bez zgłaszania błędu
Routing typu fallback to funkcja, którą najłatwiej włączyć przypadkowo. Brama (gateway) jest konfigurowana tak, aby ponowić próbę na innym modelu, gdy pierwszy zwróci błąd limitu szybkości lub kod 5xx, a następnie umieszcza nieudany model w stanie cooldown na kilka sekund. W przypadku ruchu czatowego jest to rozwiązanie poprawne. Wewnątrz długiego zadania agenta oznacza to jednak, że druga połowa zadania została wykonana przez model, którego nie wybrano.
Żaden mechanizm nie raportuje tego zdarzenia. Zadanie nie kończy się błędem, agent nie wysyła ostrzeżenia, a status wyjścia wskazuje na sukces. Wynikiem jest zadanie, w którym plan został przygotowany przez jeden model, a edycje wykonane przez inny, co skutkuje zmianą tonu i nawyków w połowie pracy. Jedynym wiarygodnym sygnałem jest pole model w logu żądań bramy lub w metadanych odpowiedzi. Jeśli stosowane są mechanizmy fallback, należy logować to pole dla każdego żądania i analizować je w przypadku nieoczekiwanych wyników. Debugowanie zachowania bez wiedzy o tym, który model je wygenerował, marnuje więcej czasu, niż zaoszczędzono dzięki fallbackowi.
Ta sama pułapka dotyczy kompresji kontekstu. Wiele agentów podsumowuje długą historię, wywołując mniejszy model. Jeśli to wywołanie korzysta z innego modelu lub innego system prompt, zapisuje własny wpis w pamięci podręcznej i nie odświeża głównej sesji, co sprawia, że kolejna pełna tura wymaga ponownego rozgrzania cache. Kompresja pozwoliła zaoszczędzić tokeny, ale doprowadziła do utraty pamięci podręcznej.
Narzut routingu jest faktem, ale to nie opóźnienia stanowią główny problem
Routery zwiększają nakład pracy na każde żądanie i warto precyzyjnie określić jego skalę. DigitalOcean podaje, że ich model Arch-Router rozpoznaje intencję routingu w około 51 milisekund, przy skuteczności routingu na poziomie 93.17% w ich własnej ocenie. Są to ich dane, pochodzące z ich pomiarów i benchmarków, a nie nasze ani uniwersalne wyniki. Przyjmując je za dobrą monetę, wniosek jest uspokajający: 51 milisekund na czterdzieści wywołań agenta to około dwie sekundy dodane do zadania, które trwa kilka minut.
To nie te dwie sekundy sprawiają, że routing jest kosztowny. Narzutem, który faktycznie szkodzi, jest router klasyfikujący za pomocą pełnego wywołania modelu, ponieważ oznacza to drugą inferencję przy każdym żądaniu, rozliczaną i kolejkowaną tak samo jak każda inna. U podstaw obu tych kwestii leży wspomniana wcześniej arytmetyka pamięci podręcznej, która wcale nie jest narzutem. Jest to koszt elementu, który routing miał optymalizować.
Na serwerze zarządzanym samodzielnie obowiązuje ta sama zasada, przy czym margines błędu jest mniejszy. Lokalnym odpowiednikiem pamięci podręcznej promptów jest buforowanie prefiksów w KV cache, który znajduje się w pamięci GPU. Hostowanie dwóch modeli na jednym GPU dzieli tę pamięć między nie, więc każdy z nich utrzymuje mniejszy KV cache i szybciej usuwa prefiksy. Routing między dwoma lokalnymi modelami może zatem jednocześnie obniżyć współczynnik trafień w pamięci podręcznej dla obu. Jeśli planujesz zasoby sprzętowe pod tym kątem, pamięć i CPU, których agent programistyczny faktycznie potrzebuje na VPS to bardziej użyteczny punkt wyjścia niż router.
Reguła decyzyjna
- Kieruj ruch między dostawcami w celu zapewnienia dostępności. Gdy alternatywą jest nieudane żądanie, każdy koszt jest uzasadniony. Przypisz mechanizm awaryjny (fallback) do modelu o tym samym formacie wywołań narzędzi, aby pętla agenta działała bez zakłóceń, i rejestruj, który model obsłużył każde wywołanie.
- Kieruj ruch między poziomami (tiers) w celu optymalizacji kosztów tylko na granicach zadań. Wybór Haiku do zmiany nazwy i Opus do refaktoryzacji to dobra decyzja podjęta raz, przed rozpoczęciem sesji. Jest to zła decyzja, jeśli zostanie podjęta w trzydziestej turze tej samej sesji.
- Przypisz jeden model na sesję dla wszelkich działań agentowych. Wartością sesji jest jej rozgrzana pamięć podręczna (warm cache). Zmianę modelu traktuj tak samo, jak czyszczenie tej pamięci, ponieważ dokładnie to się dzieje.
- Kieruj podagentami bez ograniczeń. Podagent, który rozpoczyna pracę ze świeżym, niewielkim kontekstem, nie posiada rozgrzanej pamięci podręcznej do utracenia, więc może działać na dowolnym modelu odpowiednim do jego zadania. Jest to jedyne miejsce wewnątrz agenta, gdzie routing jest praktycznie bezkosztowy.
W kwestii implementacji, bramka (gateway) wykonuje całą pracę: aliasy modeli i jawne listy awaryjne. Minimalna konfiguracja proxy LiteLLM wygląda następująco.
model_list:
- model_name: agent-primary
litellm_params:
model: anthropic/claude-opus-5
api_key: os.environ/ANTHROPIC_API_KEY
- model_name: agent-standby
litellm_params:
model: anthropic/claude-sonnet-5
api_key: os.environ/ANTHROPIC_API_KEY
router_settings:
fallbacks: [{"agent-primary": ["agent-standby"]}]
num_retries: 2
cooldown_time: 30Skieruj agenta na agent-primary, a pozostanie on przy jednym modelu, dopóki ten nie stanie się nieosiągalny. Oba wpisy znajdują się u tego samego dostawcy, więc format wywołań narzędzi nie zmienia się w momencie aktywacji mechanizmu awaryjnego. W tym momencie akceptujesz zmianę poziomu (tier), co jest kompromisem opłacalnym tylko dlatego, że alternatywą jest nieudane żądanie. Jest to routing oparty na dostępności bez powiązanego routingu kosztowego, co stanowi kombinację pożądaną przez większość agentów programistycznych. Pełna konfiguracja, w tym klucze i budżety, została opisana w uruchamianiu własnej bramki LiteLLM na własnym VPS, dlatego niniejszy wpis celowo jej nie powtarza.
Kiedy jeden dobrze dobrany model przewyższa każdy router
Routing jest rozwiązaniem problemu zmienności trudności żądań. Agent programistyczny wykazuje mniejszą zmienność, niż mogłoby się wydawać, ponieważ kosztowną częścią każdego wywołania jest ten sam prefiks, niezależnie od treści zapytania. Gdy prefiks dominuje, różnica między tańszą a droższą klasą modelu maleje do różnicy w cenach generowania wyjścia, a wyjście stanowi niewielki udział w całkowitej liczbie tokenów agenta.
Uczciwym domyślnym podejściem jest zatem jeden model, wybrany raz, z włączonym buforowaniem i czasem TTL (time to live) wystarczająco długim, aby pokryć przerwy na analizę różnic w kodzie. Anthropic oferuje zapis w pamięci podręcznej na jedną godzinę przy koszcie 2 razy wyższym niż bazowe wejście, co zwraca się już po dwóch odczytach. Często jest to skuteczniejsza dźwignia niż jakikolwiek router. Wybierz klasę modelu świadomie, korzystając z bezpośredniego porównania Opus, Sonnet i Haiku, a jeśli rachunki nadal stanowią problem, ogranicz je za pomocą budżetów i mniejszych kontekstów, zgodnie z opisem w kontrolowaniu kosztów agenta AI na VPS, zamiast stosować przełączanie w trakcie sesji.
Stosuj routing, gdy żądania są niezależne i krótkie lub gdy subagenci rozpoczynają pracę z nowymi kontekstami. Przypisuj model na stałe, gdy jedna długa sesja wykonuje jedno zadanie. Większość pracy agenta programistycznego należy do tego drugiego typu, dlatego router, który oszczędza pieniądze w produkcie typu czat, tutaj po cichu generuje dodatkowe koszty. Jeśli nie podjęto jeszcze decyzji co do samego agenta, porównanie Claude Code z Cursor, Codex i Copilot omawia sposób, w jaki każde z tych narzędzi obsługuje wybór modelu, a niektóre z nich podejmują tę decyzję za użytkownika.
FAQ
Czy zmiana modelu w trakcie sesji powoduje utratę pamięci podręcznej promptu?
Tak. Pamięć podręczna promptu jest indeksowana za pomocą skrótu prefiksu promptu i przechowywana osobno dla każdego modelu. Żądanie wysłane do innego modelu jest sprawdzane w pamięci, która nie zawiera tego prefiksu. System nie znajduje dopasowania, nalicza pełną opłatę za wejście bez użycia pamięci podręcznej, a następnie, jeśli buforowanie jest włączone, nalicza opłatę za zapis w pamięci. Powrót do poprzedniego modelu również nie przywraca wpisu, ponieważ domyślny czas życia wynoszący pięć minut zazwyczaj już upłynął. Należy sprawdzić pola cache_read_input_tokens oraz cache_creation_input_tokens w obiekcie użycia odpowiedzi: objawem jest tura, w której odczytano zero tokenów z pamięci podręcznej przy długiej sesji.
Czy kierowanie zadań do tańszego modelu jest zawsze bardziej opłacalne dla agenta?
Tylko wtedy, gdy nie ma aktywnej pamięci podręcznej, którą można by utracić. Odczyt z pamięci podręcznej w Anthropic kosztuje 0,1 stawki podstawowej za wejście, co sprawia, że odczyt z modelu Opus 5 jest tańszy niż niebuforowane wejście w modelu Haiku 4,5. Gdy sesja posiada duży buforowany prefiks, aktualnie używany model staje się najtańszą opcją pod względem kosztów wejścia. Przekierowanie opłaca się, gdy kontekst jest świeży i niewielki: na początku zadania lub w subagencie, który przenosi tylko niezbędny kontekst.
Dlaczego zachowanie agenta zmieniło się w połowie zadania?
Należy sprawdzić, czy zadziałał mechanizm przełączania awaryjnego bramy (gateway fallback). Limit szybkości lub błąd 5xx w modelu głównym powoduje, że brama ponawia próbę na modelu zapasowym i wprowadza model główny w stan chłodzenia na kilka sekund, więc reszta zadania jest wykonywana gdzie indziej. Nie generuje to błędu ani ostrzeżenia, a zadanie nadal jest raportowane jako zakończone sukcesem. Pole model w dzienniku żądań bramy lub metadanych odpowiedzi jest jedynym wiarygodnym zapisem, dlatego należy logować je dla każdego żądania, jeśli używane są mechanizmy przełączania awaryjnego.
Czy wywołania narzędzi działają tak samo u każdego dostawcy?
Nie do końca. API Messages w Anthropic używa bloków zawartości tool_use i tool_result, podczas gdy API kompatybilne z OpenAI używa tablicy tool_calls, której function.arguments jest ciągiem zakodowanym w formacie JSON. Brama dobrze tłumaczy typowe przypadki, ale równoległe wywołania narzędzi i ścisłe wymuszanie schematów różnią się w zależności od dostawcy. W przypadku samodzielnie hostowanego vLLM należy ustawić --enable-auto-tool-choice oraz --tool-call-parser pasujące do rodziny modelu. Dokumentacja vLLM wskazuje, że bez ścisłego ograniczenia schematu serwer wyodrębnia wywołania narzędzi z surowego tekstu, więc argumenty mogą czasami być niepoprawnie sformatowane.
Jak długi czas TTL pamięci podręcznej ustawić dla sesji programistycznej?
Dla ciągłej pracy należy użyć domyślnego czasu życia wynoszącego pięć minut, a opcji jednej godziny, gdy człowiek analizuje różnice (diffs) między turami. Anthropic wycenia zapis pięciominutowy na 1,25 stawki podstawowej wejścia, a zapis godzinny na 2 stawki, przy koszcie odczytu wynoszącym 0,1 stawki. Koszt zapisu pięciominutowego zwraca się przy pojedynczym odczycie, a zapisu godzinnego przy dwóch. W każdej sesji, w której przewidywany jest powrót i kontynuacja pracy, dłuższy czas życia zazwyczaj kosztuje mniej niż opłacanie zimnego prefiksu.