Designing infrastructure for autonomous agents
Observability, guardrails, and rollback patterns for systems that act on their own.
Autonomous agents are not just another application workload.
A normal web application waits for a request, does a bounded amount of work, and returns a response. An agent can decide what to do next, call several systems, retry after a failure, and keep moving until it reaches a stopping condition.
That changes the infrastructure problem.
The hard part is not getting a model to call a tool. The hard part is making sure the resulting system is observable, constrained, recoverable, and boring enough to trust with real work.
Start with a narrow operational boundary
“Autonomous” should describe a specific workflow, not a blank check.
An agent that can read alerts, collect deployment status, and draft an incident summary is useful. It does not need production shell access. An agent that can restart a staging service after verifying a failed health check may also be reasonable. That does not mean it should decide to restart every production dependency it can see.
The boundary should be explicit:
- What systems can the agent read?
- What tools can it call?
- Which actions are reversible?
- Which actions require approval?
- How many actions can it take in one run?
- What conditions cause the run to stop?
If those answers only exist in a prompt, they do not exist strongly enough.
Put them in the tool layer, permissions model, and workflow code.
Treat tools as an API contract
The tool interface is where most of the real safety work happens.
A loose tool like this gives the model too much room to improvise:
run_command(command)
A narrow tool makes both intent and limits clear:
get_service_health(service)
get_recent_deployment(service)
get_error_summary(service, window_minutes)
restart_staging_service(service)
create_incident_draft(service, summary, evidence)
request_production_action_approval(action, reason, evidence)
The second set is less flexible, and that is usually a feature.
Each tool should define:
- required inputs and valid values
- authorization requirements
- timeout and retry behavior
- idempotency behavior
- structured success and failure responses
- audit fields
- whether a human approval is required
The agent can reason about what to do with a tool response. It should not be responsible for inventing the security model around that tool.
Observability has to include the agent’s decisions
Standard service metrics are necessary but incomplete.
You still need latency, error rates, queue depth, CPU, memory, and downstream API health. But when an agent is involved, you also need to understand the decision path that led to an action.
At minimum, every run should have a correlation ID and structured events for:
{
"event": "agent_tool_call",
"run_id": "run_123",
"agent": "deployment-triage",
"tool": "get_service_health",
"input_summary": {
"service": "api"
},
"outcome": "success",
"duration_ms": 184
}
Do the same for decisions, retries, approval requests, and terminal outcomes.
You should be able to answer these questions without reconstructing a conversation from logs:
- What started this run?
- What information did the agent retrieve?
- Which tools did it call?
- What did each tool return?
- Which action did it take?
- Did it retry anything?
- Did it stop because it succeeded, failed, hit a budget, or needed approval?
If an agent makes a bad call, the audit trail is how you fix the system rather than merely blaming the model.
Use budgets as a control plane
Agents need limits that are enforced outside the model.
A useful run budget can include:
| Limit | Example | Why it matters |
|---|---|---|
| Wall-clock time | 5 minutes | Stops a stuck workflow |
| Tool calls | 20 calls | Prevents endless loops |
| Retries per tool | 2 retries | Avoids amplifying an outage |
| Spend | Fixed token or API budget | Prevents surprise cost |
| Write actions | 1 production-affecting action | Limits blast radius |
| Scope | One service or one ticket | Prevents task drift |
A budget is not a failure. It is a normal completion state.
When a run exceeds one, capture the current state, emit a clear event, and hand the work to a person or queue it for review. Do not let the agent quietly keep trying because the original task sounded important.
Idempotency is mandatory
Networks fail. Workers restart. Webhooks are duplicated. Requests time out after the downstream system has already accepted them.
That is routine infrastructure behavior. Autonomous workflows make it more expensive because retries can trigger actions with real effects.
Every write tool should accept an idempotency key or operate on a durable operation record. A simple pattern is:
- Create an operation record before calling the external system.
- Use a stable idempotency key derived from the run and intended action.
- Record the external response or failure.
- On retry, look up the prior operation instead of blindly running it again.
- Verify final state before declaring success.
For example, “create incident draft for alert abc123” should produce one draft, even if the worker retries three times.
The same rule applies to deployments, emails, ticket creation, billing actions, and anything that users can notice.
Build rollback into the workflow before the first action
Rollback is not an emergency feature. It is part of the action design.
Before allowing an agent to make a change, define what a safe reversal looks like:
- A deployment can return to the previous release.
- A feature flag can be restored to its old value.
- A staged configuration can be reverted from a versioned snapshot.
- A draft ticket can be closed or marked obsolete.
- A queued job can be canceled before execution.
Not every action is reversible. That is exactly why irreversible actions should sit behind stronger approval gates.
A good agent workflow does not say, “make the change and hope monitoring catches it.” It says:
- Capture the current state.
- Make one bounded change.
- Verify the expected result.
- Watch defined health signals for a limited period.
- Roll back automatically or request approval if the signals fail.
This is standard deployment discipline applied to agent behavior.
Separate planning from execution
One useful pattern is to let an agent create a plan, but require a separate execution stage to validate it.
The planning stage can gather context and propose actions:
1. Confirm the active deployment version.
2. Check error-rate increase after the latest release.
3. Compare against the previous release.
4. Prepare a rollback recommendation.
The execution stage then validates every step against current state and permission rules. If the plan says to restart a service but the current state shows a different incident is underway, execution stops.
This avoids treating a plan generated ten minutes ago as authoritative truth.
It also makes review easier. A person can approve a specific proposed action without approving an unrestricted agent run.
Design for partial failure
The agent will eventually encounter a workflow where one system succeeds and the next one fails.
Suppose it:
- Creates an incident ticket.
- Posts an alert to a team channel.
- Attempts to attach diagnostic logs.
- Times out while retrieving the logs.
The correct outcome is not “failed.” The ticket and alert already happened.
The workflow should record partial completion and make the next state explicit:
incident_created: true
team_notified: true
diagnostics_attached: false
next_action: retry_diagnostics_or_escalate
This is why durable workflow state matters. Chat history is not a transaction log.
Human approval should be specific
“Human in the loop” can become meaningless if the human is asked to approve a vague paragraph after the agent has already done the hard part.
A useful approval request includes:
- the exact action proposed
- the target system and environment
- the evidence supporting the action
- the expected outcome
- the rollback plan
- the expiry time for approval
For example:
Roll back
payments-apiin production from release2026.06.01.4to2026.05.29.2. Error rate increased from 0.3% to 8.1% within six minutes of the release. Health checks remain failing after two retries. Rollback is available through the release manager and is expected to take under two minutes.
That gives the operator enough context to make a decision without rereading every raw log.
Autonomous infrastructure should fail closed
When the system is uncertain, missing context, or outside its defined scope, it should stop.
That can feel less impressive than a system that always has an answer. It is much more useful in production.
A reliable agent should fail closed when:
- required state cannot be retrieved
- a tool response is malformed or ambiguous
- permission checks fail
- the action exceeds its budget
- a workflow enters an unknown state
- the blast radius is larger than the assigned scope
- the action affects money, customer data, or irreversible records
Stopping with a structured explanation is a successful safety behavior.
The infrastructure is the product
Models will improve. Tool calling will improve. Planning loops will improve.
But the part that makes an autonomous system dependable is still conventional engineering: durable state, typed interfaces, access controls, idempotency, logs, metrics, alerting, limits, and rollback procedures.
The agent is the decision-making layer inside that system.
Build the surrounding infrastructure first, and autonomy becomes something you can expand carefully. Skip it, and every demo eventually runs into the same problem: the system can act, but nobody is quite sure what it did, why it did it, or how to undo it.