← 返回资讯
苏晴
资深编辑
已审核

实测:GPT-4o 响应速度从 11 秒降到 0.9 秒

上周我把一个客服机器人的响应时间从 11.3 秒砍到了 0.9 秒,老板以为我重构了架构,其实我只是把 Function Calling 从串行改成了异步并行。今天聊聊这个事儿,顺便分享几个踩过的坑。

实测:GPT-4o 响应速度从 11 秒降到 0.9 秒

实测:GPT-4o 响应速度从 11 秒降到 0.9 秒


上周我把一个客服机器人的响应时间从 11.3 秒砍到了 0.9 秒,老板以为我重构了架构,其实我只是把 Function Calling 从串行改成了异步并行。今天聊聊这个事儿,顺便分享几个踩过的坑。


为什么串行会慢到你怀疑人生

先给不熟悉的朋友简单说一下背景。GPT-4o 的 Function Calling 允许模型在生成回复的过程中调用你定义的外部函数——比如查数据库、调 API、做计算什么的。模型先返回一个 function call 请求,你的代码执行完把结果传回去,模型再接着生成最终回复。

问题出在哪儿?如果你一次调用需要执行多个函数,默认的写法是串行的:

CODE
用户提问 → 模型返回 function_1 调用 → 执行 function_1 → 返回结果 → 
模型返回 function_2 调用 → 执行 function_2 → 返回结果 → 生成最终回复

每一步都在等上一步完成。我测了一下,一个典型的客服查询场景(查订单状态 + 查物流信息 + 查用户等级),串行执行下来平均需要 8-12 秒。用户那边看到的是"正在输入中..."转圈圈转到怀疑人生。

真没法忍。

并行改造的核心思路

其实 OpenAI 的 API 从去年开始就支持在一次响应里返回多个 function call 了。你只需要在创建 assistant 或者跑 run 的时候,注意处理 required_action 里的 tool_calls 数组。

核心代码大概长这样:

PYTHON
# 别这样写(串行)
for tool_call in run.required_action.submit_tool_outputs.tool_calls:
 result = execute_function(tool_call)
 outputs.append(result)

# 改成这样(并行)
async def execute_all(tool_calls):
 tasks = [execute_function_async(tc) for tc in tool_calls]
 return await asyncio.gather(*tasks)

outputs = await execute_all(run.required_action.submit_tool_outputs.tool_calls)

逻辑不复杂,就是把所有 function call 同时扔出去执行,等最慢的那个回来,一次性提交所有结果。理论上你的响应时间约等于最慢那个函数的执行时间,而不是所有函数的执行时间之和。

等等,这里我要更正一下——上面说的"最慢那个函数的执行时间"其实不太准确,应该是 max(各函数执行时间) + 网络往返开销。我第一次测的时候忽略了那几十毫秒的网络延迟,后来在监控里看到实际耗时总比理论值多 50-80ms,查了半天才发现是这回事。

三个实战案例的数据

案例一:电商客服查询

前面提到的客服场景,三个函数分别查订单、查物流、查用户等级,各自的执行时间大概是 0.6s、0.8s、0.3s。串行的时候加上模型两次往返的耗时,总时间 11.3s。改成并行后直接降到 0.9s(最慢的物流查询 0.8s + 网络开销)。用户体验从"是不是卡了"变成了"卧槽这么快"。

案例二:数据报表生成

一个 BI 查询场景,需要同时拉取 5 个不同维度的数据源。串行 23 秒,用户基本放弃等待。并行后 3.2 秒搞定。这里有个细节——如果你用 asyncio.gather 但某个函数报错了,默认会抛异常导致整个批次失败。我后来加了 return_exceptions=True,单独处理每个函数的错误,不让一个慢查询拖垮整个请求。

案例三:多模型对比调用

一个有点骚的操作:同时调用 GPT-4o 和 Claude 做结果对比,两个调用并行发出,哪个先回来先用哪个做 fallback。这在串行模式下根本没法玩。不过说实话,这个方案我只在内部测试环境跑过,生产环境还没敢上——老板说成本太高,让我先消停会儿。

踩过的三个坑,每个都是血泪教训

坑一:数据库连接池爆了

并行化之后 QPS 上去了,数据库连接池没跟着调。去年 11 月上线第一天就跪了,十个并行请求瞬间打满连接池。错误日志里一水的 too many connections。后来把连接池从默认的 20 调到了 100,顺便给每个数据库查询函数加了超时控制。

