LLM function calling and tool use patterns. Covers JSON tool schemas (strict mode), execution loops, structured output with Pydantic, parallel tool calls, and LangChain tool binding. OpenAI and Anthropic patterns. Use when: enabling LLMs to call external tools, defining tool schemas, building tool execution loops, or returning structured JSON. Triggers on: function calling, tool use, tool schema, structured output, Pydantic validation, tool execution, parallel tool calls, bind tools
Scanned 9/6/2026
Install to Claude Code
npx -y skills add ArieGoldkin/claude-forge --skill function-calling --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Function Calling?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/ariegoldkin-function-calling)More formats (shields.io, HTML) on the badges page.
---
name: function-calling
description: "LLM function calling and tool use patterns. Covers JSON tool schemas (strict mode), execution loops, structured output with Pydantic, parallel tool calls, and LangChain tool binding. OpenAI and Anthropic patterns. Use when: enabling LLMs to call external tools, defining tool schemas, building tool execution loops, or returning structured JSON. Triggers on: function calling, tool use, tool schema, structured output, Pydantic validation, tool execution, parallel tool calls, bind tools"
effort: low
keep-coding-instructions: true
paths:
- "**/*tool*"
- "**/*function*"
- "**/*schema*"
---
# Function Calling
Enable LLMs to use external tools and return structured data.
## Basic Tool Definition (2026 Best Practice)
```python
# OpenAI format with strict mode (2026 recommended)
tools = [{
"type": "function",
"function": {
"name": "search_documents",
"description": "Search the document database for relevant content",
"strict": True, # ← 2026: Enables structured output validation
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The search query"
},
"limit": {
"type": "integer",
"description": "Max results to return"
}
},
"required": ["query", "limit"], # All props required when strict
"additionalProperties": False # ← 2026: Required for strict mode
}
}
}]
# Note: With strict=True:
# - All properties must be listed in "required"
# - additionalProperties must be False
# - No "default" values (provide via code instead)
```
## Tool Execution Loop
```python
async def run_with_tools(messages: list, tools: list) -> str:
"""Execute tool calls until LLM returns final answer."""
while True:
response = await llm.chat(messages=messages, tools=tools)
# Check if LLM wants to call tools
if not response.tool_calls:
return response.content
# Execute each tool call
for tool_call in response.tool_calls:
result = await execute_tool(
tool_call.function.name,
json.loads(tool_call.function.arguments)
)
# Add tool result to conversation
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result)
})
# Continue loop (LLM will process tool results)
async def execute_tool(name: str, args: dict) -> any:
"""Route to appropriate tool implementation."""
tools = {
"search_documents": search_documents,
"get_weather": get_weather,
"calculate": calculate,
}
return await tools[name](**args)
```
## Structured Output (Guaranteed JSON)
```python
from pydantic import BaseModel
class Analysis(BaseModel):
sentiment: str
confidence: float
key_points: list[str]
# OpenAI structured output
response = await client.beta.chat.completions.parse(
model="gpt-4o",
messages=[{"role": "user", "content": "Analyze this text..."}],
response_format=Analysis
)
analysis = response.choices[0].message.parsed # Typed Analysis object
```
## LangChain Tool Binding
```python
from langchain_core.tools import tool
from pydantic import BaseModel, Field
@tool
def search_documents(query: str, limit: int = 5) -> list[dict]:
"""Search the document database.
Args:
query: Search query string
limit: Maximum results to return
"""
return db.search(query, limit=limit)
# Bind to model
llm_with_tools = llm.bind_tools([search_documents])
# Or with structured output
class SearchResult(BaseModel):
query: str = Field(description="The search query used")
results: list[str] = Field(description="Matching documents")
structured_llm = llm.with_structured_output(SearchResult)
```
## Parallel Tool Calls
```python
# OpenAI supports parallel tool calls
response = await llm.chat(
messages=messages,
tools=tools,
parallel_tool_calls=True # Default in GPT-4o
)
# Handle multiple calls in parallel
if response.tool_calls:
results = await asyncio.gather(*[
execute_tool(tc.function.name, json.loads(tc.function.arguments))
for tc in response.tool_calls
])
```
**⚠️ 2026 Compatibility Note:**
```python
# Structured outputs with strict=True may not work with parallel_tool_calls
# If using strict mode schemas, disable parallel calls:
response = await llm.chat(
messages=messages,
tools=tools_with_strict_true,
parallel_tool_calls=False # Required for strict mode reliability
)
```
## Key Decisions
| Decision | Recommendation |
|----------|----------------|
| Tool count | 5-15 max (more = confusion) |
| Description length | 1-2 sentences |
| Parameter validation | Use Pydantic/Zod |
| Error handling | Return error as tool result |
| **Schema mode** | **`strict: true` (2026 best practice)** |
| Output format | Structured Outputs > JSON mode |
| Parallel calls | Disable with strict mode |
## Common Mistakes
- Vague tool descriptions (LLM won't know when to use)
- No input validation (LLM sends bad params)
- Missing error handling (crashes on tool failure)
- Too many tools (LLM gets confused)
## Related Skills
- `agent-loops` - Multi-step tool use with reasoning
- `llm-patterns` - Streaming with tool calls
- `structured-output` - Complex output schemas
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!