Building AI Agents from Scratch
Structured Outputs
Force a JSON object your application can consume instead of parsing prose.
A UI, a ticket, or a test cannot reliably parse “Sure! I found three files…” Ask the model for an object. Validate it before you trust it.
Schema on the last call
When the user wants a machine-readable result, either (a) add a submit_result tool whose arguments are the schema, or (b) use the host’s structured-output / response_format JSON schema if the compatible API supports it. The tool trick works everywhere Chat Completions tools work.
RESULT_TOOL = {
"type": "function",
"function": {
"name": "submit_result",
"description": "Submit the final structured answer. Call this once when you are done.",
"parameters": {
"type": "object",
"properties": {
"summary": {"type": "string"},
"files": {"type": "array", "items": {"type": "string"}},
"ok": {"type": "boolean"},
},
"required": ["summary", "files", "ok"],
},
},
}
def submit_result(summary: str, files: list, ok: bool) -> dict:
return {"ok": True, "accepted": True, "summary": summary, "files": files[:20]}Stop the loop when submit_result runs successfully. Return that dict to your application, not the last assistant paragraph. If the model rambles instead of calling it, nudge once in a follow-up user message, then fail.
Validate again
The schema is a hint. Check types yourself. Drop extra keys. If files contains paths your tools never saw, mark ok false — the model invented them.
Check it
Run a scout question and print the dict from submit_result. Parse it with json.loads in a two-line test. If you have to use a regex on the assistant text, the lesson is not done. Next: block writes until a human says yes.