TellWorldAI 世界档案
问档案English

首页›实践›实践

实践

大模型函数调用(Function Calling)入门

函数调用让大模型返回结构化的函数调用请求,由你的代码去执行真正的工作。看完能搭通一条完整链路:定义函数、解析调用、请求真实 API、再用自然语言回答用户。

聊天模型回的是自然语言,应用需要的却是结构化数据——还要用上模型训练时没见过的数据。函数调用(function calling)一次解决两件事:你向模型描述一组函数,模型用结构化的 JSON「点单」,真正的工作由你的代码执行。这篇从一个完整例子入手:把「帮我找一门初学者学 Azure 的课」变成一次真实的课程目录 API 请求,最后再用自然语言回答用户。整条链路只用三步,代码不超过几十行,却覆盖了函数调用在生产里最常见的全部环节。

函数调用解决什么问题

没有函数调用之前,大模型的回答无结构且不稳定:开发者要写复杂的校验代码去应付回答的每种变化——同样要一份 JSON,这次字段叫 grades,下次可能叫 GPA,程序没法稳定解析;而「斯德哥尔摩现在天气怎么样」这类问题根本答不了,因为模型受限于训练数据的截止时间 [1]。两类问题指向同一个缺口:模型需要一个「出口」,把话变成结构、把请求交给系统里真正有数据的部分。

函数调用是 Azure OpenAI 服务的一项功能,针对两个限制 [1]:一是响应格式一致——格式可控,下游系统好接;二是外部数据——应用其他来源的数据可以在对话场景中使用。

必须澄清的一点:函数调用过程中,大模型并不会真的执行任何函数。你为它的响应定义一个结构,模型按结构返回「建议调用什么函数」,你的应用据此去跑真实函数;函数的返回值再回传给模型,由它用自然语言回答用户 [1]。换句话说,模型扮演的角色是「调度员」:它决定调什么、参数是什么,执行和数据访问都留在你自己的代码里,权限和安全边界也在你手里。

典型使用场景

三类场景覆盖大多数用法 [1]:

  1. 调用外部工具:聊天机器人替用户办事,比如「给我的讲师发邮件说这门课我需要更多帮助」,映射成 send_email(to: string, body: string);
  2. 把自然语言变成 API 或数据库查询:老师问「谁完成了上次作业」,映射成 get_completed(student_name: string, assignment: int, current_status: string);
  3. 生成结构化数据:把一段文字或 CSV 里的关键信息抽出来,比如 get_important_facts(agreement_name: string, date_signed: string, parties_involved: list)。

三个场景的共同点是「自然语言进、结构化出」:用户不需要学查询语法,程序也不需要猜语义,中间的翻译工作全由模型完成。判断自己的应用要不要用函数调用,就看有没有这类「人话转操作」的环节。

第一次函数调用:三步走

完整流程分三步:带着函数列表和用户消息调用 Responses API;读模型响应、执行对应动作;把函数返回值再发给 API,生成给用户的最终回答 [1]。

第一步,用户消息。role 可以是 system(定规则)、assistant(模型)或 user(最终用户);角色分明,模型才能在对话历史上往下接 [1]:

messages= [ {"role": "user", "content": "Find me a good course for a beginner student to learn Azure."} ]

第二步,定义函数。每个工具带 type、name、description、parameters 四个属性,可选的 required 数组列出必须提供的参数 [1]:

functions = [
   {
      "type":"function",
      "name":"search_courses",
      "description":"Retrieves courses from the search index based on the parameters provided",
      "parameters":{
         "type":"object",
         "properties":{
            "role":{
               "type":"string",
               "description":"The role of the learner (i.e. developer, data scientist, student, etc.)"
            },
            "product":{
               "type":"string",
               "description":"The product that the lesson is covering (i.e. Azure, Power BI, etc.)"
            },
            "level":{
               "type":"string",
               "description":"The level of experience the learner has prior to taking the course (i.e. beginner, intermediate, advanced)"
            }
         },
         "required":[
            "role"
         ]
      }
   }
]

注意 token 预算:函数定义会放进发给模型的系统消息,占用可用 token [1]。函数一多这笔开销会迅速膨胀,只把当前对话真正用得上的函数放进 definitions,其余的按需注册。

