0Pricing
Claude Architect · Lección

tool_use para garantizar la estructura

Elimine errores de sintaxis y haga cumplir los campos obligatorios.

tool_use para garantizar la estructura es una lección gratuita de Claude Architect en CoddyKit. Esta es la lección 1 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Claude Architect, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Claude Architect incluye 4 lecciones en total.

Partes de esta lección aún no han sido traducidas y se muestran en inglés.

Why Free Text Fails

You asked Claude for JSON and parsed the reply with json.loads(). It worked 95% of the time. The other 5%? A stray markdown fence, a trailing comma, a chatty preamble like "Here's the JSON you requested:" — and your pipeline throws.

For a Claude Certified Architect, that 5% is the whole problem. Production extraction, classification, and routing flows can't depend on the model happening to format text correctly. This lesson shows how tool_use plus a JSON Schema turns a probabilistic format into a guaranteed one — eliminating syntax errors and enforcing required fields.

The Core Idea

A tool isn't only for taking actions. A tool definition is also a typed output contract. When you declare a tool with an input_schema, you're telling Claude exactly what shape its arguments must take — and the API validates the tool call against that schema.

So instead of asking for JSON in prose and hoping, you define a tool whose parameters are the structure you want, then force Claude to call it. The model fills in the fields; the schema guarantees the shape. Per the fact sheet: tool_use + JSON Schema eliminates syntax errors and enforces required fields.

Defining the Schema Tool

Here's an extraction tool. Note the three pillars of a good tool definition: a clear name, a descriptive description, and a precise input_schema with per-property descriptions and a required list.

The schema below extracts a support ticket. We require only the fields that are always present — more on that decision soon.

ticket_tool = {
    "name": "record_ticket",
    "description": "Record a structured support ticket extracted from the user message.",
    "input_schema": {
        "type": "object",
        "properties": {
            "summary": {
                "type": "string",
                "description": "One-line summary of the issue",
            },
            "priority": {
                "type": "string",
                "enum": ["low", "medium", "high", "urgent"],
                "description": "Triage priority",
            },
        },
        "required": ["summary", "priority"],
    },
}

tool_choice: Forcing the Structure

Declaring the tool isn't enough — with the default, Claude might answer in text instead. The tool_choice parameter controls that decision:

  • {"type": "auto"} — Claude picks text or a tool (the default).
  • {"type": "any"} — Claude must call some tool. This is what guarantees structured output.
  • {"type": "tool", "name": "X"} — force one specific tool by name.

When you have exactly one schema tool and want a guaranteed call to it, force it by name. This removes the "answer in prose" escape hatch entirely.

response = client.messages.create(
    model="claude-opus-4-8",
    max_tokens=1024,
    tools=[ticket_tool],
    tool_choice={"type": "tool", "name": "record_ticket"},
    messages=[{"role": "user", "content": ticket_text}],
)

Reading the Tool Input Back

When Claude calls the tool, the response contains a tool_use content block. The structured data lives in block.input — already parsed into a Python dict by the SDK, conforming to your schema.

One discipline from the fact sheet: always parse with the SDK's parsed object, never raw-string-match the serialized input. Recent models may escape Unicode or forward slashes differently, so treat block.input as structured data, not text.

for block in response.content:
    if block.type == "tool_use":
        ticket = block.input  # dict, matches the schema
        print(ticket["summary"], ticket["priority"])
        # Do NOT json.loads a re-serialized string here —
        # block.input is already the structured object.

The required Trap

This is the single most tested decision in this lesson. The rule from the fact sheet is blunt:

Mark a field required ONLY if it is always present. NEVER require a field that may be absent — the model will fabricate a value to satisfy the schema.

If customer_id sometimes isn't in the source text but you list it under required, Claude won't return an empty result — it will invent a plausible-looking ID to make the tool call valid. That's a silent data-integrity bug. Required means "guaranteed by the source," not "nice to have."

Optional Fields Done Right

Fields that may be absent simply stay out of the required array. Claude omits them when the source lacks the data, instead of hallucinating.

Here customer_id and attachments are optional; only summary and priority — which we can always derive — are required.

"properties": {
    "summary": {"type": "string"},
    "priority": {"type": "string",
                 "enum": ["low", "medium", "high", "urgent"]},
    "customer_id": {"type": "string",
                    "description": "Only if explicitly stated"},
    "attachments": {"type": "array", "items": {"type": "string"}},
},
"required": ["summary", "priority"]  # NOT customer_id / attachments

