0Pricing
AI Engineering Academy · 课时

并行调用函数

处理模型同时调用多个函数的响应,使用 asyncio 并行执行这些函数,并将结果批量合并到一次后续 API 调用中。

并行调用函数 是 CoddyKit 上的免费 AI Engineering Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 AI Engineering Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 AI Engineering Academy 课程共包含 4 节课。

什么是并行函数调用

当答案需要来自多个独立来源的信息时,OpenAI 的模型可以在一次响应中同时调用多个函数。工具调用不再按顺序链式执行、每次都等待上一次完成,而是由模型一次发出多个工具调用。您的应用程序可以并行执行它们,然后一并发送所有结果,从而显著降低延迟。

识别并行工具调用

模型发出并行工具调用时,响应消息会包含一个 tool_calls 列表,其中有多个条目。每个条目都有唯一的 id、函数名称和参数。在进行后续 API 调用前,您必须处理完所有调用——模型会等待它发出的每个工具调用都有对应的结果。

from openai import OpenAI
import json

client = OpenAI()

# A question that naturally requires two independent lookups
response = client.chat.completions.create(
    model='gpt-4o',
    messages=[{'role': 'user', 'content': 'Compare the weather in London and Tokyo right now.'}],
    tools=tools
)

message = response.choices[0].message
print('Number of tool calls:', len(message.tool_calls))
# Might print: Number of tool calls: 2

for tc in message.tool_calls:
    print(f'  {tc.function.name}({tc.function.arguments})')
# get_current_weather({"location": "London"})
# get_current_weather({"location": "Tokyo"})

使用 asyncio 执行工具调用

使用 asyncio.gather() 并发运行多个工具调用。每个工具函数都在独立的协程中运行,并在所有协程完成后收集全部结果。当每次工具调用都需要发起网络请求时,这种方式会比顺序执行快得多。

import asyncio
import json

async def execute_tool_call_async(tool_call) -> tuple:
    '''Execute a single tool call and return (tool_call_id, result).'''
    name = tool_call.function.name
    args = json.loads(tool_call.function.arguments)

    # Async version of your tool (uses httpx, aiohttp, etc.)
    if name == 'get_current_weather':
        result = await async_get_weather(**args)
    elif name == 'get_stock_price':
        result = await async_get_stock(**args)
    else:
        result = f'Unknown tool: {name}'

    return tool_call.id, str(result)

async def execute_all_parallel(tool_calls) -> list:
    '''Execute all tool calls concurrently.'''
    tasks = [execute_tool_call_async(tc) for tc in tool_calls]
    results = await asyncio.gather(*tasks, return_exceptions=True)
    return results

一并发送所有结果

执行完所有并行工具调用后,请将每个结果分别作为一条 role='tool' 消息添加到对话中。每条消息都必须包含相匹配的 tool_call_id。请在一次后续 API 调用中发送全部消息,以便模型将所有结果综合成一个连贯的答案。

async def run_parallel_tool_calls(user_message: str) -> str:
    messages = [{'role': 'user', 'content': user_message}]

    response = client.chat.completions.create(
        model='gpt-4o', messages=messages, tools=tools
    )
    assistant_message = response.choices[0].message
    messages.append(assistant_message)  # Add assistant's tool_calls

    if response.choices[0].finish_reason == 'tool_calls':
        # Execute all tool calls in parallel
        results = await execute_all_parallel(assistant_message.tool_calls)

        # Add all results to conversation
        for tool_call_id, result in results:
            messages.append({
                'role': 'tool',
                'tool_call_id': tool_call_id,
                'content': result
            })

        # One more API call to synthesize results
        final = client.chat.completions.create(model='gpt-4o', messages=messages)
        return final.choices[0].message.content

    return assistant_message.content

使用 ThreadPoolExecutor 处理同步工具

如果您的工具函数是同步的(使用 requests 而不是异步的 httpx),仍然可以使用 concurrent.futures.ThreadPoolExecutor 并行运行它们。这种方式更容易设置,但效率略低于纯异步代码。

from concurrent.futures import ThreadPoolExecutor, as_completed
import json

def execute_all_with_threads(tool_calls) -> list:
    results = []
    with ThreadPoolExecutor(max_workers=len(tool_calls)) as executor:
        future_to_id = {
            executor.submit(execute_tool_call, tc): tc.id
            for tc in tool_calls
        }
        for future in as_completed(future_to_id):
            tool_call_id = future_to_id[future]
            try:
                result = future.result(timeout=15)
            except Exception as e:
                result = f'Tool failed: {str(e)}'
            results.append((tool_call_id, str(result)))
    return results

处理单个工具失败

并行执行工具调用时,某个调用可能失败,而其他调用成功。绝不要让一个失败阻塞其他调用。请在 asyncio.gather() 中使用 return_exceptions=True,这样即使某些调用引发异常,也能收集所有结果。请将异常转换为错误字符串,以便模型收到全部结果并能够分析部分失败的情况。

