GI Ops Console
Ops-question agent for General Intuition that shows its reasoning and grades its sources.
Next.js 16React 19TypeScriptVercel AI SDK v7OpenRouter (DeepSeek V4 Flash)ExaZodTailwind CSS 4react-markdown + remark-gfmVitestVercellaunchd
Problem
General Intuition's ops-generalist posting asked for someone who could make things run without them, and an ops team's questions (which teams are we scaling, how did press frame the round, who else trains on gameplay video) need internal facts, the live job board, and the open web at once. A general-purpose chatbot answers those fluently and unverifiably. The bet: an agent on a cheap model that shows every step and grades every source, so an ops lead can trust an answer in the time it takes to read one line.
What I built
A Next.js 16 console: one input, one POST route, and an NDJSON stream of typed events (step, think, delta, note, source, usage, done) rendered as a run log of labelled tool steps with the model's live reasoning behind a toggle, then a TL;DR-first markdown answer, its sources, and a cost footer computed from token usage and per-million prices pinned in code. The model is DeepSeek V4 Flash via OpenRouter on Vercel AI SDK v7, swappable with one env var. It has exactly four read-only tools: a fact sheet where every entry carries its source, the company's public Ashby job board, and Exa search and page fetch. A structured-output scope gate runs before any tool budget is spent, and a token header plus per-IP and per-instance rate limits keep drive-by traffic off real API keys. A nightly script re-asks a fixed hiring question against production, writes the answer into a static snapshot, commits, and redeploys, so a header chip can say when the agent last ran itself.
Highlights
- Boxed in by code, not prompt: CAISI's 2025 evaluation found DeepSeek models more susceptible to agent hijacking, so there is no shell and no write path, only four schema-constrained read-only tools, with fetched web content declared data-not-instructions and LinkedIn off-limits in the system prompt; the README names the trade-off instead of hiding it.
- Scope is decided by a structured-output gate before a single tool call, with the verdict enforced in code: an unrelated or injected prompt gets a one-line refusal and spends nothing; a prompt-injection probe was rejected there during QA.
- Citations are built from tool results, never parsed out of the prose, and grouped by evidence strength (opened and read, verified fact sheet, seen in search but not opened) so a search hit can never masquerade as a source the agent actually read.
- Per-run cost is arithmetic on token usage and prices pinned in code; the fact sheet dropped anything that couldn't be re-verified and labels a reported-but-unconfirmed item as such; after production QA caught the model searching last year's news, the route injects today's date because the model has no clock.
- The nightly self-run is unattended-deploy safe: launchd fires hourly, a 20-hour throttle collapses that to one real run a day, an atomic mkdir lock stops a load-time run and a manual run racing, an empty answer never overwrites a good snapshot, and a dirty working tree refuses to ship, so every deploy is a commit.
Numbers
- Typical query cost ~$0.0002 to $0.0004 per the app's own cost footer (DeepSeek V4 Flash via OpenRouter)
- Model priced at ~$0.08 per million input tokens and ~$0.16 per million output tokens (OpenRouter, checked 2026-08-30)
- Rate limit of 10 queries per IP per hour and 40 per instance per hour, behind a client token header
- Six-step budget per run, one constant driving both the system-prompt instruction and the runtime stop condition
- Collapsed TL;DR view is 1,254 characters versus 9,678 expanded on the same answer
- Nightly self-run: hourly launchd firing with a 20-hour throttle and 2-hour stale-lock recovery
- QA against production: six prompts run, three defects found and fixed
- Smoke tests cover the guard (401, per-IP 429, global 429), all four tools including failure-as-string paths, and the fact sheet's source keys
Screenshots