Enums With an Escape Hatch

Enums constrain a field to a fixed set of values — great for categories, labels, and priorities. But a rigid enum forces every input into one of your predefined buckets, which breaks on edge cases you didn't anticipate.

The fact-sheet pattern for extensibility: use enums with an "other" value plus a free-text detail field. When Claude hits something outside your categories, it picks other and explains in the detail field — instead of mislabeling.

"category": {
    "type": "string",
    "enum": ["billing", "bug", "account", "other"],
    "description": "Use 'other' if none of the named categories fit",
},
"category_detail": {
    "type": "string",
    "description": "Free-text explanation, required only when category is 'other'",
}

Validate, Then Retry With Feedback

The schema guarantees syntactic shape, but you still validate semantics — totals that add up, dates in range, cross-field consistency. Use a Pydantic-style validator on block.input.

When validation fails on a format, structural, or arithmetic error, use retry-with-feedback: resend the original source, the wrong output, and the exact validation error. Critically, the fact sheet warns: retry does NOT help when the information is simply absent from the source — retrying just invites fabrication.

from pydantic import BaseModel, ValidationError

class Ticket(BaseModel):
    summary: str
    priority: str

try:
    ticket = Ticket(**block.input)
except ValidationError as e:
    # Resend: original text + wrong output + this exact error.
    # Only worth it for format/arithmetic faults, not missing data.
    retry(ticket_text, block.input, str(e))

Self-Correction for Numbers

For numeric extraction, a powerful schema trick is to make Claude expose its work so discrepancies are detectable. Per the fact sheet: extract both a calculated_total and a stated_total to detect discrepancies.

If the document says "Total: $1,200" but the line items sum to $1,150, two separate fields surface the mismatch — your validator catches it instead of trusting a single number. The schema becomes an audit instrument, not just a container.

"properties": {
    "line_items": {"type": "array", "items": {"type": "number"}},
    "calculated_total": {"type": "number",
        "description": "Sum you computed from the line items"},
    "stated_total": {"type": "number",
        "description": "Total as literally written in the document"},
},
"required": ["line_items", "calculated_total", "stated_total"]

Where This Fits in the Loop

Structured output is one stop in the larger agentic loop: request → inspect stop_reason → if tool_use, handle the tool → repeat until end_turn. A forced schema call returns stop_reason: "tool_use"; you read block.input and proceed.

Two architect-grade reminders: terminate on the stop_reason, never by parsing text for words like "done." And keep provenance — map each extracted claim back to its source (doc name, quote, date) so downstream consumers can trust and audit the structured result.

Quick Check

You're extracting invoices. The source PDFs sometimes omit a purchase-order number. A teammate proposes putting po_number in the schema's required array "so we always get one." What should you do?

Recap: Guaranteed Structure

Key takeaways for the exam and for production:

  • tool_use + JSON Schema eliminates syntax errors and enforces required fields — far more reliable than parsing prose JSON.
  • tool_choice: "any" guarantees some tool call; {"type":"tool","name":"X"} forces a specific schema tool; "auto" leaves it optional.
  • required = always present. Never require a possibly-absent field — it causes fabrication. Optional fields simply stay out of required.
  • Enums need an "other" + detail field for extensibility.
  • Read structured data from block.input; never raw-string-match the serialized tool input.
  • Retry-with-feedback fixes format/arithmetic faults — not absent information. Use calculated_total vs stated_total for self-correction, and keep provenance.

Preguntas frecuentes

¿La lección «tool_use para garantizar la estructura» es gratis?

Sí — el texto completo de «tool_use para garantizar la estructura» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Claude Architect, actualiza a CoddyKit PRO. El curso de Claude Architect incluye 4 lecciones en total.

¿Qué aprenderé en «tool_use para garantizar la estructura»?

Elimine errores de sintaxis y haga cumplir los campos obligatorios. Practicas Claude Architect con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Claude Architect?

No se requiere experiencia previa. Claude Architect en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 1 de 4.

¿Cuánto tiempo toma la lección «tool_use para garantizar la estructura»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Claude Architect?

Sí. Cada lección de Claude Architect incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. tool_use para garantizar la estructura
  2. Diseño de un esquema JSON
  3. Campos obligatorios frente a opcionales o anulables
  4. Enums con 'other' para la extensibilidad
← Volver a Claude Architect