✨ Świeża dostawa•nowe kody na kinetyka.pl: Gemini Pro 18 mies. 170 zł•Cursor, ElevenLabs, Lovable i więcej, roczne plany AI w ułamku ceny Zobacz →
Przejdź do treści
Narzędzia

Kimi K3.1 Pro API: JSON z tekstu i walidacja

Sprawdzam w praktyce, jak wyciągać czysty JSON z tekstu za pomocą Kimi K3.1 Pro API. Przedstawiam gotowy kod w Pythonie ze zmienną środowiskową, bezpieczną walidację schematu bez wykonywania treści oraz obsługę błędów przy ucięciu odpowiedzi na skutek drenażu budżetu wnioskowania.

10 min czytania
kimi-k3-1-api-json

👁 121 przeczytań

// w skrócie
  • Kimi K3.1 Pro API poprawnie wyodrębnił obiekty JSON z polskiego tekstu w 3,553 sekundy przy koszcie 0,002004 USD, zwracając dane bez znaczników markdown.
  • Przy zadaniach z brakującymi danymi model może zużyć nawet 1797 z 1800 dostępnych tokenów na ukryty łańcuch myśli, nie zostawiając miejsca na odpowiedź.
  • Oficjalna dokumentacja deklaruje okno kontekstowe 1 048 576 tokenów przy cenie 2 USD za 1M tokenów wejściowych i 10 USD za 1M tokenów wyjściowych dla wariantu Pro.

Integracja modelu Kimi K3.1 Pro w potokach ekstrakcji danych wymaga ścisłej kontroli nad formatem wyjściowym i budżetem tokenów wnioskowania (reasoning). W moim teście API model bezbłędnie wyodrębnił obiekty JSON z polskiego tekstu w 3,553 sekundy przy koszcie 0,002004 USD, jednak przy zadaniach sprawdzających brak danych potrafił zużyć cały limit max_tokens na ukryty łańcuch myśli i zwrócić null.

  • Ekstrakcja strukturalna: Zadanie wyciągnięcia listy obiektów z tekstu oraz obliczenia pamięci w formacie JSON zakończyły się statusem stop i czystym wynikiem bez znaczników markdown.
  • Narzut wnioskowania: Nawet przy parametrze reasoning effort: low model generuje od 101 do 767 tokenów myśli w udanych zapytaniach, a w skrajnych przypadkach pochłania 1797 z 1800 dostępnych tokenów, nie zostawiając miejsca na odpowiedź.
  • Bezpieczeństwo kodu: Walidacja w Pythonie musi sprawdzać finish_reason przed parsowaniem json.loads() i nigdy nie może używać eval() na surowym tekście z API.
  • Kontekst i stawki: Oficjalna dokumentacja (platform.kimi.ai) deklaruje okno 1 048 576 tokenów przy cenie 2 USD za 1M tokenów wejściowych i 10 USD za 1M wyjściowych dla wariantu Pro.

Parametry techniczne i specyfika integracji Kimi K3.1 Pro API

W moich testach wykorzystałem bramkę OpenRouter (identyfikator modelu moonshotai/kimi-k3.1-pro), ustawiając stały protokół wywołań: temperatura 0.2 dla zwiększenia determinizmu, niskie zaangażowanie wnioskowania (reasoning effort: low), max_tokens=1800 oraz tryb bez strumieniowania (non-stream). Mierzony przeze mnie czas całkowity (wall time) pojedynczego wywołania obejmuje narzut sieciowy oraz czas potrzebny na wygenerowanie ukrytych tokenów myśli, więc z pojedynczego przebiegu nie należy wyliczać prędkości generowania tokenów na sekundę ani czasu do pierwszego tokenu (TTFT). Pełny opis podstawowej rodziny modeli zamieściłem w hubie Kimi.

Minimalne wywołanie API w Pythonie bez zaszywania klucza

Klucz pobieram ze zmiennej środowiskowej OPENROUTER_API_KEY. Przykład korzysta z synchronicznego POST i standardowej biblioteki Pythona; nie umieszczam sekretu w kodzie.

import os
import json
import urllib.request

