Pular para o conteúdo principal

Migre um fluxo de revisão de textos para o DeepSeek V4.1 Flash

Passe uma integração de chat do DeepSeek para o V4.1 Flash: ID do modelo, campos a apagar, orçamento de raciocínio, um caso verificado e o custo em tokens.

Autor
Lydia
Publicado
Leitura
13 min de leitura
Atualizado
Uma mulher e um homem conversam dos dois lados de uma bicicleta turquesa em uma oficina de reparos ensolarada.

Imagine um serviço que envia notas de reparo a um modelo DeepSeek e espera um objeto JSON em troca: o que foi inspecionado, o que foi recomendado, se o cliente aprovou e se o trabalho foi feito. Hoje ele chama deepseek-chat, deepseek-flash ou uma rota do OpenRouter, e você quer que rode no DeepSeek V4.1 Flash pela SpicyAPI (spicyapi.ai).

Quatro mudanças resolvem: use o ID de modelo deepseek/v4.1-flash/chat, apague todo campo que este modelo não aceita, escolha um reasoning_effort e dê a max_tokens uma folga de verdade, porque o raciocínio é pago com o mesmo orçamento. Depois, rode um pequeno caso de teste antes do tráfego real. Use a página do modelo V4.1 Flash para experimentar a requisição.

O que mudar na requisição

URL base, chave e ID do modelo são a parte fácil (guia de migração). É no corpo da requisição que as coisas quebram:

No seu cliente atualNa SpicyAPI
URL base https://api.deepseek.com e uma chave da DeepSeekhttps://api.spicyapi.ai/v1 e uma chave que começa com sk-spicy-
Modelo deepseek-flash, ou os antigos deepseek-chat, deepseek-reasoner, deepseek-v4-flash, deepseek-v4-prodeepseek/v4.1-flash/chat
thinking: {"type": "disabled"}, o comportamento que o deepseek-chat davareasoning_effort: "none" pula a etapa de raciocínio
Thinking ligado (o padrão)Omita reasoning_effort para high, ou defina low para trabalho em volume
reasoning_effort com max, medium, minimal, xhighSó none, low e high são aceitos; qualquer outro valor retorna 400 invalid_request
temperature, top_p, presence_penalty, frequency_penaltyApague; 400 unsupported_parameter
parallel_tool_callsApague; não exposto neste modelo
stopMantenha: até 4 sequências, e o texto correspondente fica fora da resposta
Partes image_url ou de arquivoMantenha as partes image_url para imagens; remova as partes de PDF/arquivo e extraia antes o texto delas
models, route, provider, usage, transforms, plugins do OpenRouter e um objeto reasoningApague os seis primeiros; troque reasoning pelo reasoning_effort de nível superior

Este modelo hospedado não tem controle de amostragem, nem mesmo com reasoning_effort: "none". Valide os resultados das tarefas em vez de depender de temperature: 0 para repetibilidade.

O que continua igual: messages (conteúdo de texto ou imagem, até 256), tools (até 128 definições) com tool_choice (auto, none, required ou uma função nomeada), response_format com {"type": "json_object"}, stop, max_tokens (padrão 32.768, máximo 384.000) e uma janela de entrada de 1.048.576 tokens que conta todas as mensagens enviadas, incluindo o prompt de sistema e resultados de ferramentas anteriores.

A SpicyAPI nunca descarta um campo em silêncio: o 400 nomeia o campo e nada é cobrado. A API própria da DeepSeek aceita e ignora temperature e os campos de penalidade no modo thinking, então um cliente antigo pode carregá-los sem ninguém perceber.

A falha silenciosa é o nome do modelo. Na API própria da DeepSeek, o V4 Flash foi aposentado e deepseek-v4-flash redireciona temporariamente para o V4.1 Flash; o nome atual é deepseek-flash. O V4 Pro é diferente: a DeepSeek anunciou inicialmente a troca para o Flash em 14 de setembro, mas seu registro de alterações confirmou depois que o serviço V4 Pro continuaria com a cobrança inalterada. Não migre presumindo que toda chamada antiga ao Pro já roda no Flash.

Na SpicyAPI, as três rotas de chat da DeepSeek têm contratos publicados distintos: manter deepseek/v4-flash/chat na sua configuração é uma requisição válida, com teto de saída de 65.536 tokens e preços mais altos. Um alias do serviço nativo não muda esse contrato hospedado. Use deepseek/v4.1-flash/chat de forma explícita nesta migração.

Esta rota aceita imagens por partes image_url, mas não partes de PDF/arquivo. Extraia o texto de um PDF antes de enviá-lo; os exemplos abaixo, só com notas de reparo em texto, não precisam de entrada de imagem.

Dê espaço à resposta

