AI Agents

DeepSeek Agent 函数调用实战指南

DeepSeek 函数调用(Function Calling)手把手实战:tool 模式定义、strict 严格模式、并行工具调用、思考模式与 MCP,以及生产环境的校验与修复模式——全部附可运行的 Python 示例。

Judy4 min read
DeepSeek Agent 函数调用实战指南

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-prodeepseek-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",外加一个包含 namedescriptionparameters(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_webfetch_urlquery_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 数组连同 idtypefunction.name / function.arguments 按原样带回来。省略或改动任何一个字段,都会破坏工具结果与产生它们的调用之间的关联,模型会搞不清哪个结果属于哪个动作。

如果你在 LangChain 或 LangGraph 之上开发,这些细节都被抽象掉了——直到某天抽象失效。到那时,理解原始消息形状,才是你调试卡死 Agent 循环的本钱。

4. 并行工具调用:什么时候用、怎么用

子任务相互独立时,并行工具调用最出彩。一个编码 Agent 要重构三个互不相干的模块,可以一轮读完三个文件。一个要从多个来源收集数据的研究 Agent,可以同时去查。省下的成本来自延迟——一次 API 往返,而不是三次串行——不是来自 token 单价,因为每个工具结果照样计入上下文。

当工具对共享状态有副作用时,这套模式就崩了。如果 write_fileread_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-proreasoning_effort: "high"(规划改哪些文件);中间的工具执行轮用关掉思考的 deepseek-v4-flash;最后一轮复核用 deepseek-v4-proreasoning_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 哪些模型支持函数调用?
V4 Pro 与 V4 Flash 都原生支持,请求里带上 tools 数组即可。API 兼容 OpenAI 格式,与 GPT-4o、Claude 的用法一致。两款模型单轮最多支持 128 个并行工具调用。想控制成本,工具执行轮多用 Flash,规划和综合轮交给 Pro。
工具调用循环到底怎么跑?
模型判断某工具有用时,不直接回答,而是返回结构化工具调用。你的代码执行后,把结果以 tool 消息配上对应调用 id 追加进历史,再发回 API 继续,直到模型给出最终答案。最容易踩坑:回传时原样保留 assistant 消息里的 tool_calls,否则结果与调用会失联。
思考模式和函数调用能一起用吗?
可以。V4 能把思考与工具调用组合,这是早几代模型做不到的。开启后模型会先内部推理再发工具调用;循环跑通并不依赖这段推理,但记日志有助调试。成本上,思考 token 按输出计费,高强度推理一次可能多花几百 token,建议只开在规划轮与综合轮。
DeepSeek V4 支持并行工具调用吗?什么时候该避免?
支持。V4 单轮最多返回 128 个工具调用;逐个独立执行,每个结果作为独立 tool 消息追加即可,id 对上、顺序无所谓。并行省延迟不省 token——结果都计入上下文。工具碰共享状态时别并行(如读写同一路径会产生竞态)。建议先串行跑稳,再给搜索这类只读工具开并行。
内联函数调用和 MCP 有什么区别?
内联函数调用把工具定义写进请求,由你的代码本地执行。MCP 把工具放到外部服务器,由 Agent 在运行时发现并调用,适合工具动态变化或由其他团队维护。两种方式在循环里消息格式相同,只是执行层不同。先用内联,等工具超过约 20 个、频繁变动或需要沙箱这类隔离环境时,再切到 MCP。
生产环境里 Agent 循环为什么坏?怎么修?
生产故障大多出在工具调用层而非模型:JSON 畸形、工具名写错、参数类型不对。先用清晰的 schema 预防,参数至关重要时开严格模式;再建修复层:先校验后执行,把结构化 JSON 错误返回给模型让它自我纠正。还要设轮次上限、连错即停、记录每次调用,并截断大段工具输出,防止上下文膨胀。

https://floatboat.ai/zh/blog/deepseek-agent-function-calling