Observe, estimate cost, and tear it all down
Outcome
By the end of this lesson you will know where the running agent’s behavior shows up on the local track — its stdout logs — and how to read them without leaking sensitive request text. You will be able to explain, in plain terms, what drives OpenRouter cost per invocation and where to check it, and you will run the teardown that removes every resource this track created, then confirm nothing is left running or consuming disk.
This is the last lesson. It ends the way every lesson touching a running process should end: by cleaning up.
Mental model
In a visual automation tool, a misbehaving run sends you to its run history: a
list of executions, each showing what a node received and where it failed. On the
AWS track that role is played by CloudWatch — a separate, managed service you open
in a console. On the local track there is no separate service to open: the
container’s own stdout is the run history, and docker logs is how you read
it. This is simpler than CloudWatch in every direction that matters for a
tutorial: no log group to find, no retention setting to configure, no additional
resource that itself costs money to keep — the logs live exactly as long as the
container does, on your own disk.
Cost follows the same simplification. The AWS track has three billable layers (model tokens, runtime seconds, memory records) because AgentCore is a managed service metering each one separately. The local track collapses that to one layer: OpenRouter token cost, billed per request, because Docker itself is free and the file-based memory volume is just disk space. Where the analogy stops: a visual tool’s run history and any per-run cost are both invisible defaults you never configure. Here, you decide when the container stops running and when the volume gets deleted — cleanup is a decision, not something that happens for you.
Observe: read the container’s logs
From anywhere, with the Lesson 9 container still running:
docker logs intake-agent
This prints every line the process has written to stdout since it started —
startup messages, and one entry per invocation the entrypoint handled. Two things
by design are not in there: the raw AWS Knowledge MCP responses (treated as
untrusted tool output, per the agent engineering rules, not dumped into logs), and
full prompt text (the entrypoint logs only a warning when the MCP server is
unreachable — "AWS Knowledge MCP unavailable; using local tools only: %s" — never
the request itself). What you can see is whether a call was handled and whether
the MCP path degraded; what you cannot recover from these logs is what the caller
actually typed. Add -f to follow the log stream live while you invoke the agent
again from another terminal:
docker logs -f intake-agent
Press Ctrl+C to stop following; the container keeps running.
Incident-safe debugging
The same rule from the AWS track applies here unchanged, because it is a property
of the agent code, not of the hosting platform: do not log raw prompt content,
personal data, secrets, or credential-bearing headers. If you add your own
logging while debugging locally, log decisions and metadata — which system was
looked up, whether the result was accepted or rejected, the stop reason — not the
full user text, and never print OPENROUTER_API_KEY to stdout. A correlation id
(the session_id you already pass on each call) is enough to tie one log line back
to one request without ever writing down what that request said.
Cost: where OpenRouter charges come from
There is no video paired with this section — OpenRouter billing is specific enough that no general Docker video covers it, and this course does not pair a video just to fill the slot. The reasoning below is prose drawn from OpenRouter’s own pages, with no dollar figures reproduced (rates change; check them yourself).
Every invocation that reaches the model — every curl call to /invocations
that does not get rejected before the model runs — spends OpenRouter credits, in
two parts:
- Input tokens: the system prompt, the MCP tool descriptions and results, and the caller’s request text, all counted together as what goes into the model.
- Output tokens: what the model writes back — the structured intake fields, or the rejection reasoning — counted separately and usually priced higher per token than input.
A request that fails Lesson 3’s deterministic validation before it reaches the
model (for example, a payload missing the prompt field) costs nothing — the
rejection in _handle happens in plain Python, never touching OpenRouter. A
request that reaches the model but is judged hostile or low-confidence still costs
something, because the model had to run to make that judgment; only the very
cheapest, malformed-payload case is free.
To see exact cost:
- Per-model rates: openrouter.ai/models lists
current input/output token pricing for
openai/gpt-oss-120band every other model OpenRouter serves. - Your actual spend: the OpenRouter dashboard (
openrouter.ai, once signed in) shows a running total and a per-request breakdown tied to your API key, so you can see exactly what each Lesson 9 invocation cost after the fact.
OpenRouter also enforces a credit balance and per-key rate limits; a call that
exceeds either does not silently succeed — it comes back as an error inside the
same JSON shape you already read in Lesson 9 ("ok": false), the same way a
throttled Bedrock call surfaces as an exception on the AWS track. There is no
separate runtime-hosting charge on this track the way there is for AgentCore
Runtime: your own machine’s CPU and memory are what run the container, and neither
is metered or billed to you beyond your own electricity.
Teardown
This is the step that stops anything on the local track from continuing to cost you. There are exactly two resources Lesson 9 created: the running container and the named volume holding file memory. Remove both:
docker rm -f intake-agent
docker volume rm intake-sessions
docker rm -f stops the container if it is still running and removes it in one
step. docker volume rm deletes the named volume itself — this is the one
irreversible part: any stored preference from Lesson 9’s memory demonstration goes
with it. That is intentional; a tutorial memory store should not outlive the
tutorial.
Verify removal
The checkpoint is: no container running, and the named volume gone.
-
No container. Confirm nothing named
intake-agentremains, running or stopped:docker ps -aThe output should show no row with
NAMESequal tointake-agent. (docker ps -a, unlike plaindocker ps, also lists stopped containers — the check you want here, sincedocker rm -fshould have left none of either.) -
No volume. Confirm
intake-sessionsis gone:docker volume lsThe output should list no volume named
intake-sessions.
One common failure
Symptom: docker volume rm intake-sessions fails with an error like volume is in use.
Diagnosis: a container still references the volume — most often because
docker rm -f intake-agent was skipped, or a second container was started
against the same volume during the Lesson 9 memory demonstration and never
removed.
Fix: list every container, including stopped ones, remove any still referencing the image or the volume, then retry:
docker ps -a
docker rm -f intake-agent
docker volume rm intake-sessions
Why this works
The teardown is exactly two commands because the local track deliberately created
exactly two things that outlive a single curl call: a running container and a
named volume. Nothing else on this track is a standing resource — there is no
managed service metering idle time the way AgentCore Runtime does, and the built
image (intake-agent) sitting on your disk costs nothing to leave there, unlike a
cloud image sitting in a registry. Reading logs before tearing down works the same
way it does on the AWS track: docker logs shows what happened without ever
requiring you to have logged the sensitive parts in the first place, because the
redaction discipline lives in the agent code, not in the observability tool.
Verify it yourself
The checkpoint is: no container running, the named volume removed, and you can explain where OpenRouter cost comes from.
- Run
docker ps -aand confirm nointake-agentrow remains. - Run
docker volume lsand confirm nointake-sessionsrow remains. - In your own words, name the two token categories a
curlcall to/invocationscan spend, and describe one request shape that costs nothing (a malformed payload rejected before the model runs). - State where you would check your actual OpenRouter spend after running this course (the OpenRouter dashboard, tied to your API key).
Seeing both docker ps -a and docker volume ls come back empty, and being able
to state the cost model in one sentence, is this lesson’s — and this track’s —
final checkpoint.
Cleanup
Cleanup is this lesson, so there is nothing extra required — with one optional
step. The built image (intake-agent) itself is not removed by the teardown
above; it costs nothing to leave on disk, but if you want a completely clean
machine you can remove it too:
docker rmi intake-agent
Your OPENROUTER_API_KEY is a credential, not a tutorial resource — it is not
“torn down,” but if you created it solely for this course and do not plan to keep
using it, revoke it from openrouter.ai/keys when you
are done.
That is the whole path: you understood models and tools, built a bounded agent, gave it structured output, an allow-listed tool, and memory, tested its safety, described its container, ran and invoked it on your own machine, and tore it all down. You can now carry that same shape — deterministic gates around a bounded model, a reviewed deployment artifact, and a real teardown habit — back into your own automation work, on whichever track you chose.