函数定义好之后并非只能被动等待:tool_choice="auto" 让模型自己判断要不要调用、调哪个;如果你的业务只需要特定函数,也可以强制指定。第一步的用户消息通常来自应用的输入框,动态传入。

第三步,发起调用,用 tools=functions 把函数带上 [1]:

response = client.responses.create(model=deployment,
                                        input=messages,
                                        tools=functions,
                                        tool_choice="auto",
                                        store=False)

print(response.output)

响应里会出现一个 function_call 项 [1]:

{
  "type": "function_call",
  "name": "search_courses",
  "call_id": "call_abc123",
  "arguments": "{\n  \"role\": \"student\",\n  \"product\": \"Azure\",\n  \"level\": \"beginner\"\n}"
}

注意模型做了什么:它从用户消息里抽出了 student、Azure、beginner,自动填进了函数参数——函数调用因此也是抽取信息的手段,而且输出形状有保证 [1]。

接进应用里

拿到模型的 function_call 响应只是第一步,应用侧要接住它。先从响应里筛出 function_call 项 [1]:

response_items = response.output
tool_calls = [item for item in response_items if item.type == "function_call"]

再写真正干活的 Python 函数——请求 Microsoft Learn 目录 API,取前五个匹配模块 [1]:

import requests

def search_courses(role, product, level):
  url = "https://learn.microsoft.com/api/catalog/"
  params = {
     "role": role,
     "product": product,
     "level": level
  }
  response = requests.get(url, params=params)
  modules = response.json()["modules"]
  results = []
  for module in modules[:5]:
     title = module["title"]
     url = module["url"]
     results.append({"title": title, "url": url})
  return str(results)

然后是分发逻辑:模型点名的函数名映射到本地函数,参数从 JSON 解出来,直接调用 [1]:

# Check if the model wants to call a function
if tool_calls:
 for tool_call in tool_calls:
  print("Recommended Function call:")
  print(tool_call.name)
  print()

  # Call the function.
  function_name = tool_call.name

  available_functions = {
          "search_courses": search_courses,
  }
  function_to_call = available_functions[function_name]

  function_args = json.loads(tool_call.arguments)
  function_response = function_to_call(**function_args)

  print("Output of function call:")
  print(function_response)
  print(type(function_response))

核心就是这三行:查函数、解参数、调用 [1]:

function_to_call = available_functions[function_name]

function_args = json.loads(tool_call.arguments)
function_response = function_to_call(**function_args)

最后,把函数调用和返回值追加回对话——模型发出的 function_call 项必须排在它的输出前面——再请求第二次生成 [1]:

print("Messages in next request:")
print(messages)
print()

second_response = client.responses.create(
   input=messages,
   model=deployment,
   tool_choice="auto",
   tools=functions,
   temperature=0,
   store=False,
      )  # get a new response from the model where it can see the function response


print(second_response.output_text)

用户看到的最终回答,是一份带链接的自然语言课程列表,而不是一堆 JSON [1]。示例里第二次调用把 temperature 设成了 0——这一步是在复述检索结果,输出越稳定越好。真正接入生产时,还要补上示例里省略的健壮性处理:模型点了一个不存在的函数名怎么办、参数解析失败怎么办、API 没查到合适课程怎么兜底,每一处都应该有明确的降级路径。

设计函数定义的几个要点

description 字段承担主要工作:模型靠它决定何时调用、怎么填参数,所以要具体、清晰 [1]。写 description 时把自己当成第一次接手这段代码的人:这个函数干什么、每个参数取什么值、取值的粒度是什么,一条条写明。每个参数都要写明类型和含义,用 required 强制关键参数 [1]。每个参数都要写明类型和含义,用 required 强制关键参数 [1]。还要算好上下文账:函数定义随每次请求一起发送,token 消耗是持续发生的 [1]。

本文改编自 Microsoft 开源课程《Generative AI for Beginners》第 11 课[1],原文以 MIT 许可证发布。

来源

  1. 1
    Integrating with Function Calling | Generative AI for BeginnersMicrosoft generative-ai-for-beginners (MIT)

引用本页: 大模型函数调用(Function Calling)入门. TellWorld, https://www.tellworld.net/zh/practice/practice-function-calling. 更新于 2026-11-14

相关动态