By Sagar Shankaran, Founder of CallSphere
Design a hybrid agent system that runs fast local inference on edge devices for simple tasks and routes complex requests to cloud models, with seamless fallback and synchronization patterns.
Key takeaways
Pure edge deployment limits your agent to small models. Pure cloud deployment adds latency and requires constant connectivity. A hybrid architecture combines both — the edge handles fast, simple tasks locally while the cloud handles complex reasoning.
The key design question is: how does the agent decide where to run each request? This article covers the architecture, routing logic, and synchronization patterns that make hybrid agents work in production.
A hybrid agent has three core components:
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
flowchart LR
REQ(["Request"])
BATCH["Continuous batching<br/>vLLM scheduler"]
PREF{"Prefill or<br/>decode?"}
PRE["Prefill phase<br/>parallel attention"]
DEC["Decode phase<br/>token by token"]
KV[("Paged KV cache")]
SAMP["Sampling<br/>top-p, temp"]
STREAM["Stream tokens<br/>to client"]
REQ --> BATCH --> PREF
PREF -->|First token| PRE --> KV
PREF -->|Next token| DEC
KV --> DEC --> SAMP --> STREAM
SAMP -->|EOS| DONE(["Response complete"])
style BATCH fill:#4f46e5,stroke:#4338ca,color:#fff
style KV fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
style STREAM fill:#0ea5e9,stroke:#0369a1,color:#fff
style DONE fill:#059669,stroke:#047857,color:#fff
from dataclasses import dataclass
from enum import Enum
from typing import Optional
import asyncio
import time
class InferenceLayer(Enum):
EDGE = "edge"
CLOUD = "cloud"
@dataclass
class InferenceResult:
response: str
layer: InferenceLayer
latency_ms: float
confidence: float
class HybridAgent:
"""Agent that routes between edge and cloud inference."""
def __init__(self, edge_model, cloud_client, confidence_threshold: float = 0.85):
self.edge = edge_model
self.cloud = cloud_client
self.confidence_threshold = confidence_threshold
self.cloud_available = True
async def process(self, user_input: str) -> InferenceResult:
# Always try edge first for speed
start = time.monotonic()
edge_result = await self.edge.infer(user_input)
edge_latency = (time.monotonic() - start) * 1000
# If edge is confident enough, return immediately
if edge_result.confidence >= self.confidence_threshold:
return InferenceResult(
response=edge_result.text,
layer=InferenceLayer.EDGE,
latency_ms=edge_latency,
confidence=edge_result.confidence,
)
# Edge not confident — try cloud
if self.cloud_available:
try:
start = time.monotonic()
cloud_result = await asyncio.wait_for(
self.cloud.infer(user_input),
timeout=5.0,
)
cloud_latency = (time.monotonic() - start) * 1000
return InferenceResult(
response=cloud_result.text,
layer=InferenceLayer.CLOUD,
latency_ms=cloud_latency,
confidence=cloud_result.confidence,
)
except (asyncio.TimeoutError, ConnectionError):
self.cloud_available = False
asyncio.create_task(self._check_cloud_health())
# Fallback to edge result even if low confidence
return InferenceResult(
response=edge_result.text,
layer=InferenceLayer.EDGE,
latency_ms=edge_latency,
confidence=edge_result.confidence,
)
async def _check_cloud_health(self):
"""Periodically check if cloud is back online."""
while not self.cloud_available:
await asyncio.sleep(30)
try:
await asyncio.wait_for(self.cloud.health_check(), timeout=3.0)
self.cloud_available = True
except Exception:
continue
A confidence threshold is the simplest router, but production agents need more nuance. Route based on task complexity, not just model confidence:
from dataclasses import dataclass
@dataclass
class RoutingDecision:
layer: InferenceLayer
reason: str
class TaskRouter:
"""Routes requests based on task characteristics."""
# Tasks the edge model handles well
EDGE_PATTERNS = {
"greeting", "farewell", "yes_no", "simple_query",
"intent_classification", "entity_extraction",
}
# Tasks that need cloud-scale models
CLOUD_PATTERNS = {
"multi_step_reasoning", "code_generation",
"long_form_writing", "complex_analysis",
"rag_retrieval",
}
def __init__(self, edge_classifier):
self.classifier = edge_classifier
async def route(self, user_input: str) -> RoutingDecision:
# Use edge model to classify the task type itself
task_type = await self.classifier.classify_task(user_input)
if task_type.label in self.EDGE_PATTERNS:
return RoutingDecision(
layer=InferenceLayer.EDGE,
reason=f"Task type '{task_type.label}' handled locally",
)
if task_type.label in self.CLOUD_PATTERNS:
return RoutingDecision(
layer=InferenceLayer.CLOUD,
reason=f"Task type '{task_type.label}' requires cloud model",
)
# Unknown task — route based on input length as a heuristic
if len(user_input.split()) > 50:
return RoutingDecision(
layer=InferenceLayer.CLOUD,
reason="Long input likely needs complex processing",
)
return RoutingDecision(
layer=InferenceLayer.EDGE,
reason="Default to edge for unclassified short inputs",
)
When the agent uses both edge and cloud, they need to share context. Here is a lightweight sync mechanism:
import json
import hashlib
from datetime import datetime
class ConversationSync:
"""Syncs conversation state between edge and cloud."""
def __init__(self, local_store, cloud_api):
self.local = local_store
self.cloud = cloud_api
self.pending_syncs = []
async def add_turn(self, role: str, content: str, layer: InferenceLayer):
turn = {
"id": hashlib.sha256(f"{datetime.utcnow().isoformat()}{content}".encode()).hexdigest()[:16],
"role": role,
"content": content,
"layer": layer.value,
"timestamp": datetime.utcnow().isoformat(),
}
# Always save locally
await self.local.append_turn(turn)
# Queue for cloud sync
self.pending_syncs.append(turn)
async def sync_to_cloud(self):
"""Push pending turns to cloud. Called when connectivity is available."""
if not self.pending_syncs:
return
try:
await self.cloud.batch_sync(self.pending_syncs)
self.pending_syncs.clear()
except ConnectionError:
pass # Will retry on next sync cycle
The hybrid architecture must handle network outages gracefully. When cloud is unavailable, the edge model takes over completely:
class OfflineAwareAgent(HybridAgent):
async def process(self, user_input: str) -> InferenceResult:
if not self.cloud_available:
# Pure edge mode — adjust behavior
result = await self.edge.infer(user_input)
if result.confidence < 0.5:
return InferenceResult(
response="I can handle basic requests offline. "
"For more complex questions, I will need "
"a network connection.",
layer=InferenceLayer.EDGE,
latency_ms=0,
confidence=1.0,
)
return InferenceResult(
response=result.text,
layer=InferenceLayer.EDGE,
latency_ms=result.latency_ms,
confidence=result.confidence,
)
return await super().process(user_input)
Start with 0.85 and measure. Log every request's edge confidence score and whether the cloud produced a better result. After collecting a week of data, plot the relationship between edge confidence and cloud agreement. You will typically find a natural breakpoint where edge quality drops off sharply — set your threshold just above that point.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
For requests handled by the edge, latency drops significantly — often from 200 milliseconds to under 30 milliseconds. For cloud-routed requests, there is a small overhead (5 to 15 milliseconds) for the edge classification step that decides the routing. In practice, 60 to 80 percent of typical agent requests can be handled on the edge, so average latency decreases substantially.
Maintain a shared conversation history that both layers can access. Send the full context window to whichever layer handles the current turn. The conversation sync mechanism shown above queues local turns and pushes them to the cloud when connectivity is available, ensuring the cloud model has the same context as the edge model.
#HybridArchitecture #EdgeCloud #AIAgentDesign #FallbackPatterns #DistributedAI #AgenticAI #LearnAI #AIEngineering

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
Learn how to architect AI agent swarms that self-organize without a central coordinator. Covers pheromone-like signaling, emergent task allocation, and decentralized scaling patterns with Python examples.
Hybrid architectures that interleave transformer attention layers with state-space model blocks like Mamba deliver faster inference and lower memory usage. Learn how they work and when to use them.
Master AI agent orchestration patterns for managing complex multi-agent workflows. Learn coordination strategies, state management, and fault tolerance for production systems.
Explore how multi-agent AI systems reach agreement using consensus algorithms including majority voting, weighted averaging, and Byzantine fault tolerance. Includes Python implementations for each pattern.
Learn how to design and implement multi-agent systems using the Claude API and Agent SDK. Covers architecture patterns, inter-agent communication, task delegation, and real-world production examples.
How generative AI produces verified dbt models for data migration — from scratch and incrementally — with SME validation and strict data governance.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco