---
title: Agent Architecture
---
# Agent Architecture

## Overview

The agent layer is the core intelligence of the WhatsApp shopping bot. Each user gets a **durable Rivet actor** that runs inside an AgentOS V8 isolate, powered by Pi Agent Core as the LLM orchestration runtime.

> **Design choice**: Agents are **Rivet actors**, not an in-process Python pool. Rivet provides durability, distribution, built-in message queuing, serialized execution, and idle lifecycle management for free. The FastAPI layer is a thin gateway that only extracts messages and routes them to actors.

## Agent Stack

```mermaid
graph TB
    subgraph "Thin Gateway (FastAPI)"
        WA_WEBHOOK[WhatsApp Webhook]
        ROUTER[Router / Dispatcher]
    end

    subgraph "Rivet Actor Cluster"
        RIVET[Rivet Runtime]

        subgraph "Actor: User A"
            AOS_A[AgentOS V8 Isolate]
            PI_A[Pi Agent Core]
            MCP_A[MCP Client Extension]
            FMT_A[WhatsApp Formatter]
            STATE_A[State Manager]
            ANAL_A[Analytics]
        end

        subgraph "Actor: User B"
            AOS_B[AgentOS V8 Isolate]
            PI_B[Pi Agent Core]
            MCP_B[MCP Client Extension]
        end

        subgraph "Actor: User N"
            AOS_N[AgentOS V8 Isolate]
            PI_N[Pi Agent Core]
            MCP_N[MCP Client Extension]
        end
    end

    subgraph "External Services"
        MCP_SERVER["@shopify/dev-mcp"]
        LLM[LLM Provider]
        WA_API[WhatsApp Cloud API]
        STATE[(Rivet Durable State)]
    end

    WA_WEBHOOK --> ROUTER
    ROUTER -->|get_actor(user_id)| RIVET
    RIVET --> AOS_A
    RIVET --> AOS_B
    RIVET --> AOS_N

    AOS_A --> PI_A
    PI_A --> MCP_A
    PI_A --> FMT_A
    PI_A --> STATE_A
    PI_A --> ANAL_A

    MCP_A --> MCP_SERVER
    PI_A --> LLM
    FMT_A --> WA_API
    STATE_A --> STATE

    style RIVET fill:#f3e5f5,stroke:#7b1fa2,stroke-width:3px
    style PI_A fill:#e1f5fe,stroke:#0277bd,stroke-width:2px
    style AOS_A fill:#fff9c4,stroke:#f9a825,stroke-width:2px
    style MCP_SERVER fill:#c8e6c9,stroke:#388e3c
```

## Layers of Responsibility

| Layer | Responsibility | Technology |
|-------|---------------|------------|
| **Messaging Layer** | Receive/send WhatsApp messages, verify signatures | WhatsApp Cloud API |
| **Gateway Layer** | Thin webhook handler, route to Rivet, return 200 immediately | FastAPI |
| **Orchestration Layer** | Durable actor lifecycle, routing, sleep/wake/terminate | Rivet |
| **Sandbox Layer** | Per-actor isolated OS (filesystem, network, processes) | AgentOS |
| **Agent Runtime** | LLM calls, tool orchestration, streaming, events | Pi Agent Core |
| **Extension Layer** | Shopify tools, WhatsApp formatting, state, analytics | Custom extensions |
| **E-commerce Layer** | Products, cart, checkout, orders, policies | @shopify/dev-mcp |
| **State Layer** | Durable persistence across sleep/wake/restarts | Rivet Durable State |

## Data Flow

```mermaid
sequenceDiagram
    actor User
    participant WA as WhatsApp Cloud API
    participant GW as FastAPI Gateway
    participant RV as Rivet Runtime
    participant ACTOR as ShoppingAgentActor
    participant AOS as AgentOS Isolate
    participant PI as Pi Agent
    participant LLM as LLM Provider
    participant MCP as @shopify/dev-mcp
    participant SHOP as Shopify Storefront

    User->>WA: "Find me red shoes under $50"
    WA->>GW: POST /webhook
    GW-->>WA: 200 OK (immediate)

    GW->>RV: get_actor(ShoppingAgentActor, user_id)

    alt First message (actor doesn't exist)
        RV->>ACTOR: Create actor
        ACTOR->>ACTOR: on_init()
        ACTOR->>MCP: Initialize MCP client
        ACTOR->>PI: Create Pi Agent, register tools
    else Returning user
        RV->>ACTOR: Wake actor (if sleeping)
        ACTOR->>ACTOR: on_wake()
    end

    RV-->>GW: actor reference
    GW->>ACTOR: on_message(text)
    Note over ACTOR: Rivet serializes: one<br/>execution at a time per actor

    ACTOR->>PI: agent.prompt(text)
    PI->>LLM: Stream completion
    LLM-->>PI: Tool call: search_shop_catalog
    PI->>MCP: Execute tool
    MCP->>SHOP: GraphQL query
    SHOP-->>MCP: Product results
    MCP-->>PI: Structured result
    PI->>LLM: Continue with result
    LLM-->>PI: Final response
    PI-->>ACTOR: Response

    ACTOR->>ACTOR: Save state (durable)
    ACTOR-->>GW: Response
    GW->>WA: POST /messages (send reply)
    WA-->>User: "Found 5 products..."

    Note over ACTOR: Actor sleeps after 5min idle
```

## Extension Model

Each Rivet actor contains a Pi Agent instance with extensions registered at `on_init()`:

```mermaid
graph LR
    subgraph "Rivet Actor (per user)"
        PI[Pi Agent Core]
        TOOL_REG[Tool Registry]
        EVENT_BUS[Event Bus]
        CTX[Context Manager]

        subgraph "Extensions"
            E1["MCP Client<br/>(Shopify tools)"]
            E2[WhatsApp Formatter]
            E3[State Manager]
            E4[Analytics]
        end
    end

    PI --> E1
    PI --> E2
    PI --> E3
    PI --> E4

    TOOL_REG -.->|registers tools| E1
    EVENT_BUS -.->|emits events| E2
    EVENT_BUS -.->|emits events| E4
    CTX -.->|persists| E3

    style PI fill:#e1f5fe,stroke:#0277bd,stroke-width:2px
```

## What Rivet Gives Us for Free

| Concern | In-Process Pool (rejected) | Rivet Actors (chosen) |
|---------|---------------------------|----------------------|
| Agent creation | Manual dict management | `get_actor()` creates on demand |
| Durability | ❌ Lost on process crash | ✅ Survives restarts |
| Distribution | ❌ Single process only | ✅ Cluster-wide |
| Concurrency | Build asyncio.Lock per user | ✅ Serialized per actor |
| Message queue | Build asyncio.Queue per user | ✅ Built-in per actor |
| State persistence | Manual save to S3/Redis | ✅ Rivet Durable State |
| Idle management | Build cleanup loop | ✅ Built-in sleep/wake/terminate |
| Failure recovery | ❌ Lose all state | ✅ Resume from last checkpoint |
| Scaling | ❌ Vertical only | ✅ Horizontal |

**Decision**: Rivet actors win on every axis except "no additional dependency". We're happy to pay that cost.

## Actor Lifecycle Summary

```mermaid
stateDiagram-v2
    [*] --> NotExists: User has never texted
    NotExists --> Creating: First webhook hit
    Creating --> Running: on_init() completes
    Running --> Running: on_message() serialized
    Running --> Sleeping: Idle timeout (5 min)
    Sleeping --> Running: New message wakes
    Sleeping --> Terminated: Extended idle (1 hr)
    Terminated --> NotExists: Actor destroyed
    Terminated --> Creating: Next message recreates
```

See [Rivet Actor Model](/rivet-actor-model) for the full implementation.

## Key Design Principles

1. **Actor-per-user** — Each WhatsApp user maps to one durable Rivet actor
2. **Thin gateway** — FastAPI does no agent work, only routes
3. **Tool-first** — Agents execute Shopify MCP tools, not just generate text
4. **Stateful conversations** — Durable state survives sleep/wake
5. **E-commerce native** — Official `@shopify/dev-mcp` for all shopping operations
6. **Defense in depth** — Rate limits, injection detection, input/output sanitization

## Documents in This Vault

| Document | Scope |
|----------|-------|
| [Architecture](/architecture) | Highest-level system overview |
| [Agent Architecture](/agent-architecture) | This document — agent stack |
| [Rivet Actor Model](/rivet-actor-model) | Actor implementation + code |
| [Agent Lifecycle](/agent-lifecycle) | Message → actor creation flow |
| [AgentOS Configuration](/agentos-configuration) | Per-actor sandbox setup |
| [Pi Agent Setup](/pi-agent-setup) | Pi Agent Core installation |
| [Pi Agent Core API Reference](/pi-agent-core-api-reference) | Python API reference |
| [Building Pi Extensions](/building-pi-extensions) | MCP client, formatters, state, analytics |
| [Concurrency & Security](/concurrency-security) | Rate limiting, injection prevention |
| [Shopify Integration Research](/shopify-integration-research) | MCP decision |
| [WhatsApp Business Cloud API](/whatsapp-business-cloud-api) | WhatsApp integration |
| [Integration Guide](/integration-guide) | End-to-end wiring |