O modelo raciocina antes de responder, e esse raciocínio sai de max_tokens. O esforço padrão é high, e um teto de 120 tokens não retornou conteúdo algum. O max_tokens padrão é 32.768, e uma entrega mais longa é truncada. Dimensione o teto pela resposta visível mais alguns milhares de tokens de espaço para pensar, não pelo máximo de 384.000.

Comece a extração com esforço Low. Se ele perder uma distinção necessária, tente High naquele registro em vez de encarecer todas as linhas. Para entradas grandes, use a interface de streaming.

Experimente primeiro no Playground

  1. Abra o DeepSeek V4.1 Flash, escolha Playground e defina o controle de esforço como Low.
  2. Coloque a instrução de formato JSON do script abaixo em System prompt e a nota de reparo R17 em Message to the model, e clique em Run model.
  3. Confirme que uma aprovação pendente não vira um reparo concluído antes de migrar seu app.

Rode um caso de teste que pega o erro caro

Uma nota de reparo é um bom caso de teste porque um resumo fluente pode estar errado de um jeito que custa dinheiro. “Recomendar substituição” não é aprovação do cliente, e aprovação não é trabalho concluído.

Coloque uma chave de API Keys em SPICY_API_KEY, salve este script como check_repair.py e execute python3 check_repair.py. O prompt de sistema mostra o formato do JSON e contém a palavra “json”, que o guia do modo JSON da DeepSeek pede.

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

A decisão esperada é: inspecionado, aprovação ainda pendente e conclusão desconhecida. A recomendação precisa preservar as pastilhas traseiras e o prazo de antes do próximo passeio longo.

Adicione aprovação e conclusão como casos de teste separados

Não pare no caso de aprovação pendente. Use estes como modelos para casos adicionais: atualize SOURCE, REQUIRED_QUOTES e os valores esperados de aprovação/conclusão antes de rodar cada um. O verificador do R17 não roda diretamente contra R18 ou R19:

Último registroAuthorizedCompleted
R17: “Customer approval is pending.” Nenhum registro posteriorfalsenull
R18: “Customer approved the quoted replacement. Parts ordered; work scheduled for Thursday.”truenull
R19: “Replaced pads; road check completed Thursday.” Vinculado ao serviço aprovadotruetrue

Guarde junto da fonte o ID do serviço, a ordem dos registros e as datas: o R18 substitui a aprovação pendente, mas agendar o trabalho não comprova a conclusão. Se uma nota de “feito” sem data não puder ser vinculada ao mesmo serviço, retorne o vínculo não resolvido em vez de mesclá-la ao R17.

A mesma divisão em três aparece no fluxo de produção de um criador: “Aprove este design de personagem”, “design aprovado” e “vídeo gerado a partir do design aprovado” são um pedido, uma decisão e uma execução. Peça ao prompt de extração essas etapas separadamente, com o registro que sustenta cada uma, e preserve qualquer conflito entre notas em vez de pedir ao modelo que escolha a frase que soa mais confiante.

Mantenha aprovação e conclusão separadas em fluxos com ferramentas

Um registro de aprovação não prova que o reparo aconteceu: consulte o registro de conclusão antes de mudar o status e mantenha aprovação e conclusão como campos separados no que suas ferramentas retornam.

Chat sem ferramentas não precisa de nada extra. Com tools na requisição, um cliente que reconstrói cada mensagem do assistente só com papel e conteúdo passa no primeiro turno e falha no segundo com um 400, então um teste de turno único esconde o bug. Anexe a mensagem do assistente exatamente como foi retornada, incluindo reasoning_content, depois adicione uma mensagem tool por chamada com o tool_call_id correspondente e limite o número de rodadas. Teste dois turnos, não um. As receitas de chat mostram o ciclo completo.

Deixe um Agent preparar a chamada

Para extração repetida de registros, peça ao seu Agent de programação que use o Skill da SpicyAPI: “Read deepseek/v4.1-flash/chat, then adapt the example to my input; show the request before running it.” A consulta pela CLI é spicyapi models get deepseek/v4.1-flash/chat --json; pelo MCP, é spicyapi_model_get com model: "deepseek/v4.1-flash/chat". As chamadas de chat usam um cliente compatível ou a API de chat; os comandos de tarefas de mídia não geram respostas de chat.

Recalcule a fatura

As tarifas são US$ 0,30 por milhão de tokens de entrada sem cache, US$ 0,006 por milhão de entrada em cache e US$ 1,20 por milhão de saída, uma tarifa única em qualquer tamanho de requisição. Um prompt de 40.000 tokens sem cache com 2.000 tokens de saída custa US$ 0,0144. Se 30.000 dos tokens de entrada forem cobrados como cache e 10.000 como sem cache, a mesma chamada custa US$ 0,00558. Tokens de raciocínio são cobrados como saída, então uma resposta visível curta ainda pode usar mais saída do que você espera; none é a única configuração que os mantém fora da fatura.

