前言

在上一篇文章里,我只是把 AI 大模型调通了
也正是在那一步,我开始真正意识到:
大模型不是“什么都知道的黑盒”,而是有明确能力边界的系统

比如很简单的问题:

现在几点?
现在天气怎么样?

直接问大模型,它是答不出来的。
不是它“傻”,而是它本身就不具备获取实时信息的能力

那怎么办?
答案就是:Function Calling(函数调用)

这篇文章不讲宏大概念,只记录一件事:
👉 我第一次把 Function Calling 跑通,并真正理解模型是“如何决定要不要调用函数”的。

一、什么是 Function Calling(先说人话)

Function Calling 本质只有一句话:

模型不会帮你执行函数,它只会“决定是否建议你调用函数”。

真正执行函数的,永远是你写的代码

所以 Function Calling 并不是魔法,而是一套清晰的协作流程:

用户提问

模型判断是否需要调用函数

如果需要,返回结构化的 tool_calls

本地代码执行函数

把结果再喂回模型

模型生成最终回答


理解这一点,是后面所有调试、测试、评估的前提。

二、最小 Demo:只做一件事——获取当前时间

为了避免干扰,我没有一上来做天气、接口请求这些复杂场景,
而是选择了一个最小、可验证、不会受外部影响的函数

获取当前时间

functions.py

import datetime

def get_time():
    now = datetime.datetime.now()
    return {"time": now.strftime("%Y-%m-%d %H:%M:%S")}

function_scheme = [
    {
        "type": "function",
        "function": {
            "name": "get_time",
            "description": "获取当前时间",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": ""
                    }
                },
                "required": []
            }
        }
    }
]

这里有一个点,当时我并没有意识到它的重要性

  • description

  • properties

  • required

后面我才发现,这三个字段,几乎决定了 Function Calling 的一切行为。

三、第一次调用模型:不要急着写逻辑,先看响应

main.py(第一次调用)

from zhipuai import ZhipuAI
from functions import function_scheme, get_time
import json

client = ZhipuAI(api_key="你的key")

messages = [
    {"role": "user", "content": "现在几点?"}
]

response = client.chat.completions.create(
    model="glm-4.5",
    messages=messages,
    tools=function_scheme,
    tool_choice="auto"
)

message = response.choices[0].message
print(message)

这一行非常重要:

print(message)

一开始我只关注两件事:

  • 有没有 tool_calls

  • 模型有没有返回结果

但后来我发现,真正该重点看的,是另一个字段

四、关键转折:看懂 reasoning_content

模型返回中,有一个字段:

reasoning_content

第一次我几乎忽略了它。

直到我看到类似这样的内容,

好好仔细看看,这就是模型思路的过程:

用户问现在几点,这是一个关于当前时间的问题。
我需要使用 get_time 函数来获取当前时间。
查看函数参数:
- city: 可选参数
由于 city 不是必填参数,我可以直接调用函数。

看到这里慢慢就会明白:

模型不是“碰巧调用了函数”,
而是在严格阅读我写的 function_schema,然后做判断。

五、模型到底是怎么“决定要不要调用函数”的?

在我反复修改、对比之后,结论逐渐清晰:

1️⃣ description 决定“模型能不能想起你这个函数”

如果 description 写得模糊,
模型很可能根本不会考虑这个函数。

2️⃣ required 决定“是否需要追问用户”

  • required 里有字段 → 缺参数 → 模型倾向于追问

  • required 为空 → 参数可缺 → 模型可以直接调用

这是非常明确的“契约判断”。

3️⃣ properties 决定“模型敢不敢构造参数”

properties 描述越清晰,
模型越容易正确构造 arguments。

六、完整跑通 Function Calling 的闭环

当模型返回了 tool_calls 之后,接下来就不是“AI 的事”了,而是工程逻辑

if message.tool_calls:
    tool_call = message.tool_calls[0]
    result = get_time()

    messages.append(message.model_dump())
    messages.append({
        "role": "tool",
        "tool_call_id": tool_call.id,
        "content": json.dumps(result)
    })

    response2 = client.chat.completions.create(
        model="glm-4.5",
        messages=messages
    )

    print(response2.choices[0].message.content)

最终效果是:

用户:现在几点?
模型:我需要调用 get_time
工具:2026-01-09 10:32:01
模型:现在是 10 点 32 分

到这里,Function Calling 的最小闭环算是跑通了

七、为什么这一步对“测试”和“评估”这么重要?

在真正理解这一套流程之前,我对 LLM 测试的理解是模糊的。

而现在,我突然明白了一件事:

Function Calling 的测试,本质不是测函数,
而是测模型的“决策行为”。

比如:

  • 什么情况下会调用?

  • 什么情况下不会调用?

  • 是 schema 的问题,还是 prompt 的问题?

  • 是 required 设计不合理,还是 description 描述有歧义?

这些问题,不看 reasoning_content,根本无从判断

八、从 Function Calling,自然走向“评估”

当你开始关心这些问题时,其实已经走到下一步了:

  • 行为是否稳定?

  • 决策是否符合预期?

  • schema 微调后,调用概率如何变化?

  • prompt 改动是否真的有效?

这已经不再是“用不用 AI”,
而是如何评估一个 LLM 系统是否“可控、可测、可维护”

后面的内容,我会继续围绕这个方向展开。

写在最后

这次 Function Calling 的实践,对我来说最大的收获不是代码本身,而是这一点:

只要 function_schema 没设计好,
怎么调 prompt,效果都很有限。

这是很多教程一笔带过,但在真实工程里极其关键的地方。

如果你和我一样,是从测试视角看 LLM,
那么 Function Calling,绝对值得你手敲一遍、反复改 schema、对照 reasoning 看行为变化

这一步,真的会“一通百通”。

下一篇继续是Function Calling,第一次系统性评估 LLM 决策行为

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