DeepSeek V4 API Python: Exemplos de Código Mínimos com Streaming

DeepSeek V4 API Python: Exemplos de Código Mínimos com Streaming

I’ll translate this markdown article to Portuguese. Let me read the article carefully and provide a complete translation while preserving all formatting.


Oi pessoal! Sou a Dora. Tudo começou com uma pequena irritação: eu ficava copiando o mesmo boilerplate de chat-completion entre projetos, trocando URLs base e nomes de modelos como etiquetas em potes. Não é trabalho difícil, apenas aquele tipo que adiciona atrito ao seu dia. Eu havia visto o DeepSeek aparecer o suficiente para ficar curioso, então separei algumas manhãs no final de janeiro de 2026 para integrar a API “V4” deles ao meu stack Python e ver como se sentia em uso real.

Eu não estava atrás de benchmarks. Eu queria saber: o cliente sai do meu caminho, posso fazer streaming de forma confiável, e os erros falham de uma forma que é fácil de entender? Aqui está o que tentei, o que me tropeçou, e o que funcionou silenciosamente. Vamos lá!

Configuração do Ambiente

Dependências

Mantive a configuração simples no macOS com Python 3.11. Você pode fazer isso com a biblioteca padrão, mas três pequenos pacotes facilitaram a vida:

  • requests (HTTP direto: bom o suficiente para a maioria dos casos)
  • httpx (async e timeouts que se comportam bem)
  • python-dotenv (para não colar chaves por aí) Se você planeja fazer streaming com Server-Sent Events, pode usar requests e analisar linhas por conta própria (o que fiz), ou trazer um auxiliar como sseclient-py. Eu optei por requests, menos partes móveis.

Instalação

pip install requests httpx python-dotenv

Também criei um ambiente virtual mínimo por projeto. É um conselho chato, mas economiza você da deriva de dependências quando revisita isso em três meses.

Configuração da Chave API

Armazenei a chave em uma variável de ambiente. Nada sofisticado:

# .env
DEEPSEEK_API_KEY=your_key_here

Depois em Python:

from dotenv import load_dotenv
import os

load_dotenv()

API_KEY = os.getenv("DEEPSEEK_API_KEY")

if not API_KEY:
    raise RuntimeError("Missing DEEPSEEK_API_KEY")

Duas pequenas observações da configuração:

  • A URL base e os nomes dos modelos mudam mais frequentemente do que você pensa. Verifiquei a documentação oficial da API DeepSeek antes de cada execução para confirmar caminhos e modelos disponíveis.
  • Mantive timeouts explícitos. É um hábito que compensa uma vez que você atinge limites de taxa ou ruído de rede.

Requisição de Chat Básica

O modelo mental é familiar se você já usou chat-completions em outros lugares. O DeepSeek expõe um endpoint de chat com messages=[{"role": "...", "content": "..."}]. Isso é útil porque não precisei repensar meus prompts.
Aqui está a requisição mínima que usei com requests. Os nomes dos modelos variam por conta e região, durante meus testes vi referências como deepseek-chat e deepseek-reasoner. Se sua documentação mencionar uma string de modelo “V4”, use essa. Caso contrário, escolha o modelo de propósito geral mais próximo listado em seu console.

import os
import requests

API_KEY = os.environ["DEEPSEEK_API_KEY"]
BASE_URL = "https://api.deepseek.com/v1/chat/completions"

payload = {
    "model": "deepseek-chat", # check docs/console for the exact model
    "messages": [
        {"role": "system", "content": "You are a concise assistant."},
        {"role": "user", "content": "Give me two bullet points on the value of clear commit messages."}
    ],
    "temperature": 0.3,
    "max_tokens": 200
}

resp = requests.post(
    BASE_URL,
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    },
    json=payload,
    timeout=30
)

resp.raise_for_status()

data = resp.json()
content = data["choices"][0]["message"]["content"]
print(content)

