Files
2026-07-13 12:09:03 +08:00

146 lines
7.0 KiB
Markdown

---
name: skill-function-calling-patterns
description: Decision framework for implementing function calling in production -- tool design, error handling, security, and provider patterns
version: 1.0.0
phase: 11
lesson: 09
tags: [function-calling, tool-use, agents, mcp, security, openai, anthropic]
---
# Function Calling Patterns
When building an LLM application that uses tools, apply this decision framework.
## When to use function calling
**Use function calling when:**
- The model needs real-time data (weather, stock prices, database queries)
- The task requires side effects (sending emails, creating records, deploying code)
- The model must choose between multiple actions based on user intent
- You are building an agent that interacts with external systems
**Use structured outputs instead when:**
- You need data extraction from text (no external calls needed)
- The output is the final product, not an intermediate step
- You have a single schema, not multiple tools to choose from
**Use both when:**
- The model calls a tool, then structures the tool result into a specific output format
## Tool design guidelines
1. **One tool, one action.** A tool named `manage_database` that handles queries, inserts, updates, and deletes is too broad. Split into `query_records`, `insert_record`, `update_record`. The model selects better with specific tools.
2. **Descriptions are prompts.** The model reads tool descriptions to decide selection. Write them like you would write instructions for a junior developer. Include what the tool returns, not just what it does.
3. **Constrain with enums.** If a parameter has 3-10 valid values, use an enum. The model will invent strings -- "celsius", "Celsius", "C", "metric" -- unless you constrain it.
4. **Fewer tools is better.** GPT-4o handles 5-10 tools well. At 20+ tools, selection accuracy drops. At 50+ tools, expect 10-15% wrong tool selection. Group related functionality or use a routing layer.
5. **Required means required.** Only mark a parameter as required if the tool literally cannot function without it. Optional parameters with good defaults reduce tool call failures.
## Provider-specific patterns
### OpenAI (GPT-4o, o3, GPT-4o-mini)
```python
tools=[{"type": "function", "function": {"name": ..., "parameters": ...}}]
tool_choice="auto" # model decides
tool_choice="required" # must call at least one tool
tool_choice={"type": "function", "function": {"name": "specific_tool"}}
```
- Supports parallel tool calls (multiple `tool_calls` in one response)
- Tool call IDs must be passed back with results
- `gpt-4o-mini` is 10x cheaper and handles simple tool routing well
- Structured outputs mode works with tool parameters for guaranteed schema compliance
### Anthropic (Claude 3.5 Sonnet, Claude 4 Opus)
```python
tools=[{"name": ..., "description": ..., "input_schema": ...}]
tool_choice={"type": "auto"} # model decides
tool_choice={"type": "any"} # must call at least one tool
tool_choice={"type": "tool", "name": "specific_tool"}
```
- Tool calls appear as content blocks with `type: "tool_use"`
- Results go in user messages with `type: "tool_result"`
- Field name is `input_schema`, not `parameters` (common migration bug)
- Supports multiple tool calls per response
### Google (Gemini 2.0 Flash, Gemini 2.0 Pro)
```python
function_declarations=[{"name": ..., "description": ..., "parameters": ...}]
function_calling_config={"mode": "AUTO"} # or "ANY" or "NONE"
```
- Uses `function_declarations` at the top level
- Results returned via `function_response` parts
- Supports parallel function calling
### Open-source models (Llama 3, Hermes, Qwen)
- No standardized format -- varies by model and serving framework
- Hermes format (NousResearch) is the most common fine-tuned convention
- vLLM supports OpenAI-compatible tool calling for supported models
- Ollama supports basic tool calling with compatible models
- Test tool selection accuracy before production -- open models are 15-30% less accurate than GPT-4o on the Berkeley Function Calling Leaderboard
## Error handling patterns
### Return structured errors
```json
{"error": true, "message": "City 'Toky' not found. Did you mean 'Tokyo'?", "code": "NOT_FOUND", "suggestions": ["Tokyo"]}
```
Include actionable information. "Not found" is bad. "Not found, did you mean X?" is good. The model uses error messages to self-correct.
### Retry strategy
1. Tool call fails with a correctable error (typo, wrong enum value)
2. Send the error back to the model as a tool result
3. The model adjusts and retries
4. Maximum 3 retries per tool call
5. After 3 failures, return the error to the user
### Timeout handling
Set timeouts on all tool executions. 30 seconds is a reasonable default. If a tool times out, return a structured timeout error so the model can inform the user rather than hanging.
## Security checklist
| Check | Why | How |
|-------|-----|-----|
| Allowlist functions | Prevent arbitrary code execution | Only register tools the user needs |
| Validate argument types | Prevent type confusion attacks | Check types before execution |
| Sanitize string arguments | Prevent injection | Reject or escape special characters |
| Parameterize database queries | Prevent SQL injection | Never pass model-generated SQL directly |
| Filter tool results | Prevent data leakage | Remove API keys, PII, internal errors |
| Rate limit tool calls | Prevent runaway loops | Max 10-20 calls per conversation |
| Log all tool calls | Audit trail | Store tool name, arguments, result, timestamp |
| Block path traversal | Prevent file system access | Reject `..` and absolute paths in file tools |
| Sandbox code execution | Prevent system access | Use containers or restricted builtins |
| Validate return size | Prevent context stuffing | Truncate results over 10KB |
## Performance optimization
- **Parallel calls:** When the model requests multiple independent tools, execute them concurrently with `asyncio.gather()` or `concurrent.futures`
- **Caching:** Cache tool results for identical arguments within the same session (weather does not change in 60 seconds)
- **Streaming:** Stream the model's final response while tool results are being fetched
- **Tool pruning:** If context is tight, only include tool definitions relevant to the current query (use a classifier to filter)
- **Smaller models for routing:** Use `gpt-4o-mini` or `claude-3-5-haiku` for tool selection, then pass results to a stronger model for synthesis
## Common failure patterns
| Failure | Cause | Fix |
|---------|-------|-----|
| Wrong tool selected | Ambiguous descriptions | Rewrite descriptions with specific trigger words |
| Missing required args | Model forgot a parameter | Add clear examples in parameter descriptions |
| Infinite tool loop | Model keeps calling same tool | Set max iterations (5-10) and detect repeated calls |
| Hallucinated arguments | Model invents plausible but wrong values | Use enums, validate against known values |
| Tool result too large | API returned 100KB of data | Truncate or summarize before feeding back |
| Model ignores tool result | Result format confusing | Return clean JSON with clear field names |