Initial commit
This commit is contained in:
16
skills/json-outputs-implementer/CHANGELOG.md
Normal file
16
skills/json-outputs-implementer/CHANGELOG.md
Normal file
@@ -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 JSON Outputs Implementer skill
|
||||
- Complete workflow covering schema design, SDK integration, testing, and production
|
||||
- Pydantic and Zod integration patterns
|
||||
- Error handling for refusals, token limits, and validation failures
|
||||
- Performance optimization guidance (grammar caching, token management)
|
||||
- Contact and invoice extraction examples
|
||||
90
skills/json-outputs-implementer/README.md
Normal file
90
skills/json-outputs-implementer/README.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# JSON Outputs Implementer
|
||||
|
||||
Specialized skill for implementing JSON outputs mode with guaranteed schema compliance.
|
||||
|
||||
## Purpose
|
||||
|
||||
This skill handles **end-to-end implementation** of JSON outputs mode (`output_format`), ensuring Claude's responses strictly match your JSON schema. Covers schema design, SDK integration, testing, and production deployment.
|
||||
|
||||
## Use Cases
|
||||
|
||||
- **Data Extraction**: Pull structured info from text/images
|
||||
- **Classification**: Categorize content with guaranteed output format
|
||||
- **API Formatting**: Generate API-ready JSON responses
|
||||
- **Report Generation**: Create structured reports
|
||||
- **Database Operations**: Ensure type-safe inserts/updates
|
||||
|
||||
## 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 with Pydantic:**
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
from anthropic import Anthropic
|
||||
|
||||
class Contact(BaseModel):
|
||||
name: str
|
||||
email: str
|
||||
|
||||
client = Anthropic()
|
||||
response = client.beta.messages.parse(
|
||||
model="claude-sonnet-4-5",
|
||||
betas=["structured-outputs-2025-11-13"],
|
||||
messages=[{"role": "user", "content": "Extract contact..."}],
|
||||
output_format=Contact,
|
||||
)
|
||||
|
||||
contact = response.parsed_output # Guaranteed valid
|
||||
```
|
||||
|
||||
**TypeScript with Zod:**
|
||||
```typescript
|
||||
import { z } from 'zod';
|
||||
|
||||
const ContactSchema = z.object({
|
||||
name: z.string(),
|
||||
email: z.string().email(),
|
||||
});
|
||||
|
||||
const response = await client.beta.messages.parse({
|
||||
model: "claude-sonnet-4-5",
|
||||
betas: ["structured-outputs-2025-11-13"],
|
||||
output_format: betaZodOutputFormat(ContactSchema),
|
||||
messages: [...]
|
||||
});
|
||||
```
|
||||
|
||||
## What You'll Learn
|
||||
|
||||
1. **Schema Design** - Respecting JSON Schema limitations
|
||||
2. **SDK Integration** - Pydantic/Zod helpers
|
||||
3. **Error Handling** - Refusals, token limits, validation
|
||||
4. **Production Optimization** - Caching, monitoring, cost tracking
|
||||
5. **Testing** - Comprehensive validation strategies
|
||||
|
||||
## Examples
|
||||
|
||||
- [contact-extraction.py](./examples/contact-extraction.py) - Extract contact info
|
||||
- [invoice-extraction.py](./examples/invoice-extraction.py) - Complex nested schemas
|
||||
|
||||
## Related Skills
|
||||
|
||||
- [`structured-outputs-advisor`](../structured-outputs-advisor/) - Choose the right mode
|
||||
- [`strict-tool-implementer`](../strict-tool-implementer/) - For tool validation
|
||||
|
||||
## 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.
|
||||
93
skills/json-outputs-implementer/SKILL.md
Normal file
93
skills/json-outputs-implementer/SKILL.md
Normal file
@@ -0,0 +1,93 @@
|
||||
---
|
||||
name: json-outputs-implementer
|
||||
description: >-
|
||||
Use PROACTIVELY when extracting structured data from text/images, classifying content, or formatting API responses with guaranteed schema compliance.
|
||||
Implements Anthropic's JSON outputs mode with Pydantic/Zod SDK integration.
|
||||
Covers schema design, validation, testing, and production optimization.
|
||||
Not for tool parameter validation or agentic workflows (use strict-tool-implementer instead).
|
||||
---
|
||||
|
||||
# JSON Outputs Implementer
|
||||
|
||||
## Overview
|
||||
|
||||
This skill implements Anthropic's JSON outputs mode for guaranteed schema compliance. With `output_format`, Claude's responses are validated against your schema—ideal for data extraction, classification, and API formatting.
|
||||
|
||||
**What This Skill Provides:**
|
||||
- Production-ready JSON schema design
|
||||
- SDK integration (Pydantic for Python, Zod for TypeScript)
|
||||
- Validation and error handling patterns
|
||||
- Performance optimization strategies
|
||||
- Complete 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:**
|
||||
- Extracting structured data from text/images
|
||||
- Classification tasks with guaranteed categories
|
||||
- Generating API-ready responses
|
||||
- Formatting reports with fixed structure
|
||||
- Database inserts requiring type safety
|
||||
|
||||
**NOT for:**
|
||||
- Validating tool inputs → `strict-tool-implementer`
|
||||
- Agentic workflows → `strict-tool-implementer`
|
||||
|
||||
## Response Style
|
||||
|
||||
- **Schema-first**: Design schema before implementation
|
||||
- **SDK-friendly**: Leverage Pydantic/Zod when available
|
||||
- **Production-ready**: Consider performance, caching, errors
|
||||
- **Example-driven**: Provide complete working code
|
||||
- **Limitation-aware**: Respect JSON Schema constraints
|
||||
|
||||
## Workflow
|
||||
|
||||
| Phase | Description | Details |
|
||||
|-------|-------------|---------|
|
||||
| 1 | Schema Design | → [workflow/phase-1-schema-design.md](workflow/phase-1-schema-design.md) |
|
||||
| 2 | SDK Integration | → [workflow/phase-2-sdk-integration.md](workflow/phase-2-sdk-integration.md) |
|
||||
| 3 | Error Handling | → [workflow/phase-3-error-handling.md](workflow/phase-3-error-handling.md) |
|
||||
| 4 | Testing | → [workflow/phase-4-testing.md](workflow/phase-4-testing.md) |
|
||||
| 5 | Production Optimization | → [workflow/phase-5-production.md](workflow/phase-5-production.md) |
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Python Template
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
from anthropic import Anthropic
|
||||
|
||||
class MySchema(BaseModel):
|
||||
field: str
|
||||
|
||||
response = client.beta.messages.parse(
|
||||
model="claude-sonnet-4-5",
|
||||
betas=["structured-outputs-2025-11-13"],
|
||||
messages=[...],
|
||||
output_format=MySchema,
|
||||
)
|
||||
result = response.parsed_output # Validated!
|
||||
```
|
||||
|
||||
### 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 Use Cases](reference/use-cases.md)
|
||||
- [Schema Limitations](reference/schema-limitations.md)
|
||||
|
||||
## Related Skills
|
||||
|
||||
- `structured-outputs-advisor` - Choose the right mode
|
||||
- `strict-tool-implementer` - For tool validation use cases
|
||||
138
skills/json-outputs-implementer/examples/contact-extraction.py
Normal file
138
skills/json-outputs-implementer/examples/contact-extraction.py
Normal file
@@ -0,0 +1,138 @@
|
||||
"""
|
||||
Contact Information Extraction Example
|
||||
|
||||
Extracts structured contact information from unstructured text (emails, messages, etc.)
|
||||
using JSON outputs mode with Pydantic schema validation.
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel, Field, EmailStr
|
||||
from typing import Optional, List
|
||||
from anthropic import Anthropic
|
||||
import os
|
||||
|
||||
# Initialize client
|
||||
client = Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
|
||||
|
||||
|
||||
# Define schema with Pydantic
|
||||
class ContactInfo(BaseModel):
|
||||
"""Structured contact information extracted from text."""
|
||||
|
||||
name: str = Field(description="Full name of the contact person")
|
||||
email: EmailStr = Field(description="Email address")
|
||||
phone: Optional[str] = Field(
|
||||
None, description="Phone number in any format"
|
||||
)
|
||||
company: Optional[str] = Field(
|
||||
None, description="Company or organization name"
|
||||
)
|
||||
plan_interest: Optional[str] = Field(
|
||||
None, description="Product plan or tier they're interested in"
|
||||
)
|
||||
demo_requested: bool = Field(
|
||||
False, description="Whether they requested a product demo"
|
||||
)
|
||||
tags: List[str] = Field(
|
||||
default_factory=list,
|
||||
description="Relevant tags or categories"
|
||||
)
|
||||
|
||||
|
||||
def extract_contact(text: str) -> Optional[ContactInfo]:
|
||||
"""
|
||||
Extract contact information from unstructured text.
|
||||
|
||||
Args:
|
||||
text: Unstructured text containing contact information
|
||||
|
||||
Returns:
|
||||
ContactInfo object with extracted data, or None if request refused
|
||||
"""
|
||||
try:
|
||||
response = client.beta.messages.parse(
|
||||
model="claude-sonnet-4-5",
|
||||
max_tokens=1024,
|
||||
betas=["structured-outputs-2025-11-13"],
|
||||
messages=[{
|
||||
"role": "user",
|
||||
"content": f"Extract contact information from the following text:\n\n{text}"
|
||||
}],
|
||||
output_format=ContactInfo,
|
||||
)
|
||||
|
||||
# Handle different stop reasons
|
||||
if response.stop_reason == "refusal":
|
||||
print(f"⚠️ Request refused for safety reasons")
|
||||
return None
|
||||
|
||||
if response.stop_reason == "max_tokens":
|
||||
print(f"⚠️ Response truncated - increase max_tokens")
|
||||
return None
|
||||
|
||||
# Return validated contact info
|
||||
return response.parsed_output
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error extracting contact: {e}")
|
||||
raise
|
||||
|
||||
|
||||
def main():
|
||||
"""Run contact extraction examples."""
|
||||
|
||||
examples = [
|
||||
# Example 1: Complete contact info
|
||||
"""
|
||||
Hi, I'm John Smith from Acme Corp. You can reach me at john.smith@acme.com
|
||||
or call me at (555) 123-4567. I'm interested in your Enterprise plan and
|
||||
would love to schedule a demo next week.
|
||||
""",
|
||||
|
||||
# Example 2: Minimal info
|
||||
"""
|
||||
Contact: jane.doe@example.com
|
||||
""",
|
||||
|
||||
# Example 3: Informal message
|
||||
"""
|
||||
Hey! Bob here. Email me at bob@startup.io if you want to chat about
|
||||
the Pro plan. Thanks!
|
||||
""",
|
||||
|
||||
# Example 4: Multiple contacts (extracts first/primary)
|
||||
"""
|
||||
From: alice@company.com
|
||||
CC: support@company.com
|
||||
|
||||
Hi, I'm Alice Johnson, VP of Engineering at TechCo.
|
||||
We're evaluating your platform for our team of 50 developers.
|
||||
""",
|
||||
]
|
||||
|
||||
print("=" * 70)
|
||||
print("Contact Extraction Examples")
|
||||
print("=" * 70)
|
||||
|
||||
for i, text in enumerate(examples, 1):
|
||||
print(f"\n📧 Example {i}:")
|
||||
print(f"Input: {text.strip()[:100]}...")
|
||||
|
||||
contact = extract_contact(text)
|
||||
|
||||
if contact:
|
||||
print(f"\n✅ Extracted Contact:")
|
||||
print(f" Name: {contact.name}")
|
||||
print(f" Email: {contact.email}")
|
||||
print(f" Phone: {contact.phone or 'N/A'}")
|
||||
print(f" Company: {contact.company or 'N/A'}")
|
||||
print(f" Plan Interest: {contact.plan_interest or 'N/A'}")
|
||||
print(f" Demo Requested: {contact.demo_requested}")
|
||||
print(f" Tags: {', '.join(contact.tags) if contact.tags else 'None'}")
|
||||
else:
|
||||
print(f"\n❌ No contact extracted")
|
||||
|
||||
print("-" * 70)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
160
skills/json-outputs-implementer/examples/invoice-extraction.py
Normal file
160
skills/json-outputs-implementer/examples/invoice-extraction.py
Normal file
@@ -0,0 +1,160 @@
|
||||
"""
|
||||
Invoice Data Extraction Example
|
||||
|
||||
Extracts structured invoice data from text using JSON outputs with nested schemas.
|
||||
Demonstrates handling complex nested structures (line items, tax breakdown).
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import List
|
||||
from decimal import Decimal
|
||||
from anthropic import Anthropic
|
||||
import os
|
||||
|
||||
client = Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))
|
||||
|
||||
|
||||
# Nested schema for line items
|
||||
class LineItem(BaseModel):
|
||||
"""Individual line item on an invoice."""
|
||||
description: str = Field(description="Item description")
|
||||
quantity: int = Field(description="Quantity ordered")
|
||||
unit_price: float = Field(description="Price per unit in USD")
|
||||
total: float = Field(description="Total for this line (quantity * unit_price)")
|
||||
|
||||
|
||||
class Invoice(BaseModel):
|
||||
"""Complete invoice structure."""
|
||||
invoice_number: str = Field(description="Invoice ID (format: INV-XXXXX)")
|
||||
date: str = Field(description="Invoice date in YYYY-MM-DD format")
|
||||
due_date: str = Field(description="Payment due date in YYYY-MM-DD format")
|
||||
customer_name: str = Field(description="Customer or company name")
|
||||
customer_email: str = Field(description="Customer email address")
|
||||
|
||||
line_items: List[LineItem] = Field(
|
||||
description="List of items on the invoice"
|
||||
)
|
||||
|
||||
subtotal: float = Field(description="Subtotal before tax in USD")
|
||||
tax_rate: float = Field(description="Tax rate as decimal (e.g., 0.08 for 8%)")
|
||||
tax_amount: float = Field(description="Tax amount in USD")
|
||||
total_amount: float = Field(description="Final total amount in USD")
|
||||
|
||||
notes: str = Field(
|
||||
default="",
|
||||
description="Additional notes or payment instructions"
|
||||
)
|
||||
|
||||
|
||||
def extract_invoice(invoice_text: str) -> Optional[Invoice]:
|
||||
"""Extract structured invoice data."""
|
||||
try:
|
||||
response = client.beta.messages.parse(
|
||||
model="claude-sonnet-4-5",
|
||||
max_tokens=2048, # Higher for complex nested structures
|
||||
betas=["structured-outputs-2025-11-13"],
|
||||
messages=[{
|
||||
"role": "user",
|
||||
"content": f"Extract all invoice data from:\n\n{invoice_text}"
|
||||
}],
|
||||
output_format=Invoice,
|
||||
)
|
||||
|
||||
if response.stop_reason != "end_turn":
|
||||
print(f"⚠️ Unexpected stop reason: {response.stop_reason}")
|
||||
return None
|
||||
|
||||
return response.parsed_output
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error: {e}")
|
||||
raise
|
||||
|
||||
|
||||
def main():
|
||||
"""Run invoice extraction example."""
|
||||
|
||||
invoice_text = """
|
||||
INVOICE
|
||||
|
||||
Invoice Number: INV-2024-00123
|
||||
Date: 2024-01-15
|
||||
Due Date: 2024-02-15
|
||||
|
||||
Bill To:
|
||||
Acme Corporation
|
||||
John Smith, CFO
|
||||
john.smith@acme.com
|
||||
|
||||
ITEMS:
|
||||
1. Cloud Hosting - Pro Plan (x3 servers)
|
||||
Quantity: 3
|
||||
Unit Price: $299.00
|
||||
Total: $897.00
|
||||
|
||||
2. Database Storage - 500GB
|
||||
Quantity: 500
|
||||
Unit Price: $0.50
|
||||
Total: $250.00
|
||||
|
||||
3. API Calls - Premium Tier
|
||||
Quantity: 1,000,000
|
||||
Unit Price: $0.001
|
||||
Total: $1,000.00
|
||||
|
||||
4. Support - Enterprise Level
|
||||
Quantity: 1
|
||||
Unit Price: $500.00
|
||||
Total: $500.00
|
||||
|
||||
Subtotal: $2,647.00
|
||||
Tax (8.5%): $224.99
|
||||
TOTAL: $2,871.99
|
||||
|
||||
Payment Terms: Net 30
|
||||
Please remit payment to accounts@cloudprovider.com
|
||||
"""
|
||||
|
||||
print("=" * 70)
|
||||
print("Invoice Extraction Example")
|
||||
print("=" * 70)
|
||||
|
||||
invoice = extract_invoice(invoice_text)
|
||||
|
||||
if invoice:
|
||||
print(f"\n✅ Invoice Extracted Successfully\n")
|
||||
print(f"Invoice #: {invoice.invoice_number}")
|
||||
print(f"Customer: {invoice.customer_name} ({invoice.customer_email})")
|
||||
print(f"Date: {invoice.date}")
|
||||
print(f"Due: {invoice.due_date}")
|
||||
print(f"\nLine Items:")
|
||||
|
||||
for i, item in enumerate(invoice.line_items, 1):
|
||||
print(f" {i}. {item.description}")
|
||||
print(f" Qty: {item.quantity} × ${item.unit_price:.2f} = ${item.total:.2f}")
|
||||
|
||||
print(f"\nSubtotal: ${invoice.subtotal:.2f}")
|
||||
print(f"Tax ({invoice.tax_rate * 100:.1f}%): ${invoice.tax_amount:.2f}")
|
||||
print(f"TOTAL: ${invoice.total_amount:.2f}")
|
||||
|
||||
if invoice.notes:
|
||||
print(f"\nNotes: {invoice.notes}")
|
||||
|
||||
# Validation checks
|
||||
print(f"\n🔍 Validation:")
|
||||
calculated_subtotal = sum(item.total for item in invoice.line_items)
|
||||
print(f" Subtotal matches: {abs(calculated_subtotal - invoice.subtotal) < 0.01}")
|
||||
|
||||
calculated_tax = invoice.subtotal * invoice.tax_rate
|
||||
print(f" Tax calculation matches: {abs(calculated_tax - invoice.tax_amount) < 0.01}")
|
||||
|
||||
calculated_total = invoice.subtotal + invoice.tax_amount
|
||||
print(f" Total matches: {abs(calculated_total - invoice.total_amount) < 0.01}")
|
||||
|
||||
else:
|
||||
print("❌ Failed to extract invoice")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from typing import Optional
|
||||
main()
|
||||
0
skills/json-outputs-implementer/reference/.gitkeep
Normal file
0
skills/json-outputs-implementer/reference/.gitkeep
Normal file
@@ -0,0 +1,47 @@
|
||||
# JSON Schema Limitations Reference
|
||||
|
||||
## Supported Features
|
||||
|
||||
- ✅ All basic types (object, array, string, integer, number, boolean, null)
|
||||
- ✅ `enum` (primitives only)
|
||||
- ✅ `const`, `anyOf`, `allOf`
|
||||
- ✅ `$ref`, `$def`, `definitions` (local)
|
||||
- ✅ `required`, `additionalProperties: false`
|
||||
- ✅ String formats: date-time, time, date, email, uri, uuid, ipv4, ipv6
|
||||
- ✅ `minItems: 0` or `minItems: 1` for arrays
|
||||
|
||||
## NOT Supported
|
||||
|
||||
- ❌ Recursive schemas
|
||||
- ❌ Numerical constraints (minimum, maximum, multipleOf)
|
||||
- ❌ String constraints (minLength, maxLength, pattern with complex regex)
|
||||
- ❌ Array constraints (beyond minItems 0/1)
|
||||
- ❌ External `$ref`
|
||||
- ❌ Complex types in enums
|
||||
|
||||
## SDK Transformation
|
||||
|
||||
Python and TypeScript SDKs automatically remove unsupported constraints and add them to descriptions.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [ ] Schema designed with all required fields
|
||||
- [ ] JSON Schema limitations respected
|
||||
- [ ] SDK helper integrated (Pydantic/Zod)
|
||||
- [ ] Beta header included in requests
|
||||
- [ ] Error handling for refusals and token limits
|
||||
- [ ] Tested with representative examples
|
||||
- [ ] Edge cases covered (missing fields, invalid data)
|
||||
- [ ] Production optimization considered (caching, tokens)
|
||||
- [ ] Monitoring in place (latency, costs)
|
||||
- [ ] Documentation provided
|
||||
|
||||
## Important Reminders
|
||||
|
||||
1. **Use SDK helpers** - `client.beta.messages.parse()` auto-validates
|
||||
2. **Respect limitations** - No recursive schemas, no min/max constraints
|
||||
3. **Add descriptions** - Helps Claude understand what to extract
|
||||
4. **Handle refusals** - Don't retry safety refusals
|
||||
5. **Monitor performance** - Watch for cache misses and high latency
|
||||
6. **Set `additionalProperties: false`** - Required for all objects
|
||||
7. **Test thoroughly** - Edge cases often reveal schema issues
|
||||
86
skills/json-outputs-implementer/reference/use-cases.md
Normal file
86
skills/json-outputs-implementer/reference/use-cases.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# Common Use Cases
|
||||
|
||||
## Use Case 1: Data Extraction
|
||||
|
||||
**Scenario**: Extract invoice data from text/images
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
from typing import List
|
||||
|
||||
class LineItem(BaseModel):
|
||||
description: str
|
||||
quantity: int
|
||||
unit_price: float
|
||||
total: float
|
||||
|
||||
class Invoice(BaseModel):
|
||||
invoice_number: str
|
||||
date: str
|
||||
customer_name: str
|
||||
line_items: List[LineItem]
|
||||
subtotal: float
|
||||
tax: float
|
||||
total_amount: float
|
||||
|
||||
response = client.beta.messages.parse(
|
||||
model="claude-sonnet-4-5",
|
||||
betas=["structured-outputs-2025-11-13"],
|
||||
max_tokens=2048,
|
||||
messages=[{"role": "user", "content": f"Extract invoice:\n{invoice_text}"}],
|
||||
output_format=Invoice,
|
||||
)
|
||||
|
||||
invoice = response.parsed_output
|
||||
# Insert into database with guaranteed types
|
||||
db.insert_invoice(invoice.model_dump())
|
||||
```
|
||||
|
||||
## Use Case 2: Classification
|
||||
|
||||
**Scenario**: Classify support tickets
|
||||
|
||||
```python
|
||||
class TicketClassification(BaseModel):
|
||||
category: str # "billing", "technical", "sales"
|
||||
priority: str # "low", "medium", "high", "critical"
|
||||
confidence: float
|
||||
requires_human: bool
|
||||
suggested_assignee: Optional[str] = None
|
||||
tags: List[str]
|
||||
|
||||
response = client.beta.messages.parse(
|
||||
model="claude-sonnet-4-5",
|
||||
betas=["structured-outputs-2025-11-13"],
|
||||
messages=[{"role": "user", "content": f"Classify:\n{ticket}"}],
|
||||
output_format=TicketClassification,
|
||||
)
|
||||
|
||||
classification = response.parsed_output
|
||||
if classification.requires_human or classification.confidence < 0.7:
|
||||
route_to_human(ticket)
|
||||
else:
|
||||
auto_assign(ticket, classification.category)
|
||||
```
|
||||
|
||||
## Use Case 3: API Response Formatting
|
||||
|
||||
**Scenario**: Generate API-ready responses
|
||||
|
||||
```python
|
||||
class APIResponse(BaseModel):
|
||||
status: str # "success" or "error"
|
||||
data: dict
|
||||
errors: Optional[List[dict]] = None
|
||||
metadata: dict
|
||||
|
||||
response = client.beta.messages.parse(
|
||||
model="claude-sonnet-4-5",
|
||||
betas=["structured-outputs-2025-11-13"],
|
||||
messages=[{"role": "user", "content": f"Process: {request}"}],
|
||||
output_format=APIResponse,
|
||||
)
|
||||
|
||||
# Directly return as JSON API response
|
||||
return jsonify(response.parsed_output.model_dump())
|
||||
```
|
||||
@@ -0,0 +1,92 @@
|
||||
# Phase 1: Schema Design
|
||||
|
||||
**Objective**: Create a production-ready JSON schema respecting all limitations
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Define Output Structure
|
||||
|
||||
Ask the user:
|
||||
- "What fields do you need in the output?"
|
||||
- "Which fields are required vs. optional?"
|
||||
- "What are the data types for each field?"
|
||||
- "Are there nested objects or arrays?"
|
||||
|
||||
### 2. Choose Schema Approach
|
||||
|
||||
**Option A: Pydantic (Python) - Recommended**
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional
|
||||
|
||||
class ContactInfo(BaseModel):
|
||||
name: str
|
||||
email: str
|
||||
plan_interest: Optional[str] = None
|
||||
demo_requested: bool = False
|
||||
tags: List[str] = []
|
||||
```
|
||||
|
||||
**Option B: Zod (TypeScript) - Recommended**
|
||||
```typescript
|
||||
import { z } from 'zod';
|
||||
|
||||
const ContactInfoSchema = z.object({
|
||||
name: z.string(),
|
||||
email: z.string().email(),
|
||||
plan_interest: z.string().optional(),
|
||||
demo_requested: z.boolean().default(false),
|
||||
tags: z.array(z.string()).default([]),
|
||||
});
|
||||
```
|
||||
|
||||
**Option C: Raw JSON Schema**
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string", "description": "Full name"},
|
||||
"email": {"type": "string", "description": "Email address"},
|
||||
"plan_interest": {"type": "string", "description": "Interested plan"},
|
||||
"demo_requested": {"type": "boolean"},
|
||||
"tags": {"type": "array", "items": {"type": "string"}}
|
||||
},
|
||||
"required": ["name", "email", "demo_requested"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Apply JSON Schema Limitations
|
||||
|
||||
**✅ Supported Features:**
|
||||
- All basic types: object, array, string, integer, number, boolean, null
|
||||
- `enum` (strings, numbers, bools, nulls only)
|
||||
- `const`
|
||||
- `anyOf` and `allOf` (limited)
|
||||
- `$ref`, `$def`, `definitions` (local only)
|
||||
- `required` and `additionalProperties: false`
|
||||
- String formats: date-time, time, date, email, uri, uuid, ipv4, ipv6
|
||||
- Array `minItems` (0 or 1 only)
|
||||
|
||||
**❌ NOT Supported (SDK can transform these):**
|
||||
- Recursive schemas
|
||||
- Numerical constraints (minimum, maximum)
|
||||
- String constraints (minLength, maxLength)
|
||||
- Complex array constraints
|
||||
- External `$ref`
|
||||
|
||||
### 4. Add AI-Friendly Descriptions
|
||||
|
||||
```python
|
||||
class Invoice(BaseModel):
|
||||
invoice_number: str # Field(description="Invoice ID, format: INV-XXXXX")
|
||||
date: str # Field(description="Invoice date in YYYY-MM-DD format")
|
||||
total: float # Field(description="Total amount in USD")
|
||||
items: List[LineItem] # Field(description="Line items on the invoice")
|
||||
```
|
||||
|
||||
Good descriptions help Claude understand what to extract.
|
||||
|
||||
## Output
|
||||
|
||||
Production-ready schema following Anthropic's limitations.
|
||||
@@ -0,0 +1,100 @@
|
||||
# Phase 2: SDK Integration
|
||||
|
||||
**Objective**: Implement using SDK helpers for automatic validation
|
||||
|
||||
## Python Implementation
|
||||
|
||||
**Recommended: Use `client.beta.messages.parse()`**
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import List, Optional
|
||||
from anthropic import Anthropic
|
||||
|
||||
class ContactInfo(BaseModel):
|
||||
name: str = Field(description="Full name of the contact")
|
||||
email: str = Field(description="Email address")
|
||||
plan_interest: Optional[str] = Field(
|
||||
None, description="Plan tier they're interested in"
|
||||
)
|
||||
demo_requested: bool = Field(
|
||||
False, description="Whether they requested a demo"
|
||||
)
|
||||
|
||||
client = Anthropic()
|
||||
|
||||
def extract_contact(text: str) -> ContactInfo:
|
||||
"""Extract contact information from text."""
|
||||
response = client.beta.messages.parse(
|
||||
model="claude-sonnet-4-5",
|
||||
max_tokens=1024,
|
||||
betas=["structured-outputs-2025-11-13"],
|
||||
messages=[{
|
||||
"role": "user",
|
||||
"content": f"Extract contact information from: {text}"
|
||||
}],
|
||||
output_format=ContactInfo,
|
||||
)
|
||||
|
||||
# Handle edge cases
|
||||
if response.stop_reason == "refusal":
|
||||
raise ValueError("Claude refused the request")
|
||||
|
||||
if response.stop_reason == "max_tokens":
|
||||
raise ValueError("Response truncated - increase max_tokens")
|
||||
|
||||
# Automatically validated
|
||||
return response.parsed_output
|
||||
|
||||
# Usage
|
||||
contact = extract_contact("John Smith (john@example.com) wants Enterprise plan")
|
||||
print(contact.name, contact.email) # Type-safe access
|
||||
```
|
||||
|
||||
## TypeScript Implementation
|
||||
|
||||
```typescript
|
||||
import Anthropic from '@anthropic-ai/sdk';
|
||||
import { z } from 'zod';
|
||||
import { betaZodOutputFormat } from '@anthropic-ai/sdk/helpers/beta/zod';
|
||||
|
||||
const ContactInfoSchema = z.object({
|
||||
name: z.string().describe("Full name of the contact"),
|
||||
email: z.string().email().describe("Email address"),
|
||||
plan_interest: z.string().optional().describe("Plan tier interested in"),
|
||||
demo_requested: z.boolean().default(false).describe("Demo requested"),
|
||||
});
|
||||
|
||||
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
|
||||
|
||||
async function extractContact(text: string) {
|
||||
const response = await client.beta.messages.parse({
|
||||
model: "claude-sonnet-4-5",
|
||||
max_tokens: 1024,
|
||||
betas: ["structured-outputs-2025-11-13"],
|
||||
messages: [{
|
||||
role: "user",
|
||||
content: `Extract contact information from: ${text}`
|
||||
}],
|
||||
output_format: betaZodOutputFormat(ContactInfoSchema),
|
||||
});
|
||||
|
||||
if (response.stop_reason === "refusal") {
|
||||
throw new Error("Claude refused the request");
|
||||
}
|
||||
|
||||
if (response.stop_reason === "max_tokens") {
|
||||
throw new Error("Response truncated - increase max_tokens");
|
||||
}
|
||||
|
||||
return response.parsed_output;
|
||||
}
|
||||
|
||||
// Usage
|
||||
const contact = await extractContact("John Smith (john@example.com)...");
|
||||
console.log(contact.name, contact.email); // Fully typed
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
Working implementation with SDK validation.
|
||||
@@ -0,0 +1,53 @@
|
||||
# Phase 3: Error Handling
|
||||
|
||||
**Objective**: Handle refusals, token limits, and validation errors
|
||||
|
||||
## Key Error Scenarios
|
||||
|
||||
### 1. Safety Refusals (`stop_reason: "refusal"`)
|
||||
|
||||
```python
|
||||
if response.stop_reason == "refusal":
|
||||
logger.warning(f"Request refused: {input_text}")
|
||||
# Don't retry - respect safety boundaries
|
||||
return None # or raise exception
|
||||
```
|
||||
|
||||
### 2. Token Limit Reached (`stop_reason: "max_tokens"`)
|
||||
|
||||
```python
|
||||
if response.stop_reason == "max_tokens":
|
||||
# Retry with higher limit
|
||||
return extract_with_higher_limit(text, max_tokens * 1.5)
|
||||
```
|
||||
|
||||
### 3. Schema Validation Errors (SDK raises exception)
|
||||
|
||||
```python
|
||||
from pydantic import ValidationError
|
||||
|
||||
try:
|
||||
result = response.parsed_output
|
||||
except ValidationError as e:
|
||||
logger.error(f"Schema validation failed: {e}")
|
||||
# Should be rare - indicates schema mismatch
|
||||
raise
|
||||
```
|
||||
|
||||
### 4. API Errors (400 - schema too complex)
|
||||
|
||||
```python
|
||||
from anthropic import BadRequestError
|
||||
|
||||
try:
|
||||
response = client.beta.messages.parse(...)
|
||||
except BadRequestError as e:
|
||||
if "too complex" in str(e).lower():
|
||||
# Simplify schema
|
||||
logger.error("Schema too complex, simplifying...")
|
||||
raise
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
Robust error handling for production deployments.
|
||||
56
skills/json-outputs-implementer/workflow/phase-4-testing.md
Normal file
56
skills/json-outputs-implementer/workflow/phase-4-testing.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Phase 4: Testing
|
||||
|
||||
**Objective**: Validate schema works with representative data
|
||||
|
||||
## Test Coverage
|
||||
|
||||
```python
|
||||
import pytest
|
||||
|
||||
@pytest.fixture
|
||||
def extractor():
|
||||
return ContactExtractor()
|
||||
|
||||
def test_complete_contact(extractor):
|
||||
"""Test with all fields present."""
|
||||
text = "John Smith (john@example.com) interested in Enterprise plan, wants demo"
|
||||
result = extractor.extract(text)
|
||||
|
||||
assert result.name == "John Smith"
|
||||
assert result.email == "john@example.com"
|
||||
assert result.plan_interest == "Enterprise"
|
||||
assert result.demo_requested is True
|
||||
|
||||
def test_minimal_contact(extractor):
|
||||
"""Test with only required fields."""
|
||||
text = "Contact: jane@example.com"
|
||||
result = extractor.extract(text)
|
||||
|
||||
assert result.email == "jane@example.com"
|
||||
assert result.name is not None # Claude should infer or extract
|
||||
assert result.plan_interest is None # Optional field
|
||||
assert result.demo_requested is False # Default
|
||||
|
||||
def test_invalid_input(extractor):
|
||||
"""Test with insufficient data."""
|
||||
text = "This has no contact information"
|
||||
# Depending on requirements, might raise or return partial data
|
||||
result = extractor.extract(text)
|
||||
# Define expected behavior
|
||||
|
||||
def test_refusal_scenario(extractor):
|
||||
"""Test that refusals are handled."""
|
||||
# Test with potentially unsafe content
|
||||
# Verify graceful handling without crash
|
||||
pass
|
||||
|
||||
def test_token_limit(extractor):
|
||||
"""Test with very long input."""
|
||||
text = "..." * 10000 # Very long text
|
||||
# Verify either succeeds or raises appropriate error
|
||||
pass
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
Comprehensive test suite covering edge cases.
|
||||
@@ -0,0 +1,78 @@
|
||||
# Phase 5: Production Optimization
|
||||
|
||||
**Objective**: Optimize for performance, cost, and reliability
|
||||
|
||||
## 1. Grammar Caching Strategy
|
||||
|
||||
The first request compiles a grammar from your schema (~extra latency). Subsequent requests use cached grammar (24-hour TTL).
|
||||
|
||||
**Cache Invalidation Triggers:**
|
||||
- Schema structure changes
|
||||
- Tool set changes (if using tools + JSON outputs together)
|
||||
- 24 hours of non-use
|
||||
|
||||
**Best Practices:**
|
||||
```python
|
||||
# ✅ Good: Finalize schema before production
|
||||
CONTACT_SCHEMA = ContactInfo # Reuse same schema
|
||||
|
||||
# ❌ Bad: Dynamic schema generation
|
||||
def get_schema(include_phone: bool): # Different schemas = cache misses
|
||||
if include_phone:
|
||||
class Contact(BaseModel):
|
||||
phone: str
|
||||
...
|
||||
...
|
||||
```
|
||||
|
||||
## 2. Token Cost Management
|
||||
|
||||
Structured outputs add tokens via system prompt:
|
||||
```python
|
||||
# Monitor token usage
|
||||
response = client.beta.messages.parse(...)
|
||||
print(f"Input tokens: {response.usage.input_tokens}")
|
||||
print(f"Output tokens: {response.usage.output_tokens}")
|
||||
|
||||
# Optimize descriptions for token efficiency
|
||||
# ✅ Good: Concise but clear
|
||||
name: str = Field(description="Full name")
|
||||
|
||||
# ❌ Excessive: Too verbose
|
||||
name: str = Field(description="The complete full name of the person including first name, middle name if available, and last name")
|
||||
```
|
||||
|
||||
## 3. Monitoring
|
||||
|
||||
```python
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class StructuredOutputMetrics:
|
||||
latency_ms: float
|
||||
input_tokens: int
|
||||
output_tokens: int
|
||||
cache_hit: bool # Infer from latency
|
||||
stop_reason: str
|
||||
|
||||
def track_metrics(response, start_time) -> StructuredOutputMetrics:
|
||||
latency = (time.time() - start_time) * 1000
|
||||
|
||||
return StructuredOutputMetrics(
|
||||
latency_ms=latency,
|
||||
input_tokens=response.usage.input_tokens,
|
||||
output_tokens=response.usage.output_tokens,
|
||||
cache_hit=latency < 500, # Heuristic: fast = cache hit
|
||||
stop_reason=response.stop_reason,
|
||||
)
|
||||
|
||||
# Track in production
|
||||
metrics = track_metrics(response, start_time)
|
||||
if metrics.latency_ms > 1000:
|
||||
logger.warning(f"Slow structured output: {metrics.latency_ms}ms")
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
Production-optimized implementation with caching and monitoring.
|
||||
16
skills/strict-tool-implementer/CHANGELOG.md
Normal file
16
skills/strict-tool-implementer/CHANGELOG.md
Normal file
@@ -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)
|
||||
81
skills/strict-tool-implementer/README.md
Normal file
81
skills/strict-tool-implementer/README.md
Normal file
@@ -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.
|
||||
92
skills/strict-tool-implementer/SKILL.md
Normal file
92
skills/strict-tool-implementer/SKILL.md
Normal file
@@ -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
|
||||
289
skills/strict-tool-implementer/examples/travel-booking-agent.py
Normal file
289
skills/strict-tool-implementer/examples/travel-booking-agent.py
Normal file
@@ -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
skills/strict-tool-implementer/reference/.gitkeep
Normal file
0
skills/strict-tool-implementer/reference/.gitkeep
Normal file
48
skills/strict-tool-implementer/reference/common-patterns.md
Normal file
48
skills/strict-tool-implementer/reference/common-patterns.md
Normal file
@@ -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
|
||||
23
skills/strict-tool-implementer/reference/success-criteria.md
Normal file
23
skills/strict-tool-implementer/reference/success-criteria.md
Normal file
@@ -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
|
||||
125
skills/strict-tool-implementer/workflow/phase-1-schema-design.md
Normal file
125
skills/strict-tool-implementer/workflow/phase-1-schema-design.md
Normal file
@@ -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.
|
||||
81
skills/strict-tool-implementer/workflow/phase-4-testing.md
Normal file
81
skills/strict-tool-implementer/workflow/phase-4-testing.md
Normal file
@@ -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.
|
||||
16
skills/structured-outputs-advisor/CHANGELOG.md
Normal file
16
skills/structured-outputs-advisor/CHANGELOG.md
Normal file
@@ -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 Structured Outputs Advisor skill
|
||||
- Requirements gathering workflow
|
||||
- Mode selection logic (JSON outputs vs strict tool use)
|
||||
- Decision matrix for common scenarios
|
||||
- Delegation patterns to specialized skills
|
||||
- Mode selection examples covering 8 common scenarios
|
||||
59
skills/structured-outputs-advisor/README.md
Normal file
59
skills/structured-outputs-advisor/README.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Structured Outputs Advisor
|
||||
|
||||
Expert advisor skill for choosing between JSON outputs and strict tool use modes in Anthropic's structured outputs feature.
|
||||
|
||||
## Purpose
|
||||
|
||||
This skill serves as the **entry point** for implementing structured outputs. It analyzes your requirements and recommends the right mode:
|
||||
|
||||
- **JSON Outputs** (`output_format`) - For data extraction, classification, API formatting
|
||||
- **Strict Tool Use** (`strict: true`) - For agentic workflows, validated tool parameters
|
||||
|
||||
Then delegates to specialized implementation skills.
|
||||
|
||||
## When to Use
|
||||
|
||||
Invoke this skill when you need:
|
||||
- Guaranteed JSON schema compliance
|
||||
- Validated tool input parameters
|
||||
- Structured data extraction
|
||||
- Type-safe API responses
|
||||
- Reliable agentic workflows
|
||||
|
||||
## Quick Start
|
||||
|
||||
**Trigger phrases:**
|
||||
- "implement structured outputs"
|
||||
- "need guaranteed JSON schema"
|
||||
- "extract structured data from..."
|
||||
- "build reliable agent with validated tools"
|
||||
|
||||
The advisor will ask questions to understand your use case and recommend the appropriate mode.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Requirements gathering** - Understand what you're building
|
||||
2. **Mode selection** - JSON outputs vs strict tool use
|
||||
3. **Delegation** - Hand off to specialized skill for implementation
|
||||
|
||||
## Related Skills
|
||||
|
||||
- [`json-outputs-implementer`](../json-outputs-implementer/) - Implements JSON outputs mode
|
||||
- [`strict-tool-implementer`](../strict-tool-implementer/) - Implements strict tool use mode
|
||||
|
||||
## Examples
|
||||
|
||||
See [mode-selection-examples.md](./examples/mode-selection-examples.md) for detailed scenarios.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Official Structured Outputs Docs](https://docs.anthropic.com/en/docs/build-with-claude/structured-outputs)
|
||||
- [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.
|
||||
283
skills/structured-outputs-advisor/SKILL.md
Normal file
283
skills/structured-outputs-advisor/SKILL.md
Normal file
@@ -0,0 +1,283 @@
|
||||
---
|
||||
name: structured-outputs-advisor
|
||||
description: Use PROACTIVELY when users need guaranteed schema compliance or validated tool inputs from Anthropic's structured outputs feature. Expert advisor for choosing between JSON outputs (data extraction/formatting) and strict tool use (agentic workflows). Analyzes requirements, explains trade-offs, and delegates to specialized implementation skills. Not for simple text responses or unstructured outputs.
|
||||
---
|
||||
|
||||
# Structured Outputs Advisor
|
||||
|
||||
## Overview
|
||||
|
||||
This skill serves as the entry point for implementing Anthropic's structured outputs feature. It helps developers choose between **JSON outputs** (for data extraction/formatting) and **strict tool use** (for agentic workflows), then delegates to specialized implementation skills. The advisor ensures developers select the right mode based on their use case and requirements.
|
||||
|
||||
**Two Modes Available:**
|
||||
1. **JSON Outputs** (`output_format`) - Guaranteed JSON schema compliance for responses
|
||||
2. **Strict Tool Use** (`strict: true`) - Validated tool parameters for function calls
|
||||
|
||||
**Specialized Implementation Skills:**
|
||||
- `json-outputs-implementer` - For data extraction, classification, API formatting
|
||||
- `strict-tool-implementer` - For agentic workflows, validated function calls
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
**Trigger Phrases:**
|
||||
- "implement structured outputs"
|
||||
- "need guaranteed JSON schema"
|
||||
- "extract structured data from [source]"
|
||||
- "validate tool inputs"
|
||||
- "build reliable agentic workflow"
|
||||
- "ensure type-safe responses"
|
||||
- "help me with structured outputs"
|
||||
|
||||
**Use Cases:**
|
||||
- Data extraction from text/images
|
||||
- Classification with guaranteed output format
|
||||
- API response formatting
|
||||
- Agentic workflows with validated tools
|
||||
- Type-safe database operations
|
||||
- Complex tool parameter validation
|
||||
|
||||
## Response Style
|
||||
|
||||
- **Consultative**: Ask questions to understand requirements
|
||||
- **Educational**: Explain both modes and when to use each
|
||||
- **Decisive**: Recommend the right mode based on use case
|
||||
- **Delegating**: Hand off to specialized skills for implementation
|
||||
- **Concise**: Keep mode selection phase quick (<5 questions)
|
||||
|
||||
## Core Workflow
|
||||
|
||||
### Phase 1: Understand Requirements
|
||||
|
||||
**Questions to Ask:**
|
||||
|
||||
1. **What's your goal?**
|
||||
- "What kind of output do you need Claude to produce?"
|
||||
- Examples: Extract invoice data, validate function parameters, classify tickets
|
||||
|
||||
2. **What's the data source?**
|
||||
- Text, images, API calls, user input, etc.
|
||||
|
||||
3. **What consumes the output?**
|
||||
- Database, API endpoint, function call, agent workflow, etc.
|
||||
|
||||
4. **How critical is schema compliance?**
|
||||
- Must be guaranteed vs. generally reliable
|
||||
|
||||
### Phase 2: Mode Selection
|
||||
|
||||
**Use JSON Outputs (`output_format`) when:**
|
||||
- ✅ You need Claude's **response** in a specific format
|
||||
- ✅ Extracting structured data from unstructured sources
|
||||
- ✅ Generating reports, classifications, or API responses
|
||||
- ✅ Formatting output for downstream processing
|
||||
- ✅ Single-step operations
|
||||
|
||||
**Examples:**
|
||||
- Extract contact info from emails → CRM database
|
||||
- Classify support tickets → routing system
|
||||
- Generate structured reports → API endpoint
|
||||
- Parse invoices → accounting software
|
||||
|
||||
**Use Strict Tool Use (`strict: true`) when:**
|
||||
- ✅ You need validated **tool input parameters**
|
||||
- ✅ Building multi-step agentic workflows
|
||||
- ✅ Ensuring type-safe function calls
|
||||
- ✅ Complex tools with many/nested properties
|
||||
- ✅ Critical operations requiring guaranteed types
|
||||
|
||||
**Examples:**
|
||||
- Travel booking agent (flights + hotels + activities)
|
||||
- Database operations with strict type requirements
|
||||
- API orchestration with validated parameters
|
||||
- Complex workflow automation
|
||||
|
||||
### Phase 3: Delegation
|
||||
|
||||
**After determining the mode, delegate to the specialized skill:**
|
||||
|
||||
**For JSON Outputs:**
|
||||
```
|
||||
I recommend using JSON outputs for your [use case].
|
||||
|
||||
I'm going to invoke the json-outputs-implementer skill to help you:
|
||||
1. Design a production-ready JSON schema
|
||||
2. Implement with SDK helpers (Pydantic/Zod)
|
||||
3. Add validation and error handling
|
||||
4. Optimize for production
|
||||
|
||||
[Launch json-outputs-implementer skill]
|
||||
```
|
||||
|
||||
**For Strict Tool Use:**
|
||||
```
|
||||
I recommend using strict tool use for your [use case].
|
||||
|
||||
I'm going to invoke the strict-tool-implementer skill to help you:
|
||||
1. Design validated tool schemas
|
||||
2. Implement strict mode correctly
|
||||
3. Build reliable agent workflows
|
||||
4. Test and validate tool calls
|
||||
|
||||
[Launch strict-tool-implementer skill]
|
||||
```
|
||||
|
||||
**For Both Modes (Hybrid):**
|
||||
```
|
||||
Your use case requires both modes:
|
||||
- JSON outputs for [specific use case]
|
||||
- Strict tool use for [specific use case]
|
||||
|
||||
I'll help you implement both, starting with [primary mode].
|
||||
|
||||
[Launch appropriate skill first, then the second one]
|
||||
```
|
||||
|
||||
## Decision Matrix
|
||||
|
||||
| Requirement | JSON Outputs | Strict Tool Use |
|
||||
|-------------|--------------|-----------------|
|
||||
| Extract structured data | ✅ Primary use case | ❌ Not designed for this |
|
||||
| Validate function parameters | ❌ Not designed for this | ✅ Primary use case |
|
||||
| Multi-step agent workflows | ⚠️ Possible but not ideal | ✅ Designed for this |
|
||||
| API response formatting | ✅ Ideal | ❌ Unnecessary |
|
||||
| Database inserts (type safety) | ✅ Good fit | ⚠️ If via tool calls |
|
||||
| Complex nested schemas | ✅ Supports this | ✅ Supports this |
|
||||
| Classification tasks | ✅ Perfect fit | ❌ Overkill |
|
||||
| Tool composition/chaining | ❌ Not applicable | ✅ Excellent |
|
||||
|
||||
## Feature Availability
|
||||
|
||||
**Models Supported:**
|
||||
- ✅ Claude Sonnet 4.5 (`claude-sonnet-4-5`)
|
||||
- ✅ Claude Opus 4.1 (`claude-opus-4-1`)
|
||||
|
||||
**Beta Header Required:**
|
||||
```
|
||||
anthropic-beta: structured-outputs-2025-11-13
|
||||
```
|
||||
|
||||
**Incompatible Features:**
|
||||
- ❌ Citations (with JSON outputs)
|
||||
- ❌ Message Prefilling (with JSON outputs)
|
||||
|
||||
**Compatible Features:**
|
||||
- ✅ Batch Processing (50% discount)
|
||||
- ✅ Token Counting
|
||||
- ✅ Streaming
|
||||
- ✅ Both modes together in same request
|
||||
|
||||
## Common Scenarios
|
||||
|
||||
### Scenario 1: "I need to extract invoice data"
|
||||
**Analysis**: Data extraction from unstructured text
|
||||
**Mode**: JSON Outputs
|
||||
**Delegation**: `json-outputs-implementer`
|
||||
**Reason**: Single-step extraction with structured output format
|
||||
|
||||
### Scenario 2: "Building a travel booking agent"
|
||||
**Analysis**: Multi-tool workflow (flights, hotels, activities)
|
||||
**Mode**: Strict Tool Use
|
||||
**Delegation**: `strict-tool-implementer`
|
||||
**Reason**: Multiple validated tools in agent workflow
|
||||
|
||||
### Scenario 3: "Classify customer support tickets"
|
||||
**Analysis**: Classification with guaranteed categories
|
||||
**Mode**: JSON Outputs
|
||||
**Delegation**: `json-outputs-implementer`
|
||||
**Reason**: Single classification result, structured response
|
||||
|
||||
### Scenario 4: "Validate database insert parameters"
|
||||
**Analysis**: Type-safe database operations
|
||||
**Mode**: JSON Outputs (if direct) OR Strict Tool Use (if via tool)
|
||||
**Delegation**: Depends on architecture
|
||||
**Reason**: Both work - choose based on system architecture
|
||||
|
||||
### Scenario 5: "Generate API-ready responses"
|
||||
**Analysis**: Format responses for API consumption
|
||||
**Mode**: JSON Outputs
|
||||
**Delegation**: `json-outputs-implementer`
|
||||
**Reason**: Output formatting is primary goal
|
||||
|
||||
## Quick Start Examples
|
||||
|
||||
### JSON Outputs Example
|
||||
```python
|
||||
# Extract contact information
|
||||
from pydantic import BaseModel
|
||||
from anthropic import Anthropic
|
||||
|
||||
class Contact(BaseModel):
|
||||
name: str
|
||||
email: str
|
||||
plan: str
|
||||
|
||||
client = Anthropic()
|
||||
response = client.beta.messages.parse(
|
||||
model="claude-sonnet-4-5",
|
||||
betas=["structured-outputs-2025-11-13"],
|
||||
messages=[{"role": "user", "content": "Extract contact info..."}],
|
||||
output_format=Contact,
|
||||
)
|
||||
contact = response.parsed_output # Guaranteed schema match
|
||||
```
|
||||
|
||||
### Strict Tool Use Example
|
||||
```python
|
||||
# Validated tool for agent workflow
|
||||
response = client.beta.messages.create(
|
||||
model="claude-sonnet-4-5",
|
||||
betas=["structured-outputs-2025-11-13"],
|
||||
messages=[{"role": "user", "content": "Book a flight..."}],
|
||||
tools=[{
|
||||
"name": "book_flight",
|
||||
"strict": True, # Guarantees schema compliance
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"destination": {"type": "string"},
|
||||
"passengers": {"type": "integer"}
|
||||
},
|
||||
"required": ["destination"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
}]
|
||||
)
|
||||
# Tool inputs guaranteed to match schema
|
||||
```
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [ ] Requirements clearly understood
|
||||
- [ ] Data source identified
|
||||
- [ ] Output consumer identified
|
||||
- [ ] Correct mode selected (JSON outputs vs strict tool use)
|
||||
- [ ] Reasoning for mode selection explained
|
||||
- [ ] Appropriate specialized skill invoked
|
||||
- [ ] User understands next steps
|
||||
|
||||
## Important Reminders
|
||||
|
||||
1. **Ask before assuming** - Don't guess the mode, understand requirements first
|
||||
2. **One mode is usually enough** - Most use cases need only one mode
|
||||
3. **Delegate quickly** - Keep advisor phase short, let specialists handle implementation
|
||||
4. **Both modes work together** - Can use both in same request if needed
|
||||
5. **Model availability** - Confirm Sonnet 4.5 or Opus 4.1 is available
|
||||
6. **Beta feature** - Requires beta header in API requests
|
||||
|
||||
## Next Steps After Mode Selection
|
||||
|
||||
Once mode is selected and you've delegated to the specialized skill, that skill will handle:
|
||||
- ✅ Schema design (respecting JSON Schema limitations)
|
||||
- ✅ SDK integration (Pydantic/Zod helpers)
|
||||
- ✅ Implementation with error handling
|
||||
- ✅ Testing and validation
|
||||
- ✅ Production optimization
|
||||
- ✅ Complete examples and documentation
|
||||
|
||||
---
|
||||
|
||||
**Official Documentation**: https://docs.anthropic.com/en/docs/build-with-claude/structured-outputs
|
||||
|
||||
**Related Skills**:
|
||||
- `json-outputs-implementer` - Implement JSON outputs mode
|
||||
- `strict-tool-implementer` - Implement strict tool use mode
|
||||
@@ -0,0 +1,234 @@
|
||||
# Mode Selection Examples
|
||||
|
||||
Real-world scenarios showing how the advisor helps choose between JSON outputs and strict tool use.
|
||||
|
||||
## Example 1: Invoice Data Extraction
|
||||
|
||||
**User Request:**
|
||||
> "I need to extract invoice data from PDF documents and store it in our PostgreSQL database. The invoices contain line items, tax information, and customer details."
|
||||
|
||||
**Advisor Analysis:**
|
||||
- **Goal:** Extract structured data from documents
|
||||
- **Source:** PDF documents (unstructured)
|
||||
- **Consumer:** PostgreSQL database (needs type-safe inserts)
|
||||
- **Complexity:** Single-step extraction
|
||||
|
||||
**Recommended Mode:** JSON Outputs
|
||||
|
||||
**Reasoning:**
|
||||
- This is a data extraction task (primary use case for JSON outputs)
|
||||
- Single-step operation (extract → database)
|
||||
- No multi-step agent workflow needed
|
||||
- Schema compliance ensures database insert succeeds
|
||||
|
||||
**Next Step:**
|
||||
Delegate to `json-outputs-implementer` to design invoice schema with line items, tax calculations, and customer info fields.
|
||||
|
||||
---
|
||||
|
||||
## Example 2: Travel Booking Agent
|
||||
|
||||
**User Request:**
|
||||
> "Build an agent that can help users book complete travel itineraries. It should search for flights, compare options, book the chosen flight, find hotels near their destination, and book accommodation."
|
||||
|
||||
**Advisor Analysis:**
|
||||
- **Goal:** Multi-step booking workflow
|
||||
- **Source:** User conversation
|
||||
- **Consumer:** Multiple external APIs (flights, hotels, booking systems)
|
||||
- **Complexity:** Multi-tool agent workflow with sequential dependencies
|
||||
|
||||
**Recommended Mode:** Strict Tool Use
|
||||
|
||||
**Reasoning:**
|
||||
- Multi-step workflow (search → compare → book → search → book)
|
||||
- Multiple tools that need validated parameters
|
||||
- Tool composition (flight booking influences hotel search location)
|
||||
- Type-safe API calls are critical (booking with wrong parameters could charge cards incorrectly)
|
||||
|
||||
**Next Step:**
|
||||
Delegate to `strict-tool-implementer` to design tool schemas for `search_flights`, `book_flight`, `search_hotels`, `book_hotel` with strict parameter validation.
|
||||
|
||||
---
|
||||
|
||||
## Example 3: Support Ticket Classification
|
||||
|
||||
**User Request:**
|
||||
> "We receive thousands of support tickets daily. I need to automatically classify them by category (billing, technical, sales), priority level, and route them to the right team."
|
||||
|
||||
**Advisor Analysis:**
|
||||
- **Goal:** Classification with routing
|
||||
- **Source:** Support ticket text
|
||||
- **Consumer:** Routing system + metrics dashboard
|
||||
- **Complexity:** Single classification operation
|
||||
|
||||
**Recommended Mode:** JSON Outputs
|
||||
|
||||
**Reasoning:**
|
||||
- Classification task (perfect for JSON outputs)
|
||||
- Fixed output schema (category, priority, team, confidence)
|
||||
- Single-step operation
|
||||
- No tool execution needed (just classification output)
|
||||
|
||||
**Next Step:**
|
||||
Delegate to `json-outputs-implementer` to design classification schema with enums for category/priority, confidence scoring, and routing metadata.
|
||||
|
||||
---
|
||||
|
||||
## Example 4: Database Query Agent
|
||||
|
||||
**User Request:**
|
||||
> "I want an agent that can answer questions about our sales data. It should translate natural language questions into SQL, execute the queries safely, and return formatted results."
|
||||
|
||||
**Advisor Analysis:**
|
||||
- **Goal:** Natural language → SQL query execution
|
||||
- **Source:** User questions in natural language
|
||||
- **Consumer:** Database + user (formatted results)
|
||||
- **Complexity:** Tool execution with type-safe parameters + structured output
|
||||
|
||||
**Recommended Mode:** Both (Hybrid Approach)
|
||||
|
||||
**Reasoning:**
|
||||
- Tool use for SQL execution: Need `execute_sql` tool with validated query parameters (prevent SQL injection)
|
||||
- JSON outputs for response: Want structured results formatted consistently
|
||||
- Two distinct phases: query generation/execution → result formatting
|
||||
|
||||
**Next Step:**
|
||||
1. First: Delegate to `strict-tool-implementer` for `execute_sql` tool with strict validation
|
||||
2. Then: Delegate to `json-outputs-implementer` for result formatting schema
|
||||
|
||||
---
|
||||
|
||||
## Example 5: Resume Parser
|
||||
|
||||
**User Request:**
|
||||
> "Parse resumes in various formats (PDF, DOCX, plain text) and extract structured information: personal details, work experience, education, skills. Store in our ATS database."
|
||||
|
||||
**Advisor Analysis:**
|
||||
- **Goal:** Extract structured data from documents
|
||||
- **Source:** Resume documents (various formats)
|
||||
- **Consumer:** ATS (Applicant Tracking System) database
|
||||
- **Complexity:** Single extraction operation
|
||||
|
||||
**Recommended Mode:** JSON Outputs
|
||||
|
||||
**Reasoning:**
|
||||
- Data extraction from unstructured documents
|
||||
- Well-defined output schema (resume has standard sections)
|
||||
- No tool execution needed
|
||||
- Database insertion requires type-safe data
|
||||
|
||||
**Next Step:**
|
||||
Delegate to `json-outputs-implementer` to design resume schema with nested objects for work experience, education, and skills arrays.
|
||||
|
||||
---
|
||||
|
||||
## Example 6: API Response Formatter
|
||||
|
||||
**User Request:**
|
||||
> "Our API needs to return consistent JSON responses. Sometimes Claude generates the response data, and I need it formatted exactly to our API spec with status, data, errors, and metadata fields."
|
||||
|
||||
**Advisor Analysis:**
|
||||
- **Goal:** Format API responses consistently
|
||||
- **Source:** Claude-generated content
|
||||
- **Consumer:** API clients (web/mobile apps)
|
||||
- **Complexity:** Response formatting
|
||||
|
||||
**Recommended Mode:** JSON Outputs
|
||||
|
||||
**Reasoning:**
|
||||
- Response formatting task
|
||||
- Fixed API schema that must be followed exactly
|
||||
- No tool execution
|
||||
- Consistency is critical for API clients
|
||||
|
||||
**Next Step:**
|
||||
Delegate to `json-outputs-implementer` to design API response schema matching the spec, with proper error handling structure.
|
||||
|
||||
---
|
||||
|
||||
## Example 7: Research Assistant Agent
|
||||
|
||||
**User Request:**
|
||||
> "Build an agent that researches topics by searching the web, reading articles, extracting key facts, cross-referencing sources, and generating a comprehensive research report."
|
||||
|
||||
**Advisor Analysis:**
|
||||
- **Goal:** Multi-step research workflow
|
||||
- **Source:** Web (via search tools, article fetchers)
|
||||
- **Consumer:** User (research report)
|
||||
- **Complexity:** Multi-tool workflow with sequential and parallel steps + structured output
|
||||
|
||||
**Recommended Mode:** Both (Hybrid Approach)
|
||||
|
||||
**Reasoning:**
|
||||
- Research phase: Need tools (`search_web`, `fetch_article`, `extract_facts`) with strict validation
|
||||
- Report phase: Need structured report output (JSON outputs)
|
||||
- Complex workflow with multiple stages
|
||||
|
||||
**Next Step:**
|
||||
1. First: Delegate to `strict-tool-implementer` for research tools
|
||||
2. Then: Delegate to `json-outputs-implementer` for final report schema
|
||||
|
||||
---
|
||||
|
||||
## Example 8: Form Data Extraction
|
||||
|
||||
**User Request:**
|
||||
> "Users upload scanned forms (insurance claims, applications, etc.). Extract all form fields into a structured format for processing."
|
||||
|
||||
**Advisor Analysis:**
|
||||
- **Goal:** Extract form data
|
||||
- **Source:** Scanned form images
|
||||
- **Consumer:** Processing system
|
||||
- **Complexity:** Single extraction
|
||||
|
||||
**Recommended Mode:** JSON Outputs
|
||||
|
||||
**Reasoning:**
|
||||
- Image data extraction
|
||||
- Form has known structure (predefined fields)
|
||||
- No tool execution
|
||||
- Type-safe data needed for downstream processing
|
||||
|
||||
**Next Step:**
|
||||
Delegate to `json-outputs-implementer` to design form schema matching the expected fields with proper types.
|
||||
|
||||
---
|
||||
|
||||
## Decision Patterns Summary
|
||||
|
||||
| Scenario Type | Recommended Mode | Key Indicator |
|
||||
|---------------|------------------|---------------|
|
||||
| Data extraction | JSON Outputs | "Extract X from Y" |
|
||||
| Classification | JSON Outputs | "Classify/categorize X" |
|
||||
| API formatting | JSON Outputs | "Format response as X" |
|
||||
| Report generation | JSON Outputs | "Generate report with X structure" |
|
||||
| Multi-tool workflow | Strict Tool Use | "Search, then book, then..." |
|
||||
| Agent with tools | Strict Tool Use | "Agent that can call X, Y, Z" |
|
||||
| Type-safe function calls | Strict Tool Use | "Validate parameters for X" |
|
||||
| Complex agents | Both | "Research then report" / "Query then format" |
|
||||
|
||||
---
|
||||
|
||||
## Common Misconceptions
|
||||
|
||||
### ❌ "I need reliable JSON, so I should use strict tool use"
|
||||
|
||||
**Correction:** Use JSON outputs for reliable JSON responses. Strict tool use is for tool **parameters**, not Claude's response format.
|
||||
|
||||
### ❌ "My agent just needs one tool, so I should use JSON outputs"
|
||||
|
||||
**Correction:** Even a single-tool agent benefits from strict tool use if the tool needs parameter validation. Mode choice is about **what** you're validating, not **how many** tools.
|
||||
|
||||
### ❌ "I can use both modes for the same thing"
|
||||
|
||||
**Correction:** Each mode has a specific purpose:
|
||||
- JSON outputs: Claude's response format
|
||||
- Strict tool use: Tool input validation
|
||||
|
||||
They solve different problems and can be combined when you need both.
|
||||
|
||||
---
|
||||
|
||||
**See Also:**
|
||||
- [JSON Outputs Implementer Examples](../../json-outputs-implementer/examples/)
|
||||
- [Strict Tool Implementer Examples](../../strict-tool-implementer/examples/)
|
||||
Reference in New Issue
Block a user