async def safe_execute_all(tool_calls) -> list:
    tasks = [execute_tool_call_async(tc) for tc in tool_calls]
    raw_results = await asyncio.gather(*tasks, return_exceptions=True)

    results = []
    for tc, result in zip(tool_calls, raw_results):
        if isinstance(result, Exception):
            results.append((tc.id, f'Tool error: {str(result)}'))
        else:
            tool_call_id, output = result
            results.append((tool_call_id, output))
    return results

模型何时使用并行调用

当模型判断所需信息可以独立地收集时,就会发出并行工具调用——一个结果不依赖另一个结果。例如,获取多个股票代码的价格、查询多个城市的天气,或查询多个数据库表。结果相互依赖时则会顺序调用,例如先查找用户 ID,再获取该 ID 对应的订单。

限制并行调用的速率

同时运行大量工具调用可能会因并发请求过多而使外部 API 不堪重负。请使用信号量限制并发工具调用的最大数量。这样既能遵守 API 速率限制,又能比完全顺序的处理方式更高效地执行。

import asyncio

async def rate_limited_execute_all(tool_calls, max_concurrent: int = 5) -> list:
    semaphore = asyncio.Semaphore(max_concurrent)

    async def limited_call(tc):
        async with semaphore:
            return await execute_tool_call_async(tc)

    tasks = [limited_call(tc) for tc in tool_calls]
    results = await asyncio.gather(*tasks, return_exceptions=True)

    return [
        (tc.id, str(r) if not isinstance(r, Exception) else f'Error: {r}')
        for tc, r in zip(tool_calls, results)
    ]

串联顺序调用和并行调用

实际的智能体工作流通常会混合使用顺序调用和并行调用。模型可能先调用 lookup_user(email),然后使用返回的 user_id,并行调用 get_orders(user_id) 和 get_preferences(user_id)。请实现外层循环,判断哪些调用可以并行化(相互独立),哪些必须按顺序执行(存在依赖)。

async def multi_round_agent(user_message: str) -> str:
    messages = [{'role': 'user', 'content': user_message}]
    MAX_ROUNDS = 5

    for _ in range(MAX_ROUNDS):
        response = client.chat.completions.create(
            model='gpt-4o', messages=messages, tools=tools
        )
        choice = response.choices[0]
        messages.append(choice.message)

        if choice.finish_reason == 'stop':
            return choice.message.content  # Done

        if choice.finish_reason == 'tool_calls':
            # Execute all tool calls in parallel (may be 1 or many)
            results = await safe_execute_all(choice.message.tool_calls)
            for tc_id, result in results:
                messages.append({'role': 'tool', 'tool_call_id': tc_id, 'content': result})
            # Loop continues for potentially sequential next call

    return 'Max rounds reached.'

衡量延迟改善

并行工具调用可以显著降低延迟。如果三个工具调用按顺序执行且每个耗时 500 毫秒,总耗时就是 1500 毫秒。并行运行则可将耗时降至约 500 毫秒,速度提升 3 倍。请始终在具体场景中测量并比较顺序执行与并行执行的效果,同时考虑并发开销和速率限制。

import time
import asyncio

async def benchmark_parallel_vs_sequential():
    tool_calls = [...]  # 5 independent tool calls

    # Sequential
    start = time.time()
    for tc in tool_calls:
        await execute_tool_call_async(tc)
    sequential_time = time.time() - start

    # Parallel
    start = time.time()
    await asyncio.gather(*[execute_tool_call_async(tc) for tc in tool_calls])
    parallel_time = time.time() - start

    print(f'Sequential: {sequential_time:.2f}s')
    print(f'Parallel:   {parallel_time:.2f}s')
    print(f'Speedup:    {sequential_time/parallel_time:.1f}x')

调试并行工具调用

调试并行工具调用问题时,请记录完整的请求—响应流程:包含所有 tool_calls 的模型消息、添加到对话中的每个结果,以及模型的最终响应。如果缺少某个工具结果,或结果顺序错误,模型可能会生成不一致的答案。使用 tool_call_id 作为关联键进行结构化记录,可以更轻松地追踪问题。

快速检查

测试您对使用 OpenAI 进行并行函数调用的理解。

课程回顾

本课中您学到了:并行工具调用会在 tool_calls 列表中显示为多个条目、asyncio.gather 会并发执行这些调用,以获得最大速度,以及将每个结果发送回模型时,都需要附上与之匹配的 tool_call_id。接下来,我们将使用函数调用构建一个自然语言数据库接口,把普通英语查询转换为 SQL。

常见问题解答

「并行调用函数」课时是免费的吗?

是的 — 「并行调用函数」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 AI Engineering Academy 课程的其余内容,请升级到 CoddyKit PRO。 AI Engineering Academy 课程共包含 4 节课。

「并行调用函数」这节课中我会学到什么?

处理模型同时调用多个函数的响应,使用 asyncio 并行执行这些函数,并将结果批量合并到一次后续 API 调用中。 你通过在浏览器中直接运行的动手代码来练习 AI Engineering Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 AI Engineering Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 AI Engineering Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「并行调用函数」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 AI Engineering Academy 课中编写并运行代码吗?

能。每节 AI Engineering Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 为 API 定义函数模式
  2. 在应用中处理工具调用
  3. 并行调用函数
  4. 构建自然语言数据库接口
← 返回 AI Engineering Academy