AI Expo SCSP Hackathon 2025: Outbreak Ops and the Public Health Sentinel

Jun 6, 2025

In June 2025, the Special Competitive Studies Project (SCSP) hosted the AI Expo Hackathon in Washington, DC. The event convened innovators, researchers, defense technologists, and public policy leaders to tackle urgent national challenges through cutting-edge generative AI and agentic systems.

I traveled across the country from California to Washington, DC to join forces with an exceptional team from Booz Allen Hamilton:

  • Sydney Segear: Led the frontend architecture and user experience design, and spearheaded the final presentation pitch.
  • Carlos J. Arguello: Data science and mathematical modeling lead, designing the statistical anomaly and trend detection engines.
  • Brianna Lynn: Partnered with Sydney on frontend design, public health workflow framing, and presentation delivery.
  • Csaba Toth: Backend agentic workflow architecture, LangGraph orchestration, FastMCP integration, and LLM tool engineering.

We entered the Anthropic track under the team name Outbreak Ops. Our goal: build Health Sentinel (or Public Health Sentinel), an autonomous, multi-agent public health surveillance and crisis mitigation platform.


The Mission: Overcoming Crisis Communication & Surveillance Gaps

The COVID-19 pandemic laid bare deep structural vulnerabilities in global public health infrastructure:

  1. Data Fragmentation & Latency: Key epidemiological signals—case rates, ICU bed occupancy, emergency department visits, and lab testing positivity—were scattered across disconnected county, state, and federal dashboards.
  2. Policy Bottlenecks: By the time decision-makers recognized an exponential outbreak curve, days or weeks had elapsed before mitigation strategies could be formulated, debated, and drafted into enforceable public health directives.
  3. Public Communication Disconnect: Complex epidemiological models and dense legal orders frequently confused citizens, leading to mixed compliance and eroded public trust.

Our vision for Public Health Sentinel (GitHub: bah-scsp-hackathon-2025/public-health-app) was to build an end-to-end agentic platform that continuously monitors real-world epidemiological event streams, detects emerging outbreak anomalies, generates prioritized mitigation strategies for health officials, drafts formal administrative orders, and translates policies into transparent, plain-language guidance for citizens.

To ground our system in reality, we leveraged historic epidemiological signals from the Delphi Group at Carnegie Mellon University (CMU Covidcast / Epidata API), pulling daily positive case rates, hospital admissions, and outpatient doctor visits across United States counties.


Three-Tier Agentic Architecture

Rather than relying on a single, overburdened prompt, we architected a modular multi-agent graph in LangGraph, backed by PostgreSQL for state checkpointing and conversation memory. The system is split into three coordinated agentic flows:

1. Public Health Dashboard Agent (DashboardAgent)

The DashboardAgent functions as the continuous surveillance radar of the system. It ingests multi-signal time-series data across geographic jurisdictions, executes quantitative anomaly detection tools, computes trajectory slopes, and synthesizes executive briefings for health directors.

(Architectural flowchart of the Public Health Dashboard Agent: ingesting metrics, running anomaly detection tools, and compiling executive briefings.)

2. Strategy Generator Agent (StrategyGeneratorAgent)

When the surveillance layer flags an anomaly or threshold crossing, the StrategyGeneratorAgent activates. It evaluates local healthcare headroom (ICU bed reserves, staffing ratios, ventilator inventory), historical transmission velocity, and population density to formulate three prioritized, tiered mitigation options (e.g., targeted testing blitzes, phased public gathering capacity limits, elective surgery suspensions, emergency resource transfers) complete with projected trade-offs and confidence scores.

(Strategy Generator Agent: synthesizing epidemiological context to formulate tiered, evidence-backed mitigation strategies.)

3. Policy Draft Generator Agent (PolicyDraftGeneratorAgent)

Once a public health official selects an operational strategy, the PolicyDraftGeneratorAgent translates strategic intent into formal, legally structured regulatory and policy instruments. It generates emergency declarations, public health orders, executive proclamations, and administrative directives, complete with statutory authority citations, enforcement mechanisms, jurisdictional scopes, and objective sunset criteria.

