import { NextLink, Callout } from '../views/docs/prose';
import { ExpertAnimation } from '../views/docs/ExpertAnimation';
import { CodeTabs } from '../views/docs/CodeTabs';

## 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.

<svg viewBox="0 0 760 285" xmlns="http://www.w3.org/2000/svg" className="w-full my-8">
  <defs>
    <marker id="ahd" markerWidth="8" markerHeight="7" refX="7" refY="3.5" orient="auto">
      <polygon points="0 0, 8 3.5, 0 7" fill="rgba(255,255,255,0.25)"/>
    </marker>
  </defs>

  
  <rect x="144" y="47" width="256" height="130" rx="14" fill="#1e1f21" stroke="rgba(255,255,255,0.08)" strokeWidth="1.5"/>
  <text x="272" y="65" textAnchor="middle" fill="#555558" fontSize="9" fontFamily="ui-monospace, monospace" letterSpacing="2">GUAVA CLOUD</text>

  
  <rect x="160" y="72" width="224" height="82" rx="8" fill="#272728" stroke="rgba(26,147,254,0.3)" strokeWidth="1.5"/>
  <text x="272" y="110" textAnchor="middle" fill="#1a93fe" fontSize="14" fontFamily="ui-monospace, monospace" fontWeight="700">Dialog System</text>
  <text x="272" y="132" textAnchor="middle" fill="#555558" fontSize="10" fontFamily="ui-monospace, monospace">audio · STT · LLM · TTS</text>

  
  <circle cx="65" cy="115" r="32" fill="#272728" stroke="rgba(255,255,255,0.1)" strokeWidth="1.5"/>
  
  <path d="M 51,113 C 51,103 58,97 65,97 C 72,97 79,103 79,113" fill="none" stroke="#dadada" strokeWidth="1.8" strokeLinecap="round"/>
  <rect x="47" y="113" width="8" height="12" rx="4" fill="#272728" stroke="#dadada" strokeWidth="1.5"/>
  <rect x="75" y="113" width="8" height="12" rx="4" fill="#272728" stroke="#dadada" strokeWidth="1.5"/>
  <text x="65" y="168" textAnchor="middle" fill="#acacac" fontSize="11" fontFamily="ui-monospace, monospace">Caller</text>

  
  <line x1="97" y1="111" x2="137" y2="111" stroke="#acacac" strokeWidth="1.5"/>
  <polygon points="144,111 137,109 137,113" fill="#acacac"/>
  <line x1="104" y1="119" x2="144" y2="119" stroke="#acacac" strokeWidth="1.5"/>
  <polygon points="97,119 104,117 104,121" fill="#acacac"/>
  <text x="120" y="103" textAnchor="middle" fill="#555558" fontSize="9" fontFamily="ui-monospace, monospace">audio</text>

  
  <line x1="400" y1="111" x2="458" y2="111" stroke="#acacac" strokeWidth="1.5"/>
  <polygon points="465,111 458,109 458,113" fill="#acacac"/>
  <line x1="407" y1="119" x2="465" y2="119" stroke="#acacac" strokeWidth="1.5"/>
  <polygon points="400,119 407,117 407,121" fill="#acacac"/>
  <text x="432" y="103" textAnchor="middle" fill="#555558" fontSize="9" fontFamily="ui-monospace, monospace">WebSocket</text>

  
  <rect x="465" y="72" width="186" height="82" rx="8" fill="#272728" stroke="rgba(255,255,255,0.12)" strokeWidth="1.5"/>
  <text x="558" y="108" textAnchor="middle" fill="#dadada" fontSize="13" fontFamily="ui-monospace, monospace" fontWeight="700">Your Expert</text>
  <text x="558" y="130" textAnchor="middle" fill="#555558" fontSize="10" fontFamily="ui-monospace, monospace">Python · TypeScript · ...</text>

  
  <line x1="558" y1="154" x2="465" y2="207" stroke="rgba(255,255,255,0.2)" strokeWidth="1.2" strokeDasharray="5 3" markerEnd="url(#ahd)"/>
  <line x1="558" y1="154" x2="650" y2="207" stroke="rgba(255,255,255,0.2)" strokeWidth="1.2" strokeDasharray="5 3" markerEnd="url(#ahd)"/>

  
  <rect x="385" y="210" width="160" height="56" rx="8" fill="#1e1f21" stroke="rgba(255,255,255,0.1)" strokeWidth="1"/>
  <text x="465" y="234" textAnchor="middle" fill="#dadada" fontSize="11" fontFamily="ui-monospace, monospace" fontWeight="600">Your Infrastructure</text>
  <text x="465" y="252" textAnchor="middle" fill="#555558" fontSize="9.5" fontFamily="ui-monospace, monospace">local or self-hosted</text>

  
  <text x="559" y="242" textAnchor="middle" fill="#555558" fontSize="9" fontFamily="ui-monospace, monospace">or</text>

  
  <rect x="573" y="210" width="152" height="56" rx="8" fill="#1e1f21" stroke="rgba(26,147,254,0.35)" strokeWidth="1.5"/>
  <text x="649" y="234" textAnchor="middle" fill="#dadada" fontSize="11" fontFamily="ui-monospace, monospace" fontWeight="600">Guava Hosting</text>
  <text x="649" y="252" textAnchor="middle" fill="#555558" fontSize="9.5" fontFamily="ui-monospace, monospace">managed by Guava</text>

  
  <circle r="2.5" fill="#1a93fe">
    <animateMotion dur="1.5s" repeatCount="indefinite" calcMode="linear" path="M 97,111 L 144,111" />
    <animate attributeName="opacity" dur="1.5s" repeatCount="indefinite" keyTimes="0;0.1;0.9;1" values="0;1;1;0" calcMode="linear" />
  </circle>
  <circle r="2.5" fill="#1a93fe">
    <animateMotion dur="1.5s" repeatCount="indefinite" calcMode="linear" begin="-0.75s" path="M 97,111 L 144,111" />
    <animate attributeName="opacity" dur="1.5s" repeatCount="indefinite" begin="-0.75s" keyTimes="0;0.1;0.9;1" values="0;1;1;0" calcMode="linear" />
  </circle>
  <circle r="2.5" fill="#1a93fe">
    <animateMotion dur="1.5s" repeatCount="indefinite" calcMode="linear" path="M 144,119 L 97,119" />
    <animate attributeName="opacity" dur="1.5s" repeatCount="indefinite" keyTimes="0;0.1;0.9;1" values="0;1;1;0" calcMode="linear" />
  </circle>
  <circle r="2.5" fill="#1a93fe">
    <animateMotion dur="1.5s" repeatCount="indefinite" calcMode="linear" begin="-0.75s" path="M 144,119 L 97,119" />
    <animate attributeName="opacity" dur="1.5s" repeatCount="indefinite" begin="-0.75s" keyTimes="0;0.1;0.9;1" values="0;1;1;0" calcMode="linear" />
  </circle>

  
  <circle r="2.5" fill="#1a93fe">
    <animateMotion dur="1.5s" repeatCount="indefinite" calcMode="linear" path="M 400,111 L 465,111" />
    <animate attributeName="opacity" dur="1.5s" repeatCount="indefinite" keyTimes="0;0.1;0.9;1" values="0;1;1;0" calcMode="linear" />
  </circle>
  <circle r="2.5" fill="#1a93fe">
    <animateMotion dur="1.5s" repeatCount="indefinite" calcMode="linear" begin="-0.75s" path="M 400,111 L 465,111" />
    <animate attributeName="opacity" dur="1.5s" repeatCount="indefinite" begin="-0.75s" keyTimes="0;0.1;0.9;1" values="0;1;1;0" calcMode="linear" />
  </circle>
  <circle r="2.5" fill="#1a93fe">
    <animateMotion dur="1.5s" repeatCount="indefinite" calcMode="linear" path="M 465,119 L 400,119" />
    <animate attributeName="opacity" dur="1.5s" repeatCount="indefinite" keyTimes="0;0.1;0.9;1" values="0;1;1;0" calcMode="linear" />
  </circle>
  <circle r="2.5" fill="#1a93fe">
    <animateMotion dur="1.5s" repeatCount="indefinite" calcMode="linear" begin="-0.75s" path="M 465,119 L 400,119" />
    <animate attributeName="opacity" dur="1.5s" repeatCount="indefinite" begin="-0.75s" keyTimes="0;0.1;0.9;1" values="0;1;1;0" calcMode="linear" />
  </circle>