Duas comparações importam ao deixar a API própria da DeepSeek. Os preços de tabela dela são os mesmos US$ 0,30, US$ 0,006 e US$ 1,20 no horário de pico, mas fora do pico custam metade lá, e aqui há uma tarifa única: um lote que você agenda fora do pico na DeepSeek custa cerca do dobro na SpicyAPI pelos mesmos tokens. E a tarifa de cache é 1/50 da de entrada, mas só para um prefixo idêntico byte a byte de uma chamada para outra: um prompt de sistema de 1.500 tokens em 10.000 chamadas dá 15 milhões de tokens de entrada, US$ 4,50 sem cache e no máximo US$ 0,09 se todos fossem leituras de cache. Compare o usage retornado e o custo liquidado com essas estimativas. Um carimbo de data/hora ou instruções reordenadas podem quebrar um prefixo compartilhado; entrada inalterada, por si só, não garante um acerto de cache.

Onde cada rota DeepSeek cai

V4.1 FlashV4 FlashV4 Pro
ID do modelodeepseek/v4.1-flash/chatdeepseek/v4-flash/chatdeepseek/v4-pro/chat
Saída máx.384.00065.536384.000
Entrada / cache / saída por 1 MUS$ 0,30 / US$ 0,006 / US$ 1,20US$ 0,44 / US$ 0,014 / US$ 1,32US$ 1,32 / US$ 0,044 / US$ 3,96
parallel_tool_callsNão expostoNão expostoExposto

As três aceitam 1.048.576 tokens de entrada, esforço none, low ou high (padrão high) e um max_tokens padrão de 32.768. Vindo de deepseek/v4-flash/chat, o corpo da requisição é idêntico, então só o ID muda: o teto de saída cresce quase seis vezes e todas as tarifas caem. Vindo do V4 Pro, você perde parallel_tool_calls e paga 77% menos pela entrada e cerca de 70% menos pela saída. Se o V4 Flash ou o V4 Pro é a melhor escolha para uma tarefa específica é outra decisão, tratada em DeepSeek V4 Flash vs Pro.

Se o seu app escreve ficção adulta

O V4.1 Flash está listado como sem censura na SpicyAPI, mas isso não é promessa de que ele complete todo pedido de ficção adulta. Em nossos testes de setembro de 2026, pedidos diretos de cenas maduras intensas voltaram suavizados todas as vezes, enquanto enquadrá-los como ficção ou definir o contexto no prompt de sistema rendeu mais deles por inteiro, embora não todos (detalhes na análise do V4.1 Flash). Se seus prompts foram escritos para um modelo que aceitava pedidos diretos, mova o enquadramento para a mensagem de sistema durante a migração e mantenha todo personagem adulto.

Corrija diretamente as falhas de migração

SintomaCausa provávelCorreção
400 unsupported_parameter nomeando temperature, top_p, uma penalidade, parallel_tool_calls ou thinkingCampo herdado do cliente antigoApague
content vazio ou finish_reason igual a lengthmax_tokens gasto em raciocínio, ou a resposta passou deleAumente max_tokens, reduza o esforço, tente de novo
O JSON é interpretado, mas informa trabalho concluídoRecomendação lida como conclusãoMantenha campos separados, exija citações, rode o verificador
Erro no segundo turno de uma conversa com ferramentasMensagem do assistente reconstruída sem reasoning_contentAnexe a mensagem retornada sem alterações
402 com saldo suficienteA retenção 40201 excede o saldo, ou o limite de gasto 40202 foi atingidoInspecione o erro: reduza um orçamento de saída excessivo para a retenção, ou revise os limites da chave/workspace
Idempotency-Key reutilizada rejeitada (400 ou 409)A chave pertence a uma requisição concluída ou a um corpo diferenteUm 400 para requisição concluída não consegue reproduzir a resposta; um 409 pode indicar corpo alterado. Reutilize a chave só para uma nova tentativa de rede idêntica dentro da janela dela; confira a liquidação da tarefa antes de escolher uma nova chamada paga
Uma chamada longa sem streaming cai perto de 100 segundosCorte na borda da redeUse streaming na resposta

Troque sem perder a referência antiga

Mantenha o ID do modelo antigo na configuração e rode o caso de teste nos dois. Mova o tráfego quando o teste passar com o novo ID, suas próprias fontes validarem e o usage registrado bater com a aritmética acima.

Também em English · 日本語 · 한국어 · Deutsch · Français · Español · Русский · Italiano · Polski

Experimente você mesmo

Você paga só pelo que executar — sem plano e sem assinatura.

Feito com MiniMax H3 · ByteDance Seedance 2.0 Fast