Tools and the action space
The tool list is what an agent can do, and therefore what an attacker can make it do. Every tool needs a name, a schema, a validation rule and a failure path.
The action space is a design decision
A model chooses from the tools it is given. Give it twelve and it will confuse two; give it three vague ones and it will use the wrong one confidently. The action space should be small, named after the action and described by when to use it.
Tools that overlap are the most common mistake. search and lookup both find things, and the model has no way to know which is right. Merge them, or make the difference explicit in the descriptions.
The model chooses by reading the declarations, not by inspecting the code. Each name and description is a small piece of evidence, and a list of twelve similar-sounding tools makes the choice harder rather than richer. A useful test is to read the tool list as a new colleague would: if two entries could both answer “where is my order”, the model will sometimes pick the wrong one and sound certain about it.
Validate the arguments, not just the name
A model can invent a tool, omit an argument, or pass a string where a number belongs. Each of those is a normal event: the reply should tell the model what was wrong so it can correct itself on the next turn.
The validation is also a security boundary. A tool that takes a path, a query or a URL must check that the value is one the caller is allowed to use. The model’s confidence is not authorisation.
A tool declaration
@tool
def get_order(order_id: str) -> dict:
"""Look up one order by its id. Use when the user names an order.
Returns status, total and delivery date. Fails if the id is unknown."""
if not re.fullmatch(r"[A-Z]-\d{4}", order_id):
raise ValueError("order_id must look like A-1024")
return db.orders[order_id]A registry with schemas
Run the dispatcher, then add a tool with two required arguments. Ask for it with one missing and confirm the error names the argument rather than failing inside the function.
A tool is an API for a model
Write the declaration as if the reader has never seen your codebase, because the model has not. The name should be the action, the description should say when to use it and when not to, and each argument should carry its expected form. “search(query)” is weaker than “search_policy(query): find policy sections by keyword; use for refund, billing and plan questions; returns section ids” because the second tells the model where it belongs.
The symptom of a weak declaration is a tool that is called for the wrong job, or not called when it should be. Adding more prompt text elsewhere rarely fixes it; the model is choosing from the declaration, so the declaration is where the ambiguity lives. Overlapping tools are the extreme case: merge them or make the boundary explicit.
Errors are part of the interface
A tool that raises an unhandled exception ends the run. A tool that returns a structured error lets the agent recover: “unknown order id”, “rate limited, retry in 30 seconds”, “permission denied”.
The distinction matters for behaviour. “Not found” should change the plan; “rate limited” should wait or stop; “permission denied” should never be retried with a different argument invented by the model.
Write the error shape once and use it everywhere: a kind, a message the model can read, and a flag for whether a retry could help. The flag is the important part. It keeps the decision in the tool that knows the context, rather than in the loop that is only guessing from a string.
Never expose a tool that runs arbitrary code, SQL or shell from model-supplied text. The model is the least trusted component in the system, and that tool hands it the machine.
Idempotence buys retries
If a tool is safe to call twice, a retry after a timeout is free. If it sends an email or charges a card, a retry is a second email or a second charge. Those tools need an idempotency key or a human check.
Mark the read-only tools and keep them separate from the ones with effects. That single split tells you which calls are safe to repeat and which need approval.
A useful exercise is to remove every tool the agent has not used in a week of traces. The list shrinks, the remaining descriptions get sharper, and the model’s choices get easier to predict. Capability you are not using is still capability an injected document can request.
Errors the agent can act on
The tool returns three kinds of failure. Run the loop and check that “not found” changes the plan while “rate limited” stops it.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What is the agent’s action space?
