Przejdź do treści głównej

Migracja procesu weryfikacji tekstów do DeepSeek V4.1 Flash

Przenieś integrację czatu DeepSeek na V4.1 Flash: ID modelu, pola do usunięcia, budżet rozumowania, sprawdzony przypadek testowy i koszt tokenów.

Autor
Lydia
Opublikowano
Czas czytania
12 min czytania
Zaktualizowano
Kobieta i mężczyzna rozmawiają po dwóch stronach turkusowego roweru w słonecznym warsztacie naprawczym.

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 kliencieW SpicyAPI
Bazowy URL https://api.deepseek.com i klucz DeepSeekhttps://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-prodeepseek/v4.1-flash/chat
thinking: {"type": "disabled"}, czyli zachowanie, które dawał deepseek-chatreasoning_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, xhighAkceptowane są tylko none, low i high; każda inna wartość daje 400 invalid_request
temperature, top_p, presence_penalty, frequency_penaltyUsuń; 400 unsupported_parameter
parallel_tool_callsUsuń; nieudostępnione w tym modelu
stopZostaw: do 4 sekwencji, a dopasowany tekst nie trafia do odpowiedzi
Części image_url lub plikówZachowaj 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 reasoningUsuń 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

  1. Otwórz DeepSeek V4.1 Flash, wybierz Playground i ustaw kontrolkę wysiłku na Low.
  2. 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.
  3. 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.

python
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 zapisAuthorizedCompleted
R17: “Customer approval is pending.” Brak późniejszego zapisufalsenull
R18: “Customer approved the quoted replacement. Parts ordered; work scheduled for Thursday.”truenull
R19: “Replaced pads; road check completed Thursday.” Powiązane z zatwierdzonym zleceniemtruetrue

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 FlashV4 FlashV4 Pro
ID modeludeepseek/v4.1-flash/chatdeepseek/v4-flash/chatdeepseek/v4-pro/chat
Maks. wyjście384 00065 536384 000
Wejście / cache / wyjście za 1 mln0,30 $ / 0,006 $ / 1,20 $0,44 $ / 0,014 $ / 1,32 $1,32 $ / 0,044 $ / 3,96 $
parallel_tool_callsNieudostępnioneNieudostępnioneUdostę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

ObjawPrawdopodobna przyczynaPoprawka
400 unsupported_parameter wskazujące temperature, top_p, karę, parallel_tool_calls lub thinkingPole przeniesione ze starego klientaUsuń je
content jest puste lub finish_reason to lengthmax_tokens zużyte na rozumowanie albo odpowiedź je przekroczyłaZwiększ max_tokens, obniż wysiłek, spróbuj ponownie
JSON się parsuje, ale zgłasza wykonaną pracęZalecenie odczytane jako wykonanieTrzymaj osobne pola, wymagaj cytatów, uruchom sprawdzarkę
Błąd w drugiej turze rozmowy z narzędziamiWiadomość asystenta odbudowana bez reasoning_contentDołącz zwróconą wiadomość bez zmian
402 przy wystarczającym saldzieBlokada (hold) 40201 przekracza saldo albo osiągnięto limit wydatków 40202Sprawdź 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ści400 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 sekundachOdcięcie na brzegu sieciStrumieniuj 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ą.

Także w językach English · 日本語 · 한국어 · Deutsch · Français · Español · Português (Brasil) · Русский · Italiano

Wypróbuj samodzielnie

Płacisz tylko za to, co uruchomisz — bez planów i subskrypcji.

Zrobione w MiniMax H3 · ByteDance Seedance 2.0 Fast