API_KEY = os.environ.get("OPENROUTER_API_KEY")
if not API_KEY:
    raise RuntimeError("Brak zmiennej OPENROUTER_API_KEY")

URL = "https://openrouter.ai/api/v1/chat/completions"

payload = {
    "model": "moonshotai/kimi-k3.1-pro",
    "temperature": 0.2,
    "max_tokens": 1800,
    "reasoning": {"effort": "low"},
    "messages": [
        {
            "role": "user",
            "content": (
                "Zwróć wyłącznie poprawny JSON bez markdown. "
                "Z tekstu wyciągnij listę obiektów z polami "
                "nazwa, sztuki, cena_netto. Tekst: Anna zamówiła "
                "3 notesy po 12,50 zł netto i 2 długopisy po "
                "4 zł netto. Nie dodawaj innych pól. Nazwy w "
                "liczbie pojedynczej: notes, długopis."
            )
        }
    ]
}

req = urllib.request.Request(
    URL,
    data=json.dumps(payload).encode("utf-8"),
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    },
    method="POST"
)

with urllib.request.urlopen(req, timeout=60) as resp:
    raw_body = resp.read().decode("utf-8")
    data = json.loads(raw_body)

choice = data["choices"][0]
print("Finish reason:", choice.get("finish_reason"))
print("Content:", choice["message"].get("content"))

Ustawienie "reasoning": {"effort": "low"} jest krytyczne przy zadaniach strukturalnych. Informuje ono API, aby model ograniczył długość wewnętrznych rozważań przed zwróceniem odpowiedzi. Jak pokażą moje dalsze testy, parametr ten pomaga w typowej ekstrakcji JSON, ale nie zawsze chroni przed wpadnięciem modelu w pętlę analityczną przy podchwytliwych promptach.

Własny test ekstrakcji JSON i oczekiwany schemat obiektu