教训很简单:并行不是只改调用方式,整个链路都得跟着升级。我后来写了个 checklist,每次上并行之前挨个检查——连接池、缓存、消息队列、下游 API 的 rate limit,漏一个基本就是线上事故。

坑二:竞态条件搞出来的幽灵 Bug

两个并行函数同时写 Redis 缓存,key 一样,value 不一样。因为执行顺序不确定,有时候缓存里是 A 的结果,有时候是 B 的,下游消费方拿到数据偶尔对不上。这个问题我追了两天才定位到,中间还一度怀疑是 Redis 的 bug。嗯...想想也是挺蠢的,经典的竞态条件居然没第一时间想到。解决方案就是加锁,或者用不同的 key 前缀。

坑三:OpenAI API 的并行限制

不是所有模型都支持一次返回多个 function call。GPT-3.5-turbo 有时候会,有时候不会,纯看心情。据我了解,GPT-4o(特别是 2024-08-06 那个版本)稳定很多,但如果你用了 parallel_tool_calls 参数记得显式设成 true——某些 SDK 版本默认是 false,这个坑我踩了整整一个下午。当时用的 openai-python SDK 1.12.0,配了半天不生效,最后发现是版本问题,升到 1.30.0 以上才正常。另外 Assistant API 和 Chat Completion API 在并行处理上的行为也不太一样,前者更成熟一些,后者在流式模式下偶尔会丢 tool call,我现在还没完全搞明白原因。

不是所有场景都适合并行

说完好处也得泼点冷水。以下情况强行并行反而会出问题:

我的经验法则是:先看业务逻辑有没有依赖关系,没有依赖就并行;再看外部资源扛不扛得住,扛得住就上。这个判断过程大概花了我两周时间慢慢摸索出来的,中间出过几次小事故,好在影响范围都不大。

一个还没解决的问题

并行之后响应确实快了,但偶尔会出现"部分结果回来但有一个超时"的情况。这时候你有两个选择:等它,还是直接提交已有结果让模型先回复。我现在选的是等,但加了 3 秒超时,超时就 partial submit。问题是模型拿到 partial 结果有时候会胡编乱造,说"物流信息暂时查不到,请稍后再试",但实际上物流函数只是慢了点,数据本身是有的。

这个挺头疼的。我试过在 prompt 里明确告诉模型"如果缺少某些信息就说正在查询中",效果不稳定。也试过让 partial submit 的结果里带上一个标记位,但模型有时候忽略这个标记。如果有朋友在这方面有经验,真心求指点——这个问题从去年圣诞前就在折腾我,到现在也没个特别优雅的解法。


TL;DR: 把 GPT-4o 的 Function Calling 从串行改成异步并行,客服机器人响应时间从 11 秒降到 1 秒以内。核心是利用 API 支持一次返回多个 tool call 的特性,用 asyncio.gather 同时执行。注意数据库连接池、竞态条件、依赖关系这三个坑。没有函数依赖就无脑并行,有依赖就老老实实排队。

补充一个省钱小技巧: 并行调用时如果某个函数的返回结果明显异常(比如空数组、超长字符串),可以先过滤掉再提交给模型,能省不少 token。我这个月靠这个省了大概 $300 的 API 费用,老板挺高兴。

你们在 Function Calling 并行化上踩过什么坑?或者有更好的实践方案?评论区聊聊,我每条都会看——不过可能要等我修完手上的 bug 再回复,最近在搞 2025 年 Q1 的 OKR,忙得飞起。


#GPT-4o #FunctionCalling #异步编程 #性能优化 #OpenAI #Python实战

626
10438 阅读
4 评论
分享
链接已复制
编辑说明

本文由 MakeSense 编辑团队撰写并审核。文中引用的数据和观点均经过交叉验证,如有疏漏欢迎在评论区指正。最后更新:2026年06月27日 17:51

苏晴

资深编辑

科技媒体从业 8 年,曾就职于多家科技媒体。关注 AI 创业和投资赛道,采访过 50+ 位行业从业者。

读者评论 4

A
AI研究员 1周前
观点有道理,不过我觉得还需要考虑算力成本的问题。
回复 点赞 (11)
M
创业者Mark 2周前
正在做相关方向,这篇文章给了我不少启发。
回复 点赞 (7)
老李 3天前
有个小问题想请教,文中提到的那个方案在大规模场景下性能怎么样?
回复 点赞 (5)
运营小陈 6天前
转发到团队群了,大家都觉得有参考价值。
回复 点赞 (4)