Observe, estimate cost, and tear it all down

Outcome
You can read the container's stdout logs, explain where OpenRouter cost comes from and where to check it, run the full local teardown (container and named volume), and verify with docker ps -a and docker volume ls that nothing tutorial-related remains.
Prerequisites
Lesson 9 complete (a running, invoked container with the intake-sessions volume), Lesson 8 concepts (what the image contains)
Cost
PaidReading logs is free — docker logs only reads data already on your disk. The teardown in this lesson does not spend money either; its entire point is to stop the one thing on the local track that can keep costing anything, which is a container you leave running and keep calling. Do the teardown.

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-120b and 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.

  1. No container. Confirm nothing named intake-agent remains, running or stopped:

    docker ps -a

    The output should show no row with NAMES equal to intake-agent. (docker ps -a, unlike plain docker ps, also lists stopped containers — the check you want here, since docker rm -f should have left none of either.)

  2. No volume. Confirm intake-sessions is gone:

    docker volume ls

    The 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.

  1. Run docker ps -a and confirm no intake-agent row remains.
  2. Run docker volume ls and confirm no intake-sessions row remains.
  3. In your own words, name the two token categories a curl call to /invocations can spend, and describe one request shape that costs nothing (a malformed payload rejected before the model runs).
  4. 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.

Go deeper (1)