Skip to content

The Agent System

The brain that connects everything.


Overview

The Yebo Agent is the AI orchestration layer that:

  • Understands what users want
  • Routes to the right product
  • Maintains conversation context
  • Executes actions
  • Responds naturally

Architecture

┌─────────────────────────────────────────────────────────────┐
│                    USER MESSAGE                              │
│  "Sell these shoes for 2000"                                │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│                   INTENT RECOGNITION                         │
│  Model: Gemini 2.5 Flash                                    │
│  Output: { intent: "sell", entities: { price: 2000 } }     │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│                   CONTEXT MANAGER                            │
│  Load: user profile, conversation history, pending actions  │
│  Update: conversation state                                  │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│                   PRODUCT ROUTER                             │
│  Intent "sell" → YeboShops adapter                          │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│                   ACTION EXECUTOR                            │
│  YeboShops.createListing({ price: 2000, photos: [...] })   │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│                   RESPONSE FORMATTER                         │
│  Generate natural language + UI cards                       │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│                    USER RESPONSE                             │
│  "Listed! Nike Air Max for KES 2,000. I'll notify you      │
│   when someone's interested."                               │
└─────────────────────────────────────────────────────────────┘

Components

1. Intent Recognition

Uses Gemini to classify user messages:

javascript
const INTENT_PROMPT = `
Classify the user's intent into one of these categories:

COMMERCE:
- sell: User wants to sell something
- buy: User wants to buy something
- search_products: User is browsing

JOBS:
- find_job: User wants to find work
- apply_job: User wants to apply to specific job
- post_job: User wants to hire someone

MONEY:
- check_balance: Check wallet balance
- deposit: Add money to wallet
- withdraw: Take money out
- send: Transfer to someone

COMMUNICATIONS:
- send_invoice: Create and send invoice
- send_message: Send SMS/WhatsApp/email

LEARNING:
- learn: User wants to learn something
- my_courses: Check enrolled courses

META:
- help: User needs help
- profile: User wants to see/edit profile
- settings: User wants to change settings

Also extract relevant entities.

User: "${message}"

Respond as JSON:
{
  "intent": "...",
  "confidence": 0.0-1.0,
  "entities": {
    "item": "...",
    "price": ...,
    "location": "..."
  }
}
`;

2. Context Manager

Maintains state across messages:

javascript
// Context structure
{
  // User identity
  userId: "uuid",
  handle: "@laslie",
  
  // Conversation state
  currentFlow: "sell",
  flowState: "awaiting_price",
  flowData: {
    photos: ["url1", "url2"],
    title: "Nike Air Max",
    price: null  // waiting for this
  },
  
  // History
  lastIntent: "sell",
  lastProduct: "yeboshops",
  messageCount: 5,
  
  // Preferences (learned)
  preferredResponseStyle: "concise",
  activeHours: [9, 22]
}
javascript
// Context operations
async function loadContext(userId) {
  return await redis.get(`context:${userId}`) || createDefaultContext(userId);
}

async function updateContext(userId, updates) {
  const context = await loadContext(userId);
  const updated = { ...context, ...updates };
  await redis.setex(`context:${userId}`, 3600, updated);
  return updated;
}

3. Product Router

Maps intents to product adapters:

javascript
const PRODUCT_ROUTES = {
  // Commerce
  sell: 'yeboshops',
  buy: 'yeboshops',
  search_products: 'yeboshops',
  
  // Jobs
  find_job: 'yebojobs',
  apply_job: 'yebojobs',
  post_job: 'yebojobs',
  
  // Payments
  check_balance: 'yebosafe',
  deposit: 'yebosafe',
  withdraw: 'yebosafe',
  send: 'yebosafe',
  
  // Communications
  send_invoice: 'yebolink',
  send_message: 'yebolink',
  
  // Learning
  learn: 'yebolearn',
  my_courses: 'yebolearn',
  
  // Identity
  profile: 'yeboid',
  settings: 'yeboid'
};

function getAdapter(intent) {
  const product = PRODUCT_ROUTES[intent];
  return adapters[product];
}

4. Product Adapters

Each product has an adapter:

javascript
// YeboShops adapter
const yeboshopsAdapter = {
  async sell(entities, context) {
    // Multi-step flow
    if (!context.flowData.photos) {
      return {
        response: "Send me photos of what you're selling",
        awaitingInput: 'photos',
        updateContext: { currentFlow: 'sell', flowState: 'awaiting_photos' }
      };
    }
    
    if (!context.flowData.price) {
      return {
        response: `Great photos! How much for the ${entities.item || 'item'}?`,
        awaitingInput: 'price',
        updateContext: { flowState: 'awaiting_price' }
      };
    }
    
    // Have everything, create listing
    const listing = await yeboshops.createListing({
      userId: context.userId,
      photos: context.flowData.photos,
      title: entities.item,
      price: context.flowData.price
    });
    
    return {
      response: `Listed! ${listing.title} for KES ${listing.price}`,
      card: formatListingCard(listing),
      clearFlow: true
    };
  }
};

5. Response Formatter

Creates rich responses:

javascript
function formatResponse(result, context) {
  const response = {
    text: result.response,
    cards: result.card ? [result.card] : [],
    buttons: [],
    suggestions: []
  };
  
  // Add action buttons based on state
  if (result.actions) {
    response.buttons = result.actions.map(a => ({
      label: a.label,
      action: a.action,
      data: a.data
    }));
  }
  
  // Add quick suggestions
  response.suggestions = getSuggestions(context);
  
  return response;
}

function formatListingCard(listing) {
  return {
    type: 'listing',
    image: listing.photos[0],
    title: listing.title,
    price: `KES ${listing.price}`,
    buttons: [
      { label: 'Edit', action: 'edit_listing', data: { id: listing.id } },
      { label: 'Boost', action: 'boost_listing', data: { id: listing.id } }
    ]
  };
}

Multi-Turn Conversations

State Machine

IDLE

  ├── "Sell shoes" ──► SELL_AWAITING_PHOTOS
  │                         │
  │                    [photos] ──► SELL_AWAITING_PRICE
  │                                      │
  │                                 [price] ──► SELL_CONFIRMING
  │                                                  │
  │                                             [confirm] ──► IDLE

  ├── "Find jobs" ──► JOB_SEARCH_ACTIVE
  │                        │
  │                   [results] ──► JOB_BROWSING
  │                                      │
  │                               [select] ──► JOB_APPLYING

  └── ... other flows

Interruption Handling

javascript
async function handleMessage(userId, message) {
  const context = await loadContext(userId);
  const { intent, entities, confidence } = await recognizeIntent(message);
  
  // Check if this is a new intent or continuation
  if (context.currentFlow && confidence > 0.8) {
    // Strong new intent, abandon current flow
    await updateContext(userId, { currentFlow: null, flowData: {} });
  } else if (context.currentFlow) {
    // Continue current flow
    return await continueFlow(context, message);
  }
  
  // Start new flow
  return await startFlow(intent, entities, context);
}

Proactive Actions

See Phase 3: Intelligence for:

  • Background job matching
  • Price alerts
  • Auto follow-ups
  • Opportunity detection

Performance

MetricTarget
Intent recognition<500ms
Full response<2s
Context load<50ms
Product API call<1s

Scaling

  • Stateless orchestrator: Multiple instances behind load balancer
  • Context in Redis: Fast, shared state
  • Async processing: Long operations via queue

One chat. Everything done.