Parallelle funktionskald
Håndtér svar, hvor modellen kalder flere funktioner samtidigt, kør dem parallelt med asyncio, og saml resultaterne i ét enkelt opfølgende API-kald.
Parallelle funktionskald er en gratis AI Engineering Academy-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i AI Engineering Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. AI Engineering Academy-kurset indeholder 4 lektioner i alt.
Hvad er parallelle funktionskald?
OpenAI's modeller kan kalde flere funktioner samtidig i ét svar, når svaret kræver oplysninger fra flere uafhængige kilder. I stedet for at kæde værktøjskald sammen sekventielt, hvor hvert kald venter på det forrige, udsender modellen flere værktøjskald på én gang. Din applikation udfører dem parallelt og sender alle resultater tilbage samlet, hvilket reducerer forsinkelsen markant.
Genkendelse af parallelle værktøjskald
Når modellen udsender parallelle værktøjskald, indeholder svarbeskeden en liste med tool_calls, der har mere end ét element. Hvert element har et unikt id, et funktionsnavn og argumenter. Du skal behandle dem alle, før du foretager det opfølgende API-kald — modellen forventer resultater for hvert værktøjskald, den har udsendt.
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"})Udførelse af værktøjskald med asyncio
Kør flere værktøjskald samtidigt med asyncio.gather(). Hver værktøjsfunktion kører i en separat coroutine, og alle resultater indsamles, når alle coroutiner er færdige. Det er langt hurtigere end sekventiel udførelse, når hvert værktøjskald foretager en netværksanmodning.
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 resultsSend alle resultater tilbage samlet
Efter at have udført alle parallelle værktøjskald skal du tilføje hvert resultat som en separat besked med role='tool' til samtalen. Hver besked skal indeholde det matchende tool_call_id. Send dem alle i ét opfølgende API-kald, så modellen kan sammenfatte alle resultaterne til ét sammenhængende svar.
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.contentBrug af ThreadPoolExecutor til synkrone værktøjer
Hvis dine værktøjsfunktioner er synkrone og bruger requests i stedet for asynkrone httpx, kan du stadig køre dem parallelt med concurrent.futures.ThreadPoolExecutor. Det er enklere at sætte op, men lidt mindre effektivt end ren asynkron kode.
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 resultsHåndtering af individuelle værktøjsfejl
Når du udfører værktøjskald parallelt, kan ét kald fejle, mens andre lykkes. Lad aldrig én fejl blokere de andre. Brug return_exceptions=True i asyncio.gather() for at indsamle alle resultater, selv hvis nogle kald udløser undtagelser. Konvertér undtagelser til fejlstrenge, så modellen modtager alle resultater og kan ræsonnere over delvise fejl.
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 resultsNår modeller bruger parallelle kald
Modellen udsender parallelle værktøjskald, når den vurderer, at de nødvendige oplysninger kan indsamles uafhængigt — det ene resultat afhænger ikke af det andet. Eksempler er at hente aktiekurser for flere tickere, hente vejret i flere byer eller forespørge i flere databasetabeller. Sekventielle kald bruges, når resultaterne afhænger af hinanden — først slås et bruger-id op, og derefter hentes ordrerne for dette id.
Begrænsning af parallelle kald
Hvis du kører mange værktøjskald parallelt, kan samtidige anmodninger overbelaste eksterne API'er. Brug en semaphore til at begrænse det maksimale antal samtidige værktøjskald. Det overholder API'ernes hastighedsbegrænsninger, samtidig med at udførelsen stadig er mere effektiv end ren sekventiel behandling.
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)
]Kædning af sekventielle og parallelle kald
Virkelige agentiske arbejdsgange blander ofte sekventielle og parallelle kald. Modellen kan først kalde lookup_user(email) og derefter — ved hjælp af det returnerede user_id — kalde get_orders(user_id) og get_preferences(user_id) parallelt. Implementér den ydre løkke, så den registrerer, hvilke kald der kan paralleliseres (uafhængige), og hvilke der skal være sekventielle (afhængige).
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.'Måling af forbedret latenstid
Parallelle værktøjskald kan reducere latenstiden markant. Hvis hvert af tre værktøjskald tager 500 ms sekventielt, er den samlede tid 1500 ms. Hvis de køres parallelt, reduceres den til cirka 500 ms — en hastighedsforøgelse på 3 gange. Mål og sammenlign altid sekventiel og parallel udførelse i netop dit scenarie, og tag højde for omkostninger ved samtidighed og begrænsninger i hastigheden.
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')Fejlfinding af parallelle værktøjskald
Ved fejlfinding af problemer med parallelle værktøjskald skal du logge hele anmodnings-svar-forløbet: modellens besked med alle tool_calls, hvert resultat, der føjes til samtalen, og modellens endelige svar. Hvis et værktøjsresultat mangler eller kommer i forkert rækkefølge, kan modellen producere inkonsistente svar. Struktureret logning med tool_call_id som korrelationsnøgle gør det langt nemmere at spore problemer.
Hurtigt tjek
Test din forståelse af parallelle funktionskald med OpenAI.
Opsummering af lektionen
I denne lektion lærte du: parallelle værktøjskald vises som flere poster i listen tool_calls, asyncio.gather udfører dem samtidigt for maksimal hastighed, og hvert resultat skal have sit matchende tool_call_id, når det sendes tilbage til modellen. Nu bygger vi en databasegrænseflade på naturligt sprog, der bruger funktionskald til at oversætte forespørgsler i almindeligt dansk til SQL.
Lær Python med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 30
- Lektioner
- 120
Ofte stillede spørgsmål
Er lektionen “Parallelle funktionskald” gratis?
Ja — hele teksten til “Parallelle funktionskald” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af AI Engineering Academy-kurset, skal du opgradere til CoddyKit PRO. AI Engineering Academy-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Parallelle funktionskald”?
Håndtér svar, hvor modellen kalder flere funktioner samtidigt, kør dem parallelt med asyncio, og saml resultaterne i ét enkelt opfølgende API-kald. Du øver dig i AI Engineering Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på AI Engineering Academy?
Der kræves ingen tidligere erfaring. AI Engineering Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.
Hvor lang tid tager lektionen “Parallelle funktionskald”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne AI Engineering Academy-lektion?
Ja. Alle AI Engineering Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Definition af funktionsskemaer til API'et
- Behandling af værktøjskald i Deres applikation
- Parallelle funktionskald
- Opbygning af en databasegrænseflade på naturligt sprog