Extraccion defensiva con JSON Schema, tool_choice forzado, enums con valvula de escape y nullable explicito; nunca prosa.
Kata 05 del toolkit. Enseña a extraer datos estructurados de forma defensiva: se fuerza tool_choice con un JSON Schema que declara los required reales, modela los campos opcionales como union nullable, y define enums con una válvula de escape ('other', 'unclear') acompañada de un campo details. El modelo nunca devuelve prosa: siempre invoca la herramienta de extracción y el schema actúa como contrato.
Escenarios canónicos: Structured Extraction y Customer Support.
Pedir "devuélveme JSON" en prosa garantiza alucinación silenciosa. Sin schema forzado el modelo inventa campos faltantes, llena vacíos con '', o fuerza valores fuera del dominio del enum. Como la salida parece JSON válido, el json.loads(resp.text) pasa y el dato corrupto entra al pipeline sin que nadie lo note. La extracción defensiva convierte esos fallos silenciosos en estados explícitos (null, 'unclear', 'other' + details).
required = el campo siempre está presente en la fuente. Si puede faltar, no es required: modélalo como union nullable.'' es alucinación. Si el modelo no sabe el valor, debe ser null o 'unclear', nunca cadena vacía.'other'/'unclear' más un campo details.tool_choice forzado evita la respuesta "best-effort en prosa": el modelo está obligado a poblar el schema.{"type":["string","null"],"format":"date"}.EXTRACT_TOOL = {
"name": "extract_invoice",
"input_schema": {
"type": "object",
"required": ["invoice_id", "currency", "status"],
"properties": {
"invoice_id": {"type": "string"},
"currency": {"type": "string", "enum": ["USD", "EUR", "COP", "other"]},
"currency_other_details": {"type": ["string", "null"]},
"status": {"type": "string", "enum": ["paid", "pending", "overdue", "unclear"]},
"due_date": {"type": ["string", "null"], "format": "date"}
}
}
}
resp = client.messages.create(
model=MODEL,
tools=[EXTRACT_TOOL],
tool_choice={"type": "tool", "name": "extract_invoice"},
messages=[{"role": "user", "content": source_text}],
)# ✗ ANTI: prompt en prosa pidiendo JSON + parseo ciego
prompt = "Devuelve JSON con invoice_id, currency, status, due_date..."
resp = client.messages.create(model=MODEL, messages=[{"role": "user", "content": prompt}])
data = json.loads(resp.text) # alucina campos, llena vacíos con '', valores fuera de dominioLa extracción usa tool_choice forzado + schema con required reales + enums con escape + nullable explícito; nunca prosa.
tool_choice cuando el modelo debe decidir entre varias tools, o cuando una respuesta híbrida (texto + extracción) es válida.katas-structured-errors-mcpkatas-deterministic-guardrails-pretoolusekatas-posttooluse-normalizationCapas del packet, cargables bajo demanda (disciplina ICM: una capa por vez, nunca todas juntas): knowledge/ cuerpo de conocimiento · prompts/ prompts listos · examples/ salida de ejemplo · agents/ subagentes del packet · templates/ plantilla de output.
e8f986b
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.