Structured Extraction¶
This example extracts one reusable native object rather than parsing JSON text after a model call.
Complete Program¶
> adapter: pydantic
> model: groq:qwen/qwen3-32b
> system: Extract only facts stated in the incident report.
~Owner(
name: Annotated[str, "Person or team responsible for follow-up"],
email: Email | None
)
~Incident(
title: str,
severity: Literal["low", "medium", "high", "critical"],
owner: Owner | None,
affected_services: list[str],
customer_visible: bool
)
@extract_incident(report: str) -> Incident:
>> Incident report:
<report>
Normalized incident: [incident: Incident].
= `incident`
[report: str] = Payment retries failed in checkout. The Payments team owns the follow-up.
[incident: Incident] = `extract_incident(report)`
= `incident.model_dump_json(indent=2)`
The adjacent lines after >> are newline-joined into one model request. Do not
insert a blank line inside that block: it can terminate template continuation.
[incident: Incident] creates a structured output schema and validates the
response; it is not an instruction to return an arbitrary JSON string.
Native Return Versus Rendering¶
Inside extract_incident, this return preserves the Pydantic model:
= `incident`
This alternative returns text:
= <incident>
Use the native form when another procedure, Python caller, metric, or tool needs to inspect fields. Render only at a presentation boundary.
Multiple Captures¶
For a local result that is not reused as one domain object, separate fields are often simpler:
@classify(message: str) -> tuple[str, bool]:
>> <message> is a [category: Literal["question", "request", "incident"]].
Urgent: [urgent: bool].
= `(category, urgent)`
All fields in one block are filled by one model call and become visible only after that call completes. A continuation line in the same block cannot substitute a field captured earlier in that block.
Dynamic Python Type¶
Use a backtick annotation only when the type genuinely comes from Python:
```
from typing import Literal
CurrentRegion = Literal["eu", "us", "apac"]
```
@extract_region(text: str) -> `CurrentRegion`:
>> Region mentioned in <text>: [region: `CurrentRegion`].
= `region`
For Kedi custom types and built-ins, direct annotations are clearer. Provider schema support can be narrower than Python's type system; consult the selected adapter before using URL, regex, or complex union types.
Validate Deterministic Constraints in Python¶
Do not ask the model to calculate facts your program can enforce:
@bounded_confidence(raw: float) -> float:
= `max(0.0, min(1.0, raw))`
Model schemas validate shape and type. Cross-field business invariants still belong in deterministic code, tests, or a Pydantic validator supplied through Python.