(Policy Draft Generator Agent: converting chosen strategies into official executive orders, public health advisories, and administrative directives.)


Statistical Anomaly & Trend Detection: Carlos’s Engine

In high-stakes public health emergencies, language models cannot be trusted to perform raw arithmetic or estimate epidemic trajectory slopes on their own. They require deterministic, mathematically sound tools.

Carlos designed a custom quantitative engine specifically for epidemiological time-series data:

(Carlos’s statistical pipeline: log-transformed linear regression and rolling Z-score outlier detection.)

Why Not Kalman Filters or Complex Deep Learning?

We initially debated using recursive Kalman filters or deep LSTM/Transformer forecasting models. However, in emergency management, interpretability, speed, and determinism reign supreme:

  • Complex recursive state estimators (like Kalman filters) can introduce filter lag, require delicate noise covariance tuning, and often smooth over early exponential inflections.
  • Deep neural models act as black boxes, making it difficult to defend policy decisions in court or explain them to elected officials.
  • Reporting artifacts (such as weekend reporting dips and Monday catch-up dumps) can trick complex nonlinear models into hallucinating trends.

The Dual Approach

Carlos implemented a lightweight, robust two-pronged statistical algorithm:

  1. Log-Transformed Linear Regression:
    • Epidemiological case counts are strictly positive (), and disease transmission during outbreak surge phases is intrinsically exponential: .
    • Taking the natural logarithm linearizes the equation: .
    • Fitting ordinary least squares (OLS) linear regression to over rolling 7-to-14-day windows yields the intrinsic growth rate (). From this slope, the agent directly derives the epidemic doubling time () and assesses whether transmission is accelerating or decelerating with standard statistical confidence ( and ).
  2. Rolling Z-Score Outlier Detection:
    • To distinguish genuine transmission surges from transient data dumps or reporting errors, the engine computes rolling Z-scores on detrended residuals.
    • Outliers exceeding or thresholds flag immediate surveillance alerts in the agent graph, prompting the DashboardAgent to investigate neighboring county metrics.

We exposed this engine as an external microservice tool to our LangGraph agents via FastMCP (Model Context Protocol) over Server-Sent Events (SSE), enabling seamless tool discovery and invocation.


Dual Frontend & The $50 Anthropic Token Burn

Public Health Sentinel was designed with two distinct audiences in mind:

  • Public Health Officials Portal: A high-density analytical dashboard featuring interactive heatmaps, multi-signal anomaly alerts, strategy comparison cards, and a policy drafting workspace.
  • Public Citizen Portal: A clean, accessible, jargon-free interface delivering localized risk levels, plain-language action checklists (“What this means for you and your family”), and transparent explanations of current public orders.

Sydney and Bri led the frontend architecture and user experience, crafting intuitive UI flows and orchestrating our hackathon presentation pitch. In any hackathon, flawless backend engineering means little if the story, user empathy, and practical impact aren’t communicated compellingly—and Sydney and Bri delivered a brilliant, cohesive presentation.

Burning the $50 Token Allotment

Anthropic graciously provided each competing team with a $50 API credit. We tested our workflows using Claude 3.5 Sonnet, Claude 3.7 Sonnet (preview with extended thinking), and early preview builds of Claude 4.

However, generating comprehensive legislative and regulatory policy documents—complete with statutory legal boilerplate, historical epidemiological context tables, and multi-tier strategy trade-offs—stuffed our context windows to the brim. Between lengthy document generation and iterative graph debugging, we burned through our entire $50 allotment at breakneck speed! This forced us to practice aggressive prompt hygiene, implement prompt caching, and trim conversational history in the LangGraph state.

AI-Generated Concept Video with Early OpenAI Sora

To accompany Sydney and Bri’s pitch and illustrate the vision of autonomous epidemiological sentinel systems during severe biological threats, we experimented with generating a concept video using an early version of OpenAI’s Sora.

