"A tool without validation is just a fancy way to fail in production."
Pip, Defensively Programmed AI Agent
The quality of your tools determines the quality of your agent. A model decides whether and how to call a tool based entirely on its name, description, and parameter schema. Poorly designed tools cause incorrect calls, wasted tokens on retries, and security vulnerabilities. This section covers the principles of tool design (atomicity, defensive validation, informative errors), security hardening against prompt injection through tool inputs, and practical patterns for building tools that agents can use reliably in production. The function calling mechanics from Section 23.1 provide the interface layer; this section covers the implementation layer.
Prerequisites
This section builds on agent foundations from Chapter 22 and LLM API basics from Chapter 10.
1. Principles of Tool Design
A tool is only as good as its interface. The model decides whether and how to call your tool based entirely on its name, description, and parameter schema. Poorly designed tools lead to incorrect calls, wasted tokens on retries, and frustrated users. Well-designed tools guide the model toward correct usage through clear naming, comprehensive descriptions, strict input validation, and informative error messages.
The first principle is atomicity: each tool should do one thing well. A tool called manage_database that handles creates, reads, updates, and deletes through a single "action" parameter forces the model to understand a complex interface. Four separate tools (create_record, get_record, update_record, delete_record) are clearer and less error-prone. The model can select the right tool based on the user's intent without parsing a multi-purpose interface.
The second principle is defensive design: assume the model will provide invalid input and handle it gracefully. Validate all parameters against expected types, ranges, and formats. Return clear error messages that explain what went wrong and what the correct input should look like. The model can learn from error messages and self-correct, but only if the error message is informative. "Invalid input" is useless; "Expected 'date' in ISO 8601 format (YYYY-MM-DD), received '12/25/2025'" enables self-correction.
Include examples in your tool descriptions. Instead of just "A SQL query to execute", write "A read-only SQL query to execute against the analytics database. Example: SELECT customer_id, SUM(amount) FROM orders WHERE date > '2025-01-01' GROUP BY customer_id LIMIT 100." Models that see examples in the description produce more accurate tool calls because they have a concrete template to follow.
Input Validation Patterns
This snippet validates and sanitizes tool inputs before execution to prevent injection attacks and malformed data.
from pydantic import BaseModel, Field, validator
from typing import Optional
from datetime import date
class SearchOrdersInput(BaseModel):
"""Search customer orders with filters."""
customer_id: str = Field(
description="Customer ID in format 'CUST-XXXXX'",
pattern=r"^CUST-\d{5}$",
)
start_date: Optional[date] = Field(
default=None,
description="Start date for the search range (ISO 8601: YYYY-MM-DD)",
)
end_date: Optional[date] = Field(
default=None,
description="End date for the search range (ISO 8601: YYYY-MM-DD)",
)
status: Optional[str] = Field(
default=None,
description="Order status filter",
enum=["pending", "processing", "shipped", "delivered", "cancelled"],
)
limit: int = Field(
default=20,
ge=1,
le=100,
description="Maximum number of results to return (1 to 100, default 20)",
)
@validator("end_date")
def end_after_start(cls, v, values):
if v and values.get("start_date") and v < values["start_date"]:
raise ValueError("end_date must be after start_date")
return v
def search_orders_tool(arguments: dict) -> str:
"""Execute the search with validated inputs."""
try:
params = SearchOrdersInput(**arguments)
except Exception as e:
return f"Validation error: {str(e)}. Please fix the input and try again."
results = database.search_orders(
customer_id=params.customer_id,
start_date=params.start_date,
end_date=params.end_date,
status=params.status,
limit=params.limit,
)
return format_results(results)
2. Error Handling and Recovery
Tools fail. APIs return 500 errors, databases time out, rate limits are hit, authentication tokens expire. How your tool handles these failures determines whether the agent can recover gracefully or gets stuck in an error loop. The key principle is: return actionable error information, not stack traces. The model needs to understand what happened and what it can do about it.
Implement a structured error response format that includes the error type (transient vs. permanent), a human-readable explanation, and suggested next steps. Transient errors (timeout, rate limit) should suggest waiting and retrying. Permanent errors (invalid API key, resource not found) should suggest alternative approaches. Include the original parameters in the error response so the model can modify and retry without re-deriving them from the conversation history.
Rate limiting requires special attention in agent systems because agents can make many tool calls in rapid succession. Implement client-side rate limiting in your tool wrapper rather than relying on the agent to pace itself. When a rate limit is hit, return a clear message with the retry-after time rather than an opaque error. Some teams implement automatic backoff within the tool, sleeping and retrying transparently before returning an error to the model.
Think of tool error handling as a conversation with a junior developer. When something goes wrong, you would not dump a stack trace and walk away. You would say: "The database connection timed out after 5 seconds. This usually means the database is under heavy load. You could try again in a moment, or use the cached results from the last successful query." This is exactly what your tool should return. The model is your junior developer: smart enough to handle problems when given clear guidance, but unable to extract meaning from raw error dumps. Good error messages turn tool failures from dead ends into recoverable situations.
Never return raw API responses or stack traces as tool results. A 2,000-character stack trace consumes context window tokens without helping the model reason about the error. Wrap all tool implementations in error handlers that produce concise, structured error messages. Log the full error details server-side for debugging, and return only what the model needs to decide its next action.
3. Security Considerations
Tool access is the primary attack surface for agent systems. A prompt injection that tricks the model into calling delete_all_records or send_email with attacker-controlled content can cause real damage. Defense requires multiple layers: input validation (already covered), output filtering (check tool results before returning them to the model), permission scoping (tools should have minimum necessary access), and action classification (distinguish read-only operations from state-changing ones).
Implement a permission model for your tools. Read-only tools (search, lookup, describe) can be called freely. Write tools (create, update) should require confirmation for sensitive operations. Destructive tools (delete, revoke) should require explicit human approval in the agent loop. This graduated permission model limits the blast radius of prompt injection attacks without crippling the agent's ability to act. See Section 26.1 for a deeper treatment of agent safety.
Who: A site reliability engineering (SRE) team at an e-commerce platform managing 40 Kubernetes microservices.
Situation: The team built a DevOps agent that could diagnose and remediate production incidents. During a weekend traffic spike, the agent correctly identified a memory-starved pod and attempted to fix it by scaling the deployment, but it also scaled down a healthy service that shared a resource quota.
Problem: The agent had uniform permissions across all Kubernetes operations. A single overly broad tool definition let the agent perform read, scale, restart, and delete operations with no distinction between safe and dangerous actions.
Decision: The team implemented a four-tier permission model: Tier 1 (auto-approve) for read-only operations like checking logs and listing pods; Tier 2 (log and proceed) for low-risk writes like restarting individual pods and scaling up; Tier 3 (require human approval via PagerDuty) for scaling down, modifying network policies, and rolling back deployments; Tier 4 (never allow) for deleting namespaces, modifying RBAC, and direct database access.
Result: Over the next quarter, the agent autonomously resolved 62% of Tier 1 and Tier 2 incidents. Zero unauthorized destructive actions occurred, compared to two near-misses in the month before tiering was added.
Lesson: Graduated permission tiers let agents act quickly on safe operations while ensuring that dangerous actions always require human judgment.
Exercises
Why is it important that tool names be descriptive and follow consistent naming conventions? Give an example of a poorly named tool and its improved version, explaining why the improvement helps the LLM.
Answer Sketch
LLMs use tool names and descriptions to decide which tool to call. A tool named fn1 gives the model no information. Renamed to search_customer_orders with a clear description, the model can match user intents to tools reliably. Consistent naming (verb_noun pattern) also helps the model distinguish between similar tools like search_orders vs. create_order.
Write a Python decorator @validate_tool_input that validates tool arguments against a JSON schema before execution. If validation fails, return a structured error message that helps the LLM correct its call.
Answer Sketch
Use the jsonschema library. The decorator receives the schema as a parameter, validates the incoming arguments, and on failure returns a message like: 'Validation error: field "email" must be a valid email address. You provided: "not-an-email". Please retry with a valid email.' This feedback loop helps the LLM self-correct.
Compare two approaches to returning tool errors to an LLM: (a) returning raw stack traces and (b) returning structured error messages with suggested fixes. Which approach leads to better agent behavior, and why?
Answer Sketch
Structured error messages are far better. Raw stack traces contain implementation details (file paths, line numbers) that are meaningless to the LLM and waste tokens. A structured message like 'Error: user_id 12345 not found. Suggestion: verify the user_id with the search_users tool before retrying' gives the model actionable information to recover. This reduces retry loops and improves task completion rates.
An agent has a query_database tool. Write a safe implementation that uses parameterized queries and prevents the LLM from executing arbitrary SQL. Include an allowlist of permitted query patterns.
Answer Sketch
Define allowed query templates (e.g., 'SELECT {columns} FROM orders WHERE customer_id = ?'). The tool parses the LLM's intent, matches it to a template, extracts parameters, and executes the parameterized query. Reject any input that does not match an allowed template. Never pass raw LLM output directly to a SQL engine.
Why should agent tools implement rate limiting? Describe a scenario where an agent without rate-limited tools could cause problems, and propose a solution.
Answer Sketch
An agent in a loop might call an external API hundreds of times if it misinterprets the task or gets stuck. Without rate limiting, this could exhaust API quotas, incur large costs, or trigger IP blocks. Solution: implement per-tool rate limits (e.g., max 10 calls per minute) with a token bucket algorithm. When the limit is reached, return a message telling the agent to wait or use cached results.
- Always validate tool inputs server-side; LLMs can hallucinate parameter values or produce adversarial inputs.
- Good tool design for LLMs means descriptive names, clear parameter descriptions, constrained types, and informative errors.
- Error messages should tell the LLM how to fix the call, not just report the failure.
Show Answer
LLMs can hallucinate parameter values, exceed expected ranges, or produce inputs that cause injection attacks. Input validation acts as a safety net, preventing malformed or malicious arguments from reaching downstream systems regardless of the source.
Show Answer
Tools should have descriptive names, clear parameter descriptions, constrained input types (enums over free text), sensible defaults, and informative error messages. The LLM sees only the schema and descriptions, so clarity in these fields directly determines tool use accuracy.
What Comes Next
In the next section, Agentic RAG, we bring retrieval-augmented generation into the agentic paradigm, where agents autonomously decide when, what, and how to retrieve information.
References and Further Reading
Tool Design and Validation
Demonstrates the importance of clean tool interfaces for LLM tool use, as models learn to call tools based on clear API signatures and documentation.
Shows how tool documentation quality directly impacts API call accuracy, motivating careful schema design with descriptive names and comprehensive parameter descriptions.
Anthropic (2024). "Tool Use (Function Calling)." Anthropic Documentation.
Practical guide covering tool schema best practices, error handling patterns, and security considerations for production tool implementations.
Security and Error Handling
Demonstrates how tool outputs can contain adversarial content that hijacks agent behavior, motivating input sanitization and output validation in tool design.
OWASP (2024). "OWASP Top 10 for LLM Applications."
Industry-standard security guidelines covering excessive agency, insecure output handling, and other vulnerabilities relevant to tool-using LLM systems.
Proposes ToolEmu, a framework that emulates tool execution to identify safety risks in agent tool use before deployment, informing defensive tool design practices.
