Module 1: Why Operating Is Different From Building

Operating vs. Building: The Boundary

Description

With lessons 04 and 05's four signals already calculated over real runs, this lesson stops to precisely trace two lines that separate what this guide does from what its two closest neighboring guides do. The first line separates building (agent-fundamentals-and-tool-calling-guide, already done) from operating (this guide). The second separates operating the agent (this guide, at the application level) from operating the infrastructure (sre-and-incident-response-guide, a different ecosystem). Neither line is a technicality — each one precisely decides which guide to reach for when your agent runs into a real problem.

Connection to the module

This lesson doesn't add any new signal or engineering artifact — it's deliberately the most conceptual lesson in the module. Its job is to prevent the most expensive mistake you can make starting this guide: rebuilding something that already exists, or confusing this guide's scope with a neighboring guide's. The two lines it traces here get repeated, with more technical detail, in Modules 2, 6, and 8.


Analogy: whoever builds the car, and whoever drives it every day

The factory that built your car already solved a specific problem: what happens if a spark plug fails once, in the middle of a trip. The engine's computer detects it, adjusts fuel injection on the fly, and the car keeps running without the driver noticing anything — maybe, at most, a warning light blinks on for an instant and goes off. That's factory robustness: an automatic response, within the same trip, to a single failure. Nobody who buys the car needs to redesign that mechanism — it already comes solved.

But there's a question the factory doesn't answer, because it isn't its job: what does the car's owner do when that same warning light comes on, goes off, and comes back on, trip after trip, for two weeks? The factory solved the individual failure; the owner has to decide, with the accumulated information from many trips, when to stop trusting that component and take it to the shop. That decision — made with data from many trips, not one — is, precisely, the work of operating. And there's a third question that neither the factory nor the owner answers: if the car gets stranded because the highway itself is closed for construction, that's not a problem with the car — it's a problem with the road infrastructure, and a completely different system solves it.


Boundary 1: agent-fundamentals (building) vs. this guide (operating)

What's already solved, within ONE run

agent-fundamentals M7 already built, and already tested, the answer to "what does the agent do when THIS call to THIS tool fails, right now, within this run?" — dispatch_robust, reused unchanged since lesson 01 of this module, already retries transient failures with a cap:

import reservo_tools as rt
import reservo_robust as rr

_flaky_calls = {"count": 0}


def flaky_list_rooms():
    _flaky_calls["count"] += 1
    if _flaky_calls["count"] <= 2:
        raise ConnectionError(f"timeout de red simulado (intento {_flaky_calls['count']})")
    return rt.list_rooms()


rr.TOOLS["flaky_list_rooms"] = flaky_list_rooms
rr.SCHEMAS["flaky_list_rooms"] = {"type": "object", "properties": {}}

block = {"id": "toolu_06", "name": "flaky_list_rooms", "input": {}}
print(rr.dispatch_robust(block, max_retries=3))

What to expect:

{'type': 'tool_result', 'tool_use_id': 'toolu_06', 'content': '[{"room": "Focus", "rate_cents": 2500}, {"room": "Studio", "rate_cents": 4000}, {"room": "Boardroom", "rate_cents": 8000}]'}

flaky_list_rooms fails twice and recovers on the third try, within this single call to dispatch_robust — exactly the mechanism you already built, and ran, in agent-fundamentals M7 and M8. This guide doesn't rebuild this. It doesn't improve it, doesn't rewrite it, doesn't add a new case to it. It uses it, as is, every time run_reservo_agent dispatches a tool.

What this guide does build, across MANY runs

The question dispatch_robust doesn't answer — and shouldn't have to answer, because it isn't its job — is different: what happens when book_room doesn't fail once within a run, but fails consistently, run after run, for the last twenty attempts? Retrying each individual failure with dispatch_robust would keep "working" in the sense that it never lets an uncontrolled exception through — but it would keep spending retries, time, and cost on a tool that, based on accumulated evidence, is probably still down. That decision — temporarily giving up on it, based on the history of many runs — belongs to this guide's Module 6, a CircuitBreaker with state that persists between calls (CLOSED/OPEN/HALF_OPEN, the same vocabulary from resilience-and-reliability-patterns-guide, applied here specifically to an agent's tool-call layer). It doesn't get built in this module — it's named here, with precision, to make clear it's a layer on top of dispatch_robust, not a replacement for it.

