Tools let your voice agent perform actions during a conversation — check a database, call an API, transfer a call, or anything else you can expose via a webhook or an in-process handler.
Instead of webhook URLs, you can pass a Python callable that runs in-process. This is useful for tools that query local databases, call internal APIs, or perform any logic without an external HTTP endpoint.
async def check_inventory(arguments, context): product_id = arguments["product_id"] # Your custom logic here stock = await db.get_stock(product_id) return {"product_id": product_id, "in_stock": stock > 0, "quantity": stock}agent = phone.agent( system_prompt="You are a product specialist.", tools=[ Patter.tool( name="check_inventory", description="Check if a product is in stock.", parameters={ "type": "object", "properties": { "product_id": {"type": "string", "description": "Product ID"}, }, "required": ["product_id"], }, handler=check_inventory, ), ],)
The handler receives two arguments:
arguments — A dict of the arguments extracted by the AI
context — A dict with call metadata (call_id, caller, callee)
The handler must return a str or dict. Dict values are serialized to JSON before being passed to the AI.
The AI uses the description field to decide when to call a tool. Be specific:
# Good"description": "Look up the customer's order status by order ID. Returns tracking number and estimated delivery date."# Bad"description": "Check order."
Use descriptive parameter names
Parameter names and descriptions help the AI extract the right values from the conversation:
"properties": { "order_id": { "type": "string", "description": "The order ID, usually starts with 'ORD-' followed by 6 digits", },}
Keep responses concise
The AI processes the full JSON response. Large responses add latency. Return only what the AI needs to continue the conversation.
import osimport asynciofrom dotenv import load_dotenvfrom patter import Patterload_dotenv()phone = Patter( twilio_sid=os.environ["TWILIO_SID"], twilio_token=os.environ["TWILIO_TOKEN"], openai_key=os.environ["OPENAI_KEY"], phone_number=os.environ["PHONE_NUMBER"], webhook_url=os.environ["WEBHOOK_URL"],)agent = phone.agent( system_prompt="""You are a customer service agent for an e-commerce store.Help customers check order status and process returns.Always ask for the order ID before looking anything up.""", tools=[ { "name": "get_order_status", "description": "Look up an order by ID. Returns order status, items, and tracking info.", "parameters": { "type": "object", "properties": { "order_id": { "type": "string", "description": "Order ID (e.g., ORD-123456)", }, }, "required": ["order_id"], }, "webhook_url": "https://api.example.com/orders/status", }, { "name": "initiate_return", "description": "Start a return process for a specific order item.", "parameters": { "type": "object", "properties": { "order_id": {"type": "string"}, "item_id": {"type": "string"}, "reason": { "type": "string", "enum": ["defective", "wrong_item", "changed_mind", "other"], }, }, "required": ["order_id", "item_id", "reason"], }, "webhook_url": "https://api.example.com/returns/initiate", }, ], first_message="Hi! I'm here to help with your order. Do you have an order ID I can look up?",)async def main(): await phone.serve(agent, port=8000)asyncio.run(main())