Demo agent for a 30-minute PyOhio talk, built with the Strands Agents SDK. Each stage adds one building block. The agent is unshakably optimistic — it genuinely believes this is the Browns' year, every single year.
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
aws configure # needs Bedrock model access (Claude Sonnet)
# One-time, for the guardrail stage (and the frontend/full agent, optionally):
python setup_guardrail.py
export BROWNS_GUARDRAIL_ID=<printed id>
# One-time, for the persistent memory stage:
python setup_memory.py
export BROWNS_MEMORY_ID=<printed id>| # | File | Concept | The moment |
|---|---|---|---|
| 1 | 01_minimal_agent.py |
Agent in a few lines | Interactive chat, pure belief, no tools |
| 2 | 02_agent_loop_trace.py |
The agent loop | Watch model → tool → model in agent.messages |
| 3 | 03_tools.py |
Tools (@tool) |
Real nflverse stats: 5-12 spun as "underestimated" |
| 4 | 04_hooks_and_interrupts.py |
State + hooks + interrupts | DEFCON DAWG POUND → "Authorize Emergency Bar Protocol?" |
| 5 | 05_guardrails.py |
Bedrock Guardrails | Sadness passes, trash talk gets BLOCKED |
| 6 | 06_conversation_mgmt.py |
Conversation management | A season of suffering, summarized |
| 7 | 07_persistent_memory.py |
AgentCore Memory | Quit, restart — it still remembers your hot takes |
| — | full_agent.py |
Everything composed | Interactive terminal REPL |
Run any stage: python 04_hooks_and_interrupts.py
export BROWNS_GUARDRAIL_ID=<id> # optional — enables the guardrail in the UI
streamlit run frontend/app.pyBrowns-themed chat with a live pain gauge (sidebar, with sparkline). When pain peaks past the threshold, the frontend auto-authorizes the Emergency Bar Location Protocol: a banner and map drop into the chat with the nearest Browns Backers bar pinned, the gauge holds at the peak ("💀 DEFCON DAWG POUND") for the crisis turn, then vitals stabilize. Casual "where should I watch the game" requests get a quiet approval — no sirens.
- Model knowledge handles Browns history, trivia, and coping — no tool needed. (It also gets today's date and the NFL season convention injected into the system prompt, so "last season" resolves correctly.)
- Tools return plain data; the personality lives in the system prompt.
Real stats via nflverse (
nflreadpy— games/plays back to 1999, players, draft; free, no key, cached locally so it works offline), NWS weather (real API, no key), bar lookup (fictional bars), and pain reporting. - The model decides the pain delta each turn via
update_pain_index— it understands context better than keyword matching. The tool stores it in agent state (shared with hooks, costs zero context tokens). - The hook (
hooks/pain_monitor_hook.py) shows both hook superpowers in ~30 lines:AfterToolCallEvent— when pain crosses the threshold, append an emergency directive to the pain tool's result (the model sees it and calls the bar tool); once the bar is delivered, reset the pain index — the intervention works.BeforeToolCallEvent— every bar dispatch pauses the loop with an interrupt for human sign-off. Terminal demos ask y/N; the frontend auto-authorizes.
- The guardrail (
browns-loyalty-guardrail, STANDARD tier) blocks insults aimed at the Browns while letting fan grief through. The blocked-input message does the comedy: "This is a SAFE SPACE for BELIEVERS." - Memory (stage 7) uses AgentCore Memory via a Strands session manager —
session summaries, fan preferences, and facts persist across restarts
(three long-term strategies configured in
setup_memory.py).
Flow: Model reports → Tool stores → Hook escalates → Model reacts → Hook interrupts → Human decides
- Pain climbs over a few turns: calm questions score ~0, bad news +1/+2, real
trauma (The Drive / The Fumble / Red Right 88) or despair +3 to +5. Two
heavy turns reliably cross the threshold. Tune
PAIN_THRESHOLDinhooks/pain_monitor_hook.pyif needed. - Stats are real (nflverse) and cached locally after first load — run each stage once before the talk so the parquet cache is warm and offline-safe. The weather tool hits the live NWS API (which throws the occasional 500 — rehearse it). Bars are intentionally fictional.
- Guardrail note: the topic definition needs the explicit "sadness is NOT this topic" carve-out and the STANDARD tier, or it blocks sad-but-loyal fans too.
Powered by Strands Agents SDK | Go Dawgs 🐶