While early Sora demonstrated breathtaking photorealism and dynamic cinematic lighting, it also vividly exhibited the classic uncanny failure modes and physics hallucinations of early-generation text-to-video diffusion models:

  • People morphing into other people or melting through hospital doorways.
  • Lab glassware, medical equipment, and test tubes floating or merging into technicians’ fingers.
  • Dramatic shifts in perspective where monitors, graphs, and emergency room corridors warped between cuts.

Despite these artifacts, the eerie, cinematic atmosphere captured the urgency of biological threat detection and added a memorable visual flourish to the Outbreak Ops demonstration:

This browser does not support the YouTube video player. Watch on YouTube

Concept video generated with an early version of OpenAI Sora for Outbreak Ops. Notice the atmospheric cinematic lighting alongside the surreal morphing and physics hallucinations characteristic of early neural video models.


The Overnight Hackathon Session & Classical Waltzes

Hackathons are pressure cookers. On the critical night between the opening kickoff and the main demonstration day, I went over to Carlos’s home in Washington DC for an all-night marathon coding session.

We set up our dual workstations, fueled by caffeine, technical ambition, and an unconventional choice of background soundtrack: Classical Waltzes.

Instead of lo-fi hip-hop or electronic synthwave, we filled the room with soaring, buoyant orchestral waltzes:

  • Johann Strauss II: The Blue Danube (An der schönen blauen Donau), Voices of Spring (Frühlingsstimmen), and the Emperor Waltz (Kaiser-Walzer).
  • Pyotr Ilyich Tchaikovsky: Waltz of the Flowers from The Nutcracker, the Swan Lake Waltz, and the Sleeping Beauty Waltz.

There was something uniquely exhilarating about debugging asynchronous graph loops and tuning linear regressions to the sweeping, three-quarter time rhythm of a Viennese ballroom orchestra. The buoyant cadence provided incredible focus and kept fatigue completely at bay until the sun came up over DC.


Agentic IDEs & The ReAct Tool-Binding Struggle

This hackathon was also my first intensive, end-to-end trial using Cursor as an agentic coding IDE.

Cursor proved to be a formidable accelerator: it scaffolded FastAPI routes, generated Pydantic schemas, and wrote pytest suites in seconds. However, that overnight session exposed a major failure mode in modern agentic software development: Framework Churn, Outdated Training Data, and Hallucinated Tool Loops.

The Mystery of the Missing Tool Calls

Our agents were designed to invoke Carlos’s anomaly detection tool, parse the regression slope and Z-score, and use those real metrics to assess risk.

Yet, during testing, the agent kept returning plausible-sounding numbers without ever executing the Python function!

We tried conventional prompting remedies:

  • Hardening system prompts: "You MUST call the anomaly tool before answering. Do not estimate."
  • Dropping temperature to 0.0.
  • Switching to Claude 3.7 Sonnet with extended thinking mode (test-time compute).

Hilariously, in extended thinking mode, the model would produce elaborate inner monologues:

“I need to compute the anomaly score for Montgomery County. I will call detect_anomalies(fips='24031'). The tool returned a slope of 0.14 and a Z-score of 2.8…”

The model reasoned about calling the tool, imagined what the output would look like, and then answered based on its own hallucinated data—without the Python function ever being touched!

The Root Cause: Three Generations of ReAct Loops

In the middle of the night, Carlos analyzed the execution trace and diagnosed the problem. The issue was an architectural mismatch between legacy prompt-based agent patterns and modern native tool calling.

1. Legacy ReAct Loop (2022–2023)

In early agent frameworks following the original ReAct paper (Yao et al., 2022), tools were described to the LLM as formatted text within the prompt string. The model was instructed to output structured text conforming to a specific template:

Thought: I need to check the COVID case trends.
Action: detect_anomalies
Action Input: {"fips": "24031"}

The orchestrator used regex to parse the text output, executed the corresponding Python function, and appended the result as an Observation: back into the prompt string.

