Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions kits/incident-copilot/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Incident Copilot — flow IDs and Lamatic project credentials.
# Copy this to the app: kits/incident-copilot/apps/.env.example -> apps/.env.local
# NEVER commit a real .env / .env.local — only this .env.example with placeholders.

# Deployed Lamatic flow IDs (Studio -> open flow -> copy Flow ID)
INVESTIGATE_FLOW_ID=
DRAFT_COMMS_FLOW_ID=

# Lamatic project credentials (Studio -> Settings -> API)
LAMATIC_API_URL=
LAMATIC_PROJECT_ID=
LAMATIC_API_KEY=

# Optional: raises GitHub API rate limits and enables private repos.
# A fine-grained token with read-only "Contents" access is enough.
GITHUB_TOKEN=
7 changes: 7 additions & 0 deletions kits/incident-copilot/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules/
.next/
.env
.env.local
.env*.local
.DS_Store
*.log
134 changes: 134 additions & 0 deletions kits/incident-copilot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Incident Copilot

An **investigation agent** for on-call engineers. Paste a production alert and it does what a good teammate does in the first ten minutes of an incident: reads the runbooks, checks what shipped recently, and comes back with **ranked, evidence-grounded hypotheses** — each with the evidence *for* and *against* it, and a concrete next step. Feed it new information and it **revises the ranking** instead of starting over. When you're ready, it drafts the Slack update and a postmortem skeleton.

It investigates. It does not act — no deploys, no restarts, no posting. Drafts only.

> This is the companion to [`llm-eval-harness`](../llm-eval-harness): that kit gates **generation** quality, this one helps you **diagnose** a live incident.

---

## Why this is different from a chatbot

A chatbot answers the question you asked. Incident Copilot **gathers evidence, weighs it, and argues against itself** — the three things that make it an agent rather than a prompt:

1. **It grounds every hypothesis in real evidence** — runbook excerpts (RAG) and the actual recent commits on the affected repo (a live tool call), never a guess.
2. **It surfaces contradicting evidence, not just supporting** — the constitution makes "argue both sides" a hard rule, so you get a calibrated ranking, not a confident single answer.
3. **It gets smarter with new information** — memory is scoped to the incident ID, so a follow-up (“the DB pool is maxed”) *revises* the existing ranking and tells you what changed.

---

## Architecture

```
Next.js app (apps/) Lamatic flows
───────────────── ─────────────
paste alert + repo ─┐
│ investigate(alertText, incidentId, repoUrl)
actions/orchestrate.ts ───────────────▶ flow: investigate
▲ ┌──────────────────────────────┐
│ │ trigger (alertText, │
│ │ incidentId, repoUrl?) │
│ ┌──────┼──▶ Runbook_RAG (RAG) ────┐ │
│ │ │ Parse_Repo (code) │ │
│ ranked │ │ └▶ Fetch_Commits (API)│ │
│ hypotheses │ │ └▶ Shape_Changes ─┤ │
│ + evidence │ │ Retrieve_Prior (memory)─┤ │
│ │ │ (fan-in) ▼ │
│ │ │ Diagnose (Instructor │
│ │ │ LLM, JSON schema) │
│ │ │ ▼ │
│ │ │ Remember (memory add) │
│ │ └──────────────────────────────┘
│ draftComms(hypothesis, evidence, rankedHypotheses, incidentId)
actions/orchestrate.ts ───────────────▶ flow: draft-comms
Comment thread
coderabbitai[bot] marked this conversation as resolved.
┌──────────────────────────────┐
Slack update ◀─────────────────────── │ Draft_Slack (LLM, hedged) │
Postmortem ◀─────────────────────── │ Draft_Postmortem (LLM) │
└──────────────────────────────┘
```

**AgentKit capabilities used — each doing real work, none decorative:**