</svg>

### 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](/docs/agent), 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

<ExpertAnimation />

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.

export const ARCH_CALLBACKS_PY = `@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)`;

export const ARCH_CALLBACKS_TS = `agent.onQuestion(async (call: guava.Call, question: string) => {
  // 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 await documentQA.ask(question);
});

agent.onActionRequest(async (call: guava.Call, request: string) => {
  // 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 await intentRecognizer.classify(request);
});`;

<CodeTabs
  python={{ code: ARCH_CALLBACKS_PY, filename: "expert.py" }}
  typescript={{ code: ARCH_CALLBACKS_TS, filename: "expert.ts" }}
/>

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](./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](./field).
You will receive a callback when your Agent has completed your task and is awaiting more instructions.

export const ARCH_TASK_PY = `@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.")`;

export const ARCH_TASK_TS = `agent.onCallStart(async (call) => {
  await call.setTask({
    taskId: "waitlist",
    objective: "Add callers to the waitlist.",
    checklist: [
      guava.Field({ key: "caller_name", fieldType: "text" }),
      guava.Field({ key: "party_size", fieldType: "integer" }),
      guava.Field({ key: "phone_number", fieldType: "text" }),
      "Read the phone number back to the caller to confirm.",
    ],
  });
});

agent.onTaskComplete("waitlist", async (call) => {
  console.log("Added caller to waitlist:", await call.getField("caller_name"));
  // End the call, transfer, or chain into another task.
  await call.hangup("Thank the caller and let them know we'll text when their table is ready.");
});`;

<CodeTabs
  python={{ code: ARCH_TASK_PY, filename: "expert.py" }}
  typescript={{ code: ARCH_TASK_TS, filename: "expert.ts" }}
/>

### 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 deploy`](/docs/cli-reference) command and Guava manages the rest, including horizontal scaling and redundancy.

See the [Deployment guide](/docs/deployment) for a full walkthrough of both options.

### What to read next

- The [Quickstart](/docs/quickstart) shows you how to set up your dev environment and create your first agent.
- The [Example Walkthroughs](/docs/inbound-rag-example) show full Expert implementations for common scenarios, including Q&A and scheduling.
- Once you're comfortable with the basics, the [SDK Reference](/docs/runner) covers every callback and command in detail.

<NextLink section="quickstart" label="Quickstart" />