agent-fundamentals M7        ->  dispatch_robust: "this tool failed NOW, do I retry?"
                                  (decision WITHIN a run, no memory of previous runs)

this guide, Module 6          ->  CircuitBreaker: "this tool has been failing MANY runs,
                                  do I stop trying it for a while?"
                                  (decision BETWEEN runs, with memory of all previous ones)

The same distinction applies to argument validation. check_input_v2 (M7) protects against a misspelled tier, an hours below the minimum — basic robustness engineering, within a run, already built. This guide never revalidates arguments or adds a new validation layer on top — what it does, in Module 5, is run a regression harness that deterministically confirms that validation (and the rest of the agent's behavior) still works the same way after a change, compared against a scripted case with an exact expected result.


Boundary 2: the agent (this guide) vs. the infrastructure (sre-and-incident-response-guide)

The second boundary is one of layer, not of time. This guide operates the agent: its runs, its tool calls, its tokens, its prompts — at the application level, with pure Python, no Docker, no AWS, zero cost. sre-and-incident-response-guide operates the infrastructure that would hypothetically run behind an agent in a real deployment: the service that exposes it, the database its tools use, the load balancer that spreads traffic across instances.

This guide (the agent)sre-and-incident-response-guide (the infrastructure)
What it measuresA run's error rate, a tool call's failure rate, cost per run, latency per runA service's SLI/SLO (availability, an endpoint's latency), a Lambda's metrics, an API
With whatPython 3.14 stdlib (logging, json, dataclasses, statistics)Prometheus, Grafana, CloudWatch
The unit it observesAn agent run: question → steps → responseA service: requests, replicas, nodes
When something breaks badlyA per-tool circuit breaker (Module 6), a version rollback (Module 7)An incident's full lifecycle: roles, severities, a blameless postmortem
Cost to operate$0, runs on your machineRequires real infrastructure deployed (AWS, containers)

