0Pricing
AI Engineering Academy · Lezione

Chiamate di funzioni in parallelo

Gestisca le risposte in cui il modello chiama più funzioni contemporaneamente, le esegua in parallelo con asyncio e raccolga i risultati in un'unica chiamata API successiva.

Chiamate di funzioni in parallelo è una lezione AI Engineering Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento AI Engineering Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso AI Engineering Academy include 4 lezioni in totale.

Che cos'è il function calling parallelo?

I modelli di OpenAI possono chiamare più funzioni contemporaneamente in un'unica risposta quando per rispondere servono informazioni provenienti da diverse fonti indipendenti. Invece di concatenare le chiamate agli strumenti in modo sequenziale, attendendo ogni volta quella precedente, il modello emette più chiamate agli strumenti contemporaneamente. La Sua applicazione le esegue in parallelo e invia tutti i risultati insieme, riducendo notevolmente la latenza.

Riconoscere le chiamate parallele agli strumenti

Quando il modello esegue chiamate parallele agli strumenti, il messaggio di risposta contiene un elenco tool_calls con più di una voce. Ogni voce ha un id univoco, il nome della funzione e gli argomenti. Deve elaborarle tutte prima di effettuare la chiamata API successiva: il modello si aspetta un risultato per ogni chiamata allo strumento che ha emesso.

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"})

Eseguire chiamate agli strumenti con asyncio

Esegua più chiamate agli strumenti contemporaneamente utilizzando asyncio.gather(). Ogni funzione dello strumento viene eseguita in una coroutine separata e tutti i risultati vengono raccolti al completamento di tutte le coroutine. È molto più veloce dell'esecuzione sequenziale quando ogni chiamata allo strumento effettua una richiesta di rete.

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

Inviare tutti i risultati insieme

Dopo aver eseguito tutte le chiamate parallele agli strumenti, aggiunga ogni risultato alla conversazione come messaggio role='tool' separato. Ogni messaggio deve includere il tool_call_id corrispondente. Li invii tutti in un'unica chiamata API successiva, così il modello potrà sintetizzare tutti i risultati in una risposta coerente.

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

Utilizzare ThreadPoolExecutor per gli strumenti sincroni

Se le funzioni degli strumenti sono sincrone e utilizzano requests anziché httpx asincrono, può comunque eseguirle in parallelo utilizzando concurrent.futures.ThreadPoolExecutor. È più semplice da configurare, ma leggermente meno efficiente del codice completamente asincrono.

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

Gestire i singoli errori degli strumenti

Quando esegue chiamate agli strumenti in parallelo, una può fallire mentre le altre hanno successo. Non permetta mai che un errore blocchi le altre. Utilizzi return_exceptions=True in asyncio.gather() per raccogliere tutti i risultati anche se alcune chiamate generano eccezioni. Converta le eccezioni in stringhe di errore, così il modello riceverà tutti i risultati e potrà ragionare sui fallimenti parziali.

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

Quando i modelli utilizzano chiamate parallele

Il modello esegue chiamate parallele agli strumenti quando determina che le informazioni necessarie possono essere raccolte in modo indipendente, cioè quando un risultato non dipende da un altro. Esempi: recuperare i prezzi delle azioni per più ticker, ottenere il meteo in più città o interrogare più tabelle di un database. Le chiamate sequenziali vengono utilizzate quando i risultati dipendono l'uno dall'altro: prima si cerca l'ID di un utente, poi si recuperano gli ordini per quell'ID.

Limitare la frequenza delle chiamate parallele

Eseguire molte chiamate agli strumenti in parallelo può sovraccaricare le API esterne con richieste simultanee. Utilizzi un semaforo per limitare il numero massimo di chiamate agli strumenti simultanee. In questo modo rispetta i limiti di frequenza dell'API, mantenendo comunque un'esecuzione più efficiente rispetto all'elaborazione completamente sequenziale.

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)
    ]

Concatenare chiamate sequenziali e parallele

I workflow agentici reali spesso combinano chiamate sequenziali e parallele. Il modello potrebbe prima chiamare lookup_user(email) e poi, utilizzando lo user_id restituito, chiamare get_orders(user_id) e get_preferences(user_id) in parallelo. Implementi il ciclo esterno in modo che rilevi quali chiamate possono essere eseguite in parallelo, perché indipendenti, e quali devono essere sequenziali, perché dipendenti.

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.'

Misurare il miglioramento della latenza

Le chiamate parallele agli strumenti possono ridurre notevolmente la latenza. Se ognuna di tre chiamate richiede 500 ms in sequenza, il totale è 1500 ms. Eseguendole in parallelo, il tempo si riduce a circa 500 ms, con un'accelerazione di 3 volte. Misuri e confronti sempre l'esecuzione sequenziale e parallela nello scenario specifico, tenendo conto dell'overhead della concorrenza e dei limiti di frequenza.

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')

Debugging delle chiamate parallele agli strumenti

Quando esegue il debugging dei problemi relativi alle chiamate parallele agli strumenti, registri l'intero ciclo richiesta-risposta: il messaggio del modello con tutte le tool_calls, ogni risultato aggiunto alla conversazione e la risposta finale del modello. Se manca un risultato o l'ordine non è corretto, il modello potrebbe produrre risposte incoerenti. La registrazione strutturata con tool_call_id come chiave di correlazione semplifica notevolmente il tracciamento dei problemi.

Verifica rapida

Verifichi la Sua comprensione del function calling parallelo con OpenAI.

Riepilogo della lezione

In questa lezione ha imparato che: le chiamate parallele agli strumenti compaiono come più elementi nell'elenco tool_calls, asyncio.gather le esegue in modo concorrente per ottenere la massima velocità e ogni risultato deve includere il tool_call_id corrispondente quando viene inviato nuovamente al modello. Ora costruiremo un'interfaccia per database in linguaggio naturale, usando il function calling per convertire query in inglese corrente in SQL.

Domande Frequenti

La lezione «Chiamate di funzioni in parallelo» è gratuita?

Sì — il testo completo di «Chiamate di funzioni in parallelo» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso AI Engineering Academy, passa a CoddyKit PRO. Il corso AI Engineering Academy include 4 lezioni in totale.

Cosa imparerò in «Chiamate di funzioni in parallelo»?

Gestisca le risposte in cui il modello chiama più funzioni contemporaneamente, le esegua in parallelo con asyncio e raccolga i risultati in un'unica chiamata API successiva. Eserciti AI Engineering Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare AI Engineering Academy?

Non è richiesta alcuna esperienza precedente. AI Engineering Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.

Quanto tempo richiede la lezione «Chiamate di funzioni in parallelo»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione AI Engineering Academy?

Sì. Ogni lezione AI Engineering Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Definire gli schemi delle funzioni per l'API
  2. Elaborare le chiamate agli strumenti nell'applicazione
  3. Chiamate di funzioni in parallelo
  4. Creare un'interfaccia al database in linguaggio naturale
← Torna a AI Engineering Academy