Wyobraź sobie usługę, która wysyła notatki z napraw do modelu DeepSeek i oczekuje w odpowiedzi obiektu JSON: co sprawdzono, co zalecono, czy klient się zgodził i czy praca została wykonana. Dziś wywołuje ona deepseek-chat, deepseek-flash albo trasę OpenRouter, a Ty chcesz, żeby działała na DeepSeek V4.1 Flash przez SpicyAPI (spicyapi.ai).
Wystarczą cztery zmiany: użyj ID modelu deepseek/v4.1-flash/chat, usuń każde pole, którego ten model nie przyjmuje, wybierz reasoning_effort i zapewnij max_tokens realny zapas, bo rozumowanie jest opłacane z tego samego budżetu. Potem uruchom jeden mały przypadek testowy, zanim pójdzie prawdziwy ruch. Na stronie modelu V4.1 Flash możesz wypróbować żądanie.
Co zmienić w żądaniu
Bazowy URL, klucz i ID modelu to łatwa część (przewodnik migracji). Problemy zaczynają się w treści żądania:
| W Twoim obecnym kliencie | W SpicyAPI |
|---|---|
Bazowy URL https://api.deepseek.com i klucz DeepSeek | https://api.spicyapi.ai/v1 i klucz zaczynający się od sk-spicy- |
Model deepseek-flash albo starsze deepseek-chat, deepseek-reasoner, deepseek-v4-flash, deepseek-v4-pro | deepseek/v4.1-flash/chat |
thinking: {"type": "disabled"}, czyli zachowanie, które dawał deepseek-chat | reasoning_effort: "none" pomija etap rozumowania |
| Thinking włączone (domyślnie) | Pomiń reasoning_effort, aby uzyskać high, albo ustaw low przy pracy masowej |
reasoning_effort równe max, medium, minimal, xhigh | Akceptowane są tylko none, low i high; każda inna wartość daje 400 invalid_request |
temperature, top_p, presence_penalty, frequency_penalty | Usuń; 400 unsupported_parameter |
parallel_tool_calls | Usuń; nieudostępnione w tym modelu |
stop | Zostaw: do 4 sekwencji, a dopasowany tekst nie trafia do odpowiedzi |
Części image_url lub plików | Zachowaj części image_url dla obrazów; usuń części PDF/plików i najpierw wyodrębnij z nich tekst |
OpenRouter models, route, provider, usage, transforms, plugins oraz obiekt reasoning | Usuń pierwszych sześć; zastąp reasoning polem najwyższego poziomu reasoning_effort |
Ten hostowany model nie ma kontroli próbkowania, nawet z reasoning_effort: "none". Zamiast polegać na temperature: 0 dla powtarzalności, waliduj wyniki zadań.
Co zostaje bez zmian: messages (tekst lub obraz, do 256), tools (do 128 definicji) z tool_choice (auto, none, required albo jedna nazwana funkcja), response_format z {"type": "json_object"}, stop, max_tokens (domyślnie 32 768, maksimum 384 000) oraz okno wejściowe 1 048 576 tokenów, które liczy każdą wysłaną wiadomość, łącznie z promptem systemowym i wcześniejszymi wynikami narzędzi.
SpicyAPI nigdy po cichu nie odrzuca pola: błąd 400 wskazuje pole i nic nie jest naliczane. Własne API DeepSeek w trybie thinking przyjmuje i ignoruje temperature oraz pola kar, więc stary klient może je nosić niezauważenie.
Cichą pułapką jest nazwa modelu. W samym API DeepSeek V4 Flash został wycofany, a deepseek-v4-flash tymczasowo kieruje do V4.1 Flash; aktualna nazwa to deepseek-flash. V4 Pro to inna sprawa: DeepSeek najpierw zapowiedział przełączenie na Flash 14 września, po czym jego dziennik zmian potwierdził, że usługa V4 Pro będzie działać dalej z niezmienionym rozliczaniem. Nie migruj w założeniu, że każde stare wywołanie Pro już działa na Flash.
W SpicyAPI trzy trasy czatu DeepSeek mają osobne opublikowane kontrakty: pozostawienie deepseek/v4-flash/chat w konfiguracji to poprawne żądanie, z limitem wyjścia 65 536 tokenów i wyższymi cenami. Alias natywnej usługi nie zmienia tego hostowanego kontraktu. Do tej migracji użyj wprost deepseek/v4.1-flash/chat.
Ta trasa przyjmuje obrazy przez części image_url, ale nie części PDF/plików. Wyodrębnij tekst z PDF przed wysłaniem; poniższe przykłady z notatkami naprawczymi w samym tekście nie wymagają obrazów.
Zostaw miejsce na odpowiedź
Model rozumuje, zanim odpowie, a rozumowanie jest pobierane z max_tokens. Domyślny wysiłek to high, a limit 120 tokenów nie zwrócił żadnej treści. Domyślne max_tokens to 32 768, a dłuższy wynik zostaje ucięty. Ustal limit według widocznej odpowiedzi plus kilku tysięcy tokenów zapasu na myślenie, a nie według maksimum 384 000.
Zacznij ekstrakcję od wysiłku Low. Jeśli pominie potrzebne rozróżnienie, wypróbuj High na tym rekordzie, zamiast podnosić koszt każdego wiersza. Przy dużych danych wejściowych użyj interfejsu strumieniowania.
Najpierw wypróbuj w Playground
- Otwórz DeepSeek V4.1 Flash, wybierz Playground i ustaw kontrolkę wysiłku na Low.
- Wstaw instrukcję kształtu JSON ze skryptu poniżej do System prompt, a notatkę naprawczą R17 do Message to the model, po czym kliknij Run model.
- Zanim zmigrujesz aplikację, upewnij się, że oczekująca zgoda nie zamienia się w ukończoną naprawę.
Uruchom przypadek testowy, który wyłapie kosztowny błąd
Notatka naprawcza dobrze nadaje się na przypadek testowy, bo płynne podsumowanie może być błędne w sposób, który kosztuje pieniądze. „Zalecana wymiana” to nie zgoda klienta, a zgoda to nie wykonana praca.
Wstaw klucz z API Keys do SPICY_API_KEY, zapisz poniższy skrypt jako check_repair.py i uruchom python3 check_repair.py. Prompt systemowy pokazuje kształt JSON i zawiera słowo „json”, którego wymaga przewodnik po trybie JSON DeepSeek.
import json
import os
import uuid
import urllib.request
import re
SOURCE = (
'Repair note R17: "Inspected the rear brake pads. Recommend replacement before '
'the next long ride. Customer approval is pending." No later work record is supplied.'
)
KEYS = {"inspected", "recommended_action", "authorized", "completed", "evidence"}
REQUIRED_QUOTES = (
"Inspected the rear brake pads.",
"Recommend replacement before the next long ride.",
"Customer approval is pending.",
)
def check(response: dict) -> list[str]:
choice = response["choices"][0]
problems = []
if choice.get("finish_reason") != "stop":
problems.append(f"finish_reason is {choice.get('finish_reason')!r}: the reply may be cut off")
content = (choice.get("message") or {}).get("content")
if not content or not content.strip():
return problems + ["empty content: reasoning may have used the whole max_tokens budget"]
try:
data = json.loads(content)
except json.JSONDecodeError as error:
return problems + [f"not valid JSON: {error}"]
if not isinstance(data, dict):
return problems + ["JSON must be an object"]
if missing := KEYS - data.keys():
problems.append(f"missing keys: {sorted(missing)}")
for key in ("inspected", "authorized", "completed"):
if data.get(key) is not None and type(data.get(key)) is not bool:
problems.append(f"{key} must be true, false or null")
if data.get("inspected") is not True:
problems.append("inspected should be true")
if data.get("authorized") is not False:
problems.append("authorized must be false: approval is pending")
if data.get("completed") is not None:
problems.append("completed must be null: no completion record is supplied")
action = data.get("recommended_action")
if not isinstance(action, str) or not action.strip():
problems.append("recommended_action must be a non-empty string")
else:
words = set(re.findall(r"[a-z]+", action.lower()))
# A lexical screen, not a proof of semantic equivalence.
required = {"rear", "long", "ride"}
compatible = (required <= words
and bool(words & {"replace", "replacement", "replacing"})
and bool(words & {"pad", "pads"})
and not words & {"not", "no", "never", "front", "after"})
if not compatible:
problems.append("review recommendation wording against R17")
quotes = data.get("evidence")
if not isinstance(quotes, list) or not quotes:
problems.append("evidence must be a non-empty list of quotes")
else:
problems += [f"invalid or unmatched quote: {q!r}" for q in quotes
if not isinstance(q, str) or not q.strip() or q not in SOURCE]
for expected in REQUIRED_QUOTES:
if not any(isinstance(q, str) and expected in q for q in quotes):
problems.append(f"missing supporting quote: {expected}")
return problems
body = {'model': 'deepseek/v4.1-flash/chat',
'messages': [{'role': 'system',
'content': 'Extract only facts stated in the source. Reply with one '
'JSON object shaped like {"inspected": null, '
'"recommended_action": null, "authorized": null, '
'"completed": null, "evidence": []}. inspected, '
'authorized and completed are true, false or null; use '
'null for anything the source does not state. evidence '
'lists exact quotes from the source that support every '
'non-null value.'},
{'role': 'user',
'content': 'Repair note R17: "Inspected the rear brake pads. '
'Recommend replacement before the next long ride. '
'Customer approval is pending." No later work record is '
'supplied.'}],
'max_tokens': 2048,
'reasoning_effort': 'low',
'response_format': {'type': 'json_object'}}
request = urllib.request.Request(
"https://api.spicyapi.ai/v1/chat/completions",
data=json.dumps(body).encode(),
headers={"Authorization": "Bearer " + os.environ["SPICY_API_KEY"],
"Content-Type": "application/json",
"Idempotency-Key": str(uuid.uuid4())},
)
with urllib.request.urlopen(request, timeout=90) as reply:
response = json.load(reply)
problems = check(response)
print(json.dumps(response.get("usage", {})))
if problems:
raise RuntimeError("; ".join(problems))
print(response["choices"][0]["message"]["content"])Oczekiwana decyzja: sprawdzone, zgoda nadal oczekuje, wykonanie nieznane. Zalecenie musi zachować tylne klocki hamulcowe i termin „przed następną długą jazdą”.
Dodaj zgodę i wykonanie jako osobne przypadki testowe
Nie poprzestawaj na przypadku oczekującej zgody. Użyj tych jako szablonów dla kolejnych przypadków: przed uruchomieniem każdego zaktualizuj SOURCE, REQUIRED_QUOTES oraz oczekiwane wartości zgody i wykonania. Sprawdzarki R17 nie da się uruchomić bez zmian na R18 ani R19:
| Najnowszy zapis | Authorized | Completed |
|---|---|---|
| R17: “Customer approval is pending.” Brak późniejszego zapisu | false | null |
| R18: “Customer approved the quoted replacement. Parts ordered; work scheduled for Thursday.” | true | null |
| R19: “Replaced pads; road check completed Thursday.” Powiązane z zatwierdzonym zleceniem | true | true |
Trzymaj przy źródle ID zlecenia, kolejność zapisów i daty: R18 zastępuje oczekującą zgodę, ale zaplanowanie pracy nie dowodzi jej wykonania. Jeśli notatki „zrobione” bez daty nie da się powiązać z tym samym zleceniem, zwróć nierozstrzygnięte powiązanie zamiast scalać ją z R17.
Ten sam trójpodział widać w procesie produkcji twórcy: „Zatwierdź ten projekt postaci”, „projekt zatwierdzony” i „wideo wygenerowane z zatwierdzonego projektu” to prośba, decyzja i wykonanie. Poproś prompt ekstrakcji o te etapy osobno, wraz z zapisem, który każdy z nich potwierdza, i zachowaj każdy konflikt między notatkami, zamiast prosić model o wybór zdania brzmiącego najpewniej.
Oddziel zgodę od wykonania w przepływach z narzędziami
Zapis zgody nie dowodzi, że naprawa się odbyła: sprawdź zapis wykonania, zanim zmienisz status, i trzymaj zgodę oraz wykonanie jako osobne pola w tym, co zwracają Twoje narzędzia.
Czat bez narzędzi nie wymaga niczego więcej. Gdy w żądaniu są tools, klient odbudowujący każdą wiadomość asystenta tylko z roli i treści przejdzie pierwszą turę i zawiedzie w drugiej z błędem 400, więc test jednoturowy ukrywa błąd. Dołącz wiadomość asystenta dokładnie w takiej postaci, w jakiej wróciła, razem z reasoning_content, potem dodaj po jednej wiadomości tool na wywołanie z odpowiednim tool_call_id i ogranicz liczbę rund. Testuj dwie tury, nie jedną. Przepisy na czat pokazują pełną pętlę.
Pozwól Agentowi przygotować wywołanie
Przy powtarzalnej ekstrakcji rekordów poproś swojego Agenta programistycznego o użycie Skilla SpicyAPI: “Read deepseek/v4.1-flash/chat, then adapt the example to my input; show the request before running it.” Zapytanie przez CLI to spicyapi models get deepseek/v4.1-flash/chat --json; przez MCP to spicyapi_model_get z model: "deepseek/v4.1-flash/chat". Wywołania czatu korzystają z kompatybilnego klienta lub API czatu; polecenia zadań medialnych nie generują odpowiedzi czatu.
Przelicz rachunek
Stawki to 0,30 $ za milion tokenów wejściowych bez cache, 0,006 $ za milion tokenów wejściowych z cache i 1,20 $ za milion tokenów wyjściowych, jedna stała stawka przy dowolnej długości żądania. Prompt 40 000 tokenów bez cache z 2 000 tokenów wyjściowych kosztuje 0,0144 $. Jeśli 30 000 tokenów wejściowych jest rozliczonych jako z cache, a 10 000 jako bez cache, to samo wywołanie kosztuje 0,00558 $. Tokeny rozumowania są rozliczane jako wyjście, więc krótka widoczna odpowiedź może zużyć więcej wyjścia, niż się spodziewasz; tylko none zostawia je poza rachunkiem.
Przy odejściu od własnego API DeepSeek liczą się dwa porównania. Jego ceny katalogowe to w godzinach szczytu te same 0,30 $, 0,006 $ i 1,20 $, ale poza szczytem jest tam połowa ceny, a tutaj obowiązuje jedna stała stawka: partia zaplanowana poza szczytem w DeepSeek kosztuje w SpicyAPI około dwa razy więcej za te same tokeny. Poza tym stawka z cache to jedna pięćdziesiąta stawki wejściowej, ale tylko dla prefiksu identycznego co do bajta w każdym wywołaniu: prompt systemowy 1 500 tokenów w 10 000 wywołań to 15 milionów tokenów wejściowych, 4,50 $ bez cache i co najwyżej 0,09 $, gdyby każdy był odczytem z cache. Porównaj zwrócone usage i rozliczony koszt z tymi szacunkami. Znacznik czasu albo zmieniona kolejność instrukcji mogą zerwać wspólny prefiks; niezmienne dane wejściowe same nie gwarantują trafienia w cache.
Gdzie ląduje każda trasa DeepSeek
| V4.1 Flash | V4 Flash | V4 Pro | |
|---|---|---|---|
| ID modelu | deepseek/v4.1-flash/chat | deepseek/v4-flash/chat | deepseek/v4-pro/chat |
| Maks. wyjście | 384 000 | 65 536 | 384 000 |
| Wejście / cache / wyjście za 1 mln | 0,30 $ / 0,006 $ / 1,20 $ | 0,44 $ / 0,014 $ / 1,32 $ | 1,32 $ / 0,044 $ / 3,96 $ |
parallel_tool_calls | Nieudostępnione | Nieudostępnione | Udostępnione |
Wszystkie trzy przyjmują 1 048 576 tokenów wejściowych, wysiłek none, low lub high (domyślnie high) i domyślne max_tokens równe 32 768. Przy przejściu z deepseek/v4-flash/chat treść żądania jest identyczna, więc zmienia się tylko ID: limit wyjścia rośnie prawie sześciokrotnie, a wszystkie stawki spadają. Przy przejściu z V4 Pro tracisz parallel_tool_calls i płacisz 77% mniej za wejście oraz około 70% mniej za wyjście. To, czy do danego zadania lepszy jest V4 Flash, czy V4 Pro, to osobna decyzja, omówiona w DeepSeek V4 Flash vs Pro.
Jeśli Twoja aplikacja pisze fikcję dla dorosłych
V4.1 Flash jest w SpicyAPI oznaczony jako bez cenzury, ale to nie obietnica, że zrealizuje każde żądanie fikcji dla dorosłych. W naszych testach z września 2026 bezpośrednie prośby o intensywne sceny dla dorosłych wracały złagodzone za każdym razem, a ujęcie ich jako fikcji lub ustawienie ramy w prompcie systemowym dawało więcej scen w pełnej formie, choć nie wszystkie (szczegóły w recenzji V4.1 Flash). Jeśli Twoje prompty pisano pod model przyjmujący prośby bez ramy, przy migracji przenieś ramę do wiadomości systemowej i dbaj, by każda postać była dorosła.
Napraw błędy migracji bezpośrednio
| Objaw | Prawdopodobna przyczyna | Poprawka |
|---|---|---|
400 unsupported_parameter wskazujące temperature, top_p, karę, parallel_tool_calls lub thinking | Pole przeniesione ze starego klienta | Usuń je |
content jest puste lub finish_reason to length | max_tokens zużyte na rozumowanie albo odpowiedź je przekroczyła | Zwiększ max_tokens, obniż wysiłek, spróbuj ponownie |
| JSON się parsuje, ale zgłasza wykonaną pracę | Zalecenie odczytane jako wykonanie | Trzymaj osobne pola, wymagaj cytatów, uruchom sprawdzarkę |
| Błąd w drugiej turze rozmowy z narzędziami | Wiadomość asystenta odbudowana bez reasoning_content | Dołącz zwróconą wiadomość bez zmian |
| 402 przy wystarczającym saldzie | Blokada (hold) 40201 przekracza saldo albo osiągnięto limit wydatków 40202 | Sprawdź błąd: zmniejsz nadmierny budżet wyjścia dla blokady albo przejrzyj limity klucza/przestrzeni roboczej |
Odrzucone ponownie użyte Idempotency-Key (400 lub 409) | Klucz należy do zakończonego żądania lub do innej treści | 400 dla zakończonego żądania nie może odtworzyć odpowiedzi; 409 może oznaczać zmienioną treść. Używaj klucza ponownie tylko przy identycznym ponowieniu sieciowym w jego oknie; przed wyborem nowego płatnego wywołania sprawdź rozliczenie zadania |
| Długie wywołanie bez strumieniowania pada po około 100 sekundach | Odcięcie na brzegu sieci | Strumieniuj odpowiedź |
Przełącz ruch bez utraty starej bazy porównawczej
Zachowaj stare ID modelu w konfiguracji i uruchom przypadek testowy na obu. Przenieś ruch, gdy test przejdzie na nowym ID, Twoje własne źródła przejdą walidację, a zalogowane usage zgadza się z powyższą arytmetyką.