Neither guide replaces the other — in a real system, both apply at the same time, at different layers: the agent could be operating perfectly (low error rate, stable cost) while the service that exposes it suffers an infrastructure incident completely unrelated to the agent (a database that's down, an AWS region having problems). And the reverse: the infrastructure could be healthy — the service responds, the load balancer spreads traffic fine — while the agent, at the application level, has an abnormally high per-tool failure rate because a prompt change made it request an invalid tier more often. Each guide answers a question the other can't.


How to use the two boundaries when reading the rest of this guide

A practical rule for whenever, in any later module, you're not sure if something "was already covered somewhere else":

  • If the question is "what does the agent do when THIS fails, RIGHT NOW, within this run?" → it's already solved, in agent-fundamentals M7. This guide uses it, doesn't repeat it.
  • If the question is "what happens to this run if I measure it, compare it to others, or decide to act based on its history?" → it's this guide's territory.
  • If the question is "is the service that exposes the agent available, and how do I respond to an infrastructure incident?" → it's sre-and-incident-response-guide.

Common mistakes

  1. Thinking Module 6's circuit breaker "fixes" what dispatch_robust already handles. It doesn't fix it — it complements it, at a different layer. dispatch_robust is still the only code that decides what to do with a failure within a specific run; the circuit breaker decides, from outside, whether it's even worth trying at all.

  2. Confusing "measuring a run's latency" with "measuring a service's latency." They're different numbers, from different sources. This guide models the latency of a run's internal steps (how long each tool call takes). sre-and-incident-response-guide measures the latency of an entire HTTP endpoint, with network retries, load balancing, and everything in between the client and the service.

  3. Believing that, since the agent runs in pure Python with no infrastructure, it "never needs" SRE. It does need it — the moment that same agent gets deployed behind a real service, with real users connecting over HTTP. What this guide makes clear is that deployment is a different problem, not solved here — not that it doesn't exist.

  4. Rewriting check_input_v2 or dispatch_robust "to make them more complete." They already do their job, already thoroughly tested in agent-fundamentals. Any improvement to those functions is a note for that guide, not a silent rewrite here.

  5. Thinking this lesson already built the circuit breaker. It didn't — it only named it, with precision, to trace the boundary. The real code — the CLOSED/OPEN/HALF_OPEN state machine — is Module 6's full content.


Exercises

Exercise 1: Classify five questions by guide (Easy)

For each question, say whether it's solved by agent-fundamentals (already built), this guide, or sre-and-incident-response-guide: (a) "did get_quote retry when a ConnectionError happened once?"; (b) "how much did this morning's batch of runs cost, in total?"; (c) "is the load balancer still spreading traffic across the service's three replicas?"; (d) "has book_room been failing for the last fifteen runs in a row?"; (e) "does cancel_booking's input_schema reject an id that isn't an integer?"

See solution

(a) agent-fundamentals M7 — a retry within a run, already built into dispatch_robust.

(b) This guide (Module 3, building on what you started in lesson 05) — cost aggregated over a batch of runs.

(c) sre-and-incident-response-guide — infrastructure, load balancing across a service's replicas.

(d) This guide (Module 6) — a failure pattern across many runs, the circuit breaker's territory.

(e) agent-fundamentals M2/M7input_schema validation, already built into check_input/check_input_v2.

Exercise 2: Explain in one sentence why (b) and (d) aren't the same guide as (a) and (e) (Medium)

Using your Exercise 1 answers, precisely explain what (b) and (d) have in common that sets them apart from (a) and (e).

See solution

(a) and (e) are questions about a single event, within one run: whether a retry happened this time, whether an input_schema rejects this specific value right now. Neither needs information from any other run to be answered — the answer is complete within the scope of a single execution. (b) and (d), on the other hand, only make sense when looking at several runs together: a batch's total cost doesn't exist without summing the cost of every run that makes it up, and "has been failing for the last fifteen runs" is, by definition, a statement about a sequence of events over time, not about just one. That is, precisely, the distinction between "building" (solving something within an execution) and "operating" (measuring and deciding based on the pattern across many executions).

Exercise 3: Design a scenario where the agent is healthy but the infrastructure isn't, and vice versa (Hard)

Describe, in one paragraph each, two scenarios: (a) one where the agent's lesson-04 four signals are excellent (low error rate, low cost, stable latency) but an SRE engineer would still report an incident; (b) one where the infrastructure service is perfectly healthy (available, no 5xx errors, normal network latency) but the agent's signals would show a real problem.

See solution

(a) The Reservo agent processes its runs with a 0% error rate, stable cost, modeled latency within expectations — every signal in this guide green. But the real database behind book_room (in a real deployment, not in this $0 guide) is about to run out of disk space, or the AWS region running the service has a network degradation that doesn't yet affect responses but does raise the risk. Neither condition shows up in the agent's signals — the agent knows nothing about disk space or an AWS region's health — but an SRE engineer, looking at infrastructure dashboards, would see them and report an incident before the agent is ever affected.

(b) The service exposing the agent is perfectly available: zero 5xx errors, normal network latency, every replica healthy according to any infrastructure dashboard. But a recent change to the system prompt (something entirely outside SRE's scope) made the model (concept) start requesting tier="premium" much more often than before — get_quote's per-tool failure rate, measured with this guide's tools, jumped from the usual 10% to 60%. No infrastructure dashboard would show anything unusual, because from its perspective every HTTP request was answered correctly (with an is_error inside the tool_result, which is still a perfectly valid 200 HTTP response) — the problem lives entirely in the application layer this guide operates, invisible to SRE.


Summary and next step

  • We traced the first boundary: agent-fundamentals M7 (dispatch_robust, already built) solves failures within a run; this guide's Module 6 solves failure patterns across many runs, with a circuit breaker — named here, built later.
  • We traced the second boundary: this guide operates the agent (runs, tool calls, tokens, prompts, at the application level, $0); sre-and-incident-response-guide operates the infrastructure (a service's SLI/SLO, Prometheus, an incident's lifecycle).
  • We confirmed, with two concrete scenarios, that the agent can be healthy while the infrastructure isn't, and vice versa — the two layers are independent, and each guide answers exactly one of the two.

Next lesson: 07 — The Brief: Run the Reservo Agent for Real Users. With both boundaries clear now, we lay out this guide's case as a concrete business brief: four questions nobody can answer today, and which module answers each one.


Additional resources

  1. Anthropic — Tool use (function calling) overview — The complete protocol dispatch_robust operates on, unchanged, within every run.
  2. Anthropic — Building effective agents — On the separation between the engineering that makes a mechanism reliable and the discipline that keeps it reliable in operation.
  3. Python — concurrent.futures — The foundation of call_with_timeout, the dispatch_robust mechanism reused in this lesson.
  4. Python 3.14 — What's New — The version this lesson's example ran on.