Hands-on Python exercises for the O'Reilly/Pearson live course demonstrating agentic AI patterns using the Claude API.
Each exercise builds a real platform engineering agent that uses Claude's tool-use, structured outputs, and streaming capabilities to solve operational problems.
| Resource | URL |
|---|---|
| Live demo site | agentic-pe.platformetrics.com |
| GitHub repo | github.com/achankra/agentic-ai-platformengineering |
| O'Reilly course page | oreilly.com/live-events/... |
| Book | effectiveplatformengineering.com |
| Exercise | Agent | Key Capability |
|---|---|---|
| 1 | Diagnostic Agent | Tool use — Claude decides which tools to call to investigate CI/CD failures |
| 2 | Release Readiness | Structured outputs — guaranteed JSON decisions instead of text parsing |
| 3 | Multi-Agent Orchestration | Concurrent agents with separate tool sets coordinated by a central orchestrator |
| 4 | Developer Portal | Tool-driven onboarding — Claude gathers data before generating plans |
Agentic AI in Platform Engineering/
└── code/ # <-- You are here
├── core/ # Shared AI client, schemas, memory
│ ├── client.py # AIClient with tool use, structured outputs, streaming
│ ├── schemas.py # Pydantic models for structured outputs
│ └── memory.py # ConversationState for multi-turn interactions
│
├── agents/ # One package per exercise
│ ├── diagnostic/ # Ex 1: CI/CD Failure Diagnosis
│ │ ├── agent.py # DiagnosticAgent with agentic loop
│ │ └── tools.py # fetch_logs, analyze_patterns, check_recent_commits
│ ├── release_readiness/ # Ex 2: Quality Gates
│ │ ├── agent.py # QualityGateAgent with structured outputs
│ │ └── tools.py # get_test_coverage, get_performance_metrics, check_security_scan
│ ├── multi_agent/ # Ex 3: Multi-Agent Orchestration
│ │ ├── orchestrator.py # Coordinator that runs both agents concurrently
│ │ ├── cost_agent.py # CostOptimizer with query_billing, recommend_scaling
│ │ └── reliability_agent.py # IncidentResponder with check_alerts, assess_blast_radius
│ └── portal/ # Ex 4: Developer Portal
│ ├── agent.py # PortalAgent with tool-driven onboarding
│ └── tools.py # check_service_maturity, get_team_context, generate_onboarding_plan
│
├── tests/ # Smoke tests (all run in mock mode)
│ └── test_mock_mode.py
├── logs/ # Sample CI/CD failure logs
│ └── sample.log
├── outputs/ # Generated reports (git-ignored)
├── requirements.txt
├── README.md # <- You are here
└── INSTALLATION.md # Detailed setup instructions
# Navigate to the code directory
cd code
# Install dependencies
pip install -r requirements.txt
# All exercises work without an API key (mock mode)
python3 -m agents.diagnostic.agent logs/sample.log
python3 -m agents.release_readiness.agent --simulate good
python3 -m agents.multi_agent.orchestrator --scenario balanced
python3 -m agents.portal.agent --project notification-service
# Run tests
python3 -m pytest tests/ -v- Python 3.10+ (3.11 or 3.12 recommended)
- pip (included with Python)
- An Anthropic API key (optional — all exercises work in mock mode without one)
python3 -m venv .venv
source .venv/bin/activate # macOS / Linux
# .venv\Scripts\activate # Windows
pip install -r requirements.txtpython3 -m pytest tests/ -v # All 15 tests should passSee INSTALLATION.md for detailed setup instructions, model selection, cost estimates, and troubleshooting.
You define tools as JSON schemas. Claude decides when and which to call. The client executes the tool and loops the result back. This is the agentic loop: observe -> reason -> act -> repeat.
response = client.ask_with_tools(
system_prompt="You are a diagnostic agent...",
user_prompt="Analyze this CI/CD failure",
tools=DIAGNOSTIC_TOOLS, # JSON schema definitions
tool_handler=handle_diagnostic_tool, # Your execution function
max_iterations=10, # Safety limit
)Get guaranteed-valid JSON conforming to a Pydantic schema. No regex, no text matching — Claude returns typed data.
response = client.ask_structured(
system_prompt="Make a release decision...",
user_prompt="Metrics: coverage=88%, errors=0.1%...",
json_schema=ReleaseDecision.json_schema(),
)
# response.structured_output = {"decision": "RELEASE", "confidence": 0.92, ...}Token-by-token output for interactive CLI experiences.
response = client.ask_streaming(
system_prompt="...", user_prompt="...",
callback=lambda chunk: print(chunk, end=""),
)Every exercise runs from the code/ directory using Python's -m flag (this ensures package imports resolve correctly). All four agents work in mock mode (no API key) by default — you'll see simulated AI reasoning that demonstrates the architecture. When you're ready to see real Claude responses, set your API key and re-run the same commands.
cd code # All commands assume you're here
source .venv/bin/activate # If using a virtual environmentThe diagnostic agent investigates CI/CD failures the way a senior engineer would: read logs, spot patterns, check recent commits, then synthesize a root-cause analysis. Claude autonomously decides which tools to call and in what order — you don't hardcode the investigation flow.
Pattern: Agentic loop (ask_with_tools) — Claude observes → reasons → acts → repeats until it has enough information to diagnose the problem.
Tools available to Claude: fetch_logs (retrieve CI/CD output), analyze_patterns (failure trends across recent runs), check_recent_commits (git history that might explain the breakage).
# Analyze the included sample log
python3 -m agents.diagnostic.agent logs/sample.log
# Specify a workflow and branch for context
python3 -m agents.diagnostic.agent logs/sample.log --workflow deploy-prod --branch release/2.1
# Interactive mode — describe the problem in natural language
python3 -m agents.diagnostic.agent --mode interactiveOutput: A markdown diagnostic report printed to the console, plus a JSON file saved to outputs/diagnostics/. The report includes root cause, confidence score, fix suggestions, and which tools Claude chose to call.
Why agentic? A traditional script would run a fixed sequence of checks. Here, Claude adapts its investigation based on what it finds — if the logs mention a flaky test, it calls analyze_patterns to check recurrence before drawing conclusions. The tool-calling order emerges from reasoning, not from your code.
The quality-gate agent gathers metrics (test coverage, performance, security scans) and returns a typed JSON decision — RELEASE, ROLLBACK, or HOLD — with confidence scores, risk levels, and actionable reasons. No regex parsing, no fragile text extraction.
Pattern: Structured outputs (ask_structured) with Pydantic schema (ReleaseDecision). Claude's response is guaranteed to conform to the schema. Optionally preceded by tool-use to gather metrics.
Tools available to Claude (with --use-tools): get_test_coverage, get_performance_metrics, check_security_scan.
# Simulate a healthy release
python3 -m agents.release_readiness.agent --simulate good
# Simulate a failing release (expect ROLLBACK)
python3 -m agents.release_readiness.agent --simulate bad
# Mixed signals — watch Claude weigh trade-offs
python3 -m agents.release_readiness.agent --simulate mixed
# Let Claude gather its own metrics via tool calls
python3 -m agents.release_readiness.agent --use-tools --service payment-serviceOutput: A table comparing metrics against thresholds, the structured decision (decision, confidence %, risk level), reasons, recommended actions, and deployment strategy. JSON saved to outputs/quality-gates/.
Why agentic? Conventional quality gates are threshold checklists — pass or fail. The structured-output agent weighs multiple signals holistically: 87% coverage might be acceptable if security is perfect and error rates are near zero. The decision is reasoned, not boolean.
Two specialist agents — a Cost Optimizer and an Incident Responder — run concurrently via asyncio.gather(), each with their own tools and system prompts. A Coordinator agent then synthesizes their (often conflicting) recommendations into a single infrastructure decision.
Pattern: Concurrent multi-agent with conflict resolution. Each sub-agent uses tool-use independently. The coordinator receives both reports and reasons about trade-offs.
Tools: Cost agent uses query_billing and recommend_scaling. Reliability agent uses check_alerts and assess_blast_radius.
# Balanced scenario — both agents contribute equally
python3 -m agents.multi_agent.orchestrator --scenario balanced
# Cost pressure — low utilization, high spend
python3 -m agents.multi_agent.orchestrator --scenario cost-optimization
# Active incident — high CPU, errors spiking
python3 -m agents.multi_agent.orchestrator --scenario incident-responseOutput: Infrastructure state summary, each agent's independent analysis (including tool calls made), and the coordinator's final decision with reasoning. JSON saved to outputs/multi-agent/.
Why agentic? A rules engine would need hand-coded priority logic for every conflict type. Here, the coordinator understands context — during an incident it prioritises reliability over cost savings, but in steady state it finds the right balance. Adding a third specialist (say, a compliance agent) requires no rewrite of the coordination logic.
The portal agent helps teams onboard services to an internal platform. Claude gathers context (maturity assessment, team info) via tool calls, then generates a phased adoption plan with specific tasks and time estimates tailored to the service's current state.
Pattern: Agentic loop with guided investigation — Claude is prompted to gather data in a logical order (assess maturity → understand team → build plan) but can adapt if needed.
Tools available to Claude: check_service_maturity (scores across CI/CD, monitoring, logging, tests, docs), get_team_context (language, framework, team size), generate_onboarding_plan (phased tasks based on gaps).
# Onboard a specific service
python3 -m agents.portal.agent --project notification-service
# Try different services to see how plans adapt
python3 -m agents.portal.agent --project payment-service
python3 -m agents.portal.agent --project user-service
# List all available services with maturity overview
python3 -m agents.portal.agent --list-servicesOutput: A maturity assessment, team context summary, and a multi-phase onboarding plan with week-by-week tasks. JSON saved to outputs/portal/.
Why agentic? A static onboarding checklist is the same for every team. The portal agent adapts — a service that already has CI/CD but lacks monitoring gets a plan focused on observability, not pipeline setup. The plan reflects the team's actual gaps.
export ANTHROPIC_API_KEY=sk-ant-...
python3 -m agents.diagnostic.agent logs/sample.logThe client auto-detects available API keys. Set USE_MOCK_AI=true to force mock mode even with a key present. You can switch models with the ANTHROPIC_MODEL environment variable:
# Haiku — fastest and cheapest, good for iteration
export ANTHROPIC_MODEL=claude-haiku-4-5-20251001
# Sonnet — default, best balance of quality and cost
export ANTHROPIC_MODEL=claude-sonnet-4-5-20250929
# Opus — most capable, best for complex multi-agent reasoning
export ANTHROPIC_MODEL=claude-opus-4-5-20251101Each exercise run involves 1,000–3,000 tokens of input and 500–1,500 tokens of output. The multi-agent exercise (Exercise 3) costs roughly 2× a single-agent run because it makes three separate API calls.
| Model | Input Price | Output Price | Single Exercise Run | All 4 Exercises (10 runs each) |
|---|---|---|---|---|
| Haiku 4.5 | $1/M tokens | $5/M tokens | ~$0.02 | ~$0.69 |
| Sonnet 4.5 | $3/M tokens | $15/M tokens | ~$0.05 | ~$2.07 |
| Opus 4.5 | $15/M tokens | $75/M tokens | ~$0.15 | ~$6.00 |
Start with Haiku while you're experimenting and iterating. Switch to Sonnet (the default) for realistic output quality. Use Opus when you want to see the most sophisticated reasoning, particularly in the multi-agent coordinator.
ModuleNotFoundError: No module named 'core'
You're running from the wrong directory or not using the -m flag. Both matter:
cd code # Must be in the code/ directory
python3 -m agents.diagnostic.agent logs/sample.log # Must use -m, not python3 agents/...anthropic.AuthenticationError
Your API key is missing or invalid. Verify with echo $ANTHROPIC_API_KEY. If you just want to explore the architecture without an API key, mock mode works for all exercises — set export USE_MOCK_AI=true.
anthropic.BadRequestError mentioning output_config
The structured outputs feature requires anthropic Python package version 0.45+. Upgrade: pip install --upgrade anthropic.
pydantic import errors
Pydantic v2 is required (v1 won't work). Check your version: python3 -c "import pydantic; print(pydantic.__version__)". Upgrade if needed: pip install 'pydantic>=2.5.0'.
Tests fail with import errors
Run from the code/ directory using pytest as a module:
cd code
python3 -m pytest tests/ -vRuntimeWarning: found in sys.modules after import of package
This warning from python3 -m agents.*.agent is harmless — it's a Python packaging quirk with -m execution. The agent runs correctly despite the warning.
Rate limit errors (429)
The Anthropic API has per-minute rate limits. If you hit them, wait 60 seconds and retry. Haiku has the most generous limits. See your dashboard at console.anthropic.com for current limits.
Outputs directory doesn't exist
The agents create outputs/ subdirectories automatically on first run. If you get a permission error, create it manually: mkdir -p outputs.
MIT