Mycroft Update: Why Your AI Agent Ignores Your Code
When you give an AI agent a tool, only the name, docstring, and type-hint schema cross the wire, never the function body, which means the description is the real product.
Nearly everyone who wires up their first tool for an AI agent makes the same assumption: expose the function, and the model will figure out how to use it. Then the agent either never calls it, or calls it at the worst possible moment. The instinct is to go debug the function. That's almost always the wrong place to look, because the model never actually reads your code.
What actually crosses the wire
When a language model calls a tool, it isn't reading your implementation. It's choosing from labels. What crosses the wire is the function name, the description, which is your docstring copied verbatim, and a parameter schema derived from your type hints, including which arguments are required. That's the entire payload. Printed out, it looks like a compact block: a name, a docstring-as-description, and a schema listing arguments and requirement status. The function body itself never leaves your machine. If you read only that payload, the question becomes whether you could actually tell when to use the tool from it alone, and for most first-draft tools, the honest answer is no.
Because that description is the only thing the model ever sees, writing it isn't a cleanup task you do after the real work is finished. It is the real work.
The three questions a description has to answer
Most tool descriptions fail in the same three ways, because they only attempt to answer the easiest of three necessary questions. A robust description has to cover:
- What comes back when the tool is called, the easy one that most descriptions already get right.
- When you should reach for it, which requires naming the specific situation the tool solves.
- When you should not, the question almost nobody writes an answer to, and the one that's responsible for stopping an agent from grabbing the wrong tool among several similar options.
The point is illustrated with two versions of the exact same function, byte-identical bodies, where only the description text changes. One version is three words long and answers none of the three questions. The other runs 292 characters and answers all three. Since the function itself is identical in both cases, whatever difference shows up in the agent's behavior comes entirely from the sentence, not the code.
The payload test
A practical way to check your own tool descriptions: write the description before you write the function, in the words a caller would actually think in, and explicitly name the case where reaching for this tool would be wrong. Then read back only the payload, the name, description, and schema, ignoring your own implementation, and ask whether a stranger could pick the right tool from that alone. If you can't answer that confidently, neither can the model.
Every tool has a cost, even a good one
Adding a tool isn't free, even when its description is well written. Each tool eats a share of context on every single call it's exposed for. It adds one more option an agent could confuse with a similar tool. It widens the blast radius of what a wrong decision by the agent can reach. And it becomes a promise you now have to keep maintaining. The practical rule that follows: four sharp, well-described tools beat twenty vague ones.
Key takeaways
- Only the function name, the docstring-derived description, and a type-hint-based parameter schema cross the wire to the model. The function body never does.
- A robust tool description must answer three questions: what it returns, when to use it, and when not to use it, with the third question being the one most descriptions skip.
- Two byte-identical functions can produce very different agent behavior purely because their descriptions differ.
- Test your own tools with the payload test: read only the name, description, and schema, and ask if a stranger could pick correctly.
- Every added tool costs context, adds ambiguity between similar options, and widens what a wrong call can affect.
- Four sharp tools outperform twenty vague ones.
Try it yourself
The episode ends with a direct exercise: take two tools you've defined, show only their names, descriptions, and schemas (not your implementations) to the model, and ask which one it would call for each of five different requests, noting where it's genuinely unsure. Rewrite whichever tool caused the confusion, and run this on two of your own similar tools; the resulting "unsure" list is the whole exercise. This is episode three of ten in the Mycroft series from Humanitarians AI, with the next episode covering memory and context.
Chapters
- 0:00The surprise of tool calling: The model never reads your code
- 0:40Analyzing the actual JSON payload that crosses the wire
- 1:20The 3 questions every robust tool description must answer
- 2:00The payload test: Can a stranger choose your tool correctly?
- 2:40The hidden costs of tools: Context consumption and blast radius
Full transcript(auto-generated, with timestamps)
The surprise of tool calling: The model never reads your code
[0:00]This is Onnx in for Humanitarian's AI. Two weeks in, we have a loop that can act. Today, the thing it acts with tools. And the part that surprises everyone who builds their first one, the model never sees your code. It sees what you wrote about your code, which means the documentation is the product. Everyone writes their first tool the same way. Wire up the function, expose it, done. And then the agent never calls it or calls it at the worst possible moment. The bug is almost never in the function. It is in the sentence above it. Here is what actually crosses the wire. The name, the description, everything you wrote in the docstring verbatim, and a parameter schema derived from your type hints. That is the whole payload. Your function body stays on your machine. The model is choosing from
Analyzing the actual JSON payload that crosses the wire
[0:41]Labels, not reading your implementation. And printed out, it looks like this. A name, a description that is just the docstring word for word, a schema listing the argument and whether it is required, and a last line worth staring at, not sent, the function body. Read only that and ask whether you could tell when to use it. So, if that description is the only thing the model reads, then writing it is not a chore you do after the work. It is the work, and most descriptions fail in the same three ways. This is how that payload gets built. 14 lines. The name comes from the function. The description is the docstring untouched. The parameters come from your type hints, and anything without a default is required. Now,
The 3 questions every robust tool description must answer
[1:20]Notice what is never read anywhere in there, the body. A description has to answer three questions, and only the first is easy. What comes back, when you should reach for it, and when you should not, which is the one almost nobody writes, and the one that stops an agent grabbing the wrong tool. Same function, the bodies are byte identical, and the program checks that for us. Two contracts. One is three words and answers none of the three questions. The other is 292 characters and answers all three. Only the sentence changed. The sentence is what gets read. And every tool you add has a price. It eats context on every single call. It adds one more thing to be confused with. It widens what a wrong decision can reach, and it becomes a promise you now
The payload test: Can a stranger choose your tool correctly?
[2:01]Have to keep working. Four sharp tools beat 20 vague ones. So, here's the rule. Write the description before you write the function in the words a caller would actually think in. Name the case where reaching for it is wrong. Then read only the payload, not your code, and ask whether a stranger could choose correctly. If you cannot, neither can the model. So, the verdict. A tool is not a function you expose. It is a contract you write in English, and the model reads the contract, never the code. Three things cross the wire, a name, a description, and a schema derived from your type hints. The description carries almost all of the weight, and it has to say when not to use the tool, not only what it does. And every tool you add costs context, adds ambiguity, and widens the blast radius.
The hidden costs of tools: Context consumption and blast radius
[2:41]A few sharp ones beat a drawer full of vague ones. Turn, here is the prompt. Here are two tools I have defined. Read only the names, descriptions, and schemas, not my implementations, and tell me which one you would call for each of these five requests, and where you would genuinely be unsure. Then rewrite whichever caused the confusion. Run that on two of your own tools that do similar things. The unsure list is the whole exercise. That uncertainty is not the model being weak. It is your contract being fit. Well, that was tools, giving a model hands, episode three of 10. Next Friday, memory and context, and why agents do not really forget, they overflow. Onyx in for Humanitarian's AI.
More from Mycroft Financial AI
3:18Deterministic What-Ifs in Mycroft: Scenario Analysis Without AI Guessing
3:47Fencing the AI: Why Our Finance Agent Cannot Approve Itself
3:29Building an Evidence-Driven AI Variance Engine in 3 Weeks for Mycroft
3:53Refusal by Design: Implementing 15 Failure Safeguards in AI for Mycroft
3:16How to Test Mycroft's Finance AI Agent: Breaking the Books on Purpose
2:17