(The legacy prompt-based ReAct pattern relying on regex text parsing and prompt injection.)

2. Our Unbound ReAct Attempt (What Cursor Generated)

Because coding agents like Cursor are trained heavily on pre-2024 GitHub repositories and older LangChain tutorials, Cursor scaffolded a hybrid loop where tool descriptions were dumped into system prompts, paired with custom output post-processors.

Modern instruction-tuned frontier models (such as Claude 3.5 Sonnet) are trained to be helpful and fluent. When tools are merely described in text rather than exposed through the API’s native tool parameters, the model treats the request as a conversational simulation. It happily synthesizes a plausible outcome rather than emitting a machine-readable tool call.

(The broken unbound ReAct pattern: the model simulates and hallucinates tool execution in text.)

3. Modern ReAct with Native Bound Tools (bind_tools)

Carlos pointed out that modern LLM APIs (Anthropic, OpenAI) feature first-class tool calling with explicit JSON schemas (tools=[...]), and modern LangGraph supports this natively via llm.bind_tools(tools).

We refactored our agent node to bind the tools directly:

from langchain_anthropic import ChatAnthropic
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.prebuilt import ToolNode

# 1. Define tools with type annotations
tools = [detect_epidemic_anomalies, query_cmu_covidcast]

# 2. Natively bind tools to the LLM schema
llm = ChatAnthropic(model="claude-3-5-sonnet-20241022", temperature=0)
model_with_tools = llm.bind_tools(tools)

# 3. Agent node invokes the bound model
def call_model(state: MessagesState):
    response = model_with_tools.invoke(state["messages"])
    return {"messages": [response]}

# 4. Conditional edge inspects message tool calls
def should_continue(state: MessagesState):
    last_message = state["messages"][-1]
    if last_message.tool_calls:
        return "tools"
    return END

# 5. Build the LangGraph workflow
workflow = StateGraph(MessagesState)
workflow.add_node("agent", call_model)
workflow.add_node("tools", ToolNode(tools))

workflow.add_edge(START, "agent")
workflow.add_conditional_edges("agent", should_continue, ["tools", END])
workflow.add_edge("tools", "agent")

app = workflow.compile()

(The modern LangGraph ReAct pattern with native tool binding and conditional routing.)

The effect was instantaneous:

  • Claude emitted official, structured tool_use content blocks.
  • LangGraph intercepted the tool call via should_continue and routed it to ToolNode.
  • The real Python function executed against live data.
  • The result was appended as an official ToolMessage back into the conversation state.
  • Tool hallucinations vanished completely.

Key Lessons for Agentic AI Engineering

Reflecting on the hackathon experience, several critical insights stand out:

  1. Native API Tool Schemas Over Text Prompts: Never rely on prompt engineering alone to force tool use. Modern foundation models require strict JSON schemas passed through the provider’s native function-calling API endpoints.

  2. Test-Time Compute Does Not Fix Bad Schemas: If an LLM lacks native tool bindings, giving it more thinking budget (e.g., Claude 3.7 extended thinking or o1/o3 reasoning) will simply give it more compute power to hallucinate convincing fake tool executions.

  3. The Coding Agent Trap: Beware Architectural Lag: AI coding assistants like Cursor are incredible velocity multipliers, but they suffer from “architectural lag”—they default to the most frequent patterns in their training data, which often reflect deprecated libraries and superseded paradigms. Human architectural knowledge remains essential when navigating rapidly changing ecosystems like LangGraph and MCP.

  4. Deterministic Math for Quantitative Realities: In mission-critical domains like epidemiology, national security, or clinical medicine, never let language models approximate numbers. Use deterministic, explainable statistical methods and let the LLM excel at what it does best: contextual synthesis, policy drafting, and empathetic public communication.


Huge thanks to my wonderful Booz Allen teammates Sydney Segear, Carlos J. Arguello, and Brianna Lynn for an unforgettable hackathon experience in Washington, DC!

Comments loading...