Inbound w/ Form Filling
In this example, we'll build an inbound voice agent for a fictional restaurant. Callers can join the waitlist by providing their name, party size, and a callback number — which the agent collects conversationally using a structured task.
Define the Agent
guava.Agent is our starting point for building Guava agents. We'll create one with a name and purpose scoped to the restaurant.
import guava
agent = guava.Agent(
name="Mia",
organization="Thai Palace",
purpose="Helping callers join the restaurant waitlist",
)Accept or reject the call
on_call_received fires before the call starts and gives you a chance to accept or reject based on caller info. Here we accept all calls.
@agent.on_call_received
def on_call_received(call_info: guava.CallInfo) -> guava.IncomingCallAction:
return guava.AcceptCall()If you don't register on_call_received, Guava accepts all calls by default. Implement it only when you need to screen callers or look up information based off the incoming phone number.
Set up the form
on_call_start fires at the beginning of every accepted call. We use set_task to hand the agent a structured checklist of fields to collect. The agent gathers each piece of information conversationally — it knows when all fields are filled and automatically moves on.
@agent.on_call_start
def on_call_start(call: guava.Call) -> None:
call.set_task(
"waitlist",
objective="You are a virtual assistant for Thai Palace. Add callers to the waitlist.",
checklist=[
guava.Field(key="caller_name", field_type="text", description="Name for the waitlist"),
guava.Field(key="party_size", field_type="integer", description="Number of people"),
guava.Field(
key="phone_number",
field_type="text",
description="Phone number to text when the table is ready",
),
"Read the phone number back to the caller to confirm.",
],
)The checklist can mix Field objects (typed, named values the agent extracts) with plain strings (freeform instructions the agent follows). Fields are retrievable later via get_field().
Handle task completion
on_task_complete fires once every field in the checklist is collected. This is the right place to save the data to your backend, trigger a notification, or hang up.
@agent.on_task_complete("waitlist")
def on_waitlist_done(call: guava.Call) -> None:
logger.info(
"Added %s, party of %d, to waitlist.",
call.get_field("caller_name"),
call.get_field("party_size"),
)
call.hangup("Thank the caller and let them know we'll text when their table is ready.")Start the agent
Attach the agent to a phone number to start receiving inbound calls.
# Run this to attach your agent to a phone number. Call your agent's number to talk to it.
agent.listen_phone(os.environ["GUAVA_AGENT_NUMBER"])
# Run this to receive a WebRTC link where you can talk to your agent in the browser.
agent.listen_webrtc()
# Run this to talk to your agent using your local audio device.
agent.call_local()Complete example
import os
import guava
import logging
import argparse
from guava import logging_utils
logger = logging.getLogger("thai_palace")
agent = guava.Agent(
name="Mia",
organization="Thai Palace",
purpose="Helping callers join the restaurant waitlist",
)
@agent.on_call_received
def on_call_received(call_info: guava.CallInfo) -> guava.IncomingCallAction:
return guava.AcceptCall()
@agent.on_call_start
def on_call_start(call: guava.Call) -> None:
call.set_task(
"waitlist",
objective="You are a virtual assistant for Thai Palace. Add callers to the waitlist.",
checklist=[
guava.Field(key="caller_name", field_type="text", description="Name for the waitlist"),
guava.Field(key="party_size", field_type="integer", description="Number of people"),
guava.Field(
key="phone_number",
field_type="text",
description="Phone number to text when the table is ready",
),
"Read the phone number back to the caller to confirm.",
],
)
@agent.on_task_complete("waitlist")
def on_waitlist_done(call: guava.Call) -> None:
logger.info(
"Added %s, party of %d, to waitlist.",
call.get_field("caller_name"),
call.get_field("party_size"),
)
call.hangup("Thank the caller and let them know we'll text when their table is ready.")
if __name__ == "__main__":
logging_utils.configure_logging()
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--phone", action="store_true", help="Listen for phone calls.")
group.add_argument("--webrtc", action="store_true", help="Create on a WebRTC code.")
group.add_argument("--local", action="store_true", help="Start a local call.")
args = parser.parse_args()
if args.phone:
agent.listen_phone(os.environ["GUAVA_AGENT_NUMBER"])
elif args.webrtc:
agent.listen_webrtc()
else:
agent.call_local()Questions? hi@goguava.ai
import guava
agent = guava.Agent(
name="Mia",
organization="Thai Palace",
purpose="Helping callers join the restaurant waitlist",
)@agent.on_call_start
def on_call_start(call: guava.Call) -> None:
call.set_task(
"waitlist",
objective="You are a virtual assistant for Thai Palace. Add callers to the waitlist.",
checklist=[
guava.Field(key="caller_name", field_type="text", description="Name for the waitlist"),
guava.Field(key="party_size", field_type="integer", description="Number of people"),
guava.Field(
key="phone_number",
field_type="text",
description="Phone number to text when the table is ready",
),
"Read the phone number back to the caller to confirm.",
],
)@agent.on_task_complete("waitlist")
def on_waitlist_done(call: guava.Call) -> None:
logger.info(
"Added %s, party of %d, to waitlist.",
call.get_field("caller_name"),
call.get_field("party_size"),
)
call.hangup("Thank the caller and let them know we'll text when their table is ready.")# Run this to attach your agent to a phone number. Call your agent's number to talk to it.
agent.listen_phone(os.environ["GUAVA_AGENT_NUMBER"])
# Run this to receive a WebRTC link where you can talk to your agent in the browser.
agent.listen_webrtc()
# Run this to talk to your agent using your local audio device.
agent.call_local()import os
import guava
import logging
import argparse
from guava import logging_utils
logger = logging.getLogger("thai_palace")
agent = guava.Agent(
name="Mia",
organization="Thai Palace",
purpose="Helping callers join the restaurant waitlist",
)
@agent.on_call_received
def on_call_received(call_info: guava.CallInfo) -> guava.IncomingCallAction:
return guava.AcceptCall()
@agent.on_call_start
def on_call_start(call: guava.Call) -> None:
call.set_task(
"waitlist",
objective="You are a virtual assistant for Thai Palace. Add callers to the waitlist.",
checklist=[
guava.Field(key="caller_name", field_type="text", description="Name for the waitlist"),
guava.Field(key="party_size", field_type="integer", description="Number of people"),
guava.Field(
key="phone_number",
field_type="text",
description="Phone number to text when the table is ready",
),
"Read the phone number back to the caller to confirm.",
],
)
@agent.on_task_complete("waitlist")
def on_waitlist_done(call: guava.Call) -> None:
logger.info(
"Added %s, party of %d, to waitlist.",
call.get_field("caller_name"),
call.get_field("party_size"),
)
call.hangup("Thank the caller and let them know we'll text when their table is ready.")
if __name__ == "__main__":
logging_utils.configure_logging()
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--phone", action="store_true", help="Listen for phone calls.")
group.add_argument("--webrtc", action="store_true", help="Create on a WebRTC code.")
group.add_argument("--local", action="store_true", help="Start a local call.")
args = parser.parse_args()
if args.phone:
agent.listen_phone(os.environ["GUAVA_AGENT_NUMBER"])
elif args.webrtc:
agent.listen_webrtc()
else:
agent.call_local()