kevinkiyosepyo/oncall-agent

GitHub: kevinkiyosepyo/oncall-agent

一款基于 Claude 的自主式事件响应代理,在告警触发后自动分析代码提交、日志和运维手册,输出疑似故障 commit、匹配的 runbook 和爆炸半径评估,并在告警解除后生成复盘报告。

Stars: 0 | Forks: 0

# On-Call Agent [![ci](https://static.pigsec.cn/wp-content/uploads/repos/cas/99/993938d8ce5e902ccfb9d6747725c320d855dea3235ed9a304cedf0d94c9321f.svg)](https://github.com/kevinkiyosepyo/oncall-agent/actions/workflows/ci.yml) 一个自主的事件响应 agent。当生产环境告警触发时,它会拉取 上下文(近期的 commits、服务日志、runbooks),使用 Claude 来识别 可能的错误 commit,匹配正确的 runbook,并估算爆炸半径,然后 向 Slack 发布结构化的事件简报。当告警解除时,它会 生成一份复盘报告。 ![演示:一个混沌场景触发了告警;agent 发布了一份简报,指出了确切的植入罪魁祸首 commit](https://static.pigsec.cn/wp-content/uploads/repos/cas/55/55b59bb7585751e67039f51a9bc7f8205b085ded9f99d8ca457462bb285783bd.gif) **状态: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 使用量。
标签:Claude, CVE检测, LLM代理, 事故响应, 人工智能, 告警分诊, 智能运维, 测试用例, 用户模式Hook绕过, 网络调试, 自动化, 自定义请求头, 请求拦截, 逆向工具