Перейти к основному контенту

Перенос процесса проверки текстов на DeepSeek V4.1 Flash

Переведите чат DeepSeek на V4.1 Flash: ID модели, поля для удаления, бюджет рассуждений, проверенный тест, вызовы инструментов и счёт за токены.

Автор
Lydia
Опубликовано
Время чтения
11 мин чтения
Обновлено
Женщина и мужчина разговаривают через бирюзовый велосипед в солнечной ремонтной мастерской.

Допустим, сервис отправляет заметки о ремонте модели DeepSeek и ждёт в ответ JSON-объект: что проверили, что рекомендовали, согласился ли клиент, выполнена ли работа. Сегодня он вызывает deepseek-chat, deepseek-flash или маршрут OpenRouter, а вы хотите запустить его на DeepSeek V4.1 Flash через SpicyAPI (spicyapi.ai).

Хватит четырёх правок: использовать ID модели deepseek/v4.1-flash/chat, удалить каждое поле, которое эта модель не принимает, выбрать reasoning_effort и дать max_tokens реальный запас, потому что рассуждение оплачивается из того же бюджета. Затем прогоните один небольшой тест до реального трафика. Попробовать запрос можно на странице модели V4.1 Flash.

Что менять в запросе

Базовый URL, ключ и ID модели — это простая часть (руководство по миграции). Ломается всё на теле запроса:

В вашем текущем клиентеВ SpicyAPI
Базовый URL https://api.deepseek.com и ключ DeepSeekhttps://api.spicyapi.ai/v1 и ключ, начинающийся с sk-spicy-
Модель deepseek-flash или старые deepseek-chat, deepseek-reasoner, deepseek-v4-flash, deepseek-v4-prodeepseek/v4.1-flash/chat
thinking: {"type": "disabled"} — поведение, которое раньше давал deepseek-chatreasoning_effort: "none" пропускает этап рассуждения
Thinking включён (по умолчанию)Опустите reasoning_effort, чтобы получить high, или задайте low для массовой работы
reasoning_effort со значением max, medium, minimal, xhighПринимаются только none, low и high; любое другое значение даёт 400 invalid_request
temperature, top_p, presence_penalty, frequency_penaltyУдалите; 400 unsupported_parameter
parallel_tool_callsУдалите; у этой модели не поддерживается
stopОставьте: до 4 последовательностей, совпавший текст в ответ не попадает
Части image_url или файловЧасти image_url для изображений оставьте; части PDF/файлов уберите и сначала извлеките из них текст
OpenRouter models, route, provider, usage, transforms, plugins и объект reasoningПервые шесть удалите; reasoning замените полем верхнего уровня reasoning_effort

У этой размещённой модели нет управления сэмплированием, даже с reasoning_effort: "none". Проверяйте результаты задач вместо того, чтобы рассчитывать на temperature: 0 ради повторяемости.

Что остаётся прежним: messages (текст или изображения, до 256), tools (до 128 определений) с tool_choice (auto, none, required или одна именованная функция), response_format с {"type": "json_object"}, stop, max_tokens (по умолчанию 32 768, максимум 384 000) и входное окно в 1 048 576 токенов, в которое входит каждое отправленное сообщение, включая системный промпт и прежние результаты инструментов.

SpicyAPI никогда не отбрасывает поле молча: ошибка 400 называет поле, и ничего не списывается. Собственный API DeepSeek в режиме thinking принимает и игнорирует temperature и поля штрафов, поэтому старый клиент может тянуть их незаметно.

Тихая поломка — название модели. В собственном API DeepSeek V4 Flash выведен из обращения, а deepseek-v4-flash временно направляется на V4.1 Flash; актуальное имя — deepseek-flash. С V4 Pro иначе: сначала DeepSeek объявил переход на Flash с 14 сентября, затем его журнал изменений подтвердил, что обслуживание V4 Pro продолжится с неизменной тарификацией. Не переходите, предполагая, что каждый старый вызов Pro уже выполняется на Flash.

В SpicyAPI у трёх чат-маршрутов DeepSeek отдельные опубликованные контракты: оставить в конфигурации deepseek/v4-flash/chat — допустимый запрос, но с потолком вывода 65 536 токенов и более высокими ценами. Алиас нативного сервиса этот размещённый контракт не меняет. Для этой миграции указывайте deepseek/v4.1-flash/chat явно.

Этот маршрут принимает изображения через части image_url, но не части PDF/файлов. Извлеките текст из PDF до отправки; приведённым ниже примерам с текстовыми заметками о ремонте ввод изображений не нужен.

Оставьте место для ответа

