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 flowsInterruption 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
| Metric | Target |
|---|---|
| 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