Inspiration

On-call is mostly panic and tab-switching. Something spikes at 2am, and the human is the bottleneck: they're the one cross-referencing the deploy log, squinting at error-rate graphs, deciding whether to roll back. The reasoning a good engineer does in those twenty minutes is honestly pretty mechanical; line up the anomaly with what shipped, gauge how confident you are, propose the fix. What isn't mechanical is the nerve to pull the trigger on production.

So I didn't want a bot that acts on its own. I wanted a co-pilot that does the grunt reasoning, shows its work, and then stops and waits for one tap. The other thing that pushed me: every "agent" demo I'd seen flies one perfect scripted route. I wanted the opposite, something that has to react when the data starts lying to it. That became the whole thesis: reasoning under uncertainty, with a human holding the brake.


What It Does

NERVE takes a goal and runs a living loop to reach it: decompose it into a task graph, act through real tools, score its own uncertainty, re-plan when reality shifts, and pause for human approval before anything consequential. It runs two kinds of missions today.

Incident Autopilot watches a service, and when error rate jumps it pulls recent deploys from GitLab, correlates the spike to the likely culprit commit, writes a working-memory belief like "root cause: deploy #4827, confidence 0.92", files an issue, and stages a rollback that sits pending your approval on the dashboard and on Telegram. Mid-incident, a chaos engine can inject contradictory metrics; you watch the risk index climb, the root-cause belief get contradicted then re-confirmed, and the system trip a re-plan instead of charging ahead. Approve the rollback and it executes, then watches the error rate fall from 34% back to 2% and closes the incident.

Research Agent takes any real-world goal, "find the cheapest 2026 World Cup ticket and a well-reviewed hotel nearby" it decomposes it into parallel web searches (real Tavily calls), and hands back a ranked recommendation with links, again gated behind your approval. This one runs fully live in production right now.

The orchestrator re-plans whenever the risk index $R$ crosses a threshold, $R > \tau$ with $\tau = 0.7$, where $R$ is built from failed tasks, retries, active failure injections, and belief contradictions. Risk eases smoothly toward its target so the ECG vitals on the dashboard actually feel alive rather than snapping between states.


How I Built It

The backbone is a custom async orchestration loop in Python. A mission gets decomposed by a Gemini-powered planner, then four agents cycle every loop: Planner, Execution, Risk, and Auditor. They never talk directly; everything flows through the orchestrator and a MongoDB state layer, so every belief, metric, event, and action is persisted and replayable. The "tools" are wrapped clients. GitLab over its REST API, a seeded-or-real Dynatrace path, Tavily for web search, all routed through one base client that adds audit events, retries, and a failure-injection hook.

The dashboard is its own thing. I built it as a "living nervous system": a synaptic mission graph that lights up node by node, a canvas ECG whose heartbeat quickens and shifts teal → amber → coral as risk climbs, a working-memory panel that shows beliefs flipping in real time, and the approval card that mirrors to a Telegram phone. It's React rendered straight in the browser via Babel, talking to the backend over WebSocket with a polling fallback.

Everything runs on Google Cloud Run, with Gemini through Vertex AI. By the end I'd also added real accounts (bcrypt + JWT-cookie sessions) and full per-user data isolation, so two people can use the same deployment and only ever see their own missions.


Challenges I Ran Into

The honest list is long.

The biggest one was the gap between a demo that always plays and a system that runs on real data. The beautiful incident arc only existed in the seeded scenario at first; the real Dynatrace webhook path quietly fell through to the generic planner. Closing that gap meant routing incident missions to the actual workflow with connected clients.

Vertex AI on Cloud Run was a rabbit hole. It worked on my laptop and died on the deployed service, because Cloud Run authenticates as a service account, not my personal credentials. A classic "works locally" trap. Then gemini-2.0-flash-001 returned 404, and so did gemini-2.0-flash; the project simply didn't have those, and gemini-2.5-flash was the one that answered. On top of that the Cloud Resource Manager API was disabled, which the SDK needs just to resolve the project. Each fix was a 3-minute redeploy, so the loop was slow and humbling.

The planner kept adding a "consolidate the findings" task with no tool to run it, which hung every research mission forever; the fix was teaching it that NERVE synthesizes the recommendation itself, so every task it emits must be an executable search. The demo's GitLab rollback 400'd because the real project had no CI pipeline, so I made recovery resilient to that. passlib flat-out broke on Python 3.14 with bcrypt 5.0 (it crashes on its own self-test over the 72-byte password limit), so I dropped it and used bcrypt directly with explicit truncation.

And the one that mattered most: a background security review caught that my auth middleware never gated the WebSocket. Starlette's BaseHTTPMiddleware only runs for HTTP scope, so an anonymous client could've streamed a mission's live event feed over /ws. I'd reviewed that code twice and missed it. Fixed with an explicit cookie check on the socket handshake.


What I Learned

A demo's job is to always work; a product's job is to be honest, and those two pull against each other constantly. Most of my time went into the second one. I learned how differently identity behaves between a dev machine and a managed runtime, how much a fresh pair of eyes (even an automated security pass) catches that you've gone blind to, and how to take a "make it multi-tenant" ask and decompose it into shippable pieces instead of one terrifying rewrite. Mostly I learned that the interesting part of an agent isn't that it follows steps, it's whether it does something sane when the steps stop making sense.

Built With

  • artifact-registry
  • asyncio
  • babel
  • bcrypt
  • cloud-build
  • dynatrace
  • fastapi
  • gemini-2.5-flash
  • gitlab
  • google-cloud-aiplatform
  • google-cloud-run
  • html5
  • mongodb-atlas
  • motor
  • pydantic
  • pyjwt
  • python
  • python-telegram-bot
  • react
  • structlog
  • svg
  • tavily
  • vertex-ai
  • websockets
Share this project:

Updates