Модель рассуждает перед ответом, и рассуждение берётся из max_tokens. Усилие по умолчанию — high, а лимит в 120 токенов не вернул вообще никакого содержимого. max_tokens по умолчанию равен 32 768, и более длинный результат обрезается. Рассчитывайте лимит по видимому ответу плюс несколько тысяч токенов запаса на размышление, а не по максимуму в 384 000.

Начинайте извлечение с усилия Low. Если оно упускает нужное различие, попробуйте High на этой записи, а не удорожайте каждую строку. Для больших входных данных используйте интерфейс потоковой передачи.

Сначала попробуйте в Playground

  1. Откройте DeepSeek V4.1 Flash, выберите Playground и установите элемент управления усилием на Low.
  2. Вставьте инструкцию о форме JSON из скрипта ниже в System prompt, а заметку о ремонте R17 — в Message to the model, затем нажмите Run model.
  3. Прежде чем переносить приложение, убедитесь, что ожидающее согласие не превращается в завершённый ремонт.

Прогоните тест, который ловит дорогую ошибку

Заметка о ремонте — хороший тест, потому что гладкая сводка может ошибаться так, что это стоит денег. «Рекомендуется замена» — не согласие клиента, а согласие — не выполненная работа.

Положите ключ из API Keys в SPICY_API_KEY, сохраните этот скрипт как check_repair.py и запустите python3 check_repair.py. Системный промпт показывает форму JSON и содержит слово «json», которого требует руководство DeepSeek по режиму JSON.

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"])

Ожидаемое решение: проверено, согласие всё ещё ожидается, выполнение неизвестно. Рекомендация должна сохранить задние колодки и срок «до следующей длинной поездки».

Добавьте согласие и выполнение как отдельные тесты

Не останавливайтесь на случае ожидающего согласия. Используйте эти как шаблоны для дополнительных тестов: перед запуском каждого обновите SOURCE, REQUIRED_QUOTES и ожидаемые значения согласия и выполнения. Проверку для R17 нельзя запускать без изменений на R18 или R19:

Последняя записьAuthorizedCompleted
R17: “Customer approval is pending.” Более поздней записи нетfalsenull
R18: “Customer approved the quoted replacement. Parts ordered; work scheduled for Thursday.”truenull
R19: “Replaced pads; road check completed Thursday.” Связано с согласованным заказомtruetrue

Храните вместе с источником ID заказа, порядок записей и даты: R18 заменяет ожидающее согласие, но назначение работы на дату не доказывает её выполнения. Если запись «сделано» без даты нельзя связать с тем же заказом, верните неразрешённую связь, а не сливайте её с R17.

То же разделение на три части встречается в производственном процессе автора: «Утвердите этот дизайн персонажа», «дизайн утверждён» и «видео создано по утверждённому дизайну» — это запрос, решение и исполнение. Просите промпт извлечения возвращать эти этапы раздельно, с записью, подтверждающей каждый из них, и сохраняйте любые противоречия между заметками, а не просите модель выбрать самое уверенно звучащее предложение.

Разделяйте согласие и выполнение в процессах с инструментами

Запись о согласии не доказывает, что ремонт состоялся: найдите запись о выполнении, прежде чем менять статус, и держите согласие и выполнение отдельными полями в том, что возвращают ваши инструменты.

Чату без инструментов ничего дополнительного не нужно. Если в запросе есть tools, клиент, который пересобирает каждое сообщение ассистента только из роли и содержимого, проходит первый ход и падает на втором с ошибкой 400, так что одноходовой тест скрывает баг. Добавляйте сообщение ассистента точно в том виде, в каком оно вернулось, включая reasoning_content, затем по одному сообщению tool на каждый вызов с соответствующим tool_call_id и ограничьте число раундов. Тестируйте два хода, а не один. Рецепты чата показывают полный цикл.

Пусть Agent подготовит вызов

Для повторяющегося извлечения записей попросите своего программирующего Agent использовать Skill SpicyAPI: “Read deepseek/v4.1-flash/chat, then adapt the example to my input; show the request before running it.” Запрос через CLI — spicyapi models get deepseek/v4.1-flash/chat --json; через MCP — spicyapi_model_get с model: "deepseek/v4.1-flash/chat". Чат-вызовы идут через совместимый клиент или чат-API; команды медиазадач чат-ответы не генерируют.

Пересчитайте счёт

Тарифы: 0,30 $ за миллион входных токенов без кэша, 0,006 $ за миллион кэшированных входных токенов и 1,20 $ за миллион выходных, единая ставка при любой длине запроса. Промпт в 40 000 токенов без кэша с 2 000 выходных токенов стоит 0,0144 $. Если 30 000 входных токенов оплачиваются как кэшированные, а 10 000 — как некэшированные, тот же вызов стоит 0,00558 $. Токены рассуждения оплачиваются как выходные, поэтому короткий видимый ответ может использовать больше выходных токенов, чем вы ожидаете; только none не даёт им попасть в счёт.

