Chapter 48 — Tools, structured output, and agents
Structured output: making the model return parseable data
Asking nicely for JSON works ~90–98% of the time — which is a failure every 20 requests. Three levels of reliability:
Level 1 — Ask and validate.
prompt = 'Extract the fields. Return ONLY JSON: {"name": str, "amount": float}'
# then json.loads() in a try/except, and retry on failure
Level 2 — JSON mode. A provider flag that forces syntactically valid JSON (though not necessarily your schema).
Level 3 — Schema-constrained decoding. At each step, the sampler is only allowed to pick tokens that keep the output valid against your schema. For the supported schema, invalid output becomes impossible at the decoder level, not merely unlikely — assuming the schema and constrained-decoding implementation are correct.
from pydantic import BaseModel
class Expense(BaseModel):
item: str
amount_eur: float
approved: bool
reason: str
# Supported natively by many providers (structured outputs / response_format)
# and by local servers via libraries like Outlines, XGrammar, or llama.cpp grammars.
Tip: if you need an explanation, ask for a short user-visible field like
evidence or rationale_summary, not hidden chain-of-thought. Constrained
decoding forces field order, so put fields in the order the answer should be
assembled: evidence first, then the final structured values.
Tools (function calling)
Give the model a menu of functions. It decides when to call one and with what arguments; your code executes it and returns the result.
tools = [{
"type": "function",
"function": {
"name": "get_holiday_balance",
"description": "Get an employee's remaining holiday days for this year.",
"parameters": {
"type": "object",
"properties": {"employee_id": {"type": "string"}},
"required": ["employee_id"],
},
},
}]
# The loop:
# 1. send messages + tools
# 2. if the model returns a tool call -> run YOUR function
# 3. append the result as a "tool" message
# 4. send again -> the model writes the final answer
This is how you fix the model's weaknesses:
| Weakness | Tool that fixes it |
|---|---|
| Arithmetic errors | a calculator / code execution |
| Knowledge cutoff | web search |
| Doesn't know your data | database query, internal API |
| Hallucinated specifics | any system of record |
| Can't act in the world | send email, create a ticket, call an API |
The mental model from Part 2 returns: the LLM handles language and decisions; deterministic code handles facts and actions. Neither half is sufficient alone.
Agents: models that loop
An agent is an LLM in a loop with tools and a goal:
goal
|
v
+-------------------------------------+
| 1. LLM decides the next action |
| 2. your code executes the tool |
| 3. result is appended to context |<---+
| 4. repeat until done or budget out | |
+-------------------------------------+----+
|
v
answer
Agents are powerful and fragile. The safety rules are non-negotiable:
- Least privilege. Read-only by default. Give the minimum set of tools.
- Human approval for consequential actions — sending, paying, deleting, deploying. This is the single most effective control.
- Hard limits. Max iterations, max tokens, max spend, timeouts. Always.
- Sandbox any code execution. No secrets, no network unless allowlisted, ephemeral filesystem.
- Validate every tool argument in your code before executing. Never trust model-generated paths, URLs, recipients, or SQL.
- Log every tool call and alert on anomalies.
Practice (30 min)
Extend your RAG app from Chapter 47 with one tool — for example
get_holiday_balance(employee_id) returning a hardcoded number. Then ask
"How many holiday days do I have left, and what's the policy on carrying them
over?" — a question requiring both the tool (personal data) and RAG
(policy). Watch the model orchestrate both.
Check yourself
- Why is schema-constrained decoding stronger than "please return JSON"?
- Name three weaknesses that tools fix.
- List three safety rules for agents.
Further reading
- Docs: the function-calling / tool-use guides from OpenAI, Anthropic, and Google — read at least one end to end.
- Docs: modelcontextprotocol.io — MCP, an open standard for connecting models to tools and data sources.
- Library: github.com/dottxt-ai/outlines — structured generation with guaranteed-valid output.
- Article: "Building Effective Agents" (Anthropic engineering blog) — a refreshingly practical, hype-free guide to when agents help and when a simple chain is better.