TL;DR
-
DeepSeek 函数调用让 DeepSeek Agent 能请求结构化动作——查数据库、读文件、调 API——走的还是 GPT-4o 和 Claude 用的那套 OpenAI 兼容
tools数组。V4 Pro 与 V4 Flash 每轮最多支持 128 个并行工具调用。 -
线上格式是标准的:用 JSON Schema 定义工具、随消息一起发送、查看
message.tool_calls、在本地执行、把结果以role: "tool"消息追加回去,再调一次 API。参数可靠性至关重要时,严格模式(经/beta端点开启"strict": true)会强制输出贴合 schema。 -
在 V4 上,思考模式与工具调用可以协同工作——模型在发出结构化请求前,能先推理该调哪些工具。MCP(Model Context Protocol)把工具面从内联函数定义扩展到外部服务器。
-
生产级 Agent 需要一层修复机制:校验 JSON 参数、返回模型能自我纠正的结构化错误、限制并行执行。如果你还没搭过 Agent 循环,先读 如何构建 DeepSeek Agent;本文专门深入工具调用这一层。
1. 在 DeepSeek Agent 里,「函数调用」是什么
函数调用——也叫工具调用——是把 Agent 和聊天机器人区分开来的机制。聊天机器人收到提示词,返回文本。Agent 收到提示词外加一份可用工具清单,自己判断有没有工具能帮忙,返回一个结构化请求去调用其中一款或多款工具,等你的代码执行完这些调用,再带着结果继续推理。
在 DeepSeek V4 上,函数调用是 deepseek-v4-pro 与 deepseek-v4-flash 的原生能力。API 采用 OpenAI 兼容格式:在 chat completion 请求里放一个 tools 数组,工具调用经由 message.tool_calls 返回,结果以 role: "tool" 消息配合对应的 tool_call_id 回传,详见 DeepSeek 的工具调用文档。
本文假设你已理解基本 Agent 循环——发消息、查工具调用、执行、回传、重复。如果这套模式对你陌生,先读 如何构建 DeepSeek Agent。我们这里聚焦工具调用层内部发生的事:schema 设计、并行执行、严格模式、与思考模式的交互,以及那些防止 Agent 循环在畸形参数上崩溃的生产级模式。
「函数调用」和「Agent」的区别对搜索意图和架构都重要。函数调用是 API 的一个能力;Agent 是一个系统,把这一能力包进带错误处理、状态管理与工具权限的循环里。大多数 DeepSeek Agent 在生产中的故障都追溯到工具调用层——无效 JSON、参数类型错误、并行调用和共享状态竞态——而不是模型的推理质量。
2. 设计模型真正会遵循的工具 Schema
工具定义的质量,决定模型调用它的可靠性。V4 模型在结构化输出上比 V3 强一大截,但含糊的 schema 依然会产出含糊的工具调用。
每个工具都是一个 JSON 对象:type: "function",外加一个包含 name、description 与 parameters(JSON Schema)的 function 块。description 不是给人类看的文档——它是给模型的指令。写它时,想象你在告诉一个初级开发者:什么时候用、怎么用这个函数。
tools = [
{
"type": "function",
"function": {
"name": "search_codebase",
"description": (
"Search the project codebase for files matching a query. "
"Use this when the user asks about code location, function definitions, "
"or file structure. Do NOT use for running tests or modifying files."
),
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search term — function name, file path fragment, or keyword",
},
"file_type": {
"type": "string",
"enum": ["py", "js", "ts", "all"],
"description": "Limit search to specific file extensions",
},
},
"required": ["query"],
},
},
},
{
"type": "function",
"function": {
"name": "run_tests",
"description": (
"Execute the project's test suite or a specific test file. "
"Use after code changes to verify correctness. "
"Returns pass/fail counts and failure messages."
),
"parameters": {
"type": "object",
"properties": {
"test_path": {
"type": "string",
"description": "Optional path to a specific test file or directory",
},
},
"required": [],
},
},
},
]
在 V4 上提升调用可靠性的三条设计原则:
**把边界写清楚。**上面 search_codebase 的 description 明确告诉模型什么时候别用这个工具("Do NOT use for running tests")。否定式约束能减少多工具 Agent 里的选错工具问题。
**受限选择用枚举。**当一个参数只有一组固定合法值时,把它们声明成 "enum",而不是自由文本。模型会从列表里挑,而不是在你期望 "py" 时自己发明一个 "python"。
**required 字段保持最少。**只有当工具真的缺了某参数就跑不了时,才把它标成 "required"。过度约束必填字段,会在模型省略可选上下文时增加 JSON 解析失败的概率。
对参数正确性至关重要的 Agent——计费系统、数据库写入、部署触发——请在函数定义里设 "strict": true、并调用 https://api.deepseek.com/beta 这个 /beta 端点来开启严格模式,详见 DeepSeek 的函数调用文档。严格模式约束模型产出的参数必须完全符合你的 JSON Schema,减少下游校验的需要,代价是首次工具调用延迟略高。
3. 工具调用循环:超越基础
如何构建 DeepSeek Agent 里的最小循环,每轮只处理一个工具调用。生产级 Agent 还需要三个额外控制:tool_choice、并行调用处理、会话状态保全。
tool_choice **控制模型是否必须调用工具。**默认的 "auto" 让模型自己决定。当每一轮都必须产出工具调用时设 "required"(少见——通常用于被强制的管线步骤)。在所有工具执行完毕后的最终综合轮设 "none",这样你想要纯文本答案时,模型不会再去调更多工具。
# Force a final text answer after tools complete
final_response = client.chat.completions.create(
model="deepseek-v4-flash",
messages=messages,
tools=tools,
tool_choice="none",
)
并行工具调用发生在模型单轮在 message.tool_calls 里返回多个条目时。V4 支持最多 128 个并行调用。这不是理论极限——是架构特性。一个研究 Agent 可以同时调 search_web、fetch_url 和 query_database,下一轮再合并结果。
处理并行调用:遍历 tool_calls 里所有条目、各自独立执行、把每个结果作为一条独立的 role: "tool" 消息追加。只要每条结果带着正确的 tool_call_id,顺序对模型无关紧要。
for tc in assistant_msg.tool_calls:
args = json.loads(tc.function.arguments)
result = TOOL_REGISTRY[tc.function.name](**args)
messages.append({
"role": "tool",
"tool_call_id": tc.id,
"content": json.dumps(result),
})
状态保全是最常见的生产 bug。追加那条含工具调用的 assistant 消息时,必须把完整的 tool_calls 数组连同 id、type、function.name / function.arguments 按原样带回来。省略或改动任何一个字段,都会破坏工具结果与产生它们的调用之间的关联,模型会搞不清哪个结果属于哪个动作。
如果你在 LangChain 或 LangGraph 之上开发,这些细节都被抽象掉了——直到某天抽象失效。到那时,理解原始消息形状,才是你调试卡死 Agent 循环的本钱。
4. 并行工具调用:什么时候用、怎么用
子任务相互独立时,并行工具调用最出彩。一个编码 Agent 要重构三个互不相干的模块,可以一轮读完三个文件。一个要从多个来源收集数据的研究 Agent,可以同时去查。省下的成本来自延迟——一次 API 往返,而不是三次串行——不是来自 token 单价,因为每个工具结果照样计入上下文。
当工具对共享状态有副作用时,这套模式就崩了。如果 write_file 和 read_file 操作同一条路径,并行执行会制造一个你的 Agent 循环控制不了的竞态条件。对有状态工具,要么把执行串行化(一次处理一个工具调用),要么把工具设计成带显式锁语义。
DeepSeek-TUI 的 RLM fan-out 模式把并行执行推得更远:一个 V4 Pro 协调者最多孵化 16 个 V4 Flash 子 Agent,每个在子任务上跑自己的工具循环,详见官方 awesome-deepseek-agent 仓库。那套架构是原生 DeepSeek Agent 专属的,通用 harness 配置拿不到——但底层原理(便宜的并行工人 + 贵的协调者)适用于任何基于 V4 Flash 定价构建的自定义 Agent。
对多数自定义 Agent:先用串行执行把循环跑稳,再对独立性有保证的只读工具(search、fetch、query)开并行。只有在有了幂等性保证或显式冲突解决之后,才升级到并行写入。
5. 思考模式与工具调用一起用
V4 模型支持思考模式(思维链推理)与工具调用并存——这个组合在早几代模型上不可靠。两个一起开:
response = client.chat.completions.create(
model="deepseek-v4-flash",
messages=messages,
tools=tools,
tool_choice="auto",
extra_body={
"thinking": {"type": "enabled"},
"reasoning_effort": "high",
},
)
思考模式开启时,模型在发出工具调用或最终文本前,会先产出一个 reasoning_content 字段,装着它的内部推理。循环要跑起来,你不需要解析这个字段——它会自动包含在消息历史里。但开发时把它记进日志,能帮你理解模型为什么选了某个工具、或拒绝了某次调用。
成本含义:思考 token 按输出 token 计费。一次简单工具调用前的高强度推理,可能多花 300–800 token 的推理开销。对几十轮的 Agent 循环,思考模式要挑着用——用在任务开头的规划轮、以及模型整合多个工具结果的综合轮,而不是每个工具执行轮都开。
编码 Agent 里一套很管用的分工:第一轮用 deepseek-v4-pro 配 reasoning_effort: "high"(规划改哪些文件);中间的工具执行轮用关掉思考的 deepseek-v4-flash;最后一轮复核用 deepseek-v4-pro 配 reasoning_effort: "max"。一次 Agent 运行里三种模型配置,各按那一步的经济性来选。
6. MCP:把工具面扩到内联定义之外
Model Context Protocol(MCP)是把 Agent 接到外部工具服务器的标准——数据库、文件系统、浏览器自动化、专有 API——不必把每个工具定义都内联进请求。DeepSeek V4 原生支持 MCP,DeepSeek-TUI 这类工具同时自带 MCP 客户端与服务端能力,见 DeepSeek 的编码 Agent 集成指南。
内联函数定义(本文 tools 数组那套)适合工具集固定、已知的 Agent——代码库里定义五到十五个函数那种。当工具面是动态的(插件、用户配置的集成),或工具由不同团队各自维护时(数据库团队跑 MCP 服务端,Agent 团队消费它),MCP 就变得必要。
集成模式:你的 Agent 循环不变。模型返回工具调用后,不再调用本地 Python 函数,而是把调用转发给 MCP 服务器,由它执行动作、返回结果。消息历史的格式不变——变的只是 TOOL_REGISTRY 背后的执行层。
今天起步的 Agent,内联定义更简单、也够用。撞上下面某个阈值再上 MCP:超过 20 个工具(大 tools 数组带来的上下文开销)、工具频繁变动但不想跟着改 Agent 代码、或工具需要隔离执行环境(沙箱浏览器、独立数据库凭证)。在内联工具与 MCP 架构之间选择,是 DeepSeek Agent 品类总览 覆盖的设计决策之一——那篇文章把四种 Agent 原型各自适合哪种方案讲清楚了。如果你连这层接线都想省,一些桌面客户端如 Floatboat DeepSeek Agent 出厂就带好工具调用层——文件读取、浏览器、终端、日历工具已经通过 DeepSeek 原生函数调用接口接好,你只需定义 Agent 该做什么,而不是它怎么调每个工具。
7. 生产模式:校验、修复与失败形态
演示 Agent 与生产 Agent 的差别,几乎全在工具调用的错误处理上。V4 模型在拿到结构化反馈时自我纠正能力很好,但面对被静默吞掉的错误,它们无法恢复。
**先校验,再执行。**把 tool_call.function.arguments 解析成 JSON。检查函数名是否在你的注册表里。确认必填参数都在、类型都对。把错误以模型能读的 JSON 字符串返回:
def execute_with_repair(tool_call, registry):
try:
args = json.loads(tool_call.function.arguments)
except json.JSONDecodeError as e:
return json.dumps({"error": "invalid_json", "detail": str(e)})
fn = registry.get(tool_call.function.name)
if fn is None:
return json.dumps({
"error": "unknown_tool",
"requested": tool_call.function.name,
"available": list(registry.keys()),
})
try:
return json.dumps({"result": fn(**args)})
except TypeError as e:
return json.dumps({
"error": "invalid_arguments",
"detail": str(e),
"schema": get_schema(tool_call.function.name),
})
**给循环设界。**设 max_turns,并跟踪连续失败的工具调用次数。如果模型用相同参数连续三次调同一个工具,直接报错退出,别无限烧 token。
**记工具调用日志。**生产环境里,把每次工具调用的参数、结果和延迟都记下来。当 Agent 产出错误结果,日志轨迹——而不是最终答案——能告诉你:模型是选错了工具、传错了参数,还是你的执行层喂给了它误导性的数据。
**处理上下文膨胀。**每个工具结果都会加 token。对会读大文件、或返回分页查询结果的 Agent,在把工具输出追加进上下文前先截断或摘要。第 3 轮读一个 5 万 token 的文件,到第 10 轮就会霸占整个上下文预算。
结论
函数调用是每个 DeepSeek Agent 的连接组织。模型的推理质量重要,但决定这段推理能否变成正确动作的,是工具调用层。schema 设计、并行执行、思考模式取舍、以及本文讲的修复模式——这些才是区分「演示里能跑」与「生产流量下活得下来」的 Agent 的东西。
从两三个定义良好的工具加一个串行循环起步。当参数错误成为你最头的失败模式时,加严格模式。当瓶颈是延迟、而不是正确性时,加并行调用。当工具面撑破内联定义时,加 MCP。
完整的 Agent 架构——API 配置、模型选择、以及本文所扩展的那个循环骨架——如何构建 DeepSeek Agent 每一步都带可运行代码走了一遍。
常见问题
DeepSeek 哪些模型支持函数调用?
工具调用循环到底怎么跑?
思考模式和函数调用能一起用吗?
DeepSeek V4 支持并行工具调用吗?什么时候该避免?
内联函数调用和 MCP 有什么区别?
生产环境里 Agent 循环为什么坏?怎么修?
https://floatboat.ai/zh/blog/deepseek-agent-function-calling
