Hvis du har fulgt med i vores serie om AI-udvikling i Python — fra AI-agenter med LangGraph til RAG-pipelines med ChromaDB — har du allerede set, at de mest kraftfulde AI-systemer ikke bare genererer tekst. De handler. De slår op i databaser, kalder API'er, udfører beregninger og orkestrerer komplekse arbejdsgange.
Men hvordan fungerer det egentlig under motorhjelmen?
Svaret er function calling (også kaldet tool use) — den mekanisme, der forvandler en sprogmodel fra en sofistikeret tekstgenerator til en agent, der kan interagere med den virkelige verden. Og ærligt talt, når man først forstår konceptet, ændrer det fundamentalt måden man tænker AI-arkitektur på.
I denne guide tager vi hele turen: fra de grundlæggende koncepter over arbejdende Python-kode med både OpenAI og Claude, til parallelle tool calls, fejlhåndtering i produktion og det nye Model Context Protocol (MCP), der er ved at standardisere hele feltet i 2026.
Hvad er function calling, og hvorfor er det vigtigt?
Lad os starte med det mest afgørende at forstå: LLM'en udfører aldrig selv funktionen. Det er en misforståelse, som selv erfarne udviklere har — jeg har selv set det i code reviews mere end én gang. Når en model som GPT-4o eller Claude "kalder en funktion", sker der i virkeligheden dette:
- Du beskriver dine tilgængelige funktioner (tools) i et JSON Schema-format
- Modellen analyserer brugerens besked og beslutter, om en funktion er relevant
- Modellen returnerer et struktureret JSON-objekt med funktionens navn og argumenter
- Din kode eksekverer funktionen med de angivne argumenter
- Resultatet sendes tilbage til modellen
- Modellen bruger resultatet til at formulere et endeligt svar
Tænk på det som en samtale mellem en rådgiver og en assistent. Rådgiveren (LLM'en) ved, hvilke opgaver der skal udføres, og kan specificere præcis, hvad der skal gøres — men det er assistenten (din kode), der rent faktisk udfører arbejdet.
Det er denne adskillelse, der gør arkitekturen sikker. Modellen har aldrig direkte adgang til dine systemer. Den kan kun anmode om, at du udfører noget på dens vegne. Det er et vigtigt designvalg.
Forudsætninger og opsætning
For at følge med i denne guide skal du bruge:
- Python 3.11 eller nyere
- En API-nøgle fra OpenAI og/eller Anthropic (Claude)
- Grundlæggende kendskab til Python og JSON
Installér de nødvendige pakker
python -m venv tool-use-env
source tool-use-env/bin/activate # macOS/Linux
pip install openai anthropic pydantic
Konfigurér miljøvariabler
export OPENAI_API_KEY="din-openai-noegle"
export ANTHROPIC_API_KEY="din-anthropic-noegle"
Function calling med OpenAI i Python
Lad os starte med OpenAI, da de var de første til at introducere function calling helt tilbage i juni 2023. Formatet har udviklet sig en del siden da, og i 2026 bruger vi tools-parameteren med strict mode.
Trin 1: Definér dine tools
Hver tool beskrives som et JSON Schema-objekt med et navn, en beskrivelse og parametre. Beskrivelsen er afgørende — det er den, modellen bruger til at beslutte, hvornår et tool er relevant. Så brug tid på at skrive gode beskrivelser, det betaler sig virkelig.
tools = [
{
"type": "function",
"function": {
"name": "hent_vejr",
"description": "Hent aktuelle vejrdata for en given by",
"strict": True,
"parameters": {
"type": "object",
"properties": {
"by": {
"type": "string",
"description": "Byens navn, f.eks. København"
},
"enhed": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperaturenhed"
}
},
"required": ["by", "enhed"],
"additionalProperties": False
}
}
}
]
Læg mærke til "strict": True — det anbefales altid i 2026. Strict mode sikrer, at modellens tool calls altid overholder dit schema præcist ved at bruge constrained decoding under motorhjelmen. Det eliminerer typefejl og manglende felter, og det er ærligt talt en game changer sammenlignet med de tidlige dage, hvor man konstant måtte håndtere malformede JSON-svar.
Trin 2: Send beskeden og håndtér tool calls
from openai import OpenAI
import json
client = OpenAI()
def hent_vejr(by: str, enhed: str) -> dict:
"""Simuleret vejr-API."""
data = {
"København": {"temperatur": 14, "tilstand": "Overskyet"},
"Aarhus": {"temperatur": 12, "tilstand": "Let regn"},
}
info = data.get(by, {"temperatur": 0, "tilstand": "Ukendt"})
return {"by": by, "temperatur": info["temperatur"], "enhed": enhed,
"tilstand": info["tilstand"]}
TILGAENGELIGE_FUNKTIONER = {
"hent_vejr": hent_vejr,
}
def kald_med_tools(bruger_besked: str) -> str:
messages = [{"role": "user", "content": bruger_besked}]
# Første kald: modellen beslutter om den vil bruge et tool
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
)
assistent_besked = response.choices[0].message
# Tjek om modellen vil kalde et tool
if assistent_besked.tool_calls:
messages.append(assistent_besked)
# Eksekvér hvert tool call
for tool_call in assistent_besked.tool_calls:
func_name = tool_call.function.name
func_args = json.loads(tool_call.function.arguments)
# Kald den rigtige funktion
resultat = TILGAENGELIGE_FUNKTIONER[func_name](**func_args)
# Send resultatet tilbage til modellen
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(resultat, ensure_ascii=False)
})
# Andet kald: modellen genererer endeligt svar
final_response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
)
return final_response.choices[0].message.content
return assistent_besked.content
# Test det
print(kald_med_tools("Hvordan er vejret i København?"))
Det her er kerneflowet for al function calling. Uanset om du bruger OpenAI, Claude eller et helt tredje framework, er mønsteret det samme: beskriv → kald → eksekvér → returner → svar. Når du har forstået dette loop, kan du implementere tool use med stort set enhver LLM-udbyder.
Tool use med Claude og Anthropic SDK
Claudes implementation af tool use følger det samme koncept, men med en lidt anderledes syntaks. Anthropics seneste Python SDK (2026) introducerer desuden en elegant beta_tool-dekorator og tool_runner, der automatiserer hele loopet — og det er faktisk ret elegant.
Metode 1: Manuel tool use
from anthropic import Anthropic
import json
client = Anthropic()
# Definér tools i Anthropic-format
tools = [
{
"name": "hent_aktiekurs",
"description": "Hent den aktuelle aktiekurs for et givet selskab",
"input_schema": {
"type": "object",
"properties": {
"symbol": {
"type": "string",
"description": "Aktiesymbolet, f.eks. NOVO-B for Novo Nordisk"
}
},
"required": ["symbol"]
}
}
]
def hent_aktiekurs(symbol: str) -> dict:
"""Simuleret aktie-API."""
kurser = {"NOVO-B": 892.50, "MAERSK-B": 10250.00, "DSV": 1340.75}
kurs = kurser.get(symbol, 0.0)
return {"symbol": symbol, "kurs": kurs, "valuta": "DKK"}
# Send besked med tools
response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=1024,
tools=tools,
messages=[{"role": "user",
"content": "Hvad er Novo Nordisks aktuelle aktiekurs?"}]
)
# Håndtér tool use
if response.stop_reason == "tool_use":
# Find tool use-blokken
tool_block = next(b for b in response.content if b.type == "tool_use")
# Eksekvér funktionen
resultat = hent_aktiekurs(**tool_block.input)
# Send resultatet tilbage
final_response = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=1024,
tools=tools,
messages=[
{"role": "user",
"content": "Hvad er Novo Nordisks aktuelle aktiekurs?"},
{"role": "assistant", "content": response.content},
{"role": "user", "content": [
{"type": "tool_result",
"tool_use_id": tool_block.id,
"content": json.dumps(resultat, ensure_ascii=False)}
]}
]
)
print(final_response.content[0].text)
Metode 2: Automatisk tool runner (anbefalet i 2026)
Anthropics nyeste SDK gør det hele væsentligt enklere med beta_tool-dekoratoren. I stedet for at håndtere hele loopet manuelt, lader du SDK'et klare det:
from anthropic import Anthropic, beta_tool
import json
client = Anthropic()
@beta_tool
def hent_valutakurs(fra_valuta: str, til_valuta: str) -> str:
"""Hent vekselkursen mellem to valutaer.
Args:
fra_valuta: Kildevalutaen, f.eks. DKK
til_valuta: Målvalutaen, f.eks. EUR
Returns:
En JSON-streng med vekselkursoplysninger.
"""
kurser = {("DKK", "EUR"): 0.134, ("DKK", "USD"): 0.146,
("EUR", "DKK"): 7.46, ("USD", "DKK"): 6.85}
kurs = kurser.get((fra_valuta, til_valuta), 0.0)
return json.dumps({"fra": fra_valuta, "til": til_valuta,
"kurs": kurs})
# tool_runner håndterer hele loopet automatisk
runner = client.beta.messages.tool_runner(
model="claude-sonnet-4-5-20250514",
max_tokens=1024,
tools=[hent_valutakurs],
messages=[{"role": "user",
"content": "Hvad er vekselkursen fra DKK til EUR?"}],
)
for message in runner:
if hasattr(message, "content"):
for block in message.content:
if hasattr(block, "text"):
print(block.text)
Med tool_runner behøver du ikke manuelt håndtere loopet. SDK'et opdager tool calls, kalder dine funktioner og sender resultaterne tilbage til Claude automatisk. Det reducerer markant mængden af boilerplate-kode — og gør koden meget mere læsbar i processen.
Parallelle tool calls
Okay, nu bliver det interessant. I mange realistiske scenarier har modellen brug for at kalde flere tools i samme tur. Forestil dig en bruger, der spørger: "Hvad er vejret i København og Aarhus, og hvad er Novo Nordisks aktiekurs?" — det er tre uafhængige opslag, der kan (og bør) udføres samtidigt.
OpenAI: parallelle tool calls er standard
OpenAI aktiverer parallelle tool calls som standard. Modellen kan returnere flere tool calls i et enkelt svar, og du kan eksekvere dem parallelt med Pythons ThreadPoolExecutor:
from concurrent.futures import ThreadPoolExecutor
def haandter_parallel_tool_calls(assistent_besked, messages):
"""Eksekvér flere tool calls parallelt."""
if not assistent_besked.tool_calls:
return assistent_besked.content
messages.append(assistent_besked)
def eksekver_tool(tool_call):
func_name = tool_call.function.name
func_args = json.loads(tool_call.function.arguments)
func = TILGAENGELIGE_FUNKTIONER.get(func_name)
if func:
return tool_call.id, json.dumps(func(**func_args),
ensure_ascii=False)
return tool_call.id, json.dumps({"fejl": "Ukendt funktion"})
# Eksekvér alle tool calls parallelt
with ThreadPoolExecutor() as executor:
futures = [executor.submit(eksekver_tool, tc)
for tc in assistent_besked.tool_calls]
resultater = [f.result() for f in futures]
# Tilføj alle resultater til messages
for tool_call_id, resultat in resultater:
messages.append({
"role": "tool",
"tool_call_id": tool_call_id,
"content": resultat
})
return messages
Det her er en af de ting, der virkelig gør en forskel i produktionsmiljøer. Hvis hvert API-kald tager 200ms, og du har fire parallelle tool calls, sparer du fra 800ms (sekventielt) ned til cirka 200ms (parallelt). I brugervendte applikationer er den forskel enorm — dine brugere vil kunne mærke det.
Claude: håndtering af flere tool calls
Claude kan også returnere flere tool_use-blokke i ét svar. Du håndterer dem på nøjagtig samme måde — saml alle resultater og send dem samlet tilbage. Bruger du tool_runner, klarer den også dette automatisk.
Strukturerede outputs med Pydantic
I produktionsapplikationer vil du typisk have typevaliderede, strukturerede svar — ikke bare rå JSON-strenge, du selv skal parse og validere. Her kommer Pydantic ind i billedet, og det er (efter min mening) en af de vigtigste brikker i et solidt tool-use setup.
Claude med Pydantic og output_format
Anthropics SDK understøtter nu Pydantic-modeller direkte via messages.parse():
from pydantic import BaseModel
from anthropic import Anthropic
class ProduktAnmeldelse(BaseModel):
produkt_navn: str
vurdering: int
fordele: list[str]
ulemper: list[str]
anbefaling: bool
client = Anthropic()
response = client.messages.parse(
model="claude-sonnet-4-5-20250514",
max_tokens=1024,
messages=[{
"role": "user",
"content": "Analysér denne anmeldelse: 'iPhone 16 Pro er fantastisk. "
"Kameraet er sindssygt godt, batteriet holder hele dagen, "
"og Face ID virker fejlfrit. Prisen er dog ret høj, "
"og den er tung.'"
}],
output_format=ProduktAnmeldelse,
)
anmeldelse = response.parsed_output
print(f"Produkt: {anmeldelse.produkt_navn}")
print(f"Vurdering: {anmeldelse.vurdering}/10")
print(f"Anbefalet: {'Ja' if anmeldelse.anbefaling else 'Nej'}")
Under motorhjelmen kompilerer Claude dit Pydantic-schema til en grammatik og begrænser token-generering, så outputtet altid matcher dit schema. Det er ikke bare "best effort" — det er garanteret skemaoverholdelse via constrained decoding. Ingen flere frustrerende parsingfejl kl. 2 om natten.
Byg et komplet tool-call loop til produktion
Så lad os samle alt det, vi har gennemgået, til en robust, produktionsklar implementation. Denne version håndterer flere tools, fejl og et fuldt agentic loop:
from openai import OpenAI
import json
from datetime import datetime
client = OpenAI()
# --- Definér dine tools ---
def soeg_database(foresporgsel: str, maks_resultater: int = 5) -> dict:
"""Simuleret databasesøgning."""
return {"resultater": [
{"id": 1, "titel": f"Resultat for: {foresporgsel}",
"relevans": 0.95}
], "total": 1}
def send_email(til: str, emne: str, tekst: str) -> dict:
"""Simuleret email-afsendelse."""
return {"status": "sendt", "til": til,
"tidspunkt": datetime.now().isoformat()}
def hent_bruger_info(bruger_id: int) -> dict:
"""Simuleret brugeropslag."""
brugere = {
1: {"navn": "Anders Hansen", "email": "[email protected]"},
2: {"navn": "Mette Nielsen", "email": "[email protected]"},
}
return brugere.get(bruger_id, {"fejl": "Bruger ikke fundet"})
FUNKTIONER = {
"soeg_database": soeg_database,
"send_email": send_email,
"hent_bruger_info": hent_bruger_info,
}
TOOL_DEFINITIONER = [
{
"type": "function",
"function": {
"name": "soeg_database",
"description": "Søg i virksomhedens interne database",
"strict": True,
"parameters": {
"type": "object",
"properties": {
"foresporgsel": {"type": "string",
"description": "Søgeteksten"},
"maks_resultater": {"type": "integer",
"description": "Max antal resultater"}
},
"required": ["foresporgsel", "maks_resultater"],
"additionalProperties": False
}
}
},
{
"type": "function",
"function": {
"name": "send_email",
"description": "Send en email til en modtager",
"strict": True,
"parameters": {
"type": "object",
"properties": {
"til": {"type": "string",
"description": "Modtagerens emailadresse"},
"emne": {"type": "string",
"description": "Emailens emne"},
"tekst": {"type": "string",
"description": "Emailens indhold"}
},
"required": ["til", "emne", "tekst"],
"additionalProperties": False
}
}
},
{
"type": "function",
"function": {
"name": "hent_bruger_info",
"description": "Hent information om en bruger fra databasen",
"strict": True,
"parameters": {
"type": "object",
"properties": {
"bruger_id": {"type": "integer",
"description": "Brugerens unikke ID"}
},
"required": ["bruger_id"],
"additionalProperties": False
}
}
}
]
MAKS_ITERATIONER = 10
def agent_loop(bruger_besked: str) -> str:
"""Kør et komplet agent-loop med tool calling."""
messages = [
{"role": "system",
"content": "Du er en hjælpsom assistent, der kan søge i "
"databaser, sende emails og slå brugerinfo op. "
"Brug dine tools når det er relevant."},
{"role": "user", "content": bruger_besked}
]
for iteration in range(MAKS_ITERATIONER):
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=TOOL_DEFINITIONER,
)
assistent_besked = response.choices[0].message
# Hvis ingen tool calls, er vi færdige
if not assistent_besked.tool_calls:
return assistent_besked.content
messages.append(assistent_besked)
# Eksekvér hvert tool call
for tool_call in assistent_besked.tool_calls:
func_name = tool_call.function.name
try:
func_args = json.loads(tool_call.function.arguments)
func = FUNKTIONER.get(func_name)
if func is None:
resultat = {"fejl": f"Ukendt funktion: {func_name}"}
else:
resultat = func(**func_args)
except json.JSONDecodeError:
resultat = {"fejl": "Ugyldige argumenter"}
except Exception as e:
resultat = {"fejl": str(e)}
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(resultat, ensure_ascii=False)
})
return "Agenten nåede det maksimale antal iterationer."
# Test med en multi-step opgave
svar = agent_loop(
"Find information om bruger 1, og send dem en email "
"med emnet 'Velkommen' og en venlig hilsen."
)
print(svar)
Bemærk den vigtige detalje med MAKS_ITERATIONER. Uden en øvre grænse kan et tool-call loop i teorien køre uendeligt — modellen kunne blive ved med at kalde tools frem og tilbage. I produktion er det kritisk at have det sikkerhedsnet. Ti iterationer er en fornuftig standard for de fleste use cases, men juster efter behov.
Model Context Protocol (MCP) — fremtiden for tool-integration
Indtil nu har vi defineret tools direkte i vores API-kald. Det fungerer fint med et par funktioner, men hvad hvis du har 50 tools? Eller 500? Og hvad hvis de skal fungere på tværs af flere LLM-udbydere?
Det er præcis det problem, Model Context Protocol (MCP) løser.
MCP er en åben standard, der blev lanceret af Anthropic i november 2024 og siden er blevet adopteret af OpenAI, Google DeepMind og hundredvis af andre aktører. I december 2025 blev MCP doneret til Agentic AI Foundation under Linux Foundation — et ret tydeligt signal om, at dette er ved at blive branchens fælles standard.
Tænk på MCP som USB-C for AI-applikationer. Ligesom USB-C giver én standardiseret port til at forbinde enheder, giver MCP én standardiseret protokol til at forbinde AI-modeller med eksterne tools og datakilder. Det er en analogi, der holder overraskende godt.
Hvorfor MCP er vigtigt
Før MCP var problemet fragmentering. Tools bygget til OpenAI's function calling virkede ikke med Claude's tool use eller Gemini's function declarations. Udviklere måtte vedligeholde separate integrationer for hver udbyder, og det var (mildt sagt) besværligt. MCP standardiserer dette med én fælles discovery- og eksekveringsprotokol.
I 2026 er der over 500 offentligt tilgængelige MCP-servere, der dækker databaser, fillagring, web scraping, dokumentbehandling og meget mere. Og nye servere tilføjes ugentligt af community'et.
Hvornår skal du bruge hvad?
En god tommelfingerregel:
- Native function calling — når du har 1-5 tools og arbejder med én LLM-udbyder. Simpelt og effektivt.
- MCP — når du har mange tools, har brug for portabilitet mellem modeller, eller bygger en platform. Mere opsætning, men det skalerer langt bedre.
Typiske faldgruber og best practices
Efter at have arbejdet med function calling i talrige projekter er her de fejl, jeg ser oftest — og hvordan du undgår dem.
1. Vage tool-beskrivelser
Kvaliteten af dine tool-beskrivelser er den enkeltstående vigtigste faktor for, om modellen vælger det rigtige tool. En beskrivelse som "hent data" er nærmest ubrugelig. Vær specifik:
# Dårligt:
"description": "Hent data"
# Godt:
"description": "Hent den aktuelle aktiekurs i DKK for et dansk børsnoteret selskab baseret på dets ticker-symbol"
2. Manglende input-validering
Selvom strict mode sikrer korrekt JSON-struktur, kan værdierne stadig være forkerte. En bruger-ID på -1 er teknisk set et gyldigt heltal, men det giver næppe mening. Validér altid med Pydantic eller lignende, før du eksekverer:
from pydantic import BaseModel, Field
class EmailInput(BaseModel):
til: str = Field(pattern=r"^[\w.+-]+@[\w-]+\.[\w.]+$")
emne: str = Field(min_length=1, max_length=200)
tekst: str = Field(min_length=1)
3. Ingen øvre grænse på iterationer
Et tool-call loop uden en maks-grænse kan blive en dyr fejl — bogstaveligt talt. Hver iteration koster API-kald og tokens. Sæt altid en fornuftig grænse.
4. Hallucinerede tool calls
Modeller kan nogle gange forsøge at kalde funktioner, der ikke eksisterer, eller sende argumenter, der ikke matcher skemaet. Det sker sjældnere med strict mode, men håndtér altid KeyError og valideringsfejl gracefully. Din kode skal kunne klare det.
5. For mange tools i kontekstvinduet
Tool-definitioner bruger tokens. Hvis du har 30+ tools, kan det æde en betydelig del af din kontekst. Overvej i stedet Anthropics Tool Search (tilgængelig som beta) eller OpenAI's tilsvarende tool_search (kræver GPT-5.4+). Disse lader modellen dynamisk finde relevante tools i stedet for at have dem alle i konteksten.
Sammenligning: OpenAI vs. Claude tool use
Her er et hurtigt overblik over de vigtigste forskelle mellem de to største udbydere i 2026:
| Feature | OpenAI (GPT-4o) | Claude (Sonnet/Opus) |
|---|---|---|
| Tool-format | tools med type: "function" | tools med input_schema |
| Strict mode | strict: true | strict: true (beta) |
| Parallelle calls | Standard (kan deaktiveres) | Understøttet |
| Auto tool runner | Nej (manuelt loop) | beta_tool + tool_runner |
| Programmatic calling | Nej | Ja (beta) — op til 85% tokenreduktion |
| Tool Search | GPT-5.4+ | Tilgængelig (beta) |
| MCP-support | Ja | Ja (oprinder) |
Begge platforme er stærke valg. OpenAI har et mere modent og veldokumenteret function calling-API, mens Claude tilbyder innovative features som programmatic tool calling (der kan reducere tokenforbruget med op til 85%) og den elegante tool_runner-abstraktion. Hvilket du vælger afhænger i høj grad af dit eksisterende setup og specifikke behov.
Ofte stillede spørgsmål
Hvad er forskellen på function calling og tool use?
"Function calling" refererer typisk til OpenAI's oprindelige implementation, hvor funktionsskemaer sendes direkte i API-kaldet. "Tool use" er det bredere begreb, der dækker funktioner, datahentning, kodefortolkere og mere. I praksis bruges termerne ofte synonymt i 2026, men "tool use" er det foretrukne begreb, da det bedre beskriver det fulde spektrum af muligheder.
Kan en LLM udføre farlige handlinger via function calling?
Nej — LLM'en kan aldrig selv udføre funktioner. Den returnerer kun strukturerede anmodninger, som din kode håndterer. Det er altid dit ansvar at validere inputs, implementere adgangskontrol og sikre, at brugeren godkender potentielt destruktive handlinger, før de eksekveres. MCP-specifikationen anbefaler altid et "human in the loop" for netop denne grund.
Hvor mange tools kan jeg definere i ét API-kald?
Teknisk set er der ingen fast grænse, men tool-definitioner bruger tokens fra kontekstvinduet. I praksis fungerer 5-15 tools fint. Over 30 tools bør du overveje Tool Search (Claude beta / GPT-5.4+) eller MCP, der lader modellen dynamisk opdage relevante tools uden at have dem alle i konteksten.
Hvad er Model Context Protocol (MCP), og skal jeg bruge det?
MCP er en åben standard for at forbinde AI-modeller med eksterne tools og datakilder via en ensartet protokol. Det er opfundet af Anthropic og nu adopteret af OpenAI, Google og andre. Brug MCP, når du har mange tools, har brug for at skifte mellem LLM-udbydere, eller bygger en platform. For simple projekter med 1-5 tools er native function calling stadig det nemmeste valg.
Hvordan håndterer jeg fejl i tool calls i produktion?
Implementér altid: (1) input-validering med Pydantic eller lignende, (2) try/except omkring funktionseksekvering med meningsfulde fejlbeskeder, (3) en maksimal iterationsgrænse for dit agent-loop, og (4) logging af alle tool calls og resultater til debugging. Returnér fejlbeskeder til modellen frem for at kaste exceptions — modellen kan ofte korrigere sig selv og prøve igen med andre argumenter.