Hal-Hanami/incident-triage-agent
GitHub: Hal-Hanami/incident-triage-agent
基于 Claude Agent SDK 与 MCP 的只读事件分诊 Agent,对告警进行分类、检索 runbook 并给出带引用的建议,低置信度时主动弃权上报人工。
Stars: 0 | Forks: 0
# incident-triage-agent
**基于 Claude Agent SDK + MCP 的只读首轮事件分诊。**
输入告警或事件工单 → 它会对**严重程度 + 类型**进行分类,检索
匹配的 **runbook**,并提出**带引用的首次响应建议** —
或者,当置信度较低或情况超出范围时,它会**弃权并
上报给人类**。它绝不执行修复。
[](https://github.com/Hal-Hanami/incident-triage-agent/actions/workflows/ci.yml)
一个能够采取行动的 agent,其可信度仅取决于它*不*采取行动的意愿。因此,
这个仓库衡量的不是 agent 有多准确——而是
它拒绝行动的可靠程度。在一组固定的 32 个合成事件中,经过三次
完整的 pipeline 运行:
| | 测量值 | 含义 |
|---|---|---|
| 弃权率 | **100%** (15/15 必须弃权) | 它从未对需要人类介入的事件提出过行动建议 |
| **遗漏的上报** | **0** | 在任何测量运行中均未发生过此类危险错误 |
| 错误弃权 | **17 个可回答问题中的 2 个** | 它*过度*上报了——见下文 |
| 行动正确率 | **93.3%** (14/15 PROPOSE,关键指标匹配) | LLM 裁判同意率为 15/15 |
| 成本 | 每个事件 **$0.0140** | Opus 起草器约占其中的 93% |
| 延迟 | **p50 5.22秒 / p95 10.80秒** | 起草是关键杠杆:p50 3.55秒 |
**错误弃权是诚实的一部分。** 有两个本可回答的事件被不必要地
上报了,并且该数值在其他方面完全相同的运行之间发生了波动(1,然后是 2,然后是 2)——这是 LLM 的方差,而不是固定属性。这两种
错误都偏向保守方向,对于这项工作来说,这是宁可犯的错误类型,但设计目标是 0,并未达到。
[`docs/EVALUATION.md`](docs/EVALUATION.md) 包含了每一个数字、日期、其
复现命令,以及它未能展示的内容。
Runbook 搜索**没有在这里重新实现**:它复用了来自
[tech-docs-rag](https://github.com/Hal-Hanami/tech-docs-rag) 的检索包,这是一个基于源文件的
RAG,如果其源文件不支持,它会拒绝回答。这个项目将这种
态度在自主性阶梯上提升了一级——从*不回答*变为**不行动**——在这里,错误的代价是一个行动而不是一句话。
- **可衡量** — 严重程度/类型准确率、检索召回率,以及在版本化的合成事件集上的**弃权率 /
错误弃权 / 遗漏的上报**。
- **护栏约束** — **构造上即为只读**:未给 agent 提供任何可以
修改系统的工具;其唯一的副作用是向人类上报。
- **成本上限** — 设有严格的**单事件 USD 预算**,一旦超出即触发弃权,而不是
超支。
- **可观测** — 每次分诊均有**分阶段延迟 + 分模型 $ 统计**,以及整个事件集的 p50/p95
数据。
## 工作原理
```
incident → classify → retrieve → draft → decide → TriageResult (+ trace)
sev+type runbook cited PROPOSE | ABSTAIN→escalate
haiku-4-5 (reused) opus-4-8 (deterministic rule)
```
这四个阶段是一个可衡量的 pipeline;**Claude Agent SDK** 将它们封装为
**进程内 MCP 工具**,并在只读权限策略下对它们进行编排。
这个安全关键的汇合点 —— PROPOSE 或 ABSTAIN —— **完全不运行任何模型**:它是一个
确定性规则,因此该决策不会在不同运行之间发生偏移。完整规范,包括
PROPOSE/ABSTAIN 合约和 SEV1 规则:
[`docs/design.md`](docs/design.md)。
## 快速开始(离线 — 无需密钥)
```
# 列出合成 incident set(22 个 in-scope + 10 个 abstention 测试)
python -m triage incidents
python -m triage incidents --scope out # just the must-abstain cases
# 重放五次真实的 measured triage 运行 — 无 key,无网络
python -m triage demo
# 检查 fixture 完整性
python -m triage validate
# offline test suite — 137 项测试:pricing math(含 cache tokens)、schema
# invariants、fixtures、完整的 PROPOSE/ABSTAIN decision table、budget/skip
# wiring、read-only guardrail red-team,以及使用 fakes 的 pipeline eval loop
uv run --with pytest python -m pytest -q # or: pip install -e '.[dev]' && pytest -q
```
## 测量(需要密钥)
```
# classify:severity/type 准确率 + 每模型成本 + p50/p95 (claude-haiku-4-5)
export ANTHROPIC_API_KEY=... # or put it in .env (gitignored)
uv run --with anthropic python -m triage eval --classify-only # --limit N bounds spend
# retrieve:构建 runbook index,然后测量 recall@1/@3/@k + MRR
export VOYAGE_API_KEY=... # shares .env with the Anthropic key
uv run --with sqlite-vec python -m triage index-runbooks
uv run --with sqlite-vec python -m triage eval --retrieval-only # --no-rerank ablates the rerank
# 完整 pipeline(两个 key + 已构建的 index):classify → retrieve → draft
# (claude-opus-4-8) → decide,评估 abstention / false-abstention / missed-escalation
uv run --with anthropic --with sqlite-vec python -m triage eval # --no-draft = no Opus spend
# 一个 incident 的 end to end,包含 per-stage latency + per-model $ trace + §8 budget verdict
uv run --with anthropic --with sqlite-vec python -m triage triage INC-0003
# §8 cost ceiling,实时:微小的 budget 使 classify 触发上限,Opus
# draft 永不会被购买,且 decision 降级为 ABSTAIN(cost_budget_exceeded)
uv run --with anthropic --with sqlite-vec python -m triage triage INC-0001 --budget 0.0005
# 通过 Agent SDK shell 的同一 incident:read-only MCP tools、
# 每次运行的 tool/deny audit,以及 orchestrator cost cross-check(SDK vs token-priced)
uv run --with claude-agent-sdk --with anthropic --with sqlite-vec python -m triage agent INC-0001
```
检索功能复用了同级目录的 tech-docs-rag 检出,用于 `import rag`(默认为
`../tech-docs-rag`,可通过 `TECH_DOCS_RAG_PATH` 覆盖)。
## 目录结构
```
docs/design.md the spec (numbered §, cited from code)
docs/EVALUATION.md every measured number: date, set, reproduce command, limits
triage/schema.py Incident / TriageResult / Severity / IncidentType / Outcome
triage/classify.py classify stage — Classifier Protocol + claude-haiku-4-5
triage/runbooks.py runbook corpus adapter — chunks fixtures/runbooks → rag index rows
triage/retrieve.py retrieve stage — Retriever Protocol + RagRetriever (reuses tech-docs-rag)
triage/draft.py draft stage — Drafter Protocol + claude-opus-4-8 cited recommendation
triage/decide.py decide stage — deterministic PROPOSE/ABSTAIN rule; no model
triage/agent.py Agent SDK shell — stages as in-process MCP tools, read-only guardrails
triage/eval.py eval harness — accuracy + recall@1/@3/@k + MRR + abstention / missed-escalation + per-stage p50/p95
triage/observe.py sourced per-model pricing (cache-aware), Trace, the §8 cost ceiling
triage/__main__.py CLI — incidents / validate / index-runbooks / eval / triage / agent / demo
fixtures/incidents/ synthetic incident set (JSONL) — no real/secret data
fixtures/runbooks/ synthetic runbooks the RAG indexes
tests/ offline test suite (no key, no network)
```
## 非目标
不是一个自动修复系统(它只建议 + 上报,从不执行)。没有真实
事件、遥测数据或机密——一切都是合成的,因此它可以独立构建
并且可以安全地公开。见 [`docs/design.md`](docs/design.md) §11。
## 贡献
提交约定和范围列表位于
[`CONTRIBUTING.md`](CONTRIBUTING.md)。许可证:[MIT](LICENSE)。
标签:LLM Agent, MCP, 告警分诊, 安全规则引擎, 自动化运维, 运维, 逆向工具