Architecture Overview
Every Guava call involves two systems working in parallel: Guava's hosted Dialog System and your Expert — a service you provide that connects to Guava's API and steers the conversation.
The Dialog System
The Dialog System is Guava's managed service running in the cloud. It handles everything time-sensitive during a call: receiving the caller's audio, transcribing it, synthesizing a response, and streaming it back to the caller.
Because the entire pipeline runs as a fully integrated architecture rather than a chain of off-the-shelf APIs, the Dialog System delivers best-in-class latency and naturalness.
The Expert
The Expert is the code you write. Using the Guava SDK, it connects to the Dialog System over a persistent WebSocket and steers the agent in real time.
Because your Expert is just code, you can do anything: query a CRM or database, hit an external API, or chain into another specialized AI sub-agent. The Dialog System always interacts with your Expert asynchronously, so you can spend time on complex tasks and reasoning without the caller ever noticing a pause.
During development, your Expert runs on your local machine, and Guava routes calls to it directly. You can rapidly iterate by changing the code and restarting the process — no public web server or ngrok required.
Structured Callbacks
As the Dialog System converses with the caller in natural language, it maintains a separate communication channel with your Expert. This channel consists of callbacks that follow a consistent structure and schema, and are designed to be plugged into backend systems like RAG and intent recognition. You decide which callbacks your Expert can handle — all are optional, and the Dialog System will adapt appropriately.
@agent.on_question
def on_question(call: guava.Call, question: str) -> str:
# The agent will invoke this when the caller asks a question that can't be
# answered from the current context. Use our ready-to-use RAG module. Or
# plug in your own custom one.
return document_qa.ask(question)
@agent.on_action_request
def on_action_request(call: guava.Call, request: str) -> list[SuggestedAction]:
# The agent will invoke this callback when the caller requests a new action.
# Use our ready-to-use intent classification system. Or plug in a custom one.
return intent_recognizer.classify(request)If you want ready-to-use implementations of these callbacks, you can use our helper library which contains implementations of RAG, intent recognition, and more. But all of these are optional modules and we encourage you to build your own domain specific versions.
Assign Tasks to your Agents
Instead of using a single large prompt for your agent, Guava recommends that you use Tasks. A task is a checklist of items that you assign to your agent. These items can include things to say to the caller, as well as information to collect as Fields. You will receive a callback when your Agent has completed your task and is awaiting more instructions.
@agent.on_call_start
def on_call_start(call: guava.Call):
call.set_task(
"waitlist",
objective="Add callers to the waitlist.",
checklist=[
guava.Field(key="caller_name", field_type="text"),
guava.Field(key="party_size", field_type="integer"),
guava.Field(key="phone_number", field_type="text"),
"Read the phone number back to the caller to confirm.",
],
)
@agent.on_task_complete("waitlist")
def on_waitlist_done(call: guava.Call):
print("Added caller to waitlist:", call.get_field("caller_name"))
# End the call, transfer, or chain into another task.
call.hangup("Thank the caller and let them know we'll text when their table is ready.")Deploying your Expert
When it's time to move to production, you'll want your Expert deployed in a highly-available configuration, ready to handle calls at any time. Because Guava Experts only make outbound connections, it's easy to run an Expert behind a NAT or firewall.
You have two options for deploying your Expert:
- Your Infrastructure — deploy to your own servers, VM, or serverless compute platform. You control the environment.
- Guava Hosting — push your Expert with a single
guava deploycommand and Guava manages the rest, including horizontal scaling and redundancy.
See the Deployment guide for a full walkthrough of both options.
What to read next
- The Quickstart shows you how to set up your dev environment and create your first agent.
- The Example Walkthroughs show full Expert implementations for common scenarios, including Q&A and scheduling.
- Once you're comfortable with the basics, the SDK Reference covers every callback and command in detail.
Questions? hi@goguava.ai
@agent.on_question
def on_question(call: guava.Call, question: str) -> str:
# The agent will invoke this when the caller asks a question that can't be
# answered from the current context. Use our ready-to-use RAG module. Or
# plug in your own custom one.
return document_qa.ask(question)
@agent.on_action_request
def on_action_request(call: guava.Call, request: str) -> list[SuggestedAction]:
# The agent will invoke this callback when the caller requests a new action.
# Use our ready-to-use intent classification system. Or plug in a custom one.
return intent_recognizer.classify(request)@agent.on_call_start
def on_call_start(call: guava.Call):
call.set_task(
"waitlist",
objective="Add callers to the waitlist.",
checklist=[
guava.Field(key="caller_name", field_type="text"),
guava.Field(key="party_size", field_type="integer"),
guava.Field(key="phone_number", field_type="text"),
"Read the phone number back to the caller to confirm.",
],
)
@agent.on_task_complete("waitlist")
def on_waitlist_done(call: guava.Call):
print("Added caller to waitlist:", call.get_field("caller_name"))
# End the call, transfer, or chain into another task.
call.hangup("Thank the caller and let them know we'll text when their table is ready.")