kevinkiyosepyo/oncall-agent
GitHub: kevinkiyosepyo/oncall-agent
一款基于 Claude 的自主式事件响应代理,在告警触发后自动分析代码提交、日志和运维手册,输出疑似故障 commit、匹配的 runbook 和爆炸半径评估,并在告警解除后生成复盘报告。
Stars: 0 | Forks: 0
# On-Call Agent
[](https://github.com/kevinkiyosepyo/oncall-agent/actions/workflows/ci.yml)
一个自主的事件响应 agent。当生产环境告警触发时,它会拉取
上下文(近期的 commits、服务日志、runbooks),使用 Claude 来识别
可能的错误 commit,匹配正确的 runbook,并估算爆炸半径,然后
向 Slack 发布结构化的事件简报。当告警解除时,它会
生成一份复盘报告。

**状态:v1 已完成并经过测评** — 在端到端评估套件中得分 12/12,
包括一个无罪魁祸首的阴性对照。一个
混沌场景模拟了真实事件:它在演示服务的 git 历史中埋下了一个真正的错误 commit,
降低了运行中的服务性能,并触发了一个 Alertmanager 格式的告警。
agent 拉取上下文(commits + diffs、访问日志聚合、runbooks),运行
三个 LLM 分诊步骤,发布结构化的简报,并且——当告警
解除时——编写一份包含时间线、根因假设和
行动项的复盘报告。
## 运行说明
```
cp .env.example .env # add ANTHROPIC_API_KEY; SLACK_WEBHOOK_URL optional
docker compose up --build -d
```
模拟完整的事件生命周期(主机仅需 python3 和 git):
```
python3 chaos/inject.py high-error-rate --resolve-after 60
python3 chaos/inject.py --list # available scenarios
python3 chaos/inject.py reset # turn all faults off
```
在大约 20 秒内,事件简报会出现在 Slack 中——或者当未设置
`SLACK_WEBHOOK_URL` 时出现在 `docker compose logs agent` 中——指出
可疑的 commit(包含将其 diff 与观察到的错误联系起来的机制)、
匹配的 runbook 以及爆炸半径估计值。解除告警时,一份
复盘报告会保存在 `postmortems/` 中。
每个场景都会打印其预期结果(`expected: runbook=...
culprit=...`),因此每次运行都是自检的。在事件处于打开状态时重新触发告警
会根据告警指纹进行去重,并跳过重新分析。
随时检查事件状态:
```
curl -s localhost:8080/incidents | jq
curl -s localhost:8080/incidents/ | jq # includes the event timeline
```
## 运行效果展示
`python3 chaos/inject.py high-error-rate --resolve-after 55` 的未编辑输出
(注入的 bug:一个 commit 将安全的 `DISCOUNTS.get(code, NO_DISCOUNT)`
查找替换为 `DISCOUNTS[code]`,并掩埋在一个不相关的文档 commit 之下):
```
🔴 INCIDENT BRIEF: HighErrorRate — demo-shop
severity: critical endpoint: /checkout
*Impact:* 💥 sev1 · single_endpoint · affected: POST /checkout
~72.7% of checkout requests failing (baseline 0%)
*Suspect commit:* 🔎 `6c56805` (high confidence)
Changed apply_discount to directly index DISCOUNTS[code] instead of using
.get() with a default... Any checkout request with a discount_code not
present in DISCOUNTS (including None, the default value) now raises a
KeyError instead of falling back to NO_DISCOUNT, causing a 500 on /checkout.
> diff: `- discount = DISCOUNTS.get(code, NO_DISCOUNT) if code else ...`
> error: `KeyError: None in apply_discount (pricing.py, in apply_discount)`
*Runbook:* 📖 `runbooks/high-error-rate.md` (high confidence)
```
本次确切运行产生的完整产物:[事件简报](examples/brief-high-error-rate.txt)
和[生成的复盘报告](examples/postmortem-high-error-rate.md)——
时间线、根因假设、优先级排序的行动项,以及每一步的 LLM
token/延迟统计数据。
## 实测准确率
`chaos/eval.py` 会端到端运行每个场景,并根据
场景植入的真实情况对 agent 进行评分。每次运行都会植入一个全新的证据 repo
(新的 commit SHA,新的流量),因此这衡量的是分析能力,而不是记忆能力。
`python3 chaos/eval.py` (2026-07-02) 的逐字输出:
```
scenario runbook culprit postmortem
--------------------------------------------------------
db-pool-exhausted PASS PASS PASS
high-error-rate PASS PASS PASS
high-latency PASS PASS PASS
payment-provider-outage PASS PASS PASS
--------------------------------------------------------
12/12 checks passed
```
两点保证了测试的客观性:
- `culprit` 需要确切的植入 commit SHA,而且这些场景将
错误的 commit 掩埋在一个不相关的 commit 之下——因此“指责 HEAD”会失败。
- `payment-provider-outage` 是一个**阴性对照**:故障是外部的
(提供商超时)并且没有植入错误的 commit。agent 只有在
报告 `no_culprit_found` 并附带替代假设时才能通过——因此“总是指责
某个 commit”也会失败。(这个陷阱是真实的:错误指出了 `payments.py`,
而无辜的初始导入 commit 确实引入了该文件。)
## 测试
单元测试涵盖了日志窗口聚合、runbook 索引(包括
拒绝路径遍历)、runtime-enum schema 构建器、git
收集器和复盘报告渲染器。集成测试针对真实的 Postgres 测试了
摄入生命周期,包括强制每个告警指纹仅对应一个打开事件的部分唯一索引。
```
docker compose up -d postgres # integration tests use :5433
pip install "./agent[dev]"
cd agent && python -m pytest
```
## 架构
```
flowchart TB
subgraph demo["demo environment"]
CHAOS[chaos/inject.py
scenario runner] -->|bad commit +
innocuous commits| REPO[(evidence repo
demo-app/repo)] CHAOS -->|toggle fault,
baseline + incident traffic| APP[demo-shop
toy storefront API] CHAOS -->|Alertmanager-format
firing / resolved webhook| WH APP -->|JSON access log| LOGS[(demo-app/logs)] end subgraph agent["on-call agent (FastAPI)"] WH[/POST /webhook/alert/] --> DEDUP[fingerprint dedup
partial unique index] DEDUP --> PG[(PostgreSQL
incidents · timeline_events · llm_analyses)] DEDUP --> PIPE[analysis pipeline
BackgroundTask] PIPE --> CTX[context collectors
git log/show · log aggregates · runbook index] REPO -.read-only.-> CTX LOGS -.read-only.-> CTX CTX --> LLM[three LLM steps
runbook match · commit analysis · impact] LLM --> PG LLM --> SLACK[Slack webhook
or console brief] DEDUP -->|resolved| PM[postmortem step] --> PMFILE[postmortems/*.md] end ``` 真实的 Prometheus/Alertmanager 可以直接作为注入器的替代品——webhook payload 格式是完全相同的。 值得注意的设计选择: - **agent 分析真实证据。** 混沌运行器将一个实际的 错误更改(不安全的 dict 查找、N+1 查询、连接池缩减)提交到演示 服务的 repo 中——故意*不作为* HEAD——并且运行时故障 生成与该 diff 一致的错误字符串。Commit 分析基于 真实的 `git show` 输出进行。 - **结构化输出作为正确性工具。** runbook 匹配器和 commit 分析器将其关键字段限制为 runtime 构建的 enums(实际的 runbook 文件名、实际的 commit SHA),因此幻觉产生的 runbook 或虚构的 SHA 会导致 schema 验证失败,而不会出现在 简报中。 - **Python 负责统计,模型负责解析。** 日志聚合(错误率、 延迟百分位数、在告警的 startsAt 处分割的基准与事件窗口) 在 Python 中进行;影响分析步骤接收的是数字,而不是原始 日志。 - **失败会降级,从不崩溃。** 每个 LLM 调用都记录在 `llm_analyses` 中(模型、token、延迟、输出或错误);失败的步骤 将产生一份将该部分标记为不可用的简报。 - **复盘报告的结构是代码,而不是散文。** 模型填充结构化 部分;markdown 骨架、排序和每个时间戳都来自 数据库。 模型:`claude-sonnet-5` 用于 commit 分析和复盘报告生成, `claude-haiku-4-5` 用于 runbook 匹配和影响估计(每一步均可在 `agent/app/config.py` 中配置)。一个完整的事件生命周期大约消耗 价值 $0.05 的 API 使用量。
scenario runner] -->|bad commit +
innocuous commits| REPO[(evidence repo
demo-app/repo)] CHAOS -->|toggle fault,
baseline + incident traffic| APP[demo-shop
toy storefront API] CHAOS -->|Alertmanager-format
firing / resolved webhook| WH APP -->|JSON access log| LOGS[(demo-app/logs)] end subgraph agent["on-call agent (FastAPI)"] WH[/POST /webhook/alert/] --> DEDUP[fingerprint dedup
partial unique index] DEDUP --> PG[(PostgreSQL
incidents · timeline_events · llm_analyses)] DEDUP --> PIPE[analysis pipeline
BackgroundTask] PIPE --> CTX[context collectors
git log/show · log aggregates · runbook index] REPO -.read-only.-> CTX LOGS -.read-only.-> CTX CTX --> LLM[three LLM steps
runbook match · commit analysis · impact] LLM --> PG LLM --> SLACK[Slack webhook
or console brief] DEDUP -->|resolved| PM[postmortem step] --> PMFILE[postmortems/*.md] end ``` 真实的 Prometheus/Alertmanager 可以直接作为注入器的替代品——webhook payload 格式是完全相同的。 值得注意的设计选择: - **agent 分析真实证据。** 混沌运行器将一个实际的 错误更改(不安全的 dict 查找、N+1 查询、连接池缩减)提交到演示 服务的 repo 中——故意*不作为* HEAD——并且运行时故障 生成与该 diff 一致的错误字符串。Commit 分析基于 真实的 `git show` 输出进行。 - **结构化输出作为正确性工具。** runbook 匹配器和 commit 分析器将其关键字段限制为 runtime 构建的 enums(实际的 runbook 文件名、实际的 commit SHA),因此幻觉产生的 runbook 或虚构的 SHA 会导致 schema 验证失败,而不会出现在 简报中。 - **Python 负责统计,模型负责解析。** 日志聚合(错误率、 延迟百分位数、在告警的 startsAt 处分割的基准与事件窗口) 在 Python 中进行;影响分析步骤接收的是数字,而不是原始 日志。 - **失败会降级,从不崩溃。** 每个 LLM 调用都记录在 `llm_analyses` 中(模型、token、延迟、输出或错误);失败的步骤 将产生一份将该部分标记为不可用的简报。 - **复盘报告的结构是代码,而不是散文。** 模型填充结构化 部分;markdown 骨架、排序和每个时间戳都来自 数据库。 模型:`claude-sonnet-5` 用于 commit 分析和复盘报告生成, `claude-haiku-4-5` 用于 runbook 匹配和影响估计(每一步均可在 `agent/app/config.py` 中配置)。一个完整的事件生命周期大约消耗 价值 $0.05 的 API 使用量。
标签:Claude, CVE检测, LLM代理, 事故响应, 人工智能, 告警分诊, 智能运维, 测试用例, 用户模式Hook绕过, 网络调试, 自动化, 自定义请求头, 请求拦截, 逆向工具