- Greet the caller, take their name, and understand what they need
- Answer product, return, shipping, and warranty questions in real time using a product FAQ knowledge base
- Recognize when an issue requires human follow-up (returns, refunds, complaints, damaged items, manager requests) and collect the customer’s email and order number for a specialist to reach out
How It Works
Prerequisites
- Python 3.10 or later
- A Guava account with an API key and a phone number — sign up at app.goguava.ai
- An AWS account with an Amazon Connect instance
Step 1: Install Guava
Choose whichever package manager you prefer:boto3 for the Amazon Connect API:
Step 2: Set Up Amazon Connect
2a. Create or locate your Connect instance
- Open the Amazon Connect console.
- Create an instance if you don’t have one, or select an existing one.
- Note the Instance ID — it’s the UUID at the end of the instance ARN:
2b. Create a contact flow for escalation tasks
Jordan creates Amazon Connect tasks for cases that need specialist follow-up. Tasks need a dedicated contact flow — the default queue flow doesn’t support them.- In your Connect instance, go to Routing → Contact flows → Create contact flow.
- Name it
Guava Support Task Flow(or similar). - Add a Set working queue block and point it to your support queue.
- Connect it to a Transfer to queue block, then a Disconnect block.
- Save and publish the flow.
- Open the flow, click Show additional flow information, and copy the Contact flow ID (the UUID at the end of the ARN).
2c. Create the contact flow that routes to Jordan
This is the contact flow your customers actually call into. It transfers them to Jordan’s Guava number.- Go to Routing → Contact flows → Create contact flow (type: Inbound contact flow).
- Name it
Product Support. - Build the flow:
- Add a Play prompt block: set the text to a brief hold message (or skip it).
- Add a Transfer to phone number block:
- Set Phone number to your Guava agent number (the value of
GUAVA_AGENT_NUMBER). - Set Transfer type to
Softphone transferorPSTN.
- Set Phone number to your Guava agent number (the value of
- Add a Disconnect block on the error branch.
- Save and publish the flow.
- Assign this flow to an inbound phone number: go to Channels → Phone numbers, select a number, and set its contact flow to
Product Support.
No live transfer queue here. Unlike the AI Customer Service example, this example doesn’t transfer escalations back to a live agent — it creates an asynchronous Connect task instead. If you’d rather offer a live transfer for escalations, see the AI Customer Service walkthrough for that pattern.
Step 3: Configure AWS Credentials
Step 4: Set Environment Variables
Step 5: Run Jordan
Product Support flow to in Step 2c) — Connect will transfer the call to Jordan automatically.
What to expect:
- Jordan greets the caller, takes their name, and asks what they need
- For straightforward questions (“How do I wash my jacket?”, “What’s your return window?”, “Do you ship to Canada?”), Jordan answers from the product FAQ in real time and wraps up the call
- For requests that need a human (returns, refunds, complaints, damaged items, “can I speak to a manager”), Jordan collects the customer’s email and order number, creates a Connect task with the issue summary, and tells the customer a specialist will email them within one business day
How the Code Works
The product FAQ knowledge base
knowledge_base.py file as a single multi-line string. Keeping it in its own module makes it easy to swap in a longer corpus, edit copy without touching the agent code, or load from a file at startup. There’s no chunking or indexing required — DocumentQA handles all of that.
Wiring up the agent
Agent is the top-level handle for the AI persona. DocumentQA with no store argument runs in server mode — your documents are uploaded to the Guava server and searched there, so you don’t need to set up GCP credentials or a vector store yourself. The namespace parameter scopes the upload to this agent so it doesn’t collide with documents from other DocumentQA instances in the same account.
DocumentQA is content-addressed: if PRODUCT_FAQ hasn’t changed since the last run, nothing is re-uploaded — startup is instant after the first launch.
Accepting inbound calls
listen_phone opens a persistent WebSocket to the Guava server and waits for calls. Each time a call arrives on Jordan’s number — whether dialed directly or transferred from the Amazon Connect contact flow — the agent’s registered handlers run on a fresh Call object that represents that conversation.
Kicking off the conversation
on_call_start runs as soon as the call connects. We use it to set the first task — a named intake step Jordan works through, greeting the caller, taking their name, and capturing what they need in their own words. The issue_summary field’s description tells Jordan to try to resolve the question first using the knowledge base, and only escalate if she can’t — so simple FAQ questions get answered without ever entering an escalation flow.
Answering questions with a knowledge base
on_question with the question in natural language. DocumentQA.ask retrieves the most relevant chunks from the FAQ and generates an accurate answer. Jordan speaks the answer back and continues the conversation without any perceivable delay.
This decorator is what gives Jordan her FAQ superpowers: she’ll never make up a return policy. If the FAQ doesn’t cover the question, the answer will reflect that, and the customer naturally drops into the escalation path.
Routing after the conversation
intake task finishes, the registered @agent.on_task_complete("intake") handler classifies the outcome using IntentRecognizer and routes accordingly. Using an LLM-based classifier handles natural phrasing correctly — “the seam ripped after one trip” maps to escalation_needed even without explicitly matching “broken” or “return”. The recognizer is instantiated once at module level and reused across calls.
Collecting follow-up details
required=False because not every issue is order-bound (warranty inquiries on long-owned products, general complaints), and the field description explicitly tells Jordan that “if they don’t have one, that’s fine” — so she doesn’t get stuck pestering the customer for something that doesn’t exist.
Creating a Connect task for follow-up
@agent.on_task_complete("escalation") handler creates a task in Amazon Connect via StartTaskContact. The task’s Description carries the full issue summary, and its Attributes make the customer’s name, email, and order number searchable in Connect’s contact search — so support agents can pick up the work in the Contact Control Panel just like any other contact, with full context.
The handler also prints the captured fields as JSON for local debugging and ends the call with a promise of email follow-up within one business day.
Customization Ideas
Use your own product documentation ReplacePRODUCT_FAQ with your real product manuals, policy pages, or help center articles. DocumentQA handles chunking and retrieval automatically — paste in as much text as you need, or load from files at startup:
live_agent — and use call.transfer(...) to send those calls back to a Connect queue, like the AI Customer Service example does.
Look up customer info before answering
If you can identify the caller from their phone number (CRM lookup), pass their order history into the call as variables and reference it from on_call_start. Jordan can then say “I see your recent order shipped on Tuesday” without the customer needing to provide an order number.
Add real-time inventory or order lookups
The @agent.on_question decorator can call any code, not just DocumentQA. Inspect the question, route product-availability questions to your inventory API, route order-status questions to your OMS, and fall back to the FAQ for everything else.
Pass context from Connect to Jordan via SIP
For more advanced setups, Amazon Connect can transfer calls over a SIP trunk to a Guava SIP endpoint. This lets Connect pass the original Contact ID and other attributes as SIP headers, which the agent can read from the call info — and you can link the resulting escalation task back to the original call using RelatedContactId for full call chain reporting (see the CSAT Survey example for the same RelatedContactId pattern).