Initial commit
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
# Changelog
|
||||
|
||||
## 0.2.0
|
||||
|
||||
- Refactored to Anthropic progressive disclosure pattern
|
||||
- Updated description with "Use PROACTIVELY when..." format
|
||||
- Removed version/author/category/tags from frontmatter
|
||||
|
||||
## 0.1.0
|
||||
|
||||
- Initial release of Strict Tool Implementer skill
|
||||
- Complete workflow covering tool schema design, multi-tool agents, and production deployment
|
||||
- Multi-tool agent implementation patterns
|
||||
- Error handling for tool failures and refusals
|
||||
- Testing strategies for agentic workflows
|
||||
- Travel booking agent example (multi-tool workflow)
|
||||
@@ -0,0 +1,81 @@
|
||||
# Strict Tool Implementer
|
||||
|
||||
Specialized skill for implementing strict tool use mode with guaranteed parameter validation.
|
||||
|
||||
## Purpose
|
||||
|
||||
This skill handles **end-to-end implementation** of strict tool use mode (`strict: true`), ensuring tool input parameters strictly follow your schema. Essential for building reliable agentic workflows with type-safe tool execution.
|
||||
|
||||
## Use Cases
|
||||
|
||||
- **Multi-Tool Agents**: Travel booking, research assistants, etc.
|
||||
- **Validated Function Calls**: Ensure parameters match expected types
|
||||
- **Complex Tool Schemas**: Tools with nested properties
|
||||
- **Critical Operations**: Financial transactions, booking systems
|
||||
- **Tool Composition**: Sequential and parallel tool execution
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Routed here by `structured-outputs-advisor`
|
||||
- Model: Claude Sonnet 4.5 or Opus 4.1
|
||||
- Beta header: `structured-outputs-2025-11-13`
|
||||
|
||||
## Quick Start
|
||||
|
||||
**Python:**
|
||||
```python
|
||||
response = client.beta.messages.create(
|
||||
model="claude-sonnet-4-5",
|
||||
betas=["structured-outputs-2025-11-13"],
|
||||
messages=[{"role": "user", "content": "Search for flights..."}],
|
||||
tools=[{
|
||||
"name": "search_flights",
|
||||
"description": "Search for available flights",
|
||||
"strict": True, # Enable strict validation
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"origin": {"type": "string"},
|
||||
"destination": {"type": "string"},
|
||||
"travelers": {"type": "integer", "enum": [1, 2, 3, 4, 5, 6]}
|
||||
},
|
||||
"required": ["origin", "destination", "travelers"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
}]
|
||||
)
|
||||
|
||||
# Tool inputs GUARANTEED to match schema
|
||||
for block in response.content:
|
||||
if block.type == "tool_use":
|
||||
execute_tool(block.name, block.input) # Type-safe!
|
||||
```
|
||||
|
||||
## What You'll Learn
|
||||
|
||||
1. **Tool Schema Design** - With `strict: true` and proper validation
|
||||
2. **Multi-Tool Workflows** - Sequential and parallel tool execution
|
||||
3. **Agent Patterns** - Stateful agents, retry logic, validation
|
||||
4. **Error Handling** - Tool failures, refusals, edge cases
|
||||
5. **Production Deployment** - Monitoring, testing, reliability
|
||||
|
||||
## Examples
|
||||
|
||||
- [travel-booking-agent.py](./examples/travel-booking-agent.py) - Multi-tool agent workflow
|
||||
|
||||
## Related Skills
|
||||
|
||||
- [`structured-outputs-advisor`](../structured-outputs-advisor/) - Choose the right mode
|
||||
- [`json-outputs-implementer`](../json-outputs-implementer/) - For data extraction
|
||||
|
||||
## Reference Materials
|
||||
|
||||
- [JSON Schema Limitations](../reference/json-schema-limitations.md)
|
||||
- [Best Practices](../reference/best-practices.md)
|
||||
- [API Compatibility](../reference/api-compatibility.md)
|
||||
|
||||
## Version
|
||||
|
||||
Current version: 0.1.0
|
||||
|
||||
See [CHANGELOG.md](./CHANGELOG.md) for version history.
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
name: strict-tool-implementer
|
||||
description: >-
|
||||
Use PROACTIVELY when building multi-step agentic workflows with validated tool parameters.
|
||||
Implements Anthropic's strict tool use mode for guaranteed schema compliance.
|
||||
Covers tool schema design, multi-tool agent workflows, error handling, testing, and production patterns.
|
||||
Not for data extraction or classification tasks (use json-outputs-implementer instead).
|
||||
---
|
||||
|
||||
# Strict Tool Implementer
|
||||
|
||||
## Overview
|
||||
|
||||
This skill implements Anthropic's strict tool use mode for reliable agentic systems. With `strict: true`, tool input parameters are guaranteed to match your schema—no validation needed in your tool functions.
|
||||
|
||||
**What This Skill Provides:**
|
||||
- Production-ready tool schema design
|
||||
- Multi-tool workflow patterns
|
||||
- Agentic system architecture
|
||||
- Validation and error handling
|
||||
- Complete agent implementation examples
|
||||
|
||||
**Prerequisites:**
|
||||
- Decision made via `structured-outputs-advisor`
|
||||
- Model: Claude Sonnet 4.5 or Opus 4.1
|
||||
- Beta header: `structured-outputs-2025-11-13`
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
**Use for:**
|
||||
- Building multi-step agentic workflows
|
||||
- Validating function call parameters
|
||||
- Ensuring type-safe tool execution
|
||||
- Complex tools with nested properties
|
||||
- Critical operations requiring guaranteed types
|
||||
|
||||
**NOT for:**
|
||||
- Extracting data from text/images → `json-outputs-implementer`
|
||||
- Formatting API responses → `json-outputs-implementer`
|
||||
- Classification tasks → `json-outputs-implementer`
|
||||
|
||||
## Response Style
|
||||
|
||||
- **Tool-focused**: Design tools with clear, validated schemas
|
||||
- **Agent-aware**: Consider multi-tool workflows and composition
|
||||
- **Type-safe**: Guarantee parameter types for downstream functions
|
||||
- **Production-ready**: Handle errors, retries, and monitoring
|
||||
- **Example-driven**: Provide complete agent implementations
|
||||
|
||||
## Workflow
|
||||
|
||||
| Phase | Description | Details |
|
||||
|-------|-------------|---------|
|
||||
| 1 | Tool Schema Design | → [workflow/phase-1-schema-design.md](workflow/phase-1-schema-design.md) |
|
||||
| 2 | Multi-Tool Agent Implementation | → [workflow/phase-2-implementation.md](workflow/phase-2-implementation.md) |
|
||||
| 3 | Error Handling & Validation | → [workflow/phase-3-error-handling.md](workflow/phase-3-error-handling.md) |
|
||||
| 4 | Testing Agent Workflows | → [workflow/phase-4-testing.md](workflow/phase-4-testing.md) |
|
||||
| 5 | Production Agent Patterns | → [workflow/phase-5-production.md](workflow/phase-5-production.md) |
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Schema Template
|
||||
|
||||
```python
|
||||
{
|
||||
"name": "tool_name",
|
||||
"description": "Clear description",
|
||||
"strict": True,
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {...},
|
||||
"required": [...],
|
||||
"additionalProperties": False
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Supported Schema Features
|
||||
|
||||
✅ Basic types, enums, format strings, nested objects/arrays, required fields
|
||||
|
||||
❌ Recursive schemas, min/max constraints, string length, complex regex
|
||||
|
||||
## Reference Materials
|
||||
|
||||
- [Common Agentic Patterns](reference/common-patterns.md)
|
||||
- [Success Criteria](reference/success-criteria.md)
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `structured-outputs-advisor` - Choose the right mode
|
||||
- `json-outputs-implementer` - For data extraction use cases
|
||||
@@ -0,0 +1,289 @@
|
||||
"""
|
||||
Travel Booking Agent Example
|
||||
|
||||
Multi-tool agent using strict tool use mode for guaranteed parameter validation.
|
||||
Demonstrates validated tool inputs in agentic workflows.
|
||||
"""
|
||||
|
||||
from anthropic import Anthropic
|
||||
from typing import Dict, Any, List
|
||||
import json
|
||||
import os
|
||||
|
||||
client = Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
|
||||
|
||||
|
||||
# Define tools with strict mode
|
||||
TOOLS = [
|
||||
{
|
||||
"name": "search_flights",
|
||||
"description": "Search for available flights between two cities",
|
||||
"strict": True, # Enable strict parameter validation
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"origin": {
|
||||
"type": "string",
|
||||
"description": "Departure city (e.g., 'San Francisco, CA')"
|
||||
},
|
||||
"destination": {
|
||||
"type": "string",
|
||||
"description": "Arrival city (e.g., 'Paris, France')"
|
||||
},
|
||||
"departure_date": {
|
||||
"type": "string",
|
||||
"format": "date",
|
||||
"description": "Departure date in YYYY-MM-DD format"
|
||||
},
|
||||
"return_date": {
|
||||
"type": "string",
|
||||
"format": "date",
|
||||
"description": "Return date in YYYY-MM-DD format (optional for one-way)"
|
||||
},
|
||||
"travelers": {
|
||||
"type": "integer",
|
||||
"enum": [1, 2, 3, 4, 5, 6],
|
||||
"description": "Number of travelers"
|
||||
}
|
||||
},
|
||||
"required": ["origin", "destination", "departure_date", "travelers"],
|
||||
"additionalProperties": False # Required for strict mode
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "book_flight",
|
||||
"description": "Book a selected flight",
|
||||
"strict": True,
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"flight_id": {
|
||||
"type": "string",
|
||||
"description": "Flight identifier from search results"
|
||||
},
|
||||
"passenger_names": {
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
"description": "Full names of all passengers"
|
||||
},
|
||||
"contact_email": {
|
||||
"type": "string",
|
||||
"format": "email",
|
||||
"description": "Contact email for booking confirmation"
|
||||
}
|
||||
},
|
||||
"required": ["flight_id", "passenger_names", "contact_email"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "search_hotels",
|
||||
"description": "Search for hotels in a city",
|
||||
"strict": True,
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"city": {
|
||||
"type": "string",
|
||||
"description": "City name"
|
||||
},
|
||||
"check_in": {
|
||||
"type": "string",
|
||||
"format": "date",
|
||||
"description": "Check-in date in YYYY-MM-DD format"
|
||||
},
|
||||
"check_out": {
|
||||
"type": "string",
|
||||
"format": "date",
|
||||
"description": "Check-out date in YYYY-MM-DD format"
|
||||
},
|
||||
"guests": {
|
||||
"type": "integer",
|
||||
"enum": [1, 2, 3, 4],
|
||||
"description": "Number of guests"
|
||||
}
|
||||
},
|
||||
"required": ["city", "check_in", "check_out", "guests"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
# Mock tool implementations
|
||||
def search_flights(origin: str, destination: str, departure_date: str,
|
||||
travelers: int, return_date: str = None) -> Dict:
|
||||
"""Mock flight search - would call real API."""
|
||||
print(f"🔍 Searching flights: {origin} → {destination}")
|
||||
print(f" Departure: {departure_date}, Travelers: {travelers}")
|
||||
|
||||
return {
|
||||
"flights": [
|
||||
{
|
||||
"id": "FL123",
|
||||
"airline": "Air France",
|
||||
"departure": f"{departure_date} 10:00",
|
||||
"arrival": f"{departure_date} 23:00",
|
||||
"price": 850.00,
|
||||
"class": "Economy"
|
||||
},
|
||||
{
|
||||
"id": "FL456",
|
||||
"airline": "United",
|
||||
"departure": f"{departure_date} 14:30",
|
||||
"arrival": f"{departure_date} 03:30+1",
|
||||
"price": 920.00,
|
||||
"class": "Economy"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def book_flight(flight_id: str, passenger_names: List[str],
|
||||
contact_email: str) -> Dict:
|
||||
"""Mock flight booking - would call real API."""
|
||||
print(f"✈️ Booking flight {flight_id}")
|
||||
print(f" Passengers: {', '.join(passenger_names)}")
|
||||
print(f" Email: {contact_email}")
|
||||
|
||||
return {
|
||||
"confirmation": "ABC123XYZ",
|
||||
"status": "confirmed",
|
||||
"total_price": 850.00 * len(passenger_names)
|
||||
}
|
||||
|
||||
|
||||
def search_hotels(city: str, check_in: str, check_out: str, guests: int) -> Dict:
|
||||
"""Mock hotel search - would call real API."""
|
||||
print(f"🏨 Searching hotels in {city}")
|
||||
print(f" Check-in: {check_in}, Check-out: {check_out}, Guests: {guests}")
|
||||
|
||||
return {
|
||||
"hotels": [
|
||||
{
|
||||
"id": "HTL789",
|
||||
"name": "Grand Hotel Paris",
|
||||
"rating": 4.5,
|
||||
"price_per_night": 200.00,
|
||||
"amenities": ["WiFi", "Breakfast", "Gym"]
|
||||
},
|
||||
{
|
||||
"id": "HTL101",
|
||||
"name": "Budget Inn",
|
||||
"rating": 3.5,
|
||||
"price_per_night": 80.00,
|
||||
"amenities": ["WiFi"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
# Tool registry
|
||||
TOOL_FUNCTIONS = {
|
||||
"search_flights": search_flights,
|
||||
"book_flight": book_flight,
|
||||
"search_hotels": search_hotels,
|
||||
}
|
||||
|
||||
|
||||
def run_travel_agent(user_request: str, max_turns: int = 10):
|
||||
"""
|
||||
Run travel booking agent with strict tool validation.
|
||||
|
||||
With strict mode enabled, all tool inputs are GUARANTEED to match
|
||||
the schema - no validation needed in tool functions!
|
||||
"""
|
||||
messages = [{"role": "user", "content": user_request}]
|
||||
|
||||
print("=" * 70)
|
||||
print(f"User Request: {user_request}")
|
||||
print("=" * 70)
|
||||
|
||||
for turn in range(max_turns):
|
||||
print(f"\n🤖 Agent Turn {turn + 1}")
|
||||
|
||||
response = client.beta.messages.create(
|
||||
model="claude-sonnet-4-5",
|
||||
max_tokens=2048,
|
||||
betas=["structured-outputs-2025-11-13"],
|
||||
messages=messages,
|
||||
tools=TOOLS,
|
||||
)
|
||||
|
||||
# Check stop reason
|
||||
if response.stop_reason == "end_turn":
|
||||
# Agent finished
|
||||
final_text = ""
|
||||
for block in response.content:
|
||||
if hasattr(block, "text"):
|
||||
final_text += block.text
|
||||
|
||||
print(f"\n✅ Agent Complete:")
|
||||
print(f"{final_text}")
|
||||
return final_text
|
||||
|
||||
if response.stop_reason == "tool_use":
|
||||
# Execute tools
|
||||
tool_results = []
|
||||
|
||||
for block in response.content:
|
||||
if block.type == "text":
|
||||
print(f"\n💭 Agent: {block.text}")
|
||||
|
||||
elif block.type == "tool_use":
|
||||
tool_name = block.name
|
||||
tool_input = block.input # GUARANTEED to match schema!
|
||||
|
||||
print(f"\n🔧 Tool Call: {tool_name}")
|
||||
print(f" Input: {json.dumps(tool_input, indent=2)}")
|
||||
|
||||
# Execute tool with validated inputs
|
||||
tool_function = TOOL_FUNCTIONS[tool_name]
|
||||
result = tool_function(**tool_input) # Type-safe!
|
||||
|
||||
print(f" Result: {json.dumps(result, indent=2)}")
|
||||
|
||||
tool_results.append({
|
||||
"type": "tool_result",
|
||||
"tool_use_id": block.id,
|
||||
"content": json.dumps(result)
|
||||
})
|
||||
|
||||
# Add to conversation
|
||||
messages.append({"role": "assistant", "content": response.content})
|
||||
messages.append({"role": "user", "content": tool_results})
|
||||
|
||||
else:
|
||||
print(f"⚠️ Unexpected stop reason: {response.stop_reason}")
|
||||
break
|
||||
|
||||
print("\n⚠️ Max turns reached without completion")
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
"""Run travel agent examples."""
|
||||
|
||||
examples = [
|
||||
"Book a round trip from San Francisco to Paris for 2 people, "
|
||||
"departing May 15, 2024 and returning May 22, 2024. "
|
||||
"Passengers are John Smith and Jane Doe. "
|
||||
"Email confirmation to john.smith@example.com. "
|
||||
"Also find a hotel in Paris for those dates.",
|
||||
|
||||
"Find flights from New York to London for 1 traveler on June 1, 2024.",
|
||||
|
||||
"Search for hotels in Tokyo for 2 guests, checking in July 10 "
|
||||
"and checking out July 15.",
|
||||
]
|
||||
|
||||
for i, request in enumerate(examples, 1):
|
||||
print(f"\n\n{'='*70}")
|
||||
print(f"EXAMPLE {i}")
|
||||
print(f"{'='*70}")
|
||||
|
||||
run_travel_agent(request)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,48 @@
|
||||
# Common Agentic Patterns
|
||||
|
||||
## Pattern 1: Sequential Workflow
|
||||
|
||||
Tools execute in sequence (search → book → confirm):
|
||||
|
||||
```python
|
||||
# User: "Book a flight to Paris"
|
||||
# Agent executes:
|
||||
1. search_flights(origin="SF", destination="Paris", ...)
|
||||
2. book_flight(flight_id="F1", passengers=[...])
|
||||
3. send_confirmation(confirmation_id="ABC123")
|
||||
```
|
||||
|
||||
## Pattern 2: Parallel Tool Execution
|
||||
|
||||
Multiple independent tools (flights + hotels):
|
||||
|
||||
```python
|
||||
# User: "Find flights and hotels for Paris trip"
|
||||
# Agent can call in parallel (if your implementation supports it):
|
||||
1. search_flights(destination="Paris", ...)
|
||||
2. search_hotels(city="Paris", ...)
|
||||
```
|
||||
|
||||
## Pattern 3: Conditional Branching
|
||||
|
||||
Tool selection based on context:
|
||||
|
||||
```python
|
||||
# User: "Plan my trip"
|
||||
# Agent decides which tools to call based on conversation:
|
||||
if budget_conscious:
|
||||
search_flights(class="economy")
|
||||
else:
|
||||
search_flights(class="business")
|
||||
```
|
||||
|
||||
## Important Reminders
|
||||
|
||||
1. **Always set `strict: true`** - This enables validation
|
||||
2. **Require `additionalProperties: false`** - Enforced by strict mode
|
||||
3. **Use enums for constrained values** - Better than free text
|
||||
4. **Clear descriptions matter** - Claude uses these to decide when to call tools
|
||||
5. **Tool inputs are guaranteed valid** - No validation needed in tool functions
|
||||
6. **Handle tool execution failures** - External APIs can fail
|
||||
7. **Test multi-step workflows** - Edge cases appear in tool composition
|
||||
8. **Monitor agent behavior** - Track tool usage patterns and failures
|
||||
@@ -0,0 +1,23 @@
|
||||
# Success Criteria
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
- [ ] Tool schemas designed with `strict: true`
|
||||
- [ ] All tools have `additionalProperties: false`
|
||||
- [ ] Clear descriptions for tools and parameters
|
||||
- [ ] Required fields properly specified
|
||||
- [ ] Multi-tool workflow implemented
|
||||
- [ ] Error handling for tool failures
|
||||
- [ ] Refusal scenarios handled
|
||||
- [ ] Agent tested with realistic scenarios
|
||||
- [ ] Production patterns applied (retry, validation)
|
||||
- [ ] Monitoring in place
|
||||
|
||||
## Official Documentation
|
||||
|
||||
https://docs.anthropic.com/en/docs/build-with-claude/structured-outputs
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `structured-outputs-advisor` - Choose the right mode
|
||||
- `json-outputs-implementer` - For data extraction use cases
|
||||
@@ -0,0 +1,125 @@
|
||||
# Phase 1: Tool Schema Design
|
||||
|
||||
**Objective**: Design validated tool schemas for your agent
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Identify Required Tools
|
||||
|
||||
Ask the user:
|
||||
- "What actions should the agent be able to perform?"
|
||||
- "What external systems will the agent interact with?"
|
||||
- "What parameters does each tool need?"
|
||||
|
||||
**Example agent**: Travel booking
|
||||
- `search_flights` - Find available flights
|
||||
- `book_flight` - Reserve a flight
|
||||
- `search_hotels` - Find hotels
|
||||
- `book_hotel` - Reserve accommodation
|
||||
|
||||
### 2. Design Tool Schema with `strict: true`
|
||||
|
||||
**Template:**
|
||||
```python
|
||||
{
|
||||
"name": "tool_name",
|
||||
"description": "Clear description of what this tool does",
|
||||
"strict": True, # ← Enables strict mode
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"param_name": {
|
||||
"type": "string",
|
||||
"description": "Clear parameter description"
|
||||
}
|
||||
},
|
||||
"required": ["param_name"],
|
||||
"additionalProperties": False # ← Required
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example: Flight Search Tool**
|
||||
```python
|
||||
{
|
||||
"name": "search_flights",
|
||||
"description": "Search for available flights between two cities",
|
||||
"strict": True,
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"origin": {
|
||||
"type": "string",
|
||||
"description": "Departure city (e.g., 'San Francisco, CA')"
|
||||
},
|
||||
"destination": {
|
||||
"type": "string",
|
||||
"description": "Arrival city (e.g., 'Paris, France')"
|
||||
},
|
||||
"departure_date": {
|
||||
"type": "string",
|
||||
"format": "date",
|
||||
"description": "Departure date in YYYY-MM-DD format"
|
||||
},
|
||||
"return_date": {
|
||||
"type": "string",
|
||||
"format": "date",
|
||||
"description": "Return date in YYYY-MM-DD format (optional)"
|
||||
},
|
||||
"travelers": {
|
||||
"type": "integer",
|
||||
"enum": [1, 2, 3, 4, 5, 6],
|
||||
"description": "Number of travelers"
|
||||
},
|
||||
"class": {
|
||||
"type": "string",
|
||||
"enum": ["economy", "premium", "business", "first"],
|
||||
"description": "Flight class preference"
|
||||
}
|
||||
},
|
||||
"required": ["origin", "destination", "departure_date", "travelers"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Apply JSON Schema Limitations
|
||||
|
||||
**✅ Supported:**
|
||||
- All basic types (object, array, string, integer, number, boolean)
|
||||
- `enum` for constrained values
|
||||
- `format` for strings (date, email, uri, uuid, etc.)
|
||||
- Nested objects and arrays
|
||||
- `required` fields
|
||||
- `additionalProperties: false` (required!)
|
||||
|
||||
**❌ NOT Supported:**
|
||||
- Recursive schemas
|
||||
- Numerical constraints (minimum, maximum)
|
||||
- String length constraints
|
||||
- Complex regex patterns
|
||||
|
||||
### 4. Add Clear Descriptions
|
||||
|
||||
Good descriptions help Claude:
|
||||
- Understand when to call the tool
|
||||
- Know what values to provide
|
||||
- Format parameters correctly
|
||||
|
||||
```python
|
||||
# ✅ Good: Clear and specific
|
||||
"origin": {
|
||||
"type": "string",
|
||||
"description": "Departure city and state/country (e.g., 'San Francisco, CA')"
|
||||
}
|
||||
|
||||
# ❌ Vague: Not helpful
|
||||
"origin": {
|
||||
"type": "string",
|
||||
"description": "Origin"
|
||||
}
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
Well-designed tool schemas ready for implementation.
|
||||
@@ -0,0 +1,222 @@
|
||||
# Phase 2: Multi-Tool Agent Implementation
|
||||
|
||||
**Objective**: Implement agent with multiple validated tools
|
||||
|
||||
## Python Implementation
|
||||
|
||||
```python
|
||||
from anthropic import Anthropic
|
||||
from typing import Dict, Any, List
|
||||
|
||||
client = Anthropic()
|
||||
|
||||
# Define tools
|
||||
TOOLS = [
|
||||
{
|
||||
"name": "search_flights",
|
||||
"description": "Search for available flights",
|
||||
"strict": True,
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"origin": {"type": "string", "description": "Departure city"},
|
||||
"destination": {"type": "string", "description": "Arrival city"},
|
||||
"departure_date": {"type": "string", "format": "date"},
|
||||
"travelers": {"type": "integer", "enum": [1, 2, 3, 4, 5, 6]}
|
||||
},
|
||||
"required": ["origin", "destination", "departure_date", "travelers"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "book_flight",
|
||||
"description": "Book a selected flight",
|
||||
"strict": True,
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"flight_id": {"type": "string", "description": "Flight identifier"},
|
||||
"passengers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"passport": {"type": "string"}
|
||||
},
|
||||
"required": ["name", "passport"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["flight_id", "passengers"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "search_hotels",
|
||||
"description": "Search for hotels in a city",
|
||||
"strict": True,
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"city": {"type": "string", "description": "City name"},
|
||||
"check_in": {"type": "string", "format": "date"},
|
||||
"check_out": {"type": "string", "format": "date"},
|
||||
"guests": {"type": "integer", "enum": [1, 2, 3, 4]}
|
||||
},
|
||||
"required": ["city", "check_in", "check_out", "guests"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
# Tool execution functions
|
||||
def search_flights(origin: str, destination: str, departure_date: str, travelers: int) -> Dict:
|
||||
"""Execute flight search - calls actual API."""
|
||||
# Implementation here
|
||||
return {"flights": [...]}
|
||||
|
||||
def book_flight(flight_id: str, passengers: List[Dict]) -> Dict:
|
||||
"""Book the flight - calls actual API."""
|
||||
# Implementation here
|
||||
return {"confirmation": "ABC123", "status": "confirmed"}
|
||||
|
||||
def search_hotels(city: str, check_in: str, check_out: str, guests: int) -> Dict:
|
||||
"""Search hotels - calls actual API."""
|
||||
# Implementation here
|
||||
return {"hotels": [...]}
|
||||
|
||||
# Tool registry
|
||||
TOOL_FUNCTIONS = {
|
||||
"search_flights": search_flights,
|
||||
"book_flight": book_flight,
|
||||
"search_hotels": search_hotels,
|
||||
}
|
||||
|
||||
# Agent loop
|
||||
def run_agent(user_request: str, max_turns: int = 10):
|
||||
"""Run agent with tool validation."""
|
||||
messages = [{"role": "user", "content": user_request}]
|
||||
|
||||
for turn in range(max_turns):
|
||||
response = client.beta.messages.create(
|
||||
model="claude-sonnet-4-5",
|
||||
max_tokens=2048,
|
||||
betas=["structured-outputs-2025-11-13"],
|
||||
messages=messages,
|
||||
tools=TOOLS,
|
||||
)
|
||||
|
||||
# Process response
|
||||
if response.stop_reason == "end_turn":
|
||||
# Agent finished
|
||||
return extract_final_answer(response)
|
||||
|
||||
if response.stop_reason == "tool_use":
|
||||
# Execute tools
|
||||
tool_results = []
|
||||
|
||||
for block in response.content:
|
||||
if block.type == "tool_use":
|
||||
# Tool input is GUARANTEED to match schema
|
||||
tool_name = block.name
|
||||
tool_input = block.input # Already validated!
|
||||
|
||||
# Execute tool
|
||||
tool_function = TOOL_FUNCTIONS[tool_name]
|
||||
result = tool_function(**tool_input) # Type-safe!
|
||||
|
||||
tool_results.append({
|
||||
"type": "tool_result",
|
||||
"tool_use_id": block.id,
|
||||
"content": str(result)
|
||||
})
|
||||
|
||||
# Add assistant response and tool results to conversation
|
||||
messages.append({"role": "assistant", "content": response.content})
|
||||
messages.append({"role": "user", "content": tool_results})
|
||||
|
||||
else:
|
||||
raise Exception(f"Unexpected stop reason: {response.stop_reason}")
|
||||
|
||||
raise Exception("Max turns reached")
|
||||
|
||||
# Usage
|
||||
result = run_agent("Book a flight from SF to Paris for 2 people, departing May 15")
|
||||
print(result)
|
||||
```
|
||||
|
||||
## TypeScript Implementation
|
||||
|
||||
```typescript
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
|
||||
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
|
||||
|
||||
const TOOLS: Anthropic.Tool[] = [
|
||||
{
|
||||
name: "search_flights",
|
||||
description: "Search for available flights",
|
||||
strict: true,
|
||||
input_schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
origin: { type: "string", description: "Departure city" },
|
||||
destination: { type: "string", description: "Arrival city" },
|
||||
departure_date: { type: "string", format: "date" },
|
||||
travelers: { type: "integer", enum: [1, 2, 3, 4, 5, 6] }
|
||||
},
|
||||
required: ["origin", "destination", "departure_date", "travelers"],
|
||||
additionalProperties: false
|
||||
}
|
||||
},
|
||||
// ... other tools
|
||||
];
|
||||
|
||||
async function runAgent(userRequest: string, maxTurns: number = 10) {
|
||||
const messages: Anthropic.MessageParam[] = [
|
||||
{ role: "user", content: userRequest }
|
||||
];
|
||||
|
||||
for (let turn = 0; turn < maxTurns; turn++) {
|
||||
const response = await client.beta.messages.create({
|
||||
model: "claude-sonnet-4-5",
|
||||
max_tokens: 2048,
|
||||
betas: ["structured-outputs-2025-11-13"],
|
||||
messages,
|
||||
tools: TOOLS,
|
||||
});
|
||||
|
||||
if (response.stop_reason === "end_turn") {
|
||||
return extractFinalAnswer(response);
|
||||
}
|
||||
|
||||
if (response.stop_reason === "tool_use") {
|
||||
const toolResults: Anthropic.ToolResultBlockParam[] = [];
|
||||
|
||||
for (const block of response.content) {
|
||||
if (block.type === "tool_use") {
|
||||
// Input guaranteed to match schema!
|
||||
const result = await executeTool(block.name, block.input);
|
||||
|
||||
toolResults.push({
|
||||
type: "tool_result",
|
||||
tool_use_id: block.id,
|
||||
content: JSON.stringify(result)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
messages.push({ role: "assistant", content: response.content });
|
||||
messages.push({ role: "user", content: toolResults });
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error("Max turns reached");
|
||||
}
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
Working multi-tool agent with validated tool schemas.
|
||||
@@ -0,0 +1,58 @@
|
||||
# Phase 3: Error Handling & Validation
|
||||
|
||||
**Objective**: Handle errors and edge cases in agent workflows
|
||||
|
||||
## Key Error Scenarios
|
||||
|
||||
### 1. Tool Execution Failures
|
||||
|
||||
```python
|
||||
def execute_tool_safely(tool_name: str, tool_input: Dict) -> Dict:
|
||||
"""Execute tool with error handling."""
|
||||
try:
|
||||
tool_function = TOOL_FUNCTIONS[tool_name]
|
||||
result = tool_function(**tool_input)
|
||||
return {"success": True, "data": result}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Tool {tool_name} failed: {e}")
|
||||
return {
|
||||
"success": False,
|
||||
"error": str(e),
|
||||
"message": "Tool execution failed. Please try again."
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Safety Refusals
|
||||
|
||||
```python
|
||||
if response.stop_reason == "refusal":
|
||||
logger.warning("Agent refused request")
|
||||
# Don't retry - respect safety boundaries
|
||||
return {"error": "Request cannot be completed"}
|
||||
```
|
||||
|
||||
### 3. Max Turns Exceeded
|
||||
|
||||
```python
|
||||
if turn >= max_turns:
|
||||
logger.warning("Agent exceeded max turns")
|
||||
return {
|
||||
"error": "Task too complex",
|
||||
"partial_progress": extract_progress(messages)
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Invalid Tool Name
|
||||
|
||||
```python
|
||||
# With strict mode, tool names are guaranteed valid
|
||||
# But external factors can cause issues
|
||||
if tool_name not in TOOL_FUNCTIONS:
|
||||
logger.error(f"Unknown tool: {tool_name}")
|
||||
return {"error": f"Tool {tool_name} not implemented"}
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
Robust error handling for production agent workflows.
|
||||
@@ -0,0 +1,81 @@
|
||||
# Phase 4: Testing Agent Workflows
|
||||
|
||||
**Objective**: Validate agent behavior with realistic scenarios
|
||||
|
||||
## Test Strategy
|
||||
|
||||
```python
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
@pytest.fixture
|
||||
def mock_tool_functions():
|
||||
"""Mock external tool functions."""
|
||||
return {
|
||||
"search_flights": Mock(return_value={"flights": [{"id": "F1", "price": 500}]}),
|
||||
"book_flight": Mock(return_value={"confirmation": "ABC123"}),
|
||||
"search_hotels": Mock(return_value={"hotels": [{"id": "H1", "price": 150}]}),
|
||||
}
|
||||
|
||||
def test_simple_flight_search(mock_tool_functions):
|
||||
"""Test agent handles simple flight search."""
|
||||
with patch.dict('agent.TOOL_FUNCTIONS', mock_tool_functions):
|
||||
result = run_agent("Find flights from SF to LA on May 15 for 2 people")
|
||||
|
||||
# Verify search_flights was called
|
||||
mock_tool_functions["search_flights"].assert_called_once()
|
||||
call_args = mock_tool_functions["search_flights"].call_args[1]
|
||||
|
||||
# Strict mode guarantees these match schema
|
||||
assert call_args["origin"] == "San Francisco, CA" # or similar
|
||||
assert call_args["destination"] == "Los Angeles, CA"
|
||||
assert call_args["travelers"] == 2
|
||||
assert "2024-05-15" in call_args["departure_date"]
|
||||
|
||||
def test_multi_step_booking(mock_tool_functions):
|
||||
"""Test agent completes multi-step booking."""
|
||||
with patch.dict('agent.TOOL_FUNCTIONS', mock_tool_functions):
|
||||
result = run_agent(
|
||||
"Book a round trip from SF to Paris for 2 people, "
|
||||
"May 15-22, and find a hotel"
|
||||
)
|
||||
|
||||
# Verify correct tool sequence
|
||||
assert mock_tool_functions["search_flights"].called
|
||||
assert mock_tool_functions["book_flight"].called
|
||||
assert mock_tool_functions["search_hotels"].called
|
||||
|
||||
def test_tool_failure_handling(mock_tool_functions):
|
||||
"""Test agent handles tool failures gracefully."""
|
||||
mock_tool_functions["search_flights"].side_effect = Exception("API down")
|
||||
|
||||
with patch.dict('agent.TOOL_FUNCTIONS', mock_tool_functions):
|
||||
result = run_agent("Find flights to Paris")
|
||||
|
||||
# Should handle error gracefully
|
||||
assert "error" in result or "failed" in str(result).lower()
|
||||
|
||||
def test_parameter_validation():
|
||||
"""Test that strict mode guarantees valid parameters."""
|
||||
# With strict mode, parameters are guaranteed to match schema
|
||||
# This test verifies the guarantee holds
|
||||
|
||||
response = client.beta.messages.create(
|
||||
model="claude-sonnet-4-5",
|
||||
betas=["structured-outputs-2025-11-13"],
|
||||
messages=[{"role": "user", "content": "Search flights for 2 people"}],
|
||||
tools=TOOLS,
|
||||
)
|
||||
|
||||
for block in response.content:
|
||||
if block.type == "tool_use":
|
||||
# These assertions should NEVER fail with strict mode
|
||||
assert isinstance(block.input, dict)
|
||||
assert "travelers" in block.input
|
||||
assert isinstance(block.input["travelers"], int)
|
||||
assert block.input["travelers"] in [1, 2, 3, 4, 5, 6]
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
Comprehensive test coverage for agent workflows.
|
||||
@@ -0,0 +1,98 @@
|
||||
# Phase 5: Production Agent Patterns
|
||||
|
||||
**Objective**: Production-ready agent architectures
|
||||
|
||||
## Pattern 1: Stateful Agent with Memory
|
||||
|
||||
```python
|
||||
class StatefulTravelAgent:
|
||||
"""Agent that maintains state across interactions."""
|
||||
|
||||
def __init__(self):
|
||||
self.conversation_history: List[Dict] = []
|
||||
self.booking_state: Dict[str, Any] = {}
|
||||
|
||||
def chat(self, user_message: str) -> str:
|
||||
"""Process user message and return response."""
|
||||
self.conversation_history.append({
|
||||
"role": "user",
|
||||
"content": user_message
|
||||
})
|
||||
|
||||
response = client.beta.messages.create(
|
||||
model="claude-sonnet-4-5",
|
||||
betas=["structured-outputs-2025-11-13"],
|
||||
max_tokens=2048,
|
||||
messages=self.conversation_history,
|
||||
tools=TOOLS,
|
||||
)
|
||||
|
||||
# Process tools and update state
|
||||
final_response = self._process_response(response)
|
||||
|
||||
self.conversation_history.append({
|
||||
"role": "assistant",
|
||||
"content": final_response
|
||||
})
|
||||
|
||||
return final_response
|
||||
|
||||
def _process_response(self, response) -> str:
|
||||
"""Process tool calls and maintain state."""
|
||||
# Implementation...
|
||||
pass
|
||||
|
||||
# Usage
|
||||
agent = StatefulTravelAgent()
|
||||
print(agent.chat("I want to go to Paris"))
|
||||
print(agent.chat("For 2 people")) # Remembers context
|
||||
print(agent.chat("May 15 to May 22")) # Continues booking
|
||||
```
|
||||
|
||||
## Pattern 2: Tool Retry Logic
|
||||
|
||||
```python
|
||||
def execute_tool_with_retry(
|
||||
tool_name: str,
|
||||
tool_input: Dict,
|
||||
max_retries: int = 3
|
||||
) -> Dict:
|
||||
"""Execute tool with exponential backoff retry."""
|
||||
import time
|
||||
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
tool_func = TOOL_FUNCTIONS[tool_name]
|
||||
result = tool_func(**tool_input)
|
||||
return {"success": True, "data": result}
|
||||
|
||||
except Exception as e:
|
||||
if attempt == max_retries - 1:
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
wait_time = 2 ** attempt # Exponential backoff
|
||||
logger.warning(f"Tool {tool_name} failed, retrying in {wait_time}s")
|
||||
time.sleep(wait_time)
|
||||
```
|
||||
|
||||
## Pattern 3: Tool Result Validation
|
||||
|
||||
```python
|
||||
def validate_tool_result(tool_name: str, result: Any) -> bool:
|
||||
"""Validate tool execution result."""
|
||||
validators = {
|
||||
"search_flights": lambda r: "flights" in r and len(r["flights"]) > 0,
|
||||
"book_flight": lambda r: "confirmation" in r,
|
||||
"search_hotels": lambda r: "hotels" in r,
|
||||
}
|
||||
|
||||
validator = validators.get(tool_name)
|
||||
if validator:
|
||||
return validator(result)
|
||||
|
||||
return True # No validator = assume valid
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
Production-ready agent patterns with state management, retry logic, and validation.
|
||||
Reference in New Issue
Block a user