Observações de Campo

  • A primeira execução foi tranquila (um alívio). A estrutura correspondia ao que esperava, o que tornou a migração de uma pequena biblioteca de prompts rápida.
  • Mantive a temperatura baixa para respostas repetíveis. Isso soa óbvio, mas ainda esqueço quando estou solucionando problemas.
  • Se você precisar de execuções determinísticas, também fixe top_p e seed se a API suportá-los. Quando a documentação é silenciosa, presumo não-determinístico.

Se você estiver comparando provedores, a vantagem aqui é baixo atrito. A desvantagem é que as diferenças se escondem nas margens, payloads de erro, contabilidade de tokens e formato de streaming. Essas margens são onde sua integração parece robusta ou incômoda.

Exemplo de Geração de Código

Não peço para modelos escreverem módulos completos. Torna-se um trabalho de limpeza. Mas para pequenos auxiliares, como “analisar este formato de timestamp” ou “rascunhar o SQL com placeholders”, é útil.
Usei um prompt estreito, um contrato claro e limites de saída pequenos. Isso manteve o modelo de vagar e fez os diffs fáceis de revisar.

import requests, os

API_KEY = os.environ["DEEPSEEK_API_KEY"]
BASE_URL = "https://api.deepseek.com/v1/chat/completions"

messages = [
    {"role": "system", "content": (
        "You generate small, safe Python helpers. "
        "Return only code inside one block."
    )},
    {"role": "user", "content": (
        "Write a Python function `parse_yyyymmdd` that takes a string like '2026-01-31' "
        "and returns a datetime.date. If invalid, return None. No external deps."
    )}
]

resp = requests.post(
    BASE_URL,
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    },
    json={
        "model": "deepseek-chat", # or your V4-capable model
        "messages": messages,
        "temperature": 0,
        "max_tokens": 250
    },
    timeout=30
)

resp.raise_for_status()
code = resp.json()["choices"][0]["message"]["content"]
print(code)

O que ajudou na prática

  • Sempre digo a ela para retornar apenas código. Se pulo isso, obtenho frases de encerramento que não preciso.
  • Temperatura 0 reduz edições problemáticas.
  • Leio a lógica de qualquer forma. Na minha execução lidou com ValueError, mas ainda adicionei um teste extra para espaços em branco. Dois minutos extras agora economizam horas de surpresa depois.

Isso não economizou tempo na primeira tentativa. Depois de três ou quatro pequenos auxiliares, notei que reduziu o esforço mental: menos trocas de abas, menos momentos “qual é o código exato strptime novamente?”. Isso é suficiente para mim.

Respostas em Streaming

Gosto de streaming para qualquer prompt que possa crescer. Permite que eu saia mais cedo se a resposta está se desviando, e torna respostas longas se sentirem menos pesadas.
O streaming do DeepSeek usou o padrão usual em meus testes: defina stream=true e leia linhas de dados até [DONE]. Eu não precisei de um cliente especial, requests com iter_lines foi suficiente.

import os, json, requests

API_KEY = os.environ["DEEPSEEK_API_KEY"]
BASE_URL = "https://api.deepseek.com/v1/chat/completions"

payload = {
    "model": "deepseek-chat",
    "messages": [
        {"role": "system", "content": "Be brief."},
        {"role": "user", "content": "Summarize this: Streaming keeps the UI responsive and lets me stop early."}
    ],
    "stream": True,
    "temperature": 0.2,
}

with requests.post(
    BASE_URL,
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    },
    json=payload,
    stream=True,
    timeout=60
) as r:
    r.raise_for_status()
    for line in r.iter_lines(decode_unicode=True):
        if not line:
            continue
        if line.startswith("data: "):
            chunk = line[len("data: "):]
            if chunk == "[DONE]":
                break
            try:
                obj = json.loads(chunk)
                delta = obj["choices"][0]["delta"].get("content", "")
                if delta:
                    print(delta, end="", flush=True)
            except json.JSONDecodeError:
                # I keep a small log when this happens: usually network blips
                pass

print()

Dois pequenos comportamentos que gostei:

  • Os tokens iniciais chegaram rapidamente (um ou dois segundos em uma conexão limpa). Não é científico, apenas o suficiente para parecer ágil quando integrei em uma ferramenta CLI.
  • O marcador [DONE] apareceu de forma confiável. Soa trivial até que não aparece, terminadores ausentes fazem as interfaces travarem.

