Skip to main content
This example shows how to route inbound product support calls from Amazon Connect to a Guava AI agent that can answer most questions on its own — pulling from a product FAQ — and create an Amazon Connect task when a real human needs to follow up. Unlike the AI Customer Service example, this one keeps the routing simple (no live transfer back to a queue) and focuses on two outcomes: resolve the question on the call, or collect details and hand off to a specialist asynchronously. The Guava agent (Jordan) can:
  • 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:
You also need boto3 for the Amazon Connect API:

Step 2: Set Up Amazon Connect

2a. Create or locate your Connect instance

  1. Open the Amazon Connect console.
  2. Create an instance if you don’t have one, or select an existing one.
  3. 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.
  1. In your Connect instance, go to Routing → Contact flows → Create contact flow.
  2. Name it Guava Support Task Flow (or similar).
  3. Add a Set working queue block and point it to your support queue.
  4. Connect it to a Transfer to queue block, then a Disconnect block.
  5. Save and publish the flow.
  6. 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.
  1. Go to Routing → Contact flows → Create contact flow (type: Inbound contact flow).
  2. Name it Product Support.
  3. 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 transfer or PSTN.
  • Add a Disconnect block on the error branch.
  1. Save and publish the flow.
  2. 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

The IAM user or role needs:

Step 4: Set Environment Variables

Step 5: Run Jordan

You should see:
Jordan is now live. Call your Amazon Connect number (the one you assigned the 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

The knowledge base lives in a sibling 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

Whenever a caller asks something Jordan can’t answer from context alone — “What’s your return policy?”, “Is this jacket machine washable?”, “Do you ship overnight?” — Guava invokes 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

After the 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

If the issue requires escalation, Jordan starts a second task to collect the email and order number. The order number is 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

Once the escalation task completes, the @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 Replace PRODUCT_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:
Combine with a live-transfer escalation path This example only creates async tasks. If some escalations need a live agent immediately (high-value customers, urgent complaints), add a third intent class — e.g. 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).

Complete Example

__main__.py

knowledge_base.py

This file contains the product FAQ document imported by the main script.