How to Build a DeepSeek Agent: From API Key to Your First Agent
Step-by-step guide to building a DeepSeek Agent with V4 Pro and V4 Flash. Covers API setup, model selection, tool calling, the agent loop, thinking mode, and production error handling — with Python and Node.js examples.

TL;DR
-
Building a DeepSeek Agent takes three practical steps: get an API key from platform.deepseek.com, pick
deepseek-v4-profor complex reasoning ordeepseek-v4-flashfor speed and cost, and implement the agent loop — the pattern that feeds tool call results back into context so the model can decide the next step. -
The agent loop is the part most tutorials skip. You define tools, the model returns a tool call, your code executes it, you append the result as a
toolrole message, and you send the updated history back. Repeat until the model produces a final answer. -
If you are building a coding agent, you probably do not need to write this loop from scratch — tools like DeepSeek-TUI and Reasonix already implement it. But understanding the loop is essential for debugging, customizing, and building agents for non-coding tasks. For an overview of what types of DeepSeek Agents exist, see What Is a DeepSeek Agent.
-
This tutorial uses the OpenAI Python SDK (
pip install openai) pointed athttps://api.deepseek.com. The Node.js examples use the same SDK. If your code already calls OpenAI's API, the migration is a one-line base URL change.
1. Before You Start: What You Need
You need three things and about fifteen minutes. Of course, there is also the option of not building at all — tools like Floatboat DeepSeek Agent ship the agent loop, tool wiring, and a desktop workspace preconfigured, so you skip straight to using the agent rather than engineering one. This tutorial covers the DIY path for those who want full control over the tool surface or are building agents for domain-specific tasks that off-the-shelf clients do not cover.
A Python environment — Python 3.10 or later with pip installed, or Node.js 18 or later. The OpenAI SDK (pip install openai or npm install openai) handles the API communication. DeepSeek's API is fully OpenAI-compatible at the wire level, so no specialized SDK is required.
A terminal and a text editor. The agent loop examples in this tutorial are under fifty lines each. You can type them into a single file and run it from the command line. If you want to see the full picture before building, What Is a DeepSeek Agent maps out the four archetypes and helps you decide whether you even need a custom agent or should use an existing tool.
A DeepSeek API key. The next section walks through getting one. If you already have a key, skip to Step 2.
2. Step 1: Get Your DeepSeek API Key
Go to platform.deepseek.com and sign up. After logging in, navigate to the API Keys section and create a new key. DeepSeek requires a minimum top-up before the key becomes active — typically $5 to $10, which is enough for tens of thousands of agent turns at V4 Flash pricing.
Store the key as an environment variable. Do not hard-code it in your source files.
export DEEPSEEK_API_KEY="sk-your-key-here"
On Windows PowerShell, use $env:DEEPSEEK_API_KEY="sk-your-key-here".
Once the key is set, verify it works with a minimal chat completion. This call confirms your key is active and the API is reachable — it also tells you which model names are current, which matters because the old aliases are gone.
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": "Hello. Confirm you are DeepSeek V4."}],
)
print(response.choices[0].message.content)
If you see a response identifying itself as DeepSeek V4, the key works. If you get an authentication error, double-check that your account has an active top-up — an empty balance returns a 401 even if the key is valid.
A warning about legacy model names: as of July 24, 2026, deepseek-chat and deepseek-reasoner are inaccessible. If your code references either alias, replace them with deepseek-v4-flash (with thinking mode explicitly enabled or disabled via the API parameter). Applications that still use the old names will receive errors, as documented in DeepSeek's API documentation.
3. Step 2: Choose Your Model — V4 Pro or V4 Flash
DeepSeek offers two models through the API, and the choice matters for agent performance and cost.
V4 Pro | V4 Flash | |
|---|---|---|
Architecture | 1.6T total / 49B active (MoE) | 284B total / 13B active (MoE) |
Context window | 1M tokens | 1M tokens |
Input price (cache miss) | $0.435 / 1M tokens | $0.14 / 1M tokens |
Output price | $0.87 / 1M tokens | $0.28 / 1M tokens |
Speed | ~45 tok/s | ~120 tok/s |
Best for | Complex multi-step planning, code refactoring, reasoning-heavy agent loops | High-volume tool calls, classification, routing, simple debugging |
For an agent that makes dozens or hundreds of API calls per task, the price gap compounds quickly. A single agent run that burns 80,000 input tokens and 20,000 output tokens costs roughly $0.052 on V4 Pro and $0.017 on V4 Flash. Five thousand such tasks per month: $260 versus $85, as priced on DeepSeek's pricing page.
The practical rule that has emerged from developer discussions in mid-2026: default to V4 Flash for the agent loop, and promote individual turns to V4 Pro when the model's response quality matters — typically the planning step at the start of a task and the synthesis step at the end. The two models share the same API surface, so the promotion is a single-line model name change.
The code examples in this tutorial use deepseek-v4-flash as the default and annotate where upgrading to Pro is recommended.
4. Step 3: Write Your First Agent Loop
This is the section most tutorials skip. They show you how to define a tool and get a tool call back — and then stop. But a single tool call is not an agent. An agent runs a loop: the model requests a tool, your code executes it, the result goes back into the conversation, and the model decides the next action. This section builds that loop from scratch.
Start with a simple weather agent. The model will decide when to call get_weather, your code will run it (or simulate running it), and the loop will continue until the model produces a final answer.
import os, json
from openai import OpenAI
client = OpenAI(
api_key=os.environ["DEEPSEEK_API_KEY"],
base_url="https://api.deepseek.com",
)
# Define tools the agent can call
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"}
},
"required": ["city"],
},
},
}]
# Simulated tool — in production, call a real weather API
def get_weather(city: str) -> str:
weather_data = {
"beijing": "Sunny, 28°C",
"london": "Cloudy, 15°C",
"tokyo": "Rainy, 22°C",
}
return weather_data.get(city.lower(), f"No data for {city}")
def run_agent(user_message: str, max_turns: int = 5) -> str:
messages = [{"role": "user", "content": user_message}]
for turn in range(max_turns):
response = client.chat.completions.create(
model="deepseek-v4-flash",
messages=messages,
tools=tools,
tool_choice="auto",
)
assistant_msg = response.choices[0].message
# No tool call → final answer
if not assistant_msg.tool_calls:
return assistant_msg.content
# Append assistant message (with tool_calls) to history
messages.append({
"role": "assistant",
"content": assistant_msg.content,
"tool_calls": [
{
"id": tc.id,
"type": tc.type,
"function": {
"name": tc.function.name,
"arguments": tc.function.arguments,
},
}
for tc in assistant_msg.tool_calls
],
})
# Execute each tool call and feed results back
for tc in assistant_msg.tool_calls:
args = json.loads(tc.function.arguments)
result = get_weather(**args)
messages.append({
"role": "tool",
"tool_call_id": tc.id,
"content": result,
})
return "Agent reached max turns without final answer."
# Run it
print(run_agent("What's the weather in Beijing and London?"))
The loop logic, line by line:
-
Send the user message with the tools definitions attached. The model sees the query and decides whether to answer directly or call a tool.
-
Check for tool calls. If
tool_callsis empty, the model produced a final answer — the loop exits. -
Append the assistant message to the conversation history. This is critical: you must preserve the
tool_callsarray exactly as the model returned it, including theidfields. Thetool_call_idin the next step must match. -
Execute the tool in your local code — in this case,
get_weatherwith the city argument the model chose. In production, this is where you call your database, file system, external API, or shell command. -
Append the tool result as a
role: "tool"message with the matchingtool_call_id. The model uses this ID to associate the result with the correct tool call. -
Loop back to step 1. The model now sees the full history — user question, its own tool call, and the tool's response — and decides the next action.
The max_turns parameter is a safety net for production. Agent loops can get stuck if the model repeatedly calls a tool that always returns the same answer, or if the task requires more turns than practical. Five turns is usually enough for simple tasks; coding agents frequently run 20+ turns. Always bound your loop.
For Node.js, the pattern is identical. Replace the Python client setup:
import OpenAI from "openai";
import process from "node:process";
const client = new OpenAI({
apiKey: process.env.DEEPSEEK_API_KEY,
baseURL: "https://api.deepseek.com",
});
The rest of the loop — tools definition, tool_calls inspection, tool execution, result feeding — follows the same shape. The OpenAI SDK handles the wire format identically across languages because DeepSeek's API is OpenAI-compatible at every level.
A note on model choice within the loop: if your task requires planning (the model needs to think about which tools to call in which order), swap deepseek-v4-flash for deepseek-v4-pro on the first call. Once the plan is established, the subsequent tool execution and synthesis turns can stay on Flash. The model name is a string — you can change it per turn.
Once your agent loop is solid, the next step is refining how it calls tools — DeepSeek Agent Function Calling covers strict mode, 128 parallel calls, and MCP integration for scaling beyond a single-tool agent.
5. Adding Thinking Mode: When Reasoning Matters
DeepSeek V4 supports a thinking mode that reveals the model's chain-of-thought reasoning before it produces a tool call or final answer. This is useful when the task requires multi-step planning or reasoning through contradictory constraints.
Enable thinking mode by passing extra_body with the thinking parameter:
response = client.chat.completions.create(
model="deepseek-v4-flash",
messages=messages,
tools=tools,
extra_body={
"thinking": {"type": "enabled"},
"reasoning_effort": "high",
},
)
Three reasoning_effort levels are available:
Level | Description | When to use |
|---|---|---|
low | Minimal chain-of-thought, fast | Simple classification, single-tool calls |
high | Full reasoning, balanced speed | Multi-step planning, code review |
max | Maximum reasoning depth | Complex refactors, debugging subtle logic errors |
When thinking mode is enabled, the model returns its reasoning in a reasoning_content field on the message object — separate from the final content that the user sees. You do not need to parse or act on this field; it is diagnostic, not functional. In an agent loop, the reasoning is automatically included in the message history and the model uses it to inform the next turn.
The cost trade-off: thinking mode consumes additional output tokens for the reasoning chain, and those tokens are billed at the same rate as regular output. A reasoning_effort: "high" call might produce 500 extra reasoning tokens before the final 100-token answer — that is an additional $0.00014 on V4 Flash or $0.00044 on V4 Pro. For agent loops that run hundreds of turns, enable thinking only on the planning turns where the reasoning adds measurable value. The rest of the loop — executing tools, processing results — does not benefit from chain-of-thought.
One common gotcha: if you are using strict mode ("strict": true on your function definitions via the /beta endpoint), thinking mode must use "type": "enabled" (not "type": "thinking"). The parameter changed between V3 and V4, and older tutorials may reference the deprecated format, as documented in DeepSeek's function calling guide.
6. From Demo to Production: Error Handling and Repair
The weather agent works because the model always returns valid JSON arguments for a function with a single string parameter. Production agents are messier. The model can hallucinate parameter names, pass the wrong type, or call tools that do not exist. A production-grade agent loop needs a repair layer.
Validate tool arguments before execution. Treat the model's output as untrusted user input — because in an agent architecture, that is exactly what it is.
def safe_execute_tool(tool_call):
function_name = tool_call.function.name
try:
args = json.loads(tool_call.function.arguments)
except json.JSONDecodeError:
return json.dumps({
"error": f"Invalid JSON arguments: {tool_call.function.arguments}"
})
if function_name not in TOOL_REGISTRY:
return json.dumps({
"error": f"Unknown tool: {function_name}. Available: {list(TOOL_REGISTRY.keys())}"
})
try:
result = TOOL_REGISTRY[function_name](**args)
return json.dumps({"result": result})
except TypeError as e:
return json.dumps({
"error": f"Invalid arguments for {function_name}: {str(e)}",
"expected": get_tool_signature(function_name),
})
The critical insight: when you return an error message formatted as JSON, the model reads it and often corrects itself on the next turn. This self-correction pattern works reliably on V4 models — far more reliably than on V3 — because the model can parse structured error feedback and adjust its tool call accordingly.
Watch your context budget. Every tool call result appends tokens to the message history. The 1M context window is generous, but a coding agent running 50+ turns with large file content in tool results can still approach the limit. If your agent needs to process large documents or entire repositories, implement a summarization step — periodically ask the model to compress the conversation history into a concise state summary, then continue the loop from that summary.
Add rate limiting. DeepSeek's API does not hard-throttle requests, but sending hundreds of tool_choice: "auto" calls per second can trigger transient errors. A simple exponential backoff wrapper around the API call covers most production scenarios:
import time
def call_with_backoff(messages, tools, max_retries=3):
for attempt in range(max_retries):
try:
return client.chat.completions.create(
model="deepseek-v4-flash",
messages=messages,
tools=tools,
tool_choice="auto",
)
except Exception as e:
if "rate" in str(e).lower() and attempt < max_retries - 1:
time.sleep(2 ** attempt)
else:
raise
Conclusion
You just built a DeepSeek Agent. Not a chatbot — an agent. The difference is the loop: the model does not just answer your question, it decides what information it needs, requests it through tool calls, and uses the results to determine the next step. The loop pattern you implemented here — define tools, inspect tool calls, execute, feed back, repeat — is the same architecture that powers DeepSeek-TUI's coding agent and Reasonix's cache-first assistant.
The weather agent is a toy. But the pattern scales. Swap get_weather for search_codebase, run_tests, query_database, read_file, write_file, or create_calendar_event — and the same fifty-line loop becomes a coding agent, a data analysis agent, or a scheduling agent. The tool definitions change; the loop architecture does not.
Frequently Asked Questions
What is the agent loop, and why isn't one tool call enough?
V4 Pro or V4 Flash — which model should I use?
Why build my own agent instead of using an existing tool?
Can I use DeepSeek with LangChain or LangGraph?
What if the model calls a tool that doesn't exist?
How much will this cost in production?
https://floatboat.ai/blog/how-to-build-deepseek-agent