При уходе с собственного API DeepSeek важны два сравнения. Его прайс-лист в часы пик даёт те же 0,30 $, 0,006 $ и 1,20 $, но вне пика там половина цены, а здесь единая ставка: пакет, который вы запускаете вне пика в DeepSeek, в SpicyAPI обойдётся примерно вдвое дороже за те же токены. И ставка для кэша равна одной пятидесятой от входной, но только для префикса, побайтно одинакового от вызова к вызову: системный промпт в 1 500 токенов на 10 000 вызовов — это 15 миллионов входных токенов, 4,50 $ без кэша и не более 0,09 $, если бы каждый был чтением из кэша. Сравните возвращённый usage и итоговую стоимость с этими оценками. Метка времени или переставленные инструкции могут сломать общий префикс; неизменный ввод сам по себе попадания в кэш не гарантирует.

Куда попадает каждый маршрут DeepSeek

V4.1 FlashV4 FlashV4 Pro
ID моделиdeepseek/v4.1-flash/chatdeepseek/v4-flash/chatdeepseek/v4-pro/chat
Макс. вывод384 00065 536384 000
Вход / кэш / вывод за 1 млн0,30 $ / 0,006 $ / 1,20 $0,44 $ / 0,014 $ / 1,32 $1,32 $ / 0,044 $ / 3,96 $
parallel_tool_callsНе поддерживаетсяНе поддерживаетсяПоддерживается

Все три принимают 1 048 576 входных токенов, усилие none, low или high (по умолчанию high) и max_tokens по умолчанию 32 768. При переходе с deepseek/v4-flash/chat тело запроса идентично, меняется только ID: потолок вывода вырастает почти в шесть раз, а все тарифы снижаются. При переходе с V4 Pro вы теряете parallel_tool_calls и платите на 77 % меньше за вход и примерно на 70 % меньше за вывод. Что лучше подходит для конкретной задачи, V4 Flash или V4 Pro, — отдельное решение, разобранное в DeepSeek V4 Flash vs Pro.

Если ваше приложение пишет прозу для взрослых

V4.1 Flash значится в SpicyAPI как модель без цензуры, но это не обещание, что она выполнит любой запрос на прозу для взрослых. В наших тестах в сентябре 2026 года прямые запросы на интенсивные взрослые сцены возвращались смягчёнными каждый раз, а подача их как художественного вымысла или задание рамки в системном промпте давало больше полных ответов, хотя и не все (подробности в обзоре V4.1 Flash). Если ваши промпты писались для модели, принимавшей прямые запросы, при миграции перенесите рамку в системное сообщение и следите, чтобы каждый персонаж был взрослым.

Устраняйте сбои миграции напрямую

СимптомВероятная причинаИсправление
400 unsupported_parameter с именем temperature, top_p, штрафа, parallel_tool_calls или thinkingПоле перенесено из старого клиентаУдалить
content пуст или finish_reason равен lengthmax_tokens ушёл на рассуждение, либо ответ его превысилУвеличить max_tokens, снизить усилие, повторить
JSON разбирается, но сообщает о выполненной работеРекомендация прочитана как выполнениеДержать отдельные поля, требовать цитаты, запустить проверку
Ошибка на втором ходе диалога с инструментамиСообщение ассистента пересобрано без reasoning_contentДобавлять возвращённое сообщение без изменений
402 при достаточном балансеХолд 40201 превышает баланс или достигнут лимит расходов 40202Изучить ошибку: уменьшить чрезмерный бюджет вывода для холда или проверить лимиты ключа/рабочего пространства
Повторно использованный Idempotency-Key отклонён (400 или 409)Ключ относится к завершённому запросу или к другому телу400 для завершённого запроса не может воспроизвести ответ; 409 может означать изменённое тело. Используйте ключ повторно только для идентичной сетевой повторной попытки в его окне; перед новым платным вызовом проверьте расчёт по задаче
Долгий вызов без потоковой передачи обрывается около 100 секундОбрыв на границе сетиПередавайте ответ потоком

Переключайтесь, не теряя прежнюю базу сравнения

Оставьте старый ID модели в конфигурации и прогоните тест на обоих. Переносите трафик, когда тест проходит на новом ID, ваши собственные источники проходят валидацию, а записанный usage совпадает с расчётом выше.

Другие языки English · 日本語 · 한국어 · Deutsch · Français · Español · Português (Brasil) · Italiano · Polski