{"slug": "designing-tools-llms-can-actually-use-five-failure-modes-i-keep-seeing", "title": "Designing tools LLMs can actually use: five failure modes I keep seeing", "summary": "A production AI agent integrator identifies five failure modes that cause large language models to misuse tools, including undocumented parameters, competing tool names, prose constraints instead of schema constraints, catalog bloat, and unhelpful error messages. The author argues that tool reliability depends more on design for model comprehension than on engineering quality, and recommends fixes such as adding parameter descriptions with examples, using enums over prose constraints, and writing error messages as prompts.", "body_md": "# Designing tools LLMs can actually use: five failure modes I keep seeing\n\nI integrate tools into a production AI agent for a living — first-party ones,\nthird-party ones, HTTP APIs wrapped as tools, MCP servers. After enough\nintegrations, you notice something uncomfortable: **whether a model uses a tool\ncorrectly has surprisingly little to do with how well the tool is engineered.**\n\nA tool can have clean code, solid auth, perfect uptime — and the model still picks the wrong one, invents arguments, or ignores it entirely. The failures cluster into a handful of patterns. Here are the five I keep seeing.\n\n## 1. The parameter with no description\n\nThe single most common failure, by a wide margin.\n\n```\n\"url\": { \"type\": \"string\" }\n```\n\nYour type system knows this is a string. The model needs to know *which* URL —\nthe full page URL? just the domain? with or without protocol? URL-encoded?\nA model facing an undocumented parameter doesn't stop and ask. It guesses.\nSometimes it guesses right. In production, \"sometimes\" is a bug report.\n\nThe root cause is almost always schema generation: zod, Pydantic, or an\nOpenAPI converter produces structurally perfect schemas with zero semantics,\nand nobody goes back to add `.describe()`\n\n. The type checker is satisfied.\nThe model is starving.\n\n**The fix costs an hour:** every parameter gets a description with format and\none example value. It is the highest-leverage hour you can spend on agent\nreliability, and nobody spends it.\n\n## 2. Tools that compete for the same intent\n\n`get_user`\n\nand `get_users`\n\n. `search_docs`\n\nand `query_docs`\n\n. `send_message`\n\nand `post_message`\n\n.\n\nTo you, the difference is obvious — you wrote them. To a model doing tool selection over a catalog it has never seen before, two near-identical names with near-identical descriptions is a coin flip. And a coin flip at step 2 of a 6-step task is a 50% failure rate you'll never reproduce locally.\n\nTwo rules of thumb: if two tools' descriptions could be swapped without\nanyone noticing, the model can't tell them apart either. And every\ndescription should answer *when to use this instead of the neighbors* — the\nbest catalogs cross-reference (\"for bulk queries, use X instead\").\n\n## 3. Prose constraints instead of schema constraints\n\n```\n\"status\": {\n  \"type\": \"string\",\n  \"description\": \"Must be one of: active, inactive, banned\"\n}\n```\n\nThat constraint lives in prose, which means the model *can* violate it.\nMove it into `\"enum\": [\"active\", \"inactive\", \"banned\"]`\n\nand it *can't* —\nmost inference stacks enforce enums at decoding time.\n\nThe general principle: **anything the schema can express, the schema should\nexpress.** Descriptions are for semantics; constraints belong in the type\nsystem. Every constraint you leave in prose is a runtime error you've chosen\nto discover later.\n\n## 4. The catalog tax\n\nEvery tool you expose gets serialized into every single request. A catalog\nwith 25 tools and generous schemas can cost several thousand tokens *before\nthe user has typed a word* — you pay it on every turn, forever.\n\nWorse than the money: selection accuracy degrades as catalogs grow. The model attends over everything; ten sharply-differentiated tools reliably beat thirty overlapping ones on both cost and correctness.\n\nIf your catalog is large, split it by domain, or gate rarely-used tools behind a mode. Your token bill and your error rate will both thank you.\n\n## 5. Errors that strand the model\n\nWhen a model calls your tool wrong, your error message is the only feedback loop it has. Compare:\n\n`Internal Server Error`\n\n`Missing required parameter \"query\". Expected a free-text search string, e.g. \"login bug\".`\n\nThe first strands the model — it will retry the same call, or give up, or\nhallucinate a workaround. The second lets it self-correct *in one turn*.\nError messages are prompts. Write them like prompts.\n\nThere's a nastier variant: the tool that silently *accepts* an invalid call —\ndeclared `required`\n\nfields that aren't actually enforced, so the call\n\"succeeds\" with `undefined`\n\nin the payload. A model can recover from a good\nerror. It cannot recover from a lie.\n\n## The common thread\n\nNone of these are engineering problems in the traditional sense. The code is\nfine. They're *writing* problems — descriptions, names, error messages — the\nparts that don't show up in code review because they don't break tests.\n\nThe mental model that helps: your tool definition is not an API contract,\n**it's a prompt**. It gets read by a language model under time pressure with\nno documentation open in another tab. Write it the way you'd write for a\nsharp intern on their first day: what this does, when to use it, what comes\nback, and what to do when it fails.\n\nThe teams that internalize this ship tools that agents use correctly on the first try. The teams that don't file bugs against the model.\n\n*Next up: how widespread are these failure modes across the MCP ecosystem,\nactually? I've been collecting data. It is not flattering. Stay tuned.*", "url": "https://wpnews.pro/news/designing-tools-llms-can-actually-use-five-failure-modes-i-keep-seeing", "canonical_source": "https://tengli.dev/posts/tool-design-for-llms.html", "published_at": "2026-07-12 00:00:00+00:00", "updated_at": "2026-07-22 07:08:26.767335+00:00", "lang": "en", "topics": ["artificial-intelligence", "large-language-models", "ai-agents", "developer-tools"], "entities": [], "alternates": {"html": "https://wpnews.pro/news/designing-tools-llms-can-actually-use-five-failure-modes-i-keep-seeing", "markdown": "https://wpnews.pro/news/designing-tools-llms-can-actually-use-five-failure-modes-i-keep-seeing.md", "text": "https://wpnews.pro/news/designing-tools-llms-can-actually-use-five-failure-modes-i-keep-seeing.txt", "jsonld": "https://wpnews.pro/news/designing-tools-llms-can-actually-use-five-failure-modes-i-keep-seeing.jsonld"}}