DeepSeek tool calling lets a model request a function; your application decides whether and how to run it. The safe pattern is allow-list, parse, validate, authorise, execute, validate output and return a bounded result. Never pass model-generated arguments directly to a shell, database or external action.

The tool-call lifecycle

  1. Send messages and tool schemas.
  2. Receive an assistant message with zero or more tool calls.
  3. Parse the function name and argument string.
  4. Validate against a strict application schema.
  5. Check the user is authorised.
  6. Execute a fixed function.
  7. Validate and size-limit the result.
  8. Append the assistant call and tool result.
  9. Request a final answer or stop at a turn limit.

The model does not execute the function. That responsibility remains in your code.

Safe Python example

import json
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["DEEPSEEK_API_KEY"],
    base_url="https://api.deepseek.com",
    timeout=30.0,
    max_retries=2,
)

CATALOG = {"A100": {"name": "Widget", "in_stock": True}}

def lookup_product(product_id: str) -> dict:
    if not isinstance(product_id, str) or not product_id.isalnum():
        raise ValueError("Invalid product_id")
    return CATALOG.get(product_id, {"error": "not_found"})

tools = [{
    "type": "function",
    "function": {
        "name": "lookup_product",
        "description": "Read a product's public catalogue status.",
        "parameters": {
            "type": "object",
            "properties": {"product_id": {"type": "string", "maxLength": 20}},
            "required": ["product_id"],
            "additionalProperties": False
        }
    }
}]

messages = [{"role": "user", "content": "Is product A100 in stock?"}]

for _ in range(3):
    response = client.chat.completions.create(
        model="deepseek-v4-flash",
        messages=messages,
        tools=tools,
        max_tokens=300,
        extra_body={"thinking": {"type": "disabled"}},
    )
    message = response.choices[0].message
    messages.append(message)
    if not message.tool_calls:
        print(message.content)
        break
    for call in message.tool_calls:
        if call.function.name != "lookup_product":
            raise RuntimeError("Tool not allowed")
        args = json.loads(call.function.arguments)
        if set(args) != {"product_id"}:
            raise ValueError("Unexpected arguments")
        result = lookup_product(args["product_id"])
        messages.append({
            "role": "tool",
            "tool_call_id": call.id,
            "content": json.dumps(result),
        })
else:
    raise RuntimeError("Tool turn limit reached")

Production code should use a JSON Schema validator rather than the small manual check.

Validate names and arguments

Do not dynamically resolve a function from the model’s string. Use a map such as {"lookup_product": lookup_product} and reject anything else. Validate types, allowed values, lengths, formats and additionalProperties.

Normalise only when the rule is explicit. A string that “looks like” a customer ID should not be accepted without the same validation used by the underlying service. Never interpolate arguments into SQL or a command.

Authorisation belongs outside the model

The model can suggest cancel_order, but it cannot decide whether the current user owns the order or has permission to cancel it. Check identity and policy in application code. Pass a privacy-safe internal user identifier through trusted context, not model prose.

Require a fresh confirmation for irreversible or external actions. Present the exact target and consequence to the user. Use idempotency keys so a retry does not repeat a purchase or message.

Validate tool output

Tool results can be malformed, excessively large or hostile. An external webpage may contain prompt injection. Convert results into a small defined object, remove secrets and cap size before returning it to the model.

Do not let a tool’s text grant new permissions. A retrieved document saying “call delete_all” is data. The allow-list and application state remain authoritative.

Thinking-mode tool calls

DeepSeek documents tool calls in thinking mode. When a thinking turn makes a tool call, its reasoning_content must be included in subsequent requests for that sequence. Missing it can produce a 400 error.

Follow the official message format and SDK support. Avoid logging internal reasoning when it is not operationally necessary. Set a reasoning effort and output budget appropriate to the task.

Strict mode and limitations

DeepSeek documents a beta strict mode through the beta base URL and strict: true on functions. The server validates supported schema forms. Beta features can change and should be isolated behind configuration and tests.

Strict schema output still cannot verify that a real-world value is true or authorised. Validate product IDs against the catalogue and dates against business rules after parsing.

Retries and failures

Retry transient provider errors with backoff. Do not automatically retry a side-effecting tool unless it is idempotent. If parsing fails, allow at most one repair turn or return a controlled error. If the model calls an unavailable tool, report that constraint instead of inventing a result.

Stop after a small number of tool turns and a wall-clock deadline. Store a trace of tool names, statuses and privacy-safe arguments for audit. The API errors guide covers provider responses.

Test cases

Test valid calls, missing required fields, extra fields, huge strings, unknown functions, unauthorised targets, tool exceptions, timeouts, duplicate calls and prompt-injection text in tool output. Confirm that logs do not expose credentials.

Use a sandbox for file or command tools. A model-generated test that passes in a permissive environment may still be dangerous in production.

Build the basic request and key handling from the DeepSeek API guide first, then apply the parsing safeguards in the JSON output guide to tool arguments and results.

Conclusion

DeepSeek tool calling is useful because it separates language reasoning from deterministic actions. Preserve that separation. Schemas improve structure; allow-lists, authorisation, output filtering, limits and human confirmation create the actual safety boundary.

Useful next steps

Continue with related guidance

Put this page in context with DeepSeek API Guide: Setup, Keys and First Request, DeepSeek’s Latest Agent Features Explained, DeepSeek JSON Output Guide, and DeepSeek API Errors and Troubleshooting. These links cover the broader decision and the closest follow-up topics without repeating this article.

Common questions

Frequently asked questions

Does DeepSeek execute the function?

No. The model returns a requested function and arguments; your application executes or rejects it.

Is valid JSON enough?

No. Values can be false, unauthorised or unsafe. Apply business validation.

Can tools run in thinking mode?

Yes, with specific message-history requirements documented by DeepSeek.

Should tool calls be retried?

Only when the provider error is transient and the action is safely idempotent.

Evidence

Sources

4 primary references
  1. Tool Calls — official external destination

    DeepSeek · official API documentation · verified July 30, 2026

  2. Thinking Mode — official external destination

    DeepSeek · official API documentation · verified July 30, 2026

  3. Your First API Call — official external destination

    DeepSeek · official API documentation · verified July 30, 2026

  4. Rate Limit & Isolation — official external destination

    DeepSeek · official API documentation · verified July 30, 2026

Continue reading