Se você precisar fazer streaming em um aplicativo web, colocaria uma fina camada de servidor entre para normalizar eventos. É um passo extra, mas mantém seu frontend simples.

Server-Sent Events

Sob o capô, você está efetivamente lendo Server-Sent Events. Se você preferir um auxiliar, sseclient-py funciona, mas criar o seu próprio aqui está bem, desde que você se proteja contra linhas parciais e timeouts. A página de documentação sobre streaming na documentação da API DeepSeek foi suficiente para que isso funcionasse sem surpresas.

Tratamento de Erros

A maioria dos meus erros foi previsível: chave ausente, nome de modelo ruim, ou timeouts quando limitei minha rede para simular Wi‑Fi de viagem.
Um pequeno padrão que reutilizo:

import httpx, time, os

API_KEY = os.environ["DEEPSEEK_API_KEY"]
BASE_URL = "https://api.deepseek.com/v1/chat/completions"

RETRIABLE = {408, 409, 425, 429, 500, 502, 503, 504}

async def chat_once(client, messages):
    resp = await client.post(
        BASE_URL,
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={
            "model": "deepseek-chat",
            "messages": messages,
            "temperature": 0.2,
            "max_tokens": 300,
        },
        timeout=30,
    )
    if resp.status_code == 401:
        raise RuntimeError("Unauthorized. Check DEEPSEEK_API_KEY and account access.")
    if resp.status_code == 404:
        raise RuntimeError("Endpoint or model not found. Confirm model name in console/docs.")
    if resp.status_code in RETRIABLE:
        raise RuntimeError(f"Retryable status: {resp.status_code}")
    resp.raise_for_status()
    return resp.json()

async def chat_with_retries(messages, attempts=4):
    backoff = 0.5
    async with httpx.AsyncClient() as client:
        for i in range(attempts):
            try:
                return await chat_once(client, messages)
            except RuntimeError as e:
                msg = str(e)
                if "Retryable status" in msg and i < attempts - 1:
                    time.sleep(backoff)
                    backoff *= 2
                    continue
                raise

Algumas observações práticas:

  • Limites de taxa: Eu vi 429 quando dispus testes paralelos. Backoff exponencial ajudou, mas também adicionei pequena variação aleatória (aleatória 50–150ms) para evitar rebanhos trovejadores.
  • Higiene de timeout: Defini timeouts de conexão/leitura mais curtos para verificações rápidas (5–10s) e mais longos para prompts grandes. Os timeouts não devem ser todos 30s por padrão: isso oculta problemas.
  • Payloads de erro: Quando as coisas falharam, o corpo JSON incluía uma mensagem que eu poderia passar a um log. Ainda a encapsulo em minhas próprias exceções para que eu controle o que atinge a interface de usuário.

Se seu código já fala o esquema estilo OpenAI, isso é gerenciável: mesma forma de mensagem, margens ligeiramente diferentes. A principal coisa é ser rigoroso sobre nomes de modelos e registrar o corpo de resposta completo em não-2xx para não adivinhar.
Em termos de documentação, confiei na documentação oficial da API DeepSeek para nomes de parâmetros e forma de streaming. Sempre que um provedor usa endpoints familiares, é tentador assumir paridade. Aprendi a verificar a documentação primeiro e copiar menos entre clientes do que penso que posso.

Quem pode gostar disso

  • Se você tem um wrapper Python existente para chat completions, o caminho de migração é suave.
  • Se você se importa com streaming e tentativas simples, ele se comporta de forma previsível.
  • Se você precisar de ferramentas muito específicas (schemas de chamada de função, tokens de raciocínio, ou trabalhos em lote), você vai querer ler a documentação de perto e fazer protótipos com uma tarefa estreita antes de se comprometer.

Não tentei orquestrar agentes longos e multi-etapas aqui. Foquei em prompts pequenos de uso diário, o tipo que elimina atrito. É lá que a API DeepSeek V4 com Python se sentiu estável o suficiente para manter.