Design an Agent Platform¶
The prompt: "Design a multi-tenant platform that lets customers configure and run autonomous agents (e.g. customer-support resolution agents) that can call tools, including ones with real side effects like issuing refunds."
Clarifying questions¶
- Tenancy model — is this fully multi-tenant SaaS (many customers, isolated data), or single-org internal? Assume: multi-tenant SaaS, strict data isolation required.
- Agent autonomy level — fully autonomous, or human-in-the-loop for some actions? Assume: mixed — tiered by action risk (see Evals & Production Q&A Q6).
- Concurrency — how many agent sessions run concurrently per tenant, and across all tenants? Assume: up to 10K concurrent sessions platform-wide, bursty by tenant traffic.
- Tool surface — fixed platform tools, or can tenants register custom tools/APIs? Assume: both — built-in tools plus tenant-registered webhooks.
- Session duration — short (single resolution, <5 min) or long-running (hours, multi-day)? Assume: mostly short, but must support long-running research/workflow agents too.
- Compliance — any regulatory constraints (PCI, HIPAA) affecting what an agent can access or log? Assume: PCI-adjacent — agents may see payment-related context, so redaction/handling matters.
Requirements¶
Functional: tenant-configurable agent (prompt/tools/policies); execute a perceive-reason-act-observe loop with tool calls; tiered approval for risky actions; full audit trail per session.
Non-functional: strict per-tenant data isolation; p95 agent-turn latency < 4s; support 10K concurrent sessions; bounded cost per session (no runaway loops); tool execution sandboxed so a misbehaving agent can't affect other tenants.
Back-of-envelope numbers:
| Quantity | Estimate |
|---|---|
| Concurrent sessions (peak) | 10,000 |
| Avg turns/session | 8 |
| Avg tokens/turn (context + generation) | ~3,000 |
| Peak LLM calls/sec | 10,000 sessions × (8 turns / ~90s session duration) ≈ ~900 calls/sec |
| Tool calls/session | ~4 |
The ~900 LLM calls/sec figure is the number that determines whether you need multiple model provider accounts/regions, request queuing with backpressure, or a routing/cascade strategy to stay within provider rate limits and control cost.
High-level architecture¶
flowchart TB
T[Tenant config: prompt, tools, policies] --> ORCH[Session Orchestrator]
REQ[Incoming trigger] --> ORCH
ORCH --> LOOP[Agent Loop Runtime]
LOOP --> LLM[LLM call w/ routing]
LOOP --> TOOLREG[Tool Registry]
TOOLREG --> SANDBOX[Sandboxed Tool Executor]
SANDBOX --> BUILTIN[Built-in tools]
SANDBOX --> WEBHOOK[Tenant webhooks]
LOOP --> POLICY[Action Policy Engine]
POLICY --> HITL[Human-in-the-loop queue]
LOOP --> AUDIT[(Audit log / trace store)]
ORCH --> STATE[(Per-tenant isolated state store)]
- Session orchestrator — spins up an isolated agent session per request, loads tenant config (system prompt, allowed tools, risk policies), enforces a hard turn/cost budget per session.
- Agent loop runtime — the perceive/reason/act/observe cycle; calls the LLM through a routing layer that can load-balance across provider regions/accounts and apply cascade routing (cheap model first, escalate on low confidence).
- Tool registry + sandboxed executor — every tool call runs in an isolated execution context scoped to that tenant's credentials/data only; tenant-registered webhooks are called with a signed request and a strict timeout so one tenant's slow endpoint can't starve the platform.
- Action policy engine — intercepts every tool call before execution, checks it against the tenant's risk tiering (auto-approve / require human approval / hard-block), enforced here, not just in the prompt — see Agents Q&A Q6.
- Audit/trace store — every perceive/reason/act/observe step logged per session, queryable per tenant, both for debugging and compliance.
Deep dive: tenant isolation under shared infrastructure¶
Multi-tenant agent platforms have a specific isolation risk beyond normal SaaS: an
agent's context can accidentally mix tenant data (e.g. a shared tool-response cache
keyed incorrectly, or a memory/RAG store queried without a tenant filter — same failure
class as the RAG ACL problem in Design a RAG System). The
mitigation is to make tenant ID a mandatory, non-optional dimension threaded through
every layer: session state keyed by tenant_id, tool credentials resolved per-tenant
(never a shared credential pool), and any shared caches (e.g. an embedding cache) keyed
including tenant_id even when the underlying content is identical across tenants,
trading some cache hit rate for a hard isolation guarantee rather than relying on
application logic to always filter correctly.
Deep dive: the action policy engine¶
This is the component that turns "the model decided to call issue_refund" into "the
refund actually happens." It must be enforced outside the model, at the tool-execution
layer, because prompt-level instructions are a soft constraint that can fail under
injection, context degradation, or model error (see Agents Q&A Q6).
Concretely: each tool has a declared risk tier and, for parameterized tools like refunds,
a tier that can depend on the parameter value (e.g. refund(amount) — auto-approve under
$20, HITL queue above it). The policy engine sits between the agent loop and the tool
executor as a hard gate — a tool call that requires approval is queued, the session
pauses (not blocked indefinitely — with its own timeout/escalation), and only proceeds to
execution once approved. This also gives you the audit trail for free: every
gated decision has a record of what was requested, by which policy it was
routed, and who approved it.
Tradeoffs¶
| Decision | Option A | Option B | Chosen | Why |
|---|---|---|---|---|
| Tool call risk gating | Enforced via system prompt only | Enforced at tool-executor layer | Executor layer | Prompt-level constraints are soft and can fail under injection or model error |
| Cache keying | Shared cache across tenants | Cache keyed by tenant_id even for identical content | Tenant-keyed | Hard isolation guarantee is worth the reduced cache hit rate for a compliance-relevant surface |
| LLM routing | Single model for all sessions | Cascade (cheap model first, escalate) | Cascade | Reduces average cost per session at ~900 calls/sec scale without giving up quality on hard cases |
| Session termination | Model self-reports "done" only | Model signal + turn/cost budget + stagnation detector | Combined | Any single signal has a distinct failure mode — see Agents Q&A Q3 |
Failure modes & mitigations¶
- Runaway session (loop or cost blowout) — hard per-session turn and cost budget enforced by the orchestrator, independent of the model's own termination behavior.
- Tenant webhook is slow/down — strict timeout on webhook tool calls; degrade to "tool unavailable, ask user to retry later" rather than hanging the session.
- Provider rate limit hit at peak — request queue with backpressure plus multi-region routing; sessions queue rather than error outright, with a queue-depth-based SLA warning surfaced to the tenant.
- Cross-tenant data leak via shared component — treat as the top-severity incident class; mitigated architecturally (mandatory tenant_id threading) rather than relying on per-feature review to catch it every time.
- HITL queue backs up faster than humans can review — auto-escalate policy tiers temporarily tighten (fewer things auto-approved) is the wrong direction; instead alert on queue depth and consider auto-expiring low-risk-adjacent approvals with a conservative default (deny, not approve) on timeout.
Likely follow-ups¶
- "How would you let a tenant add a completely custom tool safely?" — Tenant-defined tools (e.g. a webhook) need the same sandboxing as built-in tools: no direct access to other tenants' data or credentials, strict timeout, and the tool's own risk tier defaults to the most conservative (HITL-required) until the tenant explicitly configures otherwise.
- "How do you evaluate whether this platform's agents are actually helping vs making things worse?" — Task success rate and escalation-to-human rate as the primary product metrics, tracked per tenant, with a faithfulness/policy-compliance eval on the audit trace sample (see Evals & Production Q&A).
- "What changes if sessions need to run for days, not minutes?" — Long-running sessions need persisted, resumable state (not just in-memory), a different cost-budget model (per-day, not per-session), and periodic human check-ins built into the termination/approval design rather than only gating at the end.
Key takeaways¶
- Multi-tenant agent platforms have an isolation failure class beyond typical SaaS: agent context mixing across tenants, not just data-at-rest mixing.
- Action risk gating must be enforced at the tool-execution layer, never solely at the prompt layer — this is the single most interview-tested design decision for agent platforms.
- Termination and cost control need multiple independent signals, not one.
- Cascade/routing strategies matter once you compute realistic calls-per-second — do the back-of-envelope math before assuming a single model tier works.
- Every gated action should produce an audit record as a side effect of the gating mechanism, not as separate bolted-on logging.