DeepSeek Tool Calls permite que o modelo solicite funções definidas pela sua aplicação. O modelo pode pedir uma consulta de pedido, cálculo de frete ou abertura de ticket, mas não executa a função. Seu backend decide se a ferramenta existe, valida os argumentos, verifica a permissão do usuário, executa o código permitido e devolve o resultado com o tool_call_id correspondente.
Atualização documental — 23 de agosto de 2026.
deepseek-v4-flash,deepseek-v4-proedeepseek-v4-flash-vision-expaceitam Tool Calls. O Vision Exp também pode receber imagens produzidas por ferramentas na Responses API. A restrição de omitirtool_choicenesta página pertence a Chat Completions com Thinking; a Responses API documentatool_choiceseparadamente. Em qualquer interface, o backend continua responsável por validação e autorização.
Teste ao vivo: geração de uma chamada de ferramenta
Com V4 Flash, Thinking desativado, resposta não streaming e tool_choice: "required", declaramos a função local somar e pedimos ao modelo que a usasse para 2 + 3.

| Item | Resultado |
|---|---|
| HTTP / latência | 200 / 336 ms |
finish_reason | tool_calls |
message.content | Vazio |
| Chamadas | 1 |
| Função | somar |
| Argumentos | {"a": 2, "b": 3} |
| Entrada / saída / total | 315 / 52 / 367 |
A função externa não foi executada. Não houve cálculo em backend, serviço externo nem segundo turno com mensagem de ferramenta. O teste valida somente a etapa em que o modelo propõe o Tool Call.
Trate nome e argumentos como entrada não confiável. O backend deve validar schema, autorização, limites e contexto antes de qualquer ação, especialmente operações financeiras, exclusões ou alterações irreversíveis.
Veja JSON Output, Developer Tools e a documentação oficial de Tool Calls.
Regra de segurança: o modelo propõe; o backend autoriza e executa. JSON válido, strict mode ou uma descrição convincente nunca substituem autenticação, autorização e validação das regras do negócio.
Em 29/07/2026, os aliases legados ainda responderam nesta conta e retornaram V4 Flash, embora ausentes de
GET /models. Trate o comportamento como compatibilidade observada, não contrato; implemente Tool Calls com IDs V4 explícitos.
Última verificação documental: 23 de agosto de 2026. O teste funcional permanece datado de 29 de julho. Este site e este guia são independentes.
Modelos e entrada visual
| Model ID | Tool Calls | Entrada visual | Concorrência por conta |
|---|---|---|---|
deepseek-v4-flash | Sim | Não | 2.500 |
deepseek-v4-pro | Sim | Não | 500 |
deepseek-v4-flash-vision-exp | Sim | Sim, experimental | 2.500 |
Em Chat Completions, o conteúdo da mensagem tool é texto. Para devolver uma captura de tela ou outra imagem produzida por ferramenta diretamente ao modelo, use o formato Responses descrito abaixo.
Resumo da implementação correta
- declare funções no array
toolscom nome, descrição e JSON Schema; - envie a pergunta e as ferramentas para
/chat/completions; - aceite uma chamada de ferramenta somente com
finish_reason: "tool_calls"; - valide nome, ID e argumentos antes de executar;
- verifique a autorização do usuário dentro da função;
- devolva exatamente um resultado para cada chamada, usando o mesmo
tool_call_id; - repita o loop até uma resposta com
finish_reason: "stop"ou até o limite definido; - em Thinking Mode, se o pedido incluir
tools, preservereasoning_contentde toda mensagemassistantnas interações subsequentes, mesmo quandotool_callsestiver vazio; - não envie
tool_choiceno Thinking Mode V4; - não exponha API key, shell, banco ou cliente HTTP genérico ao modelo.
Esta página trata especificamente do loop de ferramentas. Para autenticação, endpoint e parâmetros gerais, consulte DeepSeek API. Para o objeto completo de requisição e resposta, use Chat Completions.
Como Tool Calls funciona
| Etapa | Responsável | Ação |
|---|---|---|
| 1 | Usuário | Pede uma informação ou ação |
| 2 | Aplicação | Envia messages e tools à API |
| 3 | Modelo | Responde diretamente ou retorna tool_calls |
| 4 | Backend | Valida função, argumentos, identidade e autorização |
| 5 | Backend | Executa somente a função permitida |
| 6 | Aplicação | Adiciona uma mensagem role="tool" por chamada |
| 7 | Aplicação | Envia novamente todo o contexto necessário |
| 8 | Modelo | Solicita outra ferramenta ou conclui com stop |
Uma interação pode exigir várias rodadas. Por exemplo, o modelo pode consultar a data, depois o estoque e só então responder. Um código que executa apenas a primeira chamada e espera texto final falha em fluxos multi-step. O loop precisa ser limitado para evitar custo ou repetição sem fim.
Objetos e campos essenciais
| Campo | Função | Validação necessária |
|---|---|---|
tools | Lista de funções apresentadas ao modelo | Somente ferramentas necessárias; máximo documentado de 128 |
function.name | Identificador da função | Allowlist exata; até 64 caracteres no formato aceito |
function.parameters | JSON Schema dos argumentos | Validar novamente no backend |
tool_choice | Controla escolha em Non-Thinking | Não enviar em Thinking V4 |
tool_calls | Chamadas propostas pelo modelo | Array, IDs únicos, tipo function e nome permitido |
function.arguments | String que pretende conter JSON | Limitar tamanho, fazer parse e aplicar schema |
tool_call_id | Liga o resultado à chamada | Copiar exatamente o ID recebido |
reasoning_content | Raciocínio retornado em Thinking | Quando o pedido incluir tools, preservar em toda mensagem assistant subsequente, mesmo sem Tool Call |
finish_reason | Indica por que a geração parou | Executar apenas em tool_calls; concluir apenas em stop |
A API aceita apenas funções como tipo de ferramenta e documenta até 128 funções por requisição. Isso é um limite técnico, não uma recomendação para declarar 128 ferramentas. Uma lista curta, bem descrita e específica reduz seleções erradas e facilita autorização.
Exemplo completo em Node.js
O exemplo consulta um pedido fictício. Ele usa Node.js 20 ou superior, fetch nativo e Zod. A função é read-only, mas ainda verifica se o pedido pertence ao usuário autenticado. Em uma aplicação real, authContext deve vir da sessão validada no backend, nunca de um argumento escolhido pelo modelo.
mkdir deepseek-tools-demo cd deepseek-tools-demo npm init -y npm install zod npm pkg set type=module Salve o código como tool-calls.mjs:
import { z } from "zod"; const API_KEY = process.env.DEEPSEEK_API_KEY; const MODEL = process.env.DEEPSEEK_MODEL || "deepseek-v4-flash"; const THINKING_ENABLED = process.env.DEEPSEEK_THINKING === "enabled"; const MAX_TOOL_ROUNDS = 8; if (!API_KEY) { throw new Error("DEEPSEEK_API_KEY não configurada."); } const ALLOWED_MODELS = [ "deepseek-v4-flash", "deepseek-v4-pro", "deepseek-v4-flash-vision-exp", ]; if (!ALLOWED_MODELS.includes(MODEL)) { throw new Error("DEEPSEEK_MODEL não está no allowlist atual."); } const tools = [ { type: "function", function: { name: "get_order_status", description: "Consulta um pedido pelo ID. Use somente quando o usuário pedir o status de um pedido específico.", parameters: { type: "object", properties: { order_id: { type: "string", description: "ID no formato BR seguido por cinco dígitos, por exemplo BR12345.", pattern: "^BR[0-9]{5}$", }, }, required: ["order_id"], additionalProperties: false, }, }, }, ]; const orderArgsSchema = z.object({ order_id: z.string().regex(/^BR[0-9]{5}$/), }).strict(); const orders = new Map([ [ "BR12345", { ownerId: "user_42", status: "em_transporte", previsaoEntrega: "2026-07-22", }, ], [ "BR99999", { ownerId: "user_77", status: "aguardando_pagamento", previsaoEntrega: null, }, ], ]); function getOrderStatus(args, authContext) { const order = orders.get(args.order_id); if (!order || order.ownerId !== authContext.userId) { return { found: false }; } return { found: true, order_id: args.order_id, status: order.status, previsao_entrega: order.previsaoEntrega, }; } const toolRegistry = new Map([ [ "get_order_status", { schema: orderArgsSchema, execute: getOrderStatus, }, ], ]); class DeepSeekApiError extends Error { constructor(status) { super(`Falha na DeepSeek API: ${status}`); this.name = "DeepSeekApiError"; this.status = status; } } async function callDeepSeek(messages, apiUserId) { const payload = { model: MODEL, messages, tools, thinking: { type: THINKING_ENABLED ? "enabled" : "disabled" }, max_tokens: 1200, stream: false, user_id: apiUserId, }; if (THINKING_ENABLED) { payload.reasoning_effort = "high"; } else { /* tool_choice é usado somente no modo Non-Thinking. */ payload.tool_choice = "auto"; } const response = await fetch( "https://api.deepseek.com/chat/completions", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${API_KEY}`, }, body: JSON.stringify(payload), signal: AbortSignal.timeout(90_000), }, ); if (!response.ok) { throw new DeepSeekApiError(response.status); } const data = await response.json(); const choice = data?.choices?.[0]; if (!choice?.message || typeof choice.finish_reason !== "string") { throw new Error("Resposta inválida da DeepSeek API."); } return choice; } function normalizeAssistantMessage(message) { const toolCalls = Array.isArray(message.tool_calls) ? message.tool_calls : []; const normalized = { role: "assistant", content: typeof message.content === "string" ? message.content : "", }; if (THINKING_ENABLED) { if (typeof message.reasoning_content !== "string") { throw new Error("reasoning_content ausente em uma resposta Thinking cujo pedido incluiu tools."); } normalized.reasoning_content = message.reasoning_content; } if (toolCalls.length > 0) { normalized.tool_calls = toolCalls; } return normalized; } function executeToolCall(toolCall, authContext, seenToolCallIds) { if ( !toolCall || toolCall.type !== "function" || typeof toolCall.id !== "string" || !toolCall.id || typeof toolCall.function?.name !== "string" || typeof toolCall.function?.arguments !== "string" ) { throw new Error("Tool Call malformada."); } if (seenToolCallIds.has(toolCall.id)) { throw new Error("tool_call_id duplicado."); } seenToolCallIds.add(toolCall.id); const registeredTool = toolRegistry.get(toolCall.function.name); if (!registeredTool) { throw new Error("Função não permitida."); } if (toolCall.function.arguments.length > 4096) { throw new Error("Argumentos da ferramenta excedem o limite local."); } let rawArguments; try { rawArguments = JSON.parse(toolCall.function.arguments); } catch { throw new Error("Argumentos da ferramenta não são JSON válido."); } const parsedArguments = registeredTool.schema.safeParse(rawArguments); if (!parsedArguments.success) { throw new Error("Argumentos da ferramenta foram rejeitados pelo schema local."); } const result = registeredTool.execute(parsedArguments.data, authContext); return { role: "tool", tool_call_id: toolCall.id, content: JSON.stringify(result), }; } async function runToolLoop({ userMessage, authContext, apiUserId }) { const messages = [ { role: "system", content: "Você é um assistente de pedidos. Use ferramentas para consultar dados. Nunca invente status.", }, { role: "user", content: userMessage }, ]; const seenToolCallIds = new Set(); for (let round = 1; round <= MAX_TOOL_ROUNDS; round += 1) { const choice = await callDeepSeek(messages, apiUserId); const toolCalls = Array.isArray(choice.message.tool_calls) ? choice.message.tool_calls : []; if (choice.finish_reason === "stop") { if (toolCalls.length !== 0) { throw new Error("Resposta stop contém Tool Calls inesperadas."); } const finalContent = choice.message.content; if (typeof finalContent !== "string" || !finalContent.trim()) { throw new Error("Resposta final vazia."); } messages.push(normalizeAssistantMessage(choice.message)); return finalContent; } if (choice.finish_reason !== "tool_calls") { throw new Error(`Geração não concluída: ${choice.finish_reason}`); } if (toolCalls.length === 0) { throw new Error("finish_reason=tool_calls sem chamadas de ferramenta."); } const assistantMessage = normalizeAssistantMessage(choice.message); messages.push(assistantMessage); const toolResults = toolCalls.map((toolCall) => executeToolCall(toolCall, authContext, seenToolCallIds), ); if (toolResults.length !== toolCalls.length) { throw new Error("Nem todas as Tool Calls receberam resultado."); } messages.push(...toolResults); } throw new Error(`Limite de ${MAX_TOOL_ROUNDS} rodadas de ferramentas atingido.`); } const authContext = { /* Em produção, derive da sessão autenticada no servidor. */ userId: "user_42", }; const answer = await runToolLoop({ userMessage: "Qual é o status do meu pedido BR12345?", authContext, apiUserId: "demo_user_42", }); console.log(answer); Execute em Non-Thinking:
DEEPSEEK_API_KEY="sua_chave" \ DEEPSEEK_MODEL="deepseek-v4-flash" \ DEEPSEEK_THINKING="disabled" \ node tool-calls.mjs Para Thinking, altere somente o ambiente:
DEEPSEEK_API_KEY="sua_chave" \ DEEPSEEK_MODEL="deepseek-v4-flash" \ DEEPSEEK_THINKING="enabled" \ node tool-calls.mjs Controles aplicados pelo exemplo
- aceita apenas os três IDs hospedados atuais;
- mantém a API key no ambiente do servidor;
- limita o loop a oito respostas do modelo;
- executa apenas nomes registrados em
toolRegistry; - limita o tamanho da string de argumentos antes de fazer parse;
- valida argumentos novamente com Zod em modo estrito;
- verifica o proprietário do pedido dentro da função;
- recusa IDs duplicados e cria um resultado para cada chamada;
- preserva
reasoning_contentem todas as mensagensassistantretornadas por pedidos Thinking comtools; - conclui somente quando
finish_reasonéstope há conteúdo; - rejeita
length,content_filter,insufficient_system_resourcee valores desconhecidos.
A base falsa existe apenas para tornar o exemplo reproduzível. Troque-a por uma camada de serviço que use credenciais de baixo privilégio, queries parametrizadas, timeout e auditoria. Não permita que o modelo escreva SQL, URL ou comando de shell e os execute diretamente.
Imagens produzidas por ferramentas na Responses API
Com deepseek-v4-flash-vision-exp, o output de function_call_output ou custom_tool_call_output pode ser uma lista de blocos e incluir input_image. Isso permite, por exemplo, que uma ferramenta autorizada capture uma tela e devolva a imagem ao modelo.
const response = await client.responses.create({
model: "deepseek-v4-flash-vision-exp",
input: [
{
role: "user",
content: "Leia a captura retornada pela ferramenta.",
},
{
type: "function_call",
call_id: "fc1",
name: "take_screenshot",
arguments: "{}",
},
{
type: "function_call_output",
call_id: "fc1",
output: [
{
type: "input_image",
image_url: "data:image/png;base64,<BASE64_DATA>",
},
],
},
],
});
O bloco também pode usar file_id para uma imagem enviada à Files API, mas image_url e file_id são mutuamente exclusivos. Imagens são aceitas em mensagens user ou developer e nos outputs dessas ferramentas; em system ou assistant, retornam erro 400. Com Flash ou Pro textual, um input_image não é processado como imagem real.
Valide origem, formato, tamanho, autorização, retenção e conteúdo antes de anexar a imagem. Uma tool call válida não concede permissão para capturar tela, câmera ou documento.
Thinking Mode com Tool Calls
DeepSeek V4 suporta ferramentas em Thinking Mode, mas há dois requisitos de compatibilidade que mudam a implementação:
- Não envie
tool_choice: as notas oficiais de integração informam que V4 Thinking rejeita esse parâmetro. Deixe a escolha automática nesse modo. - Preserve
reasoning_content: se a requisição incluirtools, o campo precisa voltar integralmente em todas as interações subsequentes, mesmo quando a mensagemassistantnão contiver Tool Calls. A omissão pode causar HTTP 400. - Mantenha
contentpresente: normalize valor nulo para string vazia na mensagem de assistente com ferramentas.
O exemplo adiciona tool_choice: "auto" somente quando Thinking está desativado. Em pedidos Thinking com tools, ele reconstrói cada mensagem assistant com content e reasoning_content, acrescentando tool_calls quando existirem. Não resuma nem altere reasoning_content no meio do histórico.
No Thinking Mode, os parâmetros temperature, top_p, presence_penalty e frequency_penalty não têm efeito. O exemplo não os envia. O esforço documentado é low, high ou max; o padrão é high, e medium e xhigh são mapeados para high.
Para um guia dedicado a conversas, streaming e parâmetros de raciocínio, consulte DeepSeek Thinking Mode.
Raw HTTP e OpenAI SDK usam posições diferentes
No payload HTTP enviado diretamente por fetch, thinking e user_id são campos de primeiro nível, como no exemplo Node.js. Ao usar o OpenAI SDK para Python, a documentação orienta colocar esses campos específicos em extra_body; reasoning_effort permanece como argumento da chamada.
import os from openai import OpenAI client = OpenAI( api_key=os.environ["DEEPSEEK_API_KEY"], base_url="https://api.deepseek.com", ) response = client.chat.completions.create( model="deepseek-v4-flash", messages=[{"role": "user", "content": "Consulte meu pedido."}], tools=tools, reasoning_effort="high", extra_body={ "thinking": {"type": "enabled"}, "user_id": "user_pseudonimo_42", }, ) O trecho acima demonstra apenas a posição dos parâmetros. Um aplicativo Python ainda precisa implementar o mesmo loop limitado, preservar a mensagem completa do assistente, validar todas as chamadas e encerrar somente em stop.
tool_choice em Non-Thinking
As opções abaixo pertencem ao formato OpenAI em Non-Thinking. Não copie esses exemplos para Thinking V4.
| Valor | Comportamento | Quando usar |
|---|---|---|
auto | O modelo responde ou chama uma ou mais funções | Perguntas que podem precisar de dados externos |
none | Impede chamadas de ferramentas | Resposta exclusivamente textual |
required | Exige uma ou mais chamadas | Fluxo no qual toda resposta depende de verificação externa |
| Função específica | Força o nome declarado | Fluxo em que a aplicação já determinou a única ferramenta possível |
{ "tool_choice": { "type": "function", "function": { "name": "get_order_status" } } } Forçar uma ferramenta não autoriza sua execução. O backend continua responsável por confirmar identidade, permissão, estado do recurso e validade semântica dos argumentos.
Strict mode Beta
Strict mode tenta fazer a saída da chamada cumprir o JSON Schema declarado. É um recurso Beta e requer:
- usar
https://api.deepseek.com/betacomo base URL; - definir
strict: trueem todas as funções do array; - fornecer um schema aceito pelo servidor.
{ "type": "function", "function": { "name": "get_order_status", "strict": true, "description": "Consulta um pedido pelo ID.", "parameters": { "type": "object", "properties": { "order_id": { "type": "string", "pattern": "^BR[0-9]{5}$" } }, "required": ["order_id"], "additionalProperties": false } } } Os tipos listados pela documentação Beta incluem object, string, number, integer, boolean, array, enum e anyOf. Em cada objeto, todas as propriedades precisam ser obrigatórias e additionalProperties deve ser false. Para strings, minLength e maxLength não são suportados; para arrays, minItems e maxItems também não são.
Strict mode controla formato, não intenção. Um order_id pode obedecer ao regex e pertencer a outra pessoa. Uma quantia pode ser número e ainda exceder o saldo permitido. Valide novamente com sua biblioteca, aplique regras do negócio e verifique permissão antes de qualquer efeito.
Como tratar finish_reason
| Valor | Ação segura |
|---|---|
tool_calls | Exigir chamadas válidas, executar todas as permitidas e continuar o loop |
stop | Exigir ausência de Tool Calls e conteúdo final não vazio; então encerrar |
length | Tratar como resposta incompleta; não executar nem exibir como conclusão |
content_filter | Interromper e aplicar a política da aplicação |
insufficient_system_resource | Tratar como falha transitória controlada |
| Ausente ou desconhecido | Falhar fechado e registrar apenas metadados seguros |
Não use apenas a presença de message.content para decidir que terminou. O modelo pode retornar texto junto com uma solicitação de ferramenta. O contrato do loop deve se basear em finish_reason e na consistência entre esse valor e tool_calls.
Controles de segurança para produção
1. Allowlist de ferramentas
Mapeie nomes estáticos para funções conhecidas. Nunca use eval, import dinâmico baseado no nome gerado ou reflexão que permita chamar qualquer método do processo.
2. Autorização fora do prompt
O prompt não é uma política de acesso. A função deve receber um contexto autenticado criado pelo servidor e verificar tenant, usuário, papel e recurso. Não permita que o modelo forneça user_id, tenant_id ou permissões como argumentos confiáveis.
3. Separar leitura de escrita
Consultas read-only podem seguir um fluxo automático após autorização. Cancelar, reembolsar, cobrar, enviar, apagar ou alterar precisa de confirmação explícita, proteção contra repetição e auditoria. A confirmação deve ocorrer na interface e ser associada à ação exata, não inferida de uma frase anterior.
4. Idempotência e concorrência
Operações de escrita devem aceitar uma idempotency key criada pelo backend. Grave o estado da ação antes de fazer retry e controle concorrência para impedir duas execuções simultâneas. Se houver timeout após o envio, verifique o estado real antes de repetir.
5. Reduzir a superfície de cada função
Prefira get_order_status(order_id) a ferramentas genéricas como run_sql(query), fetch_url(url) ou run_command(command). Interfaces estreitas permitem validação, autorização e logs compreensíveis.
6. Tratar resultados como não confiáveis
Resultados de CRM, páginas e documentos podem conter instruções maliciosas. Serialize apenas campos necessários e diga ao modelo que dados de ferramentas são conteúdo, não comandos. Prompt injection não deve alterar a allowlist ou permissões.
7. Logs com minimização
Registre request ID, ferramenta, decisão de autorização, duração, resultado e código de erro. Evite gravar chave, raciocínio completo, prompt, documento, token de sessão ou resposta integral sem necessidade e retenção definida.
Matriz mínima de testes
| Caso | Resultado esperado |
|---|---|
| Função desconhecida | Falhar sem executar |
| Argumentos que não são JSON | Falhar antes da função |
| Campo extra | Schema local rejeita |
| Pedido de outro usuário | Não revelar existência ou dados |
tool_call_id duplicado | Interromper o loop |
| Duas Tool Calls na mesma resposta | Um resultado correspondente para cada ID |
| Mais de oito rodadas | Encerrar sem nova chamada |
finish_reason=length | Não apresentar resposta parcial |
Pedido Thinking com tools sem reasoning_content na mensagem assistant | Falhar antes da próxima requisição |
| Prompt injection no resultado | Não ampliar ferramentas ou permissões |
| Retry de ação de escrita | Idempotência impede duplicação |
Quando usar Tool Calls
- consultar estoque, pedido ou status de serviço;
- obter dados recentes de um sistema autorizado;
- calcular frete ou elegibilidade com uma função determinística;
- criar tickets ou tarefas após confirmação e autorização;
- orquestrar etapas específicas com contratos de entrada estreitos.
Não use Tool Calls quando uma resposta textual é suficiente, quando não existe função real, quando a aplicação ainda não tem autenticação ou quando uma decisão humana obrigatória não pode ser automatizada. JSON Output estrutura texto; Tool Calls solicita execução externa. São recursos diferentes.
Privacidade e transparência
O operador do sistema downstream é responsável pelos dados dos próprios usuários. Envie à API apenas os campos necessários, não inclua dados pessoais em user_id, não retorne um registro inteiro quando três campos bastam e defina retenção para logs de ferramentas.
- informe que a saída é gerada por IA e pode conter erros;
- explique quais fornecedores e sistemas recebem dados;
- obtenha base legal e consentimento quando aplicáveis;
- implemente acesso, correção e exclusão conforme a jurisdição;
- não apresente seu aplicativo como produto oficial ou endossado.
Perguntas frequentes
O modelo executa a função?
Não. Ele gera uma proposta estruturada. A aplicação valida, autoriza e executa a função real.
Quais modelos hospedados aceitam Tool Calls?
deepseek-v4-flash, deepseek-v4-pro e deepseek-v4-flash-vision-exp. Somente o Vision Exp processa imagens.
Posso usar tool_choice no Thinking Mode?
Não no DeepSeek V4 Thinking pelo endpoint OpenAI-compatible. As notas oficiais de integração informam que o parâmetro é rejeitado. Omita-o e deixe a seleção automática.
Strict mode elimina a validação local?
Não. Ele ajuda no formato do JSON Schema, mas não verifica autorização, propriedade, estado do recurso ou regra de negócio.
Por que preservar reasoning_content?
Quando a requisição inclui tools, a DeepSeek exige que reasoning_content de cada mensagem assistant retorne nas interações subsequentes, mesmo quando nenhuma ferramenta foi chamada naquela rodada. Removê-lo pode gerar HTTP 400 e quebrar a continuidade do raciocínio.
Quantas rodadas devo permitir?
Defina um limite de acordo com o fluxo e o orçamento. O exemplo usa oito como política local, não como limite oficial. Também monitore número de chamadas, tempo e tokens.
Conclusão
Uma implementação confiável de DeepSeek Tool Calls é um loop de controle, não uma execução automática de qualquer JSON produzido pelo modelo. Declare ferramentas estreitas, valide nome e argumentos, autorize com contexto do servidor, associe cada resultado ao tool_call_id e encerre somente em finish_reason: "stop".
Em Chat Completions com Thinking, se a requisição incluir tools, omita tool_choice e preserve reasoning_content de toda mensagem assistant nas interações subsequentes, mesmo quando tool_calls estiver vazio. A Responses API possui matriz própria e documenta tool_choice separadamente. Em qualquer interface, mantenha validação local, limites de rodadas, confirmação para ações sensíveis e idempotência.
Fontes oficiais
- Guia oficial de Tool Calls e strict mode
- Referência oficial de Chat Completions
- Responses API e outputs de ferramentas
- Vision e imagens em tool outputs
- Models & Pricing
- Thinking Mode e preservação de reasoning_content
- Notas oficiais de compatibilidade V4 para ferramentas em Thinking
- Release V4 e retirada dos aliases antigos
- Termos da DeepSeek Open Platform
Este conteúdo é independente e educacional. A DeepSeek não revisou, autorizou ou endossou este código ou guia.
