Backend for AI Agents: Durable Runs, Tool Execution, Memory, Safety, and Observability
An AI agent backend is not just an API endpoint that repeatedly calls an LLM.
Once an agent can use tools, wait for approvals, survive long tasks, resume after failures, manipulate external systems, or maintain memory across sessions, it becomes a distributed workflow runtime.
The backend has to answer questions such as:
- What exactly is the current run state?
- Which tools is this user allowed to invoke?
- What if a tool succeeds but the network times out before the result is recorded?
- What happens if the server restarts halfway through a 30-minute task?
- How does the user cancel or steer an active run?
- How many iterations can the model take before it stops?
- How are approvals represented durably?
- Which model/tool event caused a bad action?
That is the real backend architecture problem for AI agents.
The Agent Loop Belongs Inside a Controlled Runtime
At its simplest, an agent loop is:
user goal
↓
model decides next step
↓
tool call or answer
↓
tool result returns to model
↓
repeat until finished
The dangerous implementation is an unbounded loop:
while model_wants_tool:
execute_tool()
ask_model_again()
A production runtime needs policy around every transition.
Run Controller
├── model call
├── tool validation
├── authorization
├── approval policy
├── execution
├── state persistence
├── budgets / stop rules
├── cancellation
└── observability
The model proposes what should happen next. The runtime decides what is actually allowed to happen.
1. Give Every Agent Run a Durable Identity
Every task should have a stable run ID.
A useful run record might include:
run_id
user_id / tenant_id
conversation_id
agent_id
status
model configuration
started_at
updated_at
current_step
iteration_count
cost/token counters
cancellation state
Possible states:
queued
running
waiting_for_tool
waiting_for_approval
waiting_for_external_event
completed
failed
cancelled
Do not infer run state only from the latest chat message.
Explicit state makes recovery, UI status, debugging, cancellation, and concurrency controls much easier.
2. Persist Important Transitions, Not Just Chat Messages
A transcript alone cannot tell you everything that happened.
Store structured events such as:
run.started
model.requested
tool.proposed
tool.authorized
tool.started
tool.completed
approval.requested
approval.granted
state.compacted
run.completed
Each event should preserve safe identifiers and relevant metadata.
This creates an execution history that can answer:
> Did the model request the action twice, or did the executor run it twice?
That distinction is impossible to debug from a flattened chat transcript.
3. Separate Conversation State From Execution State
These are related but different.
Conversation state
Contains what the model needs to reason:
- user messages
- assistant messages
- tool calls/results
- summaries/compaction
- retrieved context
Execution state
Contains what the runtime needs to remain correct:
- active tool call IDs
- approval status
- retry counts
- idempotency keys
- deadlines
- external job handles
- run ownership
- cancellation flags
Do not hide critical execution state inside natural-language conversation summaries.
A model may forget or reinterpret text. Your backend should not.
4. Tool Calls Need Stable IDs and Lifecycle States
Every tool invocation should have a unique call ID.
Conceptually:
proposed
↓
validated
↓
authorized
↓
approved if required
↓
executing
↓
succeeded / failed / cancelled
Persist enough state that a retry can answer:
Has this exact tool action already run?
This becomes essential for side effects.
5. Idempotency Protects Against Duplicate Actions
Imagine an agent calls:
create_refund(order_123)
The refund succeeds, but your worker crashes before recording the response.
After restart, the runtime sees an incomplete step and executes it again.
Without idempotency, one infrastructure failure becomes a duplicate business action.
Use a stable idempotency key derived from the logical action where supported:
run_id + tool_call_id
For systems that do not natively support idempotency, maintain your own durable action ledger where possible.
Retries should repeat safe execution, not blindly replay side effects.
6. Tool Definitions Are Capabilities, Not Permissions
The model may know that a tool exists without being allowed to use it in the current context.
Separate:
available tools
from:
allowed tools for this user/run/step
Authorization should consider:
- authenticated user
- tenant/workspace
- role
- resource
- tool
- action
- current run policy
The system prompt is not an authorization layer.
OWASP's Excessive Agency guidance specifically warns about giving agents unnecessary functionality, excessive permissions, or too much autonomy.
7. Prefer Narrow Tools Over Open-Ended Power
Compare:
run_shell(command: string)
with:
restart_service(service_id)
read_log(service_id, range)
deploy_release(release_id)
The generic shell tool is flexible but gives the model a much larger action surface.
Narrow tools:
- constrain inputs
- make authorization easier
- improve audit logs
- reduce accidental actions
- allow resource-level policy
Open-ended tools can still be justified for coding or infrastructure agents, but they need stronger sandboxing, path/network restrictions, approvals, and validation.
8. Validate Tool Arguments Deterministically
A model-generated argument object is untrusted input.
Validate:
- schema
- types
- ranges
- allowed enum values
- path boundaries
- resource ownership
- tenant scope
- maximum payload size
Do not rely on a tool description saying:
"Only use safe paths."
If the tool accepts a filesystem path, trusted code should enforce the allowed root.
If it accepts an order ID, trusted code should verify that order belongs to the current tenant.
9. Approval Is a Durable Workflow State
Human approval should not be implemented as:
agent asks in chat
server hopes the next message means yes
Create an explicit approval object:
approval_id
run_id
tool_call_id
action summary
risk level
requested_at
expires_at
decision
decided_by
The agent run transitions to:
waiting_for_approval
When approval arrives, the runtime resumes from the exact suspended action.
This remains correct even if the server restarts or the user returns hours later.
10. Long-Running Work Needs Durable Execution
Some agent work lasts longer than one HTTP request:
- repository-wide coding changes
- browser research
- large data imports
- infrastructure migrations
- deployment workflows
- approval-dependent business processes
Do not keep one web request open as the source of truth.
Use a durable execution mechanism:
API request
↓
create run
↓
queue/workflow system
↓
worker executes steps
↓
persist checkpoints/events
↓
stream progress separately
Systems such as Temporal are built around durable workflows that resume after process, network, or infrastructure failures. You do not need Temporal specifically, but you do need the same fundamental property for important long-running agent work: execution must survive the worker that happens to be running it.
11. MCP Is a Tool Protocol, Not Your Agent Runtime
Model Context Protocol (MCP) is useful for standardizing how agents/clients discover and call external tools and data services.
But MCP does not replace your application-level run state, policy, memory, approval model, or business workflow.
The latest MCP specification released on July 28, 2026 moved the protocol core toward stateless HTTP requests and placed long-running Tasks in an extension.
That reinforces an important separation:
MCP
→ tool/context transport contract
Your backend
→ durable agent execution + policy + state
A scalable MCP server can remain stateless while the agent application using it maintains durable state elsewhere.
12. Tool Catalogs Should Be Discoverable Without Flooding Context
Large platforms can have hundreds of tools.
Sending every schema to the model on every request increases:
- token usage
- prompt size
- model confusion
- tool-selection errors
Use grouping, discovery, or tool-search patterns so only relevant tools enter the active context.
The runtime can maintain a large registry while exposing a much smaller active subset to the model.
Think:
all platform tools
↓
policy + task relevance
↓
small allowed tool set
↓
model
This improves both safety and context efficiency.
13. Memory Is Not One Table
Agent systems often mix several kinds of memory.
Working/conversation memory
The active context needed for the current task.
Compacted session state
A condensed representation of long conversations/runs.
User memory
Durable preferences or facts intentionally retained across sessions.
Project/domain memory
Knowledge tied to a repository, customer, workspace, or long-running project.
Episodic execution history
What the agent actually did in previous runs.
Do not retrieve every memory type for every model request.
Use scoped retrieval and explicit ownership boundaries.
14. Compaction Must Preserve Operational Facts
Long agent runs can exceed useful context size.
When compacting, preserve facts such as:
- completed actions
- failed attempts
- IDs created during the run
- user constraints
- tool outputs that affect future steps
- unresolved blockers
- current plan
- next action
Do not preserve every token equally.
Conversation compaction is lossy by design, so durable execution facts should exist outside the summary as structured state.
15. Budgets and Stop Rules Prevent Runaway Agents
Every run should have bounded resources.
Useful limits include:
maximum model turns
maximum tool calls
maximum wall-clock duration
maximum token usage
maximum spend
maximum repeated failures
maximum consecutive no-progress steps
The runtime should stop or escalate when limits are exceeded.
This protects against:
- accidental loops
- repeated failing tools
- adversarial prompts
- unexpected provider behavior
- runaway cost
“Continue until done” is a behavioral instruction, not a resource policy.
16. Detect Lack of Progress
A model can repeat variations of the same failing action.
Track signals such as:
same tool + equivalent arguments repeatedly failing
same error repeated N times
no new state produced across several iterations
same file/query repeatedly inspected
The runtime can then:
- stop
- ask the user
- switch strategy
- escalate to another model/workflow
Do not rely entirely on the model to notice its own loop.
17. Cancellation Must Propagate Downstream
When the user presses Stop, setting:
run.status = cancelled
is not enough.
Cancellation should propagate to:
- active model stream/request
- tool execution
- subprocess
- browser job
- remote workflow where supported
- queued future steps
Every executor should periodically observe cancellation or receive an explicit cancellation signal.
A cancelled agent that keeps modifying external systems is not actually cancelled.
18. Steering and New User Input Need Ordering Rules
Modern agents may receive new instructions while already working.
Define semantics for:
- steer — modify the active run
- follow-up — execute after the current run finishes
- cancel-and-replace — stop current work and start a new run
Do not silently map every new message to cancellation.
Store user input with ordering/causality so the runtime knows which run or checkpoint it applies to.
19. Concurrency Needs Ownership
Two workers should not execute the same run step simultaneously.
Use one of:
- durable workflow ownership
- leases
- row/version locks
- optimistic concurrency
- compare-and-swap state transitions
A state transition might require:
UPDATE run
SET status='running', version=version+1
WHERE run_id=? AND version=?
The exact mechanism varies, but there must be one logical owner for side-effecting work.
20. Retries Belong at the Correct Layer
Different failures deserve different retries.
Model request retry
Appropriate for some transient provider failures.
Tool transport retry
Appropriate when the operation is idempotent or its state can be checked safely.
Business action retry
Requires stronger idempotency/transaction semantics.
Whole agent-step retry
Dangerous if previous side effects may already have succeeded.
Do not put a generic retry wrapper around the entire agent loop.
21. Sandboxing Is Essential for High-Power Tools
Coding, shell, browser, and infrastructure tools can execute attacker-controlled or model-generated instructions.
Use defense in depth:
- isolated processes/containers/VMs where appropriate
- filesystem allowlists
- network policy
- CPU/memory/time limits
- secret isolation
- dropped privileges
- command/tool allowlists where feasible
- cleanup after runs
The more generic the tool, the stronger the execution boundary should be.
22. Treat Tool Output as Untrusted Too
External tools can return malicious or misleading content.
Examples:
- webpage with prompt injection
- repository file containing instructions to the agent
- compromised MCP tool response
- user-controlled database field
Label external results as data and prevent them from changing authorization policy.
A tool result should influence reasoning, not redefine what the runtime allows.
23. Observability Must Follow the Entire Run
A useful trace hierarchy is:
agent.run
├── model.call
├── tool.call
│ └── external dependency
├── model.call
├── approval.wait
└── model.call
Record metrics such as:
- run completion rate
- run duration
- model calls per run
- tool calls per run
- tool failure rate
- approval frequency
- tokens/cost per successful run
- cancellation rate
- loop/limit termination rate
A model latency dashboard alone cannot explain agent behavior.
24. Build Evals From Real Agent Trajectories
Evaluate more than the final answer.
Useful agent metrics include:
- task success
- correct tool selection
- unnecessary tool calls
- policy violations
- side-effect correctness
- recovery after tool failure
- number of iterations
- cost per successful task
- whether the run stopped when it should
Store sanitized failed trajectories and convert recurring failures into regression tests.
Agent engineering is empirical. Prompts, models, tool schemas, and runtime policies should be changed against measured outcomes.
25. A Practical Production Architecture
Client / UI
↓
Agent API
↓
Run Controller ───────────────→ Durable Run Store
│ │
├── Model Gateway ├── events/checkpoints
│ ├── approvals
├── Policy / Authorization └── budgets/status
│
├── Tool Registry / MCP Clients
│ ↓
│ Tool Executors
│ ↓
│ sandbox / external APIs
│
├── Memory / Retrieval
│
└── Queue / Durable Workflow Engine
↓
Workers
Start with fewer services if the workload is small, but preserve these logical boundaries so the system can evolve without mixing model reasoning with trusted execution policy.
Production Checklist
Run state
- Every run has a stable ID and explicit status
- Critical transitions are stored as structured events
- Execution state is separate from chat text
- Worker ownership prevents duplicate execution
- Runs can recover after process/server failure
Tools
- Every tool call has a stable call ID
- Arguments are validated in trusted code
- Authorization is enforced outside prompts
- Side-effecting actions are idempotent or reconciled
- High-power tools run inside appropriate isolation
- External tool output is treated as untrusted data
Control
- Approval states are durable
- Cancellation reaches active downstream work
- Steer/follow-up/cancel semantics are explicit
- Iteration, time, token, and cost budgets exist
- Repeated no-progress behavior can be detected
Memory
- Working memory and durable memory are separate concepts
- Memory is tenant/user/project scoped
- Compaction preserves critical operational facts
- Structured state is not replaced by summaries
Operations
- Run-level traces connect model and tool calls
- Cost per successful task is measurable
- Failed trajectories feed regression evals
- Provider/tool retries are error-aware and bounded
Final Takeaway
A production AI agent backend is a durable, policy-controlled execution runtime.
The model should be responsible for reasoning and proposing actions. Trusted backend code should remain responsible for identity, authorization, validation, approvals, idempotency, resource limits, persistence, and recovery.
That separation lets you change models, tool protocols, or orchestration libraries without rebuilding the security and correctness boundary of the system.
Start with one controlled run loop. Make every side effect traceable and recoverable. Add memory, MCP, long-running Tasks, multi-agent coordination, and advanced orchestration only when the product genuinely needs them.
References
BUILDING AN AI PRODUCT?
Talk to Lofingo about custom autonomous AI agents, enterprise workflow automation, and scalable software systems.
EXPLORE AI SERVICES →