Designing a Production-Ready Toolset for AI Agents
Design robust APIs for LLMs. Master tool sequencing prerequisites, semantic description engineering, policy verification gates, and atomic execution boundaries.

TL;DR: Designing tools for an AI agent is fundamentally different from designing APIs for human software engineers. An LLM cannot inspect your backend code β it only sees the tool name, parameter schemas, and text descriptions. In this guide, we establish the 5 core principles of agent tool design, enforce execution prerequisites, and separate policy checks from state mutations.
π¦ Interactive Notebook: Test and run this code live on GitHub:
11_production_toolset.ipynbin the ai-architect-foundation-gemini repository.
What You'll Learn
How to architect a coherent toolset for production agents, how to write descriptions that prevent hallucinated parameters, how to enforce verification prerequisites (e.g. verify customer $\to$ lookup order $\to$ process action), and how to prevent dangerous unauthorized actions.
The Core Concept: Tools Are the Agent's API
In traditional software development, developers read Swagger/OpenAPI docs and write deterministic client calls.
In an agentic system:
- The model reads tool descriptions at runtime as part of its prompt context.
- The model decides invocation order based solely on its reasoning.
- Ambiguous descriptions lead to catastrophic failures (e.g. calling
process_refundbefore checking whether the customer actually returned the item).
The 5 Principles of Agent Tool Design
1. Granular Single Responsibility
Avoid creating monolithic "God tools" like handle_customer_issue(type, details). Create distinct, single-purpose tools:
verify_customer(email, session_token)lookup_order(order_id)check_return_eligibility(order_id, reason)process_refund(order_id, amount)
2. Enforce Strict Execution Sequencing via Preconditions
Tools that execute financial or destructive actions must require proof of earlier verification steps:
# β Dangerous: Any agent can call this with arbitrary numbers
def process_refund(order_id: str, amount: float):
...
# β
Safe: Requires verified customer_id and eligibility_token
def process_refund(customer_id: str, order_id: str, amount: float, eligibility_token: str):
...3. Separate Policy Checks from Mutation Actions
Never combine "check if allowed" and "execute action" into a single opaque step. When policy evaluation is a separate tool (check_return_eligibility), the agent can report policy rules back to the user without executing unnecessary transactions.
4. Provide Self-Documenting Return Schemas
Ensure tool return values explicitly guide the next logical step:
{
"status": "success",
"eligible_for_refund": true,
"max_refundable_amount": 89.99,
"next_step": "Call process_refund with amount <= 89.99"
}5. Backend Hard Limits Over Prompt Soft Limits
Never rely on prompt instructions alone to prevent large financial transfers. Enforce hard safety gates in your Python backend:
def process_refund(order_id: str, amount: float) -> dict:
# HARD GATE: Never allow autonomous refunds > $100
if amount > 100.00:
return {
"status": "blocked",
"error_category": "policy_limit",
"message": f"Refund of ${amount:.2f} exceeds automatic agent threshold ($100.00).",
"suggested_action": "Call escalate_to_human tool with case summary."
}
...Complete EcommAssist Toolset Implementation
Here is how a production-grade customer support toolset is defined in Gemini:
from google import genai
from google.genai import types
# Tool 1: Verification
verify_tool = types.FunctionDeclaration(
name="verify_customer",
description="Verifies customer session token. MUST be called first before accessing private order data.",
parameters=types.Schema(
type="OBJECT",
properties={
"session_token": types.Schema(type="STRING", description="Active user session token.")
},
required=["session_token"]
)
)
# Tool 2: Order Lookup
lookup_tool = types.FunctionDeclaration(
name="lookup_order",
description="Fetch order status, items, delivery dates, and return flags. Requires verified customer_id.",
parameters=types.Schema(
type="OBJECT",
properties={
"customer_id": types.Schema(type="STRING"),
"order_id": types.Schema(type="STRING")
},
required=["customer_id", "order_id"]
)
)
# Tool 3: Policy Evaluator
policy_tool = types.FunctionDeclaration(
name="check_return_policy",
description="Evaluates store return policy against order delivery date and item condition. Returns eligibility boolean and max allowed refund.",
parameters=types.Schema(
type="OBJECT",
properties={
"order_id": types.Schema(type="STRING"),
"return_reason": types.Schema(type="STRING", enum=["damaged", "wrong_size", "buyer_remorse"])
},
required=["order_id", "return_reason"]
)
)
# Tool 4: Refund Execution (Gated)
refund_tool = types.FunctionDeclaration(
name="process_refund",
description="Issues monetary refund to customer card. Only call if check_return_policy returned eligible=true.",
parameters=types.Schema(
type="OBJECT",
properties={
"customer_id": types.Schema(type="STRING"),
"order_id": types.Schema(type="STRING"),
"amount": types.Schema(type="NUMBER", description="Refund amount in USD.")
},
required=["customer_id", "order_id", "amount"]
)
)
# Tool 5: Human Escalation
escalate_tool = types.FunctionDeclaration(
name="escalate_to_human",
description="Escalates case to a human support agent when policy limits are exceeded or customer dispute occurs.",
parameters=types.Schema(
type="OBJECT",
properties={
"order_id": types.Schema(type="STRING"),
"reason": types.Schema(type="STRING"),
"case_summary": types.Schema(type="STRING")
},
required=["reason", "case_summary"]
)
)
production_toolset = [
types.Tool(function_declarations=[
verify_tool,
lookup_tool,
policy_tool,
refund_tool,
escalate_tool
])
]
# Test sequential tool execution
print("1. Customer Verification:", verify_customer("alex@example.com"))
print("2. Order Lookup:", lookup_order("ORD-12345678"))
print("3. Policy Evaluation:", check_return_policy("ORD-12345678"))
# --- Execution Output ---
# 1. Customer Verification: {"status": "success", "customer_id": "CUS-1001", "name": "Alex Morgan", "verified": true}
# 2. Order Lookup: {"status": "success", "order_id": "ORD-12345678", "item": "Mechanical Keyboard", "delivered_days_ago": 12}
# 3. Policy Evaluation: {"status": "success", "eligible_for_refund": true, "reason": "Order delivered 12 days ago (within 30-day window)."}Key Takeaways
- Tool Descriptions Guide Planning: Invest in precise, clear documentation for each function declaration.
- Backend Enforcement Over Prompt Prompts: Enforce validation, authentication, and financial limits in Python code.
- Always Provide Escape Hatches: Include an
escalate_to_humantool whenever automated rules cannot resolve a dispute.
What's Next
In Part 12: Multi-Agent Architecture: Coordinator and Specialist Agents, we explore decomposing complex workflows across specialized child agents with narrow scopes.