my-add-function-calling
用一个最小的加法工具示例理解 Function Calling 的两轮调用流程与宿主执行职责。
[!info] related notes
- 前置笔记: Function Calling
- 相关 MOC: AI MOC
my-add-function-calling
目标
写一个自己的 add 函数, 让 codex 调用
步骤
实现 add
import json
from openai import OpenAI
client = OpenAI()
def add(a: float, b: float) -> float:
return a + b
tools = [
{
"type": "function",
"name": "add",
"description": "Add two numbers and return the sum.",
"strict": True,
"parameters": {
"type": "object",
"properties": {
"a": {
"type": "number",
"description": "The first number"
},
"b": {
"type": "number",
"description": "The second number"
}
},
"required": ["a", "b"],
"additionalProperties": False
}
}
]
# 第一次请求:让模型决定要不要调用 add
response = client.responses.create(
model="gpt-5",
input="请帮我计算 12.5 + 7.3",
tools=tools,
)
# 把第一轮输出先存起来
input_items = response.output
# 找到模型发出的 function_call
for item in response.output:
if item.type == "function_call" and item.name == "add":
args = json.loads(item.arguments)
result = add(
a=float(args["a"]),
b=float(args["b"])
)
# 把工具执行结果回传给模型
input_items.append({
"type": "function_call_output",
"call_id": item.call_id,
"output": json.dumps({"result": result})
})
# 第二次请求:让模型基于工具结果生成最终自然语言回复
final_response = client.responses.create(
model="gpt-5",
input=input_items,
tools=tools,
)
print(final_response.output_text)
ai 给了我如上示例,主要的工具是 add 函数,然后直接模拟了一次对话。
可以注意到是:
- 通过运行时(外壳)来发起请求(传入可用 function),
- 并根据 ai 响应来执行对应的 function,
- 把函数结果再一起发给 ai
- 得到最终结果
[!importance] 重点 要用 function calling,必须有一层你控制的运行时。它不一定是很重的“agent 框架”,也可以只是几十行 Python/Node 的小外壳。
想法
当前的网页聊天中应该就是一个 app 壳,设想一个查询天气的流程:
- 用户:xx地方明天的气温
- 聊天应用 携带问题和可用工具 把请求发送给 LLM
- LLM 判断需要使用 “天气查询工具”,让 应用 传入 xx 参数给 “天气查询工具” 来调用这个工具
- 应用 调用后,把 结果 发送给 LLM
- LLM 返回最终结果 xx地方气温是 yy, 应用看到是最终结果,返回给用户
function 管理问题
这个应用如果应用很广,比如聊天应用,会需要成千上万的 function 吧,查天气,查网页,读文件等等。现在是怎么处理的
回答:
通常不会把成千上万个 function 每轮都直接塞给模型。
原因有两个:
- token 会很贵
- 工具越多,模型越难选,误选率也会上升
OpenAI 现在给出的方向其实很明确:除了普通 function calling,还支持 built-in tools、remote MCP servers,以及 tool search。其中 tool search 的作用就是 按需延迟加载工具定义,官方原文是 “load deferred tool definitions at runtime”,并且说它可以 “dynamically load relevant tools into the model’s context to optimize token usage”。
所以现实里一般会这样管:
A. 分层,不是平铺
先做一层路由,只把当前任务相关的工具集交给模型。
比如:
- 浏览类任务:
web_search,fetch_url,summarize_page - 本地文件类任务:
read_file,write_file,list_dir - 运维类任务:
run_test,deploy_preview,check_logs
而不是把所有业务工具一次性全给。
B. 能用内建工具,就别自己重复造
OpenAI 的 Responses/Tools 体系本身就支持一批内建能力,例如 web search、file search、computer use 等;模型页也会标注某个模型支持哪些工具。比如 GPT-5.4/5.4 mini 支持 Functions、Web search、File search、Computer use,GPT-5.4 nano 还列出了 MCP。
C. 大规模工具场景,用 MCP 或 tool search
如果工具很多,更适合把它们组织成:
- 若干个 MCP server
- 若干个 domain registry
- 或者 tool search 的延迟加载体系
这样模型不是“面对 3000 个函数”,而是“面对几个能力入口,再逐步展开”。
D. 需要更强控制时,用 Agents SDK / 自己的 orchestration
官方 Agents SDK 文档明确说:当 你的服务器拥有 orchestration、tool execution、state、approvals 时,SDK 路线更合适;它也适合你需要直接控制 tools、MCP servers 和 runtime behavior 的场景。
一句话总结这题:
不是“一个大应用 = 一个巨型 functions 列表”,而是“一个运行时 + 分层工具注册 + 按需暴露”。