From 3d2cb201f0dece1ca7047cf246392bfdfe9c8f06 Mon Sep 17 00:00:00 2001 From: Zhongwei Li Date: Sat, 29 Nov 2025 18:16:46 +0800 Subject: [PATCH] Initial commit --- .claude-plugin/plugin.json | 14 + README.md | 3 + plugin.lock.json | 160 ++++++++++ skills/json-outputs-implementer/CHANGELOG.md | 16 + skills/json-outputs-implementer/README.md | 90 ++++++ skills/json-outputs-implementer/SKILL.md | 93 ++++++ .../examples/contact-extraction.py | 138 +++++++++ .../examples/invoice-extraction.py | 160 ++++++++++ .../reference/.gitkeep | 0 .../reference/schema-limitations.md | 47 +++ .../reference/use-cases.md | 86 ++++++ .../workflow/phase-1-schema-design.md | 92 ++++++ .../workflow/phase-2-sdk-integration.md | 100 ++++++ .../workflow/phase-3-error-handling.md | 53 ++++ .../workflow/phase-4-testing.md | 56 ++++ .../workflow/phase-5-production.md | 78 +++++ skills/strict-tool-implementer/CHANGELOG.md | 16 + skills/strict-tool-implementer/README.md | 81 +++++ skills/strict-tool-implementer/SKILL.md | 92 ++++++ .../examples/travel-booking-agent.py | 289 ++++++++++++++++++ .../reference/.gitkeep | 0 .../reference/common-patterns.md | 48 +++ .../reference/success-criteria.md | 23 ++ .../workflow/phase-1-schema-design.md | 125 ++++++++ .../workflow/phase-2-implementation.md | 222 ++++++++++++++ .../workflow/phase-3-error-handling.md | 58 ++++ .../workflow/phase-4-testing.md | 81 +++++ .../workflow/phase-5-production.md | 98 ++++++ .../structured-outputs-advisor/CHANGELOG.md | 16 + skills/structured-outputs-advisor/README.md | 59 ++++ skills/structured-outputs-advisor/SKILL.md | 283 +++++++++++++++++ .../examples/mode-selection-examples.md | 234 ++++++++++++++ .../reference/.gitkeep | 0 33 files changed, 2911 insertions(+) create mode 100644 .claude-plugin/plugin.json create mode 100644 README.md create mode 100644 plugin.lock.json create mode 100644 skills/json-outputs-implementer/CHANGELOG.md create mode 100644 skills/json-outputs-implementer/README.md create mode 100644 skills/json-outputs-implementer/SKILL.md create mode 100644 skills/json-outputs-implementer/examples/contact-extraction.py create mode 100644 skills/json-outputs-implementer/examples/invoice-extraction.py create mode 100644 skills/json-outputs-implementer/reference/.gitkeep create mode 100644 skills/json-outputs-implementer/reference/schema-limitations.md create mode 100644 skills/json-outputs-implementer/reference/use-cases.md create mode 100644 skills/json-outputs-implementer/workflow/phase-1-schema-design.md create mode 100644 skills/json-outputs-implementer/workflow/phase-2-sdk-integration.md create mode 100644 skills/json-outputs-implementer/workflow/phase-3-error-handling.md create mode 100644 skills/json-outputs-implementer/workflow/phase-4-testing.md create mode 100644 skills/json-outputs-implementer/workflow/phase-5-production.md create mode 100644 skills/strict-tool-implementer/CHANGELOG.md create mode 100644 skills/strict-tool-implementer/README.md create mode 100644 skills/strict-tool-implementer/SKILL.md create mode 100644 skills/strict-tool-implementer/examples/travel-booking-agent.py create mode 100644 skills/strict-tool-implementer/reference/.gitkeep create mode 100644 skills/strict-tool-implementer/reference/common-patterns.md create mode 100644 skills/strict-tool-implementer/reference/success-criteria.md create mode 100644 skills/strict-tool-implementer/workflow/phase-1-schema-design.md create mode 100644 skills/strict-tool-implementer/workflow/phase-2-implementation.md create mode 100644 skills/strict-tool-implementer/workflow/phase-3-error-handling.md create mode 100644 skills/strict-tool-implementer/workflow/phase-4-testing.md create mode 100644 skills/strict-tool-implementer/workflow/phase-5-production.md create mode 100644 skills/structured-outputs-advisor/CHANGELOG.md create mode 100644 skills/structured-outputs-advisor/README.md create mode 100644 skills/structured-outputs-advisor/SKILL.md create mode 100644 skills/structured-outputs-advisor/examples/mode-selection-examples.md create mode 100644 skills/structured-outputs-advisor/reference/.gitkeep diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..10df690 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,14 @@ +{ + "name": "api-tools", + "description": "Tools for leveraging Anthropic API features like structured outputs", + "version": "0.0.0-2025.11.28", + "author": { + "name": "Connor", + "email": "noreply@claudex.dev" + }, + "skills": [ + "./skills/structured-outputs-advisor", + "./skills/json-outputs-implementer", + "./skills/strict-tool-implementer" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b7c1098 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# api-tools + +Tools for leveraging Anthropic API features like structured outputs diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..0dc01d9 --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,160 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:cskiro/claudex:api-tools", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "32a4af94f0baa6026847245149f66c1391f87b4e", + "treeHash": "6f7e48c29523e2b49125ed742e7d35d377d96024f74d43f3e60bc3d9dbc218a9", + "generatedAt": "2025-11-28T10:15:48.350047Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "api-tools", + "description": "Tools for leveraging Anthropic API features like structured outputs" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "78cfc39342cb9dfc3064f3345c6e3b27f9e13307b9ed932de049f1e3ef11e371" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "54b255b37a844cebf4d83019eaec594b5aa6e566adef43be9ddcd4c91ca473c0" + }, + { + "path": "skills/json-outputs-implementer/CHANGELOG.md", + "sha256": "86c6eb2979a4e46e962f58b15a3e826e8d97e9bb363cc54c6074ec99feb602f0" + }, + { + "path": "skills/json-outputs-implementer/README.md", + "sha256": "935fa93d89d903442908af54745a222860a705fe544aad27cb5d491e21f8daed" + }, + { + "path": "skills/json-outputs-implementer/SKILL.md", + "sha256": "5a2a9f5faeab4caac92fc1fb6db4b5a644709e9ac87413198affc1265650fc22" + }, + { + "path": "skills/json-outputs-implementer/examples/invoice-extraction.py", + "sha256": "9df74a95f69296053017d5937ddc71ad4dbd6ca00aed6a97e2c3a8813f8abd53" + }, + { + "path": "skills/json-outputs-implementer/examples/contact-extraction.py", + "sha256": "cf920c2efadbd182d390a2df2d115df8c2757d26b7733bc6d56b2d4726c9a3a2" + }, + { + "path": "skills/json-outputs-implementer/workflow/phase-1-schema-design.md", + "sha256": "a9b8f67cfbedb9ae7a01e3fb1a39edafb41ac19964d3403b8b4c578b6072057f" + }, + { + "path": "skills/json-outputs-implementer/workflow/phase-2-sdk-integration.md", + "sha256": "6e7ec79a9243a798fc2f257bbfaa3ddcb8fa4b17462feffc3f2e547c1bcf3aba" + }, + { + "path": "skills/json-outputs-implementer/workflow/phase-3-error-handling.md", + "sha256": "245086dfd11f373de5518e2b5adc85b821873fabddbcb67e9bc840dad6a440a9" + }, + { + "path": "skills/json-outputs-implementer/workflow/phase-5-production.md", + "sha256": "3309d9209908d42a7a132e5318e16a1f545adb5a5e96aa275a13599107a8bc27" + }, + { + "path": "skills/json-outputs-implementer/workflow/phase-4-testing.md", + "sha256": "e400fdb32a2195068d4307fa44bad257f2e3ce74aaeb61b3dc02d971e94227a8" + }, + { + "path": "skills/json-outputs-implementer/reference/.gitkeep", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "skills/json-outputs-implementer/reference/schema-limitations.md", + "sha256": "4083a194732b6e4b6845e49a5b66484325d5b6df4ec247b8bce52a645b9dcb2a" + }, + { + "path": "skills/json-outputs-implementer/reference/use-cases.md", + "sha256": "d6909ea7cead37dddcb80ea8c3a9be48193125371c2c9e2633828c75d1e7a2d4" + }, + { + "path": "skills/strict-tool-implementer/CHANGELOG.md", + "sha256": "fc4f5ba2ec3a7d11e0ab335bf75cdf35ce0fa13fe361fc9f948f9306d9c0e583" + }, + { + "path": "skills/strict-tool-implementer/README.md", + "sha256": "f981cc68c997db219f73064946f0da84ed0730b6c2c70eb1a0071888b22e007b" + }, + { + "path": "skills/strict-tool-implementer/SKILL.md", + "sha256": "1891b1d5a673286ffb25e7cec0863bfa8eb28f722faeead07138168928dc1ab5" + }, + { + "path": "skills/strict-tool-implementer/examples/travel-booking-agent.py", + "sha256": "74fe96fd5928e0e5e4fe33fecc5cf7f38b056b7123118a230c20b31b8eadd092" + }, + { + "path": "skills/strict-tool-implementer/workflow/phase-1-schema-design.md", + "sha256": "cb9e2574cbc20481a5c8bb7a957b1291f4af7d943b44549ec0fa8ecb8d64a461" + }, + { + "path": "skills/strict-tool-implementer/workflow/phase-2-implementation.md", + "sha256": "6df3dbf3a4f5a37a639ee291e227ddb7b9c19ff2c9dfc09206df687f8344cc91" + }, + { + "path": "skills/strict-tool-implementer/workflow/phase-3-error-handling.md", + "sha256": "00ec291acb1bdb30d7fcb0116bf9aafda5f658fc13ad4d995d0e879bfd51641a" + }, + { + "path": "skills/strict-tool-implementer/workflow/phase-5-production.md", + "sha256": "453e311d46f215a2f57bde62b84c4bbc7abd4b656117971f6e9201bfd82603c6" + }, + { + "path": "skills/strict-tool-implementer/workflow/phase-4-testing.md", + "sha256": "41a69b32a1ea99592c92decbb19a45d1e84dea1efcacdc6273cf03a0008a34b2" + }, + { + "path": "skills/strict-tool-implementer/reference/success-criteria.md", + "sha256": "943784fc9456649f25eb11ae3b34e099c8bfd0c4f18615313dc11cc4ea249821" + }, + { + "path": "skills/strict-tool-implementer/reference/.gitkeep", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "skills/strict-tool-implementer/reference/common-patterns.md", + "sha256": "0f313193fb1ed90f102a614288ecb834650688fd61beb86ea5ebe446888d4644" + }, + { + "path": "skills/structured-outputs-advisor/CHANGELOG.md", + "sha256": "8c9273bf04b8c9a3cae56ebe2e7c9671c0c3998eb945e1bd86991bf9a73683f4" + }, + { + "path": "skills/structured-outputs-advisor/README.md", + "sha256": "7fa0c089b7b696f231992d301f50b328b3d847eedc9ccb9e94e6c0623f715381" + }, + { + "path": "skills/structured-outputs-advisor/SKILL.md", + "sha256": "e1ba71fce7858b696e5eb18908be188614acc807892891c2304b51be842002d2" + }, + { + "path": "skills/structured-outputs-advisor/examples/mode-selection-examples.md", + "sha256": "3ae282b847a6f656bc6c52cda303daa7c288ca8bea88649efdf443fd681a166b" + }, + { + "path": "skills/structured-outputs-advisor/reference/.gitkeep", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + ], + "dirSha256": "6f7e48c29523e2b49125ed742e7d35d377d96024f74d43f3e60bc3d9dbc218a9" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/skills/json-outputs-implementer/CHANGELOG.md b/skills/json-outputs-implementer/CHANGELOG.md new file mode 100644 index 0000000..9014907 --- /dev/null +++ b/skills/json-outputs-implementer/CHANGELOG.md @@ -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 diff --git a/skills/json-outputs-implementer/README.md b/skills/json-outputs-implementer/README.md new file mode 100644 index 0000000..9d03e09 --- /dev/null +++ b/skills/json-outputs-implementer/README.md @@ -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. diff --git a/skills/json-outputs-implementer/SKILL.md b/skills/json-outputs-implementer/SKILL.md new file mode 100644 index 0000000..d52ff6f --- /dev/null +++ b/skills/json-outputs-implementer/SKILL.md @@ -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 diff --git a/skills/json-outputs-implementer/examples/contact-extraction.py b/skills/json-outputs-implementer/examples/contact-extraction.py new file mode 100644 index 0000000..8ba9db3 --- /dev/null +++ b/skills/json-outputs-implementer/examples/contact-extraction.py @@ -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() diff --git a/skills/json-outputs-implementer/examples/invoice-extraction.py b/skills/json-outputs-implementer/examples/invoice-extraction.py new file mode 100644 index 0000000..819a53c --- /dev/null +++ b/skills/json-outputs-implementer/examples/invoice-extraction.py @@ -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() diff --git a/skills/json-outputs-implementer/reference/.gitkeep b/skills/json-outputs-implementer/reference/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/skills/json-outputs-implementer/reference/schema-limitations.md b/skills/json-outputs-implementer/reference/schema-limitations.md new file mode 100644 index 0000000..44dcd6c --- /dev/null +++ b/skills/json-outputs-implementer/reference/schema-limitations.md @@ -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 diff --git a/skills/json-outputs-implementer/reference/use-cases.md b/skills/json-outputs-implementer/reference/use-cases.md new file mode 100644 index 0000000..268f49f --- /dev/null +++ b/skills/json-outputs-implementer/reference/use-cases.md @@ -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()) +``` diff --git a/skills/json-outputs-implementer/workflow/phase-1-schema-design.md b/skills/json-outputs-implementer/workflow/phase-1-schema-design.md new file mode 100644 index 0000000..f037acf --- /dev/null +++ b/skills/json-outputs-implementer/workflow/phase-1-schema-design.md @@ -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. diff --git a/skills/json-outputs-implementer/workflow/phase-2-sdk-integration.md b/skills/json-outputs-implementer/workflow/phase-2-sdk-integration.md new file mode 100644 index 0000000..58d9a53 --- /dev/null +++ b/skills/json-outputs-implementer/workflow/phase-2-sdk-integration.md @@ -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. diff --git a/skills/json-outputs-implementer/workflow/phase-3-error-handling.md b/skills/json-outputs-implementer/workflow/phase-3-error-handling.md new file mode 100644 index 0000000..5510ba8 --- /dev/null +++ b/skills/json-outputs-implementer/workflow/phase-3-error-handling.md @@ -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. diff --git a/skills/json-outputs-implementer/workflow/phase-4-testing.md b/skills/json-outputs-implementer/workflow/phase-4-testing.md new file mode 100644 index 0000000..de6d91c --- /dev/null +++ b/skills/json-outputs-implementer/workflow/phase-4-testing.md @@ -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. diff --git a/skills/json-outputs-implementer/workflow/phase-5-production.md b/skills/json-outputs-implementer/workflow/phase-5-production.md new file mode 100644 index 0000000..6cea048 --- /dev/null +++ b/skills/json-outputs-implementer/workflow/phase-5-production.md @@ -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. diff --git a/skills/strict-tool-implementer/CHANGELOG.md b/skills/strict-tool-implementer/CHANGELOG.md new file mode 100644 index 0000000..870e1cc --- /dev/null +++ b/skills/strict-tool-implementer/CHANGELOG.md @@ -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) diff --git a/skills/strict-tool-implementer/README.md b/skills/strict-tool-implementer/README.md new file mode 100644 index 0000000..d1607b1 --- /dev/null +++ b/skills/strict-tool-implementer/README.md @@ -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. diff --git a/skills/strict-tool-implementer/SKILL.md b/skills/strict-tool-implementer/SKILL.md new file mode 100644 index 0000000..9e9e57c --- /dev/null +++ b/skills/strict-tool-implementer/SKILL.md @@ -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 diff --git a/skills/strict-tool-implementer/examples/travel-booking-agent.py b/skills/strict-tool-implementer/examples/travel-booking-agent.py new file mode 100644 index 0000000..21451b5 --- /dev/null +++ b/skills/strict-tool-implementer/examples/travel-booking-agent.py @@ -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() diff --git a/skills/strict-tool-implementer/reference/.gitkeep b/skills/strict-tool-implementer/reference/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/skills/strict-tool-implementer/reference/common-patterns.md b/skills/strict-tool-implementer/reference/common-patterns.md new file mode 100644 index 0000000..0660281 --- /dev/null +++ b/skills/strict-tool-implementer/reference/common-patterns.md @@ -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 diff --git a/skills/strict-tool-implementer/reference/success-criteria.md b/skills/strict-tool-implementer/reference/success-criteria.md new file mode 100644 index 0000000..140fe45 --- /dev/null +++ b/skills/strict-tool-implementer/reference/success-criteria.md @@ -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 diff --git a/skills/strict-tool-implementer/workflow/phase-1-schema-design.md b/skills/strict-tool-implementer/workflow/phase-1-schema-design.md new file mode 100644 index 0000000..9edd33c --- /dev/null +++ b/skills/strict-tool-implementer/workflow/phase-1-schema-design.md @@ -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. diff --git a/skills/strict-tool-implementer/workflow/phase-2-implementation.md b/skills/strict-tool-implementer/workflow/phase-2-implementation.md new file mode 100644 index 0000000..0fef423 --- /dev/null +++ b/skills/strict-tool-implementer/workflow/phase-2-implementation.md @@ -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. diff --git a/skills/strict-tool-implementer/workflow/phase-3-error-handling.md b/skills/strict-tool-implementer/workflow/phase-3-error-handling.md new file mode 100644 index 0000000..da18143 --- /dev/null +++ b/skills/strict-tool-implementer/workflow/phase-3-error-handling.md @@ -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. diff --git a/skills/strict-tool-implementer/workflow/phase-4-testing.md b/skills/strict-tool-implementer/workflow/phase-4-testing.md new file mode 100644 index 0000000..6cc0052 --- /dev/null +++ b/skills/strict-tool-implementer/workflow/phase-4-testing.md @@ -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. diff --git a/skills/strict-tool-implementer/workflow/phase-5-production.md b/skills/strict-tool-implementer/workflow/phase-5-production.md new file mode 100644 index 0000000..93c15d2 --- /dev/null +++ b/skills/strict-tool-implementer/workflow/phase-5-production.md @@ -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. diff --git a/skills/structured-outputs-advisor/CHANGELOG.md b/skills/structured-outputs-advisor/CHANGELOG.md new file mode 100644 index 0000000..a4e667f --- /dev/null +++ b/skills/structured-outputs-advisor/CHANGELOG.md @@ -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 diff --git a/skills/structured-outputs-advisor/README.md b/skills/structured-outputs-advisor/README.md new file mode 100644 index 0000000..a6fc92d --- /dev/null +++ b/skills/structured-outputs-advisor/README.md @@ -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. diff --git a/skills/structured-outputs-advisor/SKILL.md b/skills/structured-outputs-advisor/SKILL.md new file mode 100644 index 0000000..902470b --- /dev/null +++ b/skills/structured-outputs-advisor/SKILL.md @@ -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 diff --git a/skills/structured-outputs-advisor/examples/mode-selection-examples.md b/skills/structured-outputs-advisor/examples/mode-selection-examples.md new file mode 100644 index 0000000..fd12df0 --- /dev/null +++ b/skills/structured-outputs-advisor/examples/mode-selection-examples.md @@ -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/) diff --git a/skills/structured-outputs-advisor/reference/.gitkeep b/skills/structured-outputs-advisor/reference/.gitkeep new file mode 100644 index 0000000..e69de29