Core Exercise: Put an Agent Behind FastAPI and Docker¶
You will wrap one model call in two HTTP endpoints: /health says the service process is alive, while /chat accepts a question and returns an answer.
Pairs with Core Exercise 4 in Stage 7 — Agent Production Engineering: Harness, Loops, and Graphs. Complete Eval, Observability, and Safe Execution first, then make the service available to others.
🎯 Learning goals¶
- Use Pydantic to reject blank or oversized text and excessive
max_tokens. - Separate liveness from real upstream readiness; the health check never calls a model.
- Reduce risk with a non-root container, loopback port, and read-only filesystem.
Run the tests without starting a server¶
Open PowerShell in this folder and copy:
py -3.11 -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
.\.venv\Scripts\python.exe test.py
.\.venv\Scripts\python.exe test_anthropic.py
Two 🎉 messages mean the offline tests cover 200, 422, 429, 502, 503, and input boundaries. TestClient opens no real network port.
Path A: Start the Ollama API locally
ollama pull qwen3.5:4b
ollama serve
Open another PowerShell window:
.\.venv\Scripts\python.exe -m uvicorn starter:app --host 127.0.0.1 --port 8000
Open a third PowerShell window:
Invoke-RestMethod -Method Post -Uri http://127.0.0.1:8000/chat `
-ContentType "application/json" `
-Body '{"message":"Explain an agent in one sentence.","max_tokens":100}'
Ollama does not charge a provider model API fee. Hardware, electricity, and time still have costs.
Path B: Start the Anthropic API locally
$env:ANTHROPIC_API_KEY = "paste-your-key"
$env:MODEL = "claude-haiku-4-5-20251001"
.\.venv\Scripts\python.exe -m uvicorn starter_anthropic:app --host 127.0.0.1 --port 8000
Haiku 4.5 costs $1 / 1M input tokens and $5 / 1M output tokens:
estimated cost = (input_tokens × $1 / 1M) + (output_tokens × $5 / 1M)
Set a $1 provider spend limit first. Use returned input/output tokens for an estimate; do not turn one short prompt's cost into a permanent price.
Five important terms¶
- Endpoint: one entry offered by a service, such as
POST /chat. - Schema validation: checks input shape and limits first; invalid input receives 422.
- Liveness: confirms the program is alive. This
/healthroute performs only a cheap process check. - Request ID: a tracking number for one request; logs store the ID, not the full prompt.
- Non-root container: the process is not the container administrator, reducing some possible damage.
| Situation | HTTP code | What the client should do |
|---|---|---|
| Answer returned | 200 | use the answer |
| Input is missing or outside limits | 422 | fix the request; do not retry unchanged data |
| Provider rate limit | 429 | wait and follow Retry-After or backoff |
| Model returns an empty answer | 502 | record it, then retry a bounded number of times or ask a human |
| Upstream connection fails | 503 | retry later, with a limit |
| Unexpected program error | 500 | alert and fix the program |
Change one thing¶
Send max_tokens=1001 to /chat. Confirm that FastAPI returns 422 and never calls the model.
Success check¶
-
/healthcalls neither Ollama nor Anthropic. - A blank message, 4,001 characters, and
max_tokens=1001are rejected. - Logs have a request ID but no full user prompt or API key.
- You know these Docker settings reduce risk; do not treat them as a sandbox.
Start Docker with safer teaching defaults
docker build -t stage7-agent-api .
Ollama Path A:
docker run --rm --read-only --tmpfs /tmp `
-p 127.0.0.1:8000:8000 `
-e OLLAMA_API_BASE=http://host.docker.internal:11434/v1 `
stage7-agent-api
Anthropic Path B:
docker run --rm --read-only --tmpfs /tmp `
-p 127.0.0.1:8000:8000 `
-e ANTHROPIC_API_KEY=$env:ANTHROPIC_API_KEY `
stage7-agent-api `
uvicorn starter_anthropic:app --host 0.0.0.0 --port 8000
These settings provide a non-root user, loopback port, and read-only filesystem. Do not treat them as a sandbox; they do not add TLS, authentication, authorization, rate limiting, egress policy, or a secret manager.
Production additions and common problems
- Add authentication, authorization, TLS, and rate limiting before exposing the service.
- Keep liveness cheap. If you need readiness, build a separate dependency check with a timeout and caching.
- Store API keys in a secret manager or protected environment variables, never in an image, program, or README.
- Set request-body, concurrency, timeout, retry, and daily token limits.
- For streaming, use SSE or WebSocket and handle interruption and cancellation.
- Scan the image and Python dependencies, then pin reproducible versions before production deployment.
📚 Required reading and learning resources¶
- ⭐⭐⭐⭐⭐ FastAPI official tutorial: primary documentation for schemas, errors, dependencies, and deployment.
- ⭐⭐⭐⭐⭐ Dockerfile best practices: build smaller, updateable, non-root images.
- ⭐⭐⭐⭐⭐ Docker port publishing: understand why
127.0.0.1:8000:8000is more conservative than publishing on every interface. - ⭐⭐⭐⭐
awesome-harness-engineering: explore additional harness patterns. - ⭐⭐⭐⭐⭐
datawhalechina/hello-agents: chapter-style agent production background.
See the full list in Stage 7 Featured Projects.
Models, prices, packages, deployment documents, and links checked: 2026-08-28 UTC.