Skip to content
chatAgent
Esc
↑↓navigate↵open⌘Jpreview
On this page

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

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

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():

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

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 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 Highest-level system overview
Agent Architecture This document — agent stack
Rivet Actor Model Actor implementation + code
Agent Lifecycle Message → actor creation flow
AgentOS Configuration Per-actor sandbox setup
Pi Agent Setup Pi Agent Core installation
Pi Agent Core API Reference Python API reference
Building Pi Extensions MCP client, formatters, state, analytics
Concurrency & Security Rate limiting, injection prevention
Shopify Integration Research MCP decision
WhatsApp Business Cloud API WhatsApp integration
Integration Guide End-to-end wiring

Last updated on July 26, 2026