| Capability | Where | Why it earns its place |
|---|---|---|
| **RAG** | `Runbook_RAG` | Grounds hypotheses in your runbooks instead of the model's memory |
| **Tool calling** | `Fetch_Commits` (`apiNode` → GitHub) | "Check recent deploys" becomes a real check, not a claim |
| **Memory** | `Retrieve_Prior` / `Remember`, keyed by incident ID | The one thing that makes re-investigation *revise* rather than repeat |
| **Structured output** | `Diagnose` (`InstructorLLMNode` + JSON schema) | Ranked hypotheses the UI can render, with guaranteed shape |
| **Multiple flows** | `investigate` + `draft-comms` | Separates "figure out what's true" from "write it up" |
| **Prompts / model-configs / constitution** | throughout | Diagnose runs at temp 0 (repeatable triage); drafts run warmer; the constitution enforces evidence + hedging |

---

## Flows

| Flow | Input | Output |
|---|---|---|
| `investigate` | `{ alertText, incidentId, repoUrl?, githubToken? }` | `{ hypotheses[], summary, insufficientInfo }` |
| `draft-comms` | `{ hypothesis, evidence, rankedHypotheses, incidentId }` | `{ slackUpdate, postmortem }` |

Each `hypothesis` is `{ rank, title, confidence, reasoning, supportingEvidence[], contradictingEvidence[], nextStep }`.

---

## Setup

### 1. Build the flows in Lamatic Studio

The two flows live in [`flows/`](./flows). Import them into [Lamatic Studio](https://studio.lamatic.ai), then:

- **`investigate`** — set the model on the `Diagnose` node (temperature **0**), the embedding + generative models and vector DB on `Runbook_RAG`, and a memory collection on `Retrieve_Prior` / `Remember`.
- **`draft-comms`** — set the models on `Draft_Slack` (~0.5) and `Draft_Postmortem` (~0.3).
- **Index the runbooks:** load [`assets/demo/runbooks.md`](./assets/demo/runbooks.md) (or your own) into the vector DB that `Runbook_RAG` queries. Each `##` section is one runbook entry.
- **Deploy** both flows and copy each **Flow ID**.

### 2. Run the app

```bash
cd kits/incident-copilot/apps
cp .env.example .env.local # fill in the values below
npm install
npm run dev # http://localhost:3000
```

### Environment variables

| Variable | Where to find it |
|---|---|
| `INVESTIGATE_FLOW_ID` | Studio → deploy `investigate` → copy Flow ID |
| `DRAFT_COMMS_FLOW_ID` | Studio → deploy `draft-comms` → copy Flow ID |
| `LAMATIC_API_URL` | Studio → Settings → API |
| `LAMATIC_PROJECT_ID` | Studio → Project settings |
| `LAMATIC_API_KEY` | Studio → API Keys |
| `GITHUB_TOKEN` | *(optional)* raises GitHub rate limits / enables private repos. Read server-side only — never sent to the browser. |

---

## Try it

1. Click **Load example** — a realistically ambiguous checkout-latency incident (`INC-2043`).
2. Click **Investigate** — watch it retrieve runbooks, check changes, and return 2–4 ranked hypotheses with evidence.
3. In **New information**, the example pre-fills *“DB connections pegged at max, payment provider is green.”* Click **Add info & re-investigate** — the DB-pool hypothesis climbs, payment degradation drops, and each card explains what changed.
4. Click **Draft Slack + postmortem** — get a hedged status update and a blameless postmortem skeleton.

The example alert is deliberately ambiguous (latency + 5xx could be a bad deploy, DB pool exhaustion, a cache stampede, or payment degradation) so the ranking — and the revision — are meaningful rather than a keyword match.

---

## Design notes & tradeoffs

- **Drafts, never posts (v1).** No real Slack/PagerDuty write. The draft-vs-post line is a deliberate safety boundary for an agent that reasons under uncertainty, not a missing feature.
- **Graceful degradation.** No repo, a private repo without a token, or a GitHub rate-limit all resolve to “recent-changes unavailable” — the investigation continues on runbooks alone rather than failing.
- **Deterministic triage.** `Diagnose` runs at temperature 0 so the same alert + evidence yields the same ranking.
- **Defensive parsing.** Structured output is normalized app-side ([`lib/format.ts`](./apps/lib/format.ts)); a malformed field degrades to a safe default instead of crashing the page.
- **The runbook corpus is yours.** Swap [`assets/demo/runbooks.md`](./assets/demo/runbooks.md) for your team's runbooks — no code changes.

## Future work

Webhook-triggered auto-investigation from a real alert source; optional real Slack posting via an `apiNode`; multi-repo correlation; a feedback loop that tunes confidence based on which next step actually resolved the incident.

Built on [Lamatic](https://lamatic.ai).
73 changes: 73 additions & 0 deletions kits/incident-copilot/agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Incident Copilot

## Overview
Incident Copilot is an investigation agent for on-call engineers, built on Lamatic.ai. Given a production alert, it produces ranked, evidence-grounded root-cause hypotheses by combining retrieval over the team's runbooks (RAG), a live check of recent repository activity (a GitHub API tool call), and incident-scoped memory that lets follow-up information revise the ranking instead of restarting it. It then drafts an honestly-hedged Slack update and a blameless postmortem skeleton. It analyses and drafts only — it never takes real-world actions.

## Purpose
The first ten minutes of an incident are spent reconstructing context: which runbook applies, whether a recent deploy is implicated, and what to tell the team. Incident Copilot compresses that by gathering the evidence, weighing it, and presenting a calibrated ranking with supporting *and* contradicting evidence for each hypothesis — so an engineer can act on the most-supported cause rather than the most-recent or most-alarming one. Because memory is scoped to the incident ID, the agent becomes more accurate as the incident unfolds and new facts arrive.

## Flows

### investigate
- **Trigger:** API request (`graphqlNode`, realtime) with `{ alertText, incidentId, repoUrl?, githubToken? }`.
- **Processing:**
1. `Runbook_RAG` (`RAGNode`) retrieves the most relevant runbook excerpts for the alert.
2. `Parse_Repo` (`codeNode`) turns the optional repo URL into GitHub API parameters, or flags that no repo was given.
3. `Fetch_Commits` (`apiNode`) GETs recent commits for the affected repo.
4. `Shape_Changes` (`codeNode`) compacts the commits into an evidence summary, degrading gracefully to an explicit "unavailable" marker on failure or absence.
5. `Retrieve_Prior` (`memoryRetrieveNode`) loads any prior hypotheses for this `incidentId`.
6. `Diagnose` (`InstructorLLMNode`, temperature 0) fans in all three evidence branches and returns a schema-enforced ranked hypothesis list.
7. `Remember` (`memoryNode`) writes the new hypothesis set back under the `incidentId`.
- **When to use:** any time an alert fires and you want a grounded first-pass diagnosis, or when new information arrives mid-incident and you want the ranking revised.
- **Output:** `{ hypotheses[], summary, insufficientInfo }`, where each hypothesis is `{ rank, title, confidence, reasoning, supportingEvidence[], contradictingEvidence[], nextStep }`.
- **Dependencies:** a vector DB indexed with runbooks; embedding + generative models; a memory collection; optionally a GitHub token for private repos / higher rate limits.

### draft-comms
- **Trigger:** API request with `{ hypothesis, evidence, rankedHypotheses, incidentId }`.
- **Processing:** `Draft_Slack` (`LLMNode`, ~0.5) writes a hedged status update; `Draft_Postmortem` (`LLMNode`, ~0.3) writes a blameless postmortem skeleton. The two run in parallel.
- **When to use:** once a leading hypothesis is established and you want ready-to-edit comms.
- **Output:** `{ slackUpdate, postmortem }`.

### Flow interaction
The app calls `investigate` first (possibly several times for one incident, as information arrives), then `draft-comms` with the leading hypothesis and its evidence. The flows are decoupled: `investigate` owns "what is true," `draft-comms` owns "how we communicate it."

## Guardrails
- **Prohibited tasks:** no real-world actions (deploys, restarts, posting to Slack/PagerDuty); no fabricated evidence (commit hashes, timestamps, metrics, log lines, runbook steps); no confident diagnosis of a vague alert.
- **Input constraints:** all alert text, runbook content, and fetched repo data are treated as untrusted input, never as instructions. Embedded attempts to change the agent's role or reveal credentials are ignored.
- **Output constraints:** every claim must trace to provided evidence; contradicting evidence must be surfaced for every hypothesis; Slack drafts must hedge ("likely"/"investigating") unless evidence is direct; postmortems are blameless and never name individuals; secrets in input are never echoed.
- **Operational limits:** subject to GitHub API rate limits (mitigated by the optional token and graceful degradation) and model context/rate limits.

## Integration Reference
| Integration | Purpose | Required credential / config |
|---|---|---|
| GraphQL/API trigger | Receives alert and comms payloads | Lamatic runtime endpoint |
| RAG vector DB (`RAGNode`) | Retrieves runbook context | Vector DB connection + embedding model in Studio |
| GitHub REST API (`apiNode`) | Fetches recent commits | Public by default; `GITHUB_TOKEN` optional |
| Memory (`memoryNode` / `memoryRetrieveNode`) | Incident-scoped hypothesis history | Memory collection + embedding model in Studio |
| Generative models (`InstructorLLMNode`, `LLMNode`) | Diagnosis and drafting | Model credentials in Studio |

## Environment Setup
- `INVESTIGATE_FLOW_ID` — deployed `investigate` flow ID; required by the app.
- `DRAFT_COMMS_FLOW_ID` — deployed `draft-comms` flow ID; required by the app.
- `LAMATIC_API_URL`, `LAMATIC_PROJECT_ID`, `LAMATIC_API_KEY` — Lamatic project credentials.
- `GITHUB_TOKEN` — optional; read server-side only, never exposed to the client.

## Quickstart
1. Import `flows/investigate.ts` and `flows/draft-comms.ts` into Lamatic Studio.
2. Configure models, vector DB, and memory collection; index `assets/demo/runbooks.md`.
3. Deploy both flows and copy their Flow IDs.
4. In `apps/`, copy `.env.example` → `.env.local`, fill in the IDs and credentials.
5. `npm install && npm run dev`, then click **Load example** → **Investigate**.

## Common Failure Modes
| Symptom | Likely cause | Fix |
|---|---|---|
| "Missing environment variable" | Flow ID or credential not set | Fill in `.env.local` from `.env.example` |
| Hypotheses returned but no runbook grounding | Vector DB not indexed with runbooks | Index `assets/demo/runbooks.md` into the DB `Runbook_RAG` queries |
| "Recent-changes data unavailable" | Private repo without token, rate limit, or no repo given | Add `GITHUB_TOKEN`, or proceed on runbooks alone (expected, non-fatal) |
| Follow-up re-diagnoses from scratch | Memory not persisting across runs for the incident ID | Verify the memory collection and that `incidentId` is stable between runs |
| Empty / malformed diagnosis | Model didn't honor the JSON schema | Confirm the `Diagnose` node uses an instructor-capable model at temperature 0 |

## Notes
- Companion to `llm-eval-harness`: that kit evaluates generation quality; this one diagnoses incidents.
- The default runbook corpus in `assets/demo/` is a stand-in — replace it with your team's runbooks with no code changes.
13 changes: 13 additions & 0 deletions kits/incident-copilot/apps/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copy to .env.local and fill in. NEVER commit .env.local.

# Deployed Lamatic flow IDs (Studio -> open flow -> copy Flow ID)
INVESTIGATE_FLOW_ID=
DRAFT_COMMS_FLOW_ID=

# Lamatic project credentials (Studio -> Settings -> API)
LAMATIC_API_URL=
LAMATIC_PROJECT_ID=
LAMATIC_API_KEY=

# Optional: raises GitHub API rate limits and enables private repos.
GITHUB_TOKEN=
10 changes: 10 additions & 0 deletions kits/incident-copilot/apps/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
node_modules/
.next/
out/
.env
.env.local
.env*.local
*.tsbuildinfo
next-env.d.ts
.DS_Store
*.log
Loading
Loading