function-calling-best-practices
verified981ba549-55fb-4b23-b6f5-41c980fd1ae4
Design robust LLM function/tool calling — schemas, constrained outputs, retry, and validation so agents call tools correctly the first time.
Metadata
Skill file
# Robust LLM Function Calling
Use when you expose tools to an LLM and the model's calls drift from your
schema — wrong argument types, invented parameters, malformed JSON, or a
never-ending retry loop. This is the discipline that keeps agent tool use
reliable at scale.
## Think of tools as a typed API, not free-form
Every tool you expose is a contract. The model only "sees" what you give it —
descriptions, parameter names, and types. Put genuine effort into that surface:
- **Names** that read like verbs: `send_email`, `list_issues`, `get_balance`.
- **One-line descriptions** stating what it does *and* when to use it.
- **Parameters** as explicit JSON Schema: `type`, `required`, `description`,
`enum` where the value is closed, `minimum`/`maximum` for numbers.
## Enforce schema with the provider's structured mode
Most providers let you force tool calls / JSON against a schema
(`response_format`, `tools`, `tool_choice`). Prefer that over "please return
JSON" prompting — it constrains *decoding*, not just intent.
## Validate before you execute
Assume the model output is what a slightly careless developer would write:
1. Parse and `jsonschema`-validate every call against your tool schema.
2. Drop unknown parameters; reject out-of-range values instead of silently
coercing.
3. Log the raw call for debugging — but strip any secrets from the payload.
## Retry with repair, and cap it
```
llm -> raw tool call -> validate
-> fail: send the invalid call + error back to the model ("fix this") -> retry
-> success: execute, enforce a hard cap (e.g. 2-3 retries)
```
Never let a validation loop run unbounded — cap retries by count *and* time.
## Pitfalls
- **Empty descriptions** — an undocumented parameter invites hallucinated values.
- **Optional-but-crucial fields** — mark genuinely-required params `required`,
don't leave them optional "just in case".
- **Trusting the parsed JSON blindly** — validate the *values*, not just syntax.
- **Tools that can do anything** — a single oversized tool defeats the point;
split by verb and keep blast radius small.
- **No cap** — a model that keeps fixing the same call burns tokens and latency.
## Verify
- Replay N>50 real prompts; count first-call schema-conformance vs repair rate.
- Assert every executed call passed `jsonschema.validate` (writes a log row).
- Confirm a tool that strips secrets is used (no raw keys in captured payloads).