# AI Agents Explained Through the Tool Schemas That Keep Them From Calling the Wrong Function

AI agents explained through tool design: snake_case names, 'Use when / Do not use for' descriptions under 1024 characters, and a linter that flags vague tools.

Source: https://mextop-3h.top/ai-agents-explained-through/ · based on the channel [Research Treats](https://www.youtube.com/channel/UCxL-CHpsg_8BhYL9yftCz7Q) · Video: [AI Agents Explained: Tool Descriptions and Snake_Case Names](https://www.youtube.com/watch?v=Rsuf9TKscdk) · 2026-10-24

![AI Agents Explained Through the Tool Schemas That Keep Them From Calling the Wrong Function](https://mextop-3h.top/ai-agents-explained-through/ai-agents-explained-through.webp)

## Key takeaways

- A tool description must follow the pattern 'Use when …, Do not use for …' and stay under 1024 characters.
- Tool names must be stable, unambiguous snake_case; get_weather_for_tokyo fails the name check because it hardcodes a location.
- A linter built in Python flags vague descriptions and parameters that lack documentation, and is verified with pytest.
- Lesson 3 of the AI Agents from Scratch series ships runnable code on GitHub; Lesson 4 covers function calling in Python.
- The course is based on Rohit Ghumare's MIT-licensed 'AI Engineering from Scratch' repository.

AI agents explained in one sentence: an agent is a language model that decides which functions to call, so the quality of your tool names and descriptions determines whether it calls the right one. The practice code for the third lesson of the Research Treats series is available in the [lesson-03 folder of the ai-agents-from-scratch repository](https://github.com/ResearchTreats/ai-agents-from-scratch/tree/main/code/lesson-03), which contains a Python linter that checks tool schemas before an agent ever sees them.

## What an AI agent actually is when tools are involved

An AI agent is a loop in which a model reads a task, picks a tool from a declared toolbox, fills in arguments, reads the result and repeats until the task is done. The model never reads your source code; it reads the JSON schema you expose — the tool name, the description string and the parameter list. That schema is the entire contract, and every ambiguity in it becomes a wrong call at runtime.

This is why tool design is a discipline of its own rather than an afterthought. A function that is obvious to a human reader (`get_refund_status(order_id)`) can still be misused if the description says only "gets refund info", because the model cannot tell whether it should be called before or after a return is created, or whether it accepts a customer ID instead of an order ID. The lesson treats the toolbox the way an API team treats a public SDK: names and docs are reviewed, and the review is automated.

## The snake_case naming rules a tool must pass

Tool names should be stable, unambiguous snake_case identifiers that describe one action on one resource. The check in the lesson rejects names that bake in data values, because a name is a permanent identifier while a value is a runtime argument.

1. Use lowercase words joined by underscores: `get_refund_status`, `create_support_ticket`, `list_open_invoices`.
2. Start with a verb that states the operation: `get`, `list`, `create`, `update`, `delete`, `search`.
3. Put the resource after the verb and qualify it only when a sibling tool exists: `get_order` versus `get_order_history`.
4. Never embed a literal value. `get_weather_for_tokyo` fails the check, because the city belongs in a `city` parameter; the correct tool is `get_weather` with `city="Tokyo"`.
5. Keep the name identical across versions of the agent — renaming a tool silently breaks every prompt and cached plan that referenced it.

The practical test is simple: if two tools in the same toolbox could plausibly be called for the same request, their names are too close and at least one needs a sharper verb or resource noun.

## Writing a tool description an agent cannot misread

The description must follow the pattern "Use when …, Do not use for …" and stay under 1024 characters. The first half tells the model when the tool is the right choice; the second half names the near-miss cases and, ideally, the tool that should handle them instead.

A compliant example for a refund lookup:

```
Use when: you need the current status of an existing refund for a known order ID.
Do not use for: creating a refund, checking whether an item is eligible for return,
or looking up order history — use create_refund, check_return_eligibility
and get_order_history instead.
```

Every parameter should be documented in the same string: its type, whether it is required, and what format is expected (`order_id`: string, required, format `ORD-12345`). The 1024-character ceiling forces you to drop marketing language and keep only decision-relevant facts, which is exactly what the model needs. A description like "Looks up a customer's order history for support" fails the linter because it has no negative guidance, no parameter documentation and no statement of the input it expects.

## Building the linter and proving it with pytest

The linter is a Python module that takes a toolbox definition and returns a list of violations, so it can run in CI on every pull request. It performs three groups of checks: the naming rules above, the description pattern (presence of a "Use when" clause, a "Do not use for" clause, and a length under 1024 characters), and parameter coverage — every declared parameter must appear in the description with a type.

Tests are written with pytest, one test per rule plus a passing fixture for a well-formed tool. The workflow that matters is negative testing: a fixture named `get_weather_for_tokyo` must produce a name violation, and a fixture with an undocumented `amount` parameter must produce a parameter violation. Once those tests are green, adding a new tool to the toolbox cannot regress the schema silently. The exercise in the lesson asks you to write a `get_refund_status` tool that passes all checks and register it in a toolbox.

## Common questions about agent tool design

A recurring doubt is whether strict naming matters when the model is large enough to "figure it out". It does, because the failure mode is not a crash but a plausible wrong call: the agent invents an argument, the function raises, and the loop burns tokens retrying. Descriptive schemas reduce that retry cost directly.

Another frequent concern is description length. Shorter is better, but the 1024-character limit is a ceiling, not a target — a two-line description with a clear negative clause beats a paragraph of prose. Teams also ask whether to expose many small tools or a few large ones; the lesson's guidance points to one action per tool, since a multi-purpose function forces the description to enumerate modes, and mode selection is where models err most.

## Source links from the lesson

- Practice code for this lesson: https://github.com/ResearchTreats/ai-agents-from-scratch/tree/main/code/lesson-03
- Source lesson on tool schema design: https://github.com/rohitg00/ai-engineering-from-scratch/tree/8bc378c2e07777899322ae77cd0dde94cb12fab3/phases/13-tools-and-protocols/05-tool-schema-design
- Field guide to building tools for AI agents: https://composio.dev/blog/how-to-build-tools-for-ai-agents-a-field-guide
- Notes on tool schemas: https://oneuptime.com/blog/post/2026-01-30-tool-schemas/view
- Agent system design patterns: https://docs.databricks.com/aws/en/generative-ai/guide/agent-system-design-patterns

## Takeaways for shipping a reliable toolbox

Tool schemas are the interface between your code and the model's decisions, so they deserve the same review process as a public API. Names in stable snake_case, descriptions in the "Use when / Do not use for" pattern under 1024 characters, and a pytest-backed linter together remove the most common cause of agent misfires. The Research Treats video breakdown walks through writing those checks line by line, and the [lesson-03 code on GitHub](https://github.com/ResearchTreats/ai-agents-from-scratch/tree/main/code/lesson-03) gives you the finished linter to adapt to your own toolbox. Start with the refund example, add a negative test for every rule, and the next tool you ship will be one the agent cannot misuse.

## Questions & answers

**Why does a tool name like get_weather_for_tokyo fail the naming check?**

The name embeds a runtime value — the city — into a permanent identifier. A tool should be named for the action and resource, such as get_weather, with the city supplied as a parameter. Otherwise every new location requires a new tool, and the model has to choose between near-identical names.

**What happens if a description lacks a 'Do not use for' clause?**

The linter reports it as a violation, because the model gets no guidance on near-miss cases. Without that clause an agent may call get_refund_status when the user actually wants to create a refund or check return eligibility. The clause should name the tools that handle those cases instead.

**Is the 1024-character limit a hard requirement?**

Yes, the linter enforces it as a maximum length for the description string. In practice most well-written descriptions are far shorter, since the limit exists to push out vague prose rather than to be filled. Anything essential — the use case, the exclusions and the parameter formats — fits comfortably.

**How do I test the linter itself?**

Write pytest cases with deliberately broken fixtures: a tool with a value baked into its name, a tool with a vague description, and a tool with an undocumented parameter. Each must produce the expected violation, and a well-formed tool must produce none. That gives you regression coverage whenever the rules change.