Aby sprawdzić, jak Kimi K3.1 Pro radzi sobie z polską fleksją, zamianą przecinków dziesiętnych na kropki w standardzie JSON oraz rygorem pomijania bloków markdown (```json), przeprowadziłem dwa testy zwracające czysty JSON: ekstrakcję pozycji zamówienia z tekstu (zadanie json) oraz wyliczenie rozmiaru wag dla hipotetycznego modelu 2,8 biliona parametrów (zadanie pamiec).

W zadaniu json oczekiwałem tablicy zawierającej dokładnie dwa obiekty o ściśle określonych kluczach i typach danych: pole nazwa jako łańcuch znaków w mianowniku liczby pojedynczej ("notes" oraz "długopis"), pole sztuki jako liczba całkowita (3 oraz 2) i pole cena_netto jako liczba (12.5 oraz 4). Model Kimi K3.1 Pro zwrócił dokładnie taki ładunek:

[
  {"nazwa": "notes", "sztuki": 3, "cena_netto": 12.5},
  {"nazwa": "długopis", "sztuki": 2, "cena_netto": 4}
]

Żadnych zbędnych komentarzy, żadnych znaczników markdown ani dodatkowych kluczy. W drugim zadaniu (pamiec) poprosiłem o wyliczenie rozmiaru samych wag dla hipotetycznego modelu 2.8e12 parametrów (nawiązującego do architektury poprzednika K3 z repozytorium huggingface.co/moonshotai, gdyż wagi K3.1 mają zostać opublikowane dopiero do 2026-11-04) i zwrócenie wyniku jako obiektu JSON z kluczami FP8, Q4 i Q2. Ponownie otrzymałem nieskazitelny ciąg znaków: {"FP8":2.8,"Q4":1.4,"Q2":0.7}.

Poniższa tabela zestawia surowe wyniki z mojego protokołu testowego dla zadań JSON na tle konkurencyjnych modeli zaplanowany wspólny test po polsku oraz zaplanowany wspólny test po polsku (zobacz zaplanowane porównania bezpośrednie w linkach).

Model (zadanie)Czas (s)Tokeny (wej / wyj / myśl)Koszt (USD)
Kimi K3.1 Pro (json)3,553197 / 161 / 1010,002004
Claude Opus 5.5 (json)2,906152 / 61 / 00,001828
GPT-6 Astra (json)3,48898 / 46 / 00,003280
Kimi K3.1 Pro (pamiec)3,291185 / 159 / 1220,001960
Claude Opus 5.5 (pamiec)5,535137 / 102 / 690,002588
GPT-6 Astra (pamiec)3,61493 / 26 / 00,002230

Walidacja struktury JSON w Pythonie bez wykonywania danych wyjściowych

Nawet jeśli model w testach zwraca poprawny format, w kodzie produkcyjnym odpowiedź z LLM trzeba traktowaćć jako niezaufane dane wejściowe (untrusted input). Pod żadnym pozorem nie wolno parsować odpowiedzi za pomocą funkcji eval() ani wykonywać niezwalidowanego kodu. Własny parser powinien składać się z trzech warstw obronnych: weryfikacji metadanych odpowiedzi API, bezpiecznej deserializacji przez json.loads() oraz ścisłego sprawdzenia typów i kluczy schematu.

Poniżej prezentuję kompletną funkcję walidującą napisaną w czystym Pythonie, która sprawdza dokładny schemat naszego zamówienia i odrzuca jakiekolwiek odchylenia (np. brakujące klucze, nadmiarowe pola wyhalucynowane przez model, ujemne liczby czy błędne typy takie jak wartości logiczne bool, które w Pythonie dziedziczą po typie int).

import json
import math

EXPECTED_KEYS = {"nazwa", "sztuki", "cena_netto"}
ALLOWED_NAMES = {"notes", "długopis"}

def validate_order_json(raw_text: str) -> list:
    if not isinstance(raw_text, str) or not raw_text.strip():
        raise ValueError("Pusta odpowiedź z modelu")

    try:
        parsed = json.loads(raw_text)
    except json.JSONDecodeError as err:
        raise ValueError(f"Błąd składni JSON: {err}") from err

    if not isinstance(parsed, list):
        raise ValueError("Oczekiwano listy obiektów JSON")

    if len(parsed) != 2:
        raise ValueError("Oczekiwano dwóch pozycji")

    validated = []
    for idx, item in enumerate(parsed):
        if not isinstance(item, dict):
            raise ValueError(f"Element {idx} nie jest obiektem")

        keys = set(item.keys())
        if keys != EXPECTED_KEYS:
            raise ValueError(
                f"Niezgodne klucze w elemencie {idx}: {keys}"
            )

        nazwa = item["nazwa"]
        sztuki = item["sztuki"]
        cena = item["cena_netto"]

        if not isinstance(nazwa, str) or nazwa not in ALLOWED_NAMES:
            raise ValueError(f"Niepoprawna nazwa: {nazwa!r}")

        if isinstance(sztuki, bool) or not isinstance(sztuki, int):
            raise ValueError(f"Pole sztuki musi być int: {sztuki!r}")

        if isinstance(cena, bool) or not isinstance(cena, (int, float)):
            raise ValueError(f"Cena musi być liczbą: {cena!r}")

        if not math.isfinite(cena):
            raise ValueError("Cena musi być skończona")
        if sztuki <= 0 or cena < 0:
            raise ValueError("Wartości muszą być dodatnie")

        validated.append({
            "nazwa": nazwa,
            "sztuki": sztuki,
            "cena_netto": float(cena)
        })

    if {x["nazwa"] for x in validated} != ALLOWED_NAMES:
        raise ValueError("Po jednej pozycji każdego produktu")
    return validated

Dlaczego w powyższym kodzie jawnie sprawdzam isinstance(sztuki, bool)? W języku Python typ bool jest podklasą int (isinstance(True, int) zwraca True). Gdyby model zwrócił uszkodzony obiekt {"nazwa": "notes", "sztuki": true, "cena_netto": 12.5}, naiwna walidacja oparta wyłącznie na isinstance(x, int) przepuściłaby wartość logiczną jako liczbę sztuk równą 1. Tego typu detale decydują o stabilności integracji API.

Obsługa pustych odpowiedzi, ucięcia tokenów (finish_reason: length) i braku danych

Nie przepłacaj za te subskrypcje

Prowadzę sklep z rocznymi dostępami do narzędzi AI - te same konta, o których piszę wyżej, tylko taniej niż w cenniku producenta.

Zobacz, co jest dostępne

Najpoważniejsza pułapka, na jaką natrafiłem podczas testowania Kimi K3.1 Pro API, ujawniła się w zadaniu weryfikującym odporność na halucynacje przy brakujących informacjach (zadanie brak). Prompt brzmiał: „Na podstawie wyłącznie notatki odpowiedz na pytanie. Notatka: Produkt Alfa kosztuje 90 zł. Produkt Beta kosztuje 120 zł. Pytanie: Jaką marżę procentową ma Alfa? Jeżeli danych brakuje, napisz dokładnie BRAK DANYCH.”

Zarówno Claude Opus 5.5 (w 2,757 s, 31 tokenów myśli), jak i GPT-6 Astra (w 2,293 s, 0 tokenów myśli) bez problemu zwróciły ciąg BRAK DANYCH ze statusem finish_reason: "stop". Co zrobił Kimi K3.1 Pro? Przy standardowym limicie protokołu max_tokens=1800 zapytanie trwało aż 21,567 s, zwróciło kod HTTP 200, ale w polu answer (message.content) znalazła się wartość null! Powodem był status finish_reason: "length". Analiza obiektu usage pokazała, że model wygenerował 1800 tokenów wyjściowych, z czego aż 1797 stanowiły ukryte reasoning_tokens.

W korekcie polszczyzny wynik również był pusty: 37,078 s, 1797 tokenów rozumowania. Osobne powtórki z limitem 6000 także zakończyły się flagą length. Nie łączę ich z porównaniem przy 1800. W osobnym, łatwiejszym zestawie 10 zadań z limitem 16000 Pro ukończył 10/10. Dobór budżetu i zadania zmienia wynik.

Należy jasno rozróżnić przyczynę tego stanu: pusta odpowiedź ze statusem finish_reason: "length" to efekt ucięcia generowania po wyczerpaniu budżetu tokenów na łańcuch myśli, a nie fałszywa odpowiedź merytoryczna czy błąd znajomości języka. Dla programisty oznacza to konieczność napisania twardej obsługi odpowiedzi przed przekazaniem jej do parsera JSON:

def extract_content_safely(api_response: dict) -> str:
    choices = api_response.get("choices", [])
    if not choices:
        raise RuntimeError("Brak tablicy choices w odpowiedzi API")

    choice = choices[0]
    finish_reason = choice.get("finish_reason")
    message = choice.get("message") or {}
    content = message.get("content")
    usage = api_response.get("usage") or {}
    comp_details = usage.get("completion_tokens_details") or {}
    reasoning_toks = comp_details.get("reasoning_tokens", 0)

    if finish_reason == "length":
        raise RuntimeError(
            f"Przekroczono limit max_tokens. "
            f"Tokeny wnioskowania: {reasoning_toks}. "
            f"Odpowiedź została ucięta przed wygenerowaniem JSON."
        )

    if finish_reason != "stop":
        raise RuntimeError(
            f"Nieoczekiwany status zakończenia: {finish_reason}"
        )

    if content is None or not str(content).strip():
        raise ValueError(
            "Model zwrócił pusty content mimo statusu stop"
        )

    return str(content).strip()

Wprowadzenie powyższej funkcji chroni aplikację przed awarią typu TypeError: the JSON object must be str, bytes or bytearray, not NoneType i pozwala od razu zalogować anomalie kosztowe wynikające z zapętlenia procesu wnioskowania.

Budżet wnioskowania (reasoning_tokens) a koszty wywołań w praktyce

Planując wykorzystanie Kimi K3.1 Pro API do masowej obróbki dokumentów, musisz monitorować stosunek tokenów myśli do właściwych tokenów odpowiedzi. Przyjrzyjmy się wszystkim moim zadaniom z głównego protokołu testowego (max_tokens=1800) dla tego modelu:

ZadanieStatusTokeny wyjściowe (w tym myśl)Koszt (USD)
pamiec (JSON)stop159 (122 myśli)0,001960
json (JSON)stop161 (101 myśli)0,002004
rachunek (tekst)stop617 (479 myśli)0,006548
kod (Python)stop826 (767 myśli)0,008632
polski (korekta)length (null)1800 (1797 myśli)0,018310
brak (notatka)length (null)1800 (1797 myśli)0,018348

Najczęstsze pytania (FAQ)

Czy Kimi K3.1 Pro API zwraca czysty JSON bez znaczników markdown?

Tak. W moich testach (zadania json oraz pamiec) po jawnym zażądaniu w prompcie zwrotu wyłącznie poprawnego formatu JSON bez markdown model zwrócił czyste ciągi znaków zaczynające się od nawiasu [ lub {, gotowe do bezpośredniego przekazania do funkcji json.loads().

Dlaczego pole content w odpowiedzi Kimi K3.1 Pro bywa równe null przy kodzie HTTP 200?

Dzieje się tak, gdy model zużyje całą pulę max_tokens na ukryty proces wnioskowania (reasoning_tokens). W moich testach brak i polski model wygenerował 1797 tokenów myśli przy limicie 1800, co zakończyło generowanie statusem finish_reason: "length" przed wypisaniem właściwej odpowiedzi tekstowej.

Ile kosztuje ekstrakcja pojedynczego obiektu JSON w Kimi K3.1 Pro?

W moim teście wyciągnięcia dwuelementowej listy obiektów z krótkiego tekstu (197 tokenów wejściowych i 161 wyjściowych, w tym 101 tokenów myśli) pojedyncze wywołanie kosztowało 0,002004 USD. Oficjalne stawki bazowe to 2 USD za milion tokenów wejściowych i 10 USD za milion wyjściowych (przed podatkami).

Jak bezpiecznie walidować JSON zwrócony przez model w Pythonie?

Należy najpierw upewnić się, że finish_reason == "stop" i treść nie jest pusta, następnie sparsować tekst wyłącznie za pomocą json.loads() (nigdy eval()), a na końcu zweryfikować dokładny zestaw kluczy oraz typy wartości, pamiętając o wykluczeniu typu bool przy sprawdzaniu liczb całkowitych (int).

Google · Twoje źródłaPromptowy wyżej w Twoim Google - jednym kliknięciemDodaj do preferowanych źródeł →

Źródła

Benchmarki wg Moonshot

Wg Moonshot, 23.10.2026. Wyniki producenta, nie mój pomiar. Różne testy nie są wymienne.

BenchmarkKimi K3.1 ProClaude Opus 5.5GPT-6 Astra
Terminal-Bench 4.065.766.458.2
SWE Marathon v1.151.746.342.5
HLE-Full51.159.853.6

Niezależny indeks Artificial Analysis: 55,1 (24.10.2026).

// Newsletter

Cały tydzień w AI, w jednym mailu

Wybrane premiery, narzędzia i analizy. Raz w tygodniu, prosto do skrzynki.

Zapisz się za darmo →
Za darmo. Wypisujesz się jednym kliknięciem.

Link do Kimi jest partnerski: jeśli kupisz przez niego, serwis dostaje prowizję, a Ty płacisz tyle samo. Nie wpływa to na treść, także tam, gdzie piszę krytycznie.

// czytaj też

Podobne tematy na Promptowym

Piotr Olszewski

Piotr Olszewski

AUTOR I WYDAWCA

Piotr Olszewski - twórca i autor Promptowego, polskiego serwisu o sztucznej inteligencji. Codziennie śledzi premiery modeli, narzędzia i regulacje AI, i tłumaczy je prostym, konkretnym językiem.

// mapa strony

🛒 Sklep Kinetyka Google AI Gemini Pro 170 zł CapCut Pro 460 zł/rok Lovable Pro 450 zł/rok Zobacz wszystko →
promptowy w liczbach 0tekstów w archiwum0newsów z ostatnich 7 dni0modeli wideo w obserwatorium0zagadek w grach
× ‹ powiększenie ›