I lost an entire Tuesday afternoon to a bug that turned out to be a writing problem, not a code problem. We were building an internal agent for our support team — it could pull customer data and log support interactions. Two tools: get_customer_info and log_support_interaction. And our agent had developed a deeply annoying habit. Whenever a teammate asked it 'what was the last thing this customer contacted us about?', it would call log_support_interaction — the WRITE tool — and cheerfully create a new, empty log entry. It was treating the logging tool as a general-purpose 'do a customer thing' action, and quietly polluting our database in the process.
My first instinct was to blame the model. My second instinct, after some coffee, was to actually read the tool descriptions I'd written. They were embarrassing. log_support_interaction's description said, in full: 'Logs a support interaction.' That's it. No mention of WHEN to use it, no warning about the read-vs-write confusion, nothing that would help the tool-selection layer tell it apart from a lookup. The model wasn't being dumb. I'd given it nothing to work with.
Running the tools through the schema prompt
I fed both tools into the Design a Tool Function Schema for an AI Agent prompt and ran it on GPT 5.2, because GPT 5.2 is genuinely sharp at reasoning about tool boundaries and producing clean, spec-compliant JSON. The schema it returned for log_support_interaction had a description I would never have thought to write myself: 'Call this tool ONLY when a support interaction has concluded and you need to record its outcome. Do NOT call this to retrieve or read existing interaction history — use get_customer_info for any lookup.' That one 'Do NOT' clause, written directly at the model, nearly eliminated the misfire in testing.
The other things the generated schema got right that I'd skipped
- It made 'outcome' an enum — ['resolved', 'escalated', 'pending'] — instead of the free-text string I'd used, so our logs went from inconsistent prose to clean structured values overnight
- The USAGE NOTES section spelled out a false-positive case: 'Do not call when the user just asks a question you can answer without recording anything'
- The ON ERROR guidance told the agent to apologize and offer to retry rather than silently swallowing the failure and pretending it worked
- Every parameter description was written for the model — 'The customer's account ID; ask the user for it if not already known' — not the cryptic developer notes I'd left
The cleanup that taught me to respect enums
Once the misfiring stopped, I had a second, quieter problem to face: the empty log entries the agent had already created, plus months of real logs where the 'outcome' field was free text. I had 'resolved', 'Resolved', 'resolved.', 'closed', 'done', 'fixed' and at least four other ways of saying the same thing. Reporting on it was impossible. The enum the schema prompt added — ['resolved', 'escalated', 'pending'] — meant every NEW log was one of exactly three values, and I could finally build a dashboard that didn't lie to me. I went back and migrated the old free-text values into the three enum buckets, and the act of doing that migration is what permanently converted me to designing schemas instead of improvising them.
The mental model that finally clicked
Tool descriptions are prompts. The tool-selection layer is just the model reading those descriptions and deciding. So a vague description is a vague prompt, and you get vague behavior. The single most valuable line in any tool schema is the one that says when NOT to use it, because that's the line that disambiguates two tools that otherwise look similar. The second most valuable thing is using enums and types instead of free-form strings, because the schema isn't only telling the model when to call the tool — it's defining the shape of the data you'll live with afterward. I now refuse to ship a tool definition I wrote by hand, and I run every one through the schema prompt even when I'm 'pretty sure' my hand-written version is fine. I am, it turns out, frequently not fine.
One honest caveat: the prompt is only as good as the agent context you give it. The first time I ran log_support_interaction through it, I forgot to mention that get_customer_info existed, so the generated 'do NOT' clause was generic. The moment I added the sibling tool to the agent context input, it wrote the specific 'use get_customer_info for lookups' line that actually fixed my bug. Tell it about the neighboring tools and it disambiguates them for you.
Grab the Design a Tool Function Schema for an AI Agent (OpenAI Tool-Calling Format) prompt on Prompt Dock and run every tool your agent has through it on GPT 5.2 before you deploy. I pair it with my Build a Chain-of-Steps Workflow for a Multi-Step Agent Task prompt when I'm wiring those tools into an actual sequence — the schema gets each tool right, the workflow gets the order right. Together they turned my agent from a database-polluting menace into something I'd actually trust on a Tuesday afternoon.