krishnashakula/IRAS
GitHub: krishnashakula/IRAS
一款自主AI事件响应代理,可自动完成告警分诊、根因分析、修复方案生成,并在人工批准后执行修复。
Stars: 2 | Forks: 0
# ⚡ IRAS
### 您的值班工程师在凌晨 3 点被叫醒。
### IRAS 已经找到了根本原因,写好了修复方案,正在等待您的批准。
[](https://github.com/krishnashakula/IRAS/actions/workflows/ci.yml) [](https://python.org) [](https://fastapi.tiangolo.com) [](https://langchain-ai.github.io/langgraph/) [](https://ai.pydantic.dev) [](https://anthropic.com) [](https://coverage.readthedocs.io) [](https://pytest.org) [](LICENSE)
[**快速开始**](#-quick-start) · [**工作原理**](#-how-it-works) · [**我们不信任模型**](#-we-dont-trust-the-model) · [**架构设计**](#-architecture) · [**配置说明**](#-configuration) · [**贡献指南**](#-contributing)
## 问题所在
您一定经历过。凌晨 3 点。PagerDuty 报警。您跌跌撞撞地打开笔记本,眯着眼睛看图表,翻找日志,交叉参考最近的部署,形成假设,编写 Slack 消息,等待批准,应用修复,然后花一个小时写一份没人看的故障报告。
每次都是这样。
IRAS 会自动完成所有这些——在 2 分钟内——只在需要您按批准时才叫醒您。
## 快速开始
```
# 1. Clone
git clone https://github.com/krishnashakula/IRAS.git && cd IRAS
# 2. Start Postgres
docker run -d --name iras-postgres \
-e POSTGRES_USER=iras -e POSTGRES_PASSWORD=secret -e POSTGRES_DB=iras \
-p 5432:5432 postgres:16
# 3. Install
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
# 4. Configure (only two fields required)
cp .env.example .env
# Set ANTHROPIC_API_KEY and POSTGRES_URL
# 5. Run
python run.py
```
```
INFO IRAS graph compiled and ready
INFO Uvicorn running on http://0.0.0.0:8000
```
```
# Fire an alert
curl -X POST http://localhost:8000/webhook/alert \
-H "Content-Type: application/json" \
-d '{
"title": "High error rate on payment-service",
"timestamp": "2026-05-03T10:30:00Z",
"service": "payment-service",
"error_rate": 0.45
}'
# → {"incident_id": "550e8400-...", "status": "processing"}
# Watch it work in your terminal, then approve
curl -X POST http://localhost:8000/incidents/550e8400-.../approve
```
## 工作原理
IRAS 运行一个**9 节点的 LangGraph 状态机**。每个阶段都会生成类型化的 Pydantic 模型——没有原始字符串,没有需要解析的提示词输出。
```
Alert → Triage → Context → RCA → Plan → [YOU] → Apply → Post-mortem
↑ ↓
└── retry if confidence < 0.7
```
**阶段 1 — 接收**:任何带有 `title` + `timestamp` 的 JSON webhook 都会被接受。PagerDuty、Prometheus AlertManager、Datadog、Grafana——或者原始的 `curl`。额外字段会直接传递给 AI。
**阶段 2 — 分诊** *(Claude Haiku)*:P0–P3 严重程度、受影响服务、预估爆炸半径、置信度分数。快速且便宜——这里特意使用 Haiku。
**阶段 3 — 上下文收集** *(Claude Haiku + 工具调用)*:三个并行工具调用:
- `fetch_logs` → 来自 Elasticsearch 或 Loki 的错误/警告行
- `fetch_metrics` → 来自 Prometheus 的当前值与 7 天基线对比
- `fetch_deployments` → 受影响服务的最近 GitHub 部署
**阶段 4 — 根因分析** *(Claude Sonnet)*:生成一个 `RootCauseHypothesis`,包含 `primary_cause`、`contributing_factors`、`evidence`(具体日志行)和 `confidence` 分数。
置信度门控:如果分数 < 0.7,图会回到上下文收集阶段以获取更广泛的证据窗口。经过 `RCA_MAX_ATTEMPTS` 次尝试后,它会自动升级。
**阶段 5 — 修复计划** *(Claude Sonnet)*:有序的步骤,包含人类可读的描述、精确的回滚命令、风险等级和预估时长。
**阶段 6 — 人工批准** *(您)*:LangGraph 的 `interrupt()` 暂停图。状态被检查点到 PostgreSQL——服务器可以重启而事件仍然存活。您会收到一条带有批准/拒绝按钮的 Slack 消息,或者直接调用 API。
**阶段 7 — 应用修复**:步骤顺序执行。失败时,已完成的步骤会按照其存储的 `rollback_command` 逆序回滚。
**阶段 8 — 故障报告** *(Claude Sonnet)*:时间线、根本原因、解决方案、行动项。无论结果如何——已解决或已升级——都会编写。存储在 PostgreSQL 中,并发布到 Slack。
## 我们不信任模型
大多数 AI 代理项目都信任模型的输出。IRAS 不会。
**安全不变量在代码中强制执行,而不是在提示词中:**
```
# The model cannot generate an unsafe plan that bypasses approval.
# These checks run regardless of what the model returns.
if any(step.risk_level == "high" for step in plan.steps):
plan.requires_human_approval = True # forced
if any(not step.rollback_command.strip() for step in plan.steps):
plan.reversible = False # forced
plan.requires_human_approval = True # forced
```
**292 个测试,99% 覆盖率**——包括专门设计用于测试模型错误行为的对抗场景:
- 模型在 `risk_level` 上撒谎 → 安全覆盖会捕获它
- 模型返回空的 `rollback_command` → 计划被阻止
- 所有上下文工具同时失败 → 优雅降级,而不是崩溃
- 20 个并发事件 → 零状态污染
- Unicode、XSS、10,000 字符的有效载荷 → 干净处理
- 模型置信度永远达不到阈值 → 自动 PagerDuty 升级
```
pytest -q # 292 tests, ~30s
pytest tests/stress/ -v --no-cov # adversarial scenarios
pytest --cov=src/iras --cov-report=html # 99% coverage report
```
## 架构设计
### 系统概览
```
graph TB
subgraph Sources["Alert Sources"]
PD[PagerDuty]
PROM[Prometheus AlertManager]
DD[Datadog / Grafana]
ANY[Any JSON Webhook]
end
subgraph API["FastAPI REST API"]
WH["POST /webhook/alert"]
APR["POST /incidents/{id}/approve"]
REJ["POST /incidents/{id}/reject"]
HLT["GET /health"]
end
subgraph Graph["LangGraph State Machine"]
ING[Ingestion]
TRI["Triage Agent\nClaude Haiku"]
CTX["Context Gathering\nClaude Haiku"]
RCA["RCA Agent\nClaude Sonnet"]
GEN["Generate Plan\nClaude Sonnet"]
APP["Approval\n⏸ interrupt"]
REM[Apply Remediation]
ESC[Escalation]
PM["Post-mortem Agent\nClaude Sonnet"]
end
subgraph Integrations["External Integrations"]
SL[Slack]
PG2[PagerDuty]
LOGS["Elasticsearch / Loki"]
METRICS[Prometheus]
DEPLOY[GitHub Deployments]
DB[(PostgreSQL)]
end
Sources --> WH
WH --> ING
ING --> TRI --> CTX --> RCA
RCA -->|"conf < 0.7, attempts < max"| CTX
RCA -->|"conf >= 0.7"| GEN
RCA -->|"attempts exhausted"| ESC
GEN --> APP
APP -->|approved| REM
APP -->|rejected| ESC
REM --> PM
ESC --> PM
PM --> DB
PM --> SL
APR --> APP
REJ --> APP
CTX --> LOGS & METRICS & DEPLOY
ESC --> PG2 & SL
GEN --> SL
Graph --> DB
```
### 中断模式
IRAS 最有趣的技术部分是它如何处理人工介入的批准步骤。
大多数代理框架用轮询或超时来假装实现这个。LangGraph 的 `interrupt()` 不同:图**真正在中途暂停**,将其整个状态序列化到 PostgreSQL,并在人类响应时从 exactly 那一点恢复——即使跨越服务器重启、部署或进程崩溃。
这就是为什么 IRAS 构建在 LangGraph 上而不是更简单的框架上。持久执行对生产事件响应至关重要。
### 请求生命周期
```
sequenceDiagram
actor Monitor as Monitoring System
participant API as FastAPI
participant Graph as LangGraph
participant Claude as Claude (Anthropic)
participant Tools as External Tools
participant DB as PostgreSQL
participant Slack as Slack
actor Human as On-Call Engineer
Monitor->>API: POST /webhook/alert
API-->>Monitor: 202 {"incident_id": "abc123"}
API->>Graph: ainvoke(state, thread_id="abc123")
Graph->>Graph: ingestion — validate + init state
Graph->>Claude: triage_agent
Claude-->>Graph: TriageResult {severity: P1, confidence: 0.9}
Graph->>Claude: context_agent (tool-calling)
Claude->>Tools: fetch_logs() + fetch_metrics() + fetch_deployments()
Tools-->>Claude: raw evidence
Claude-->>Graph: ContextBundle {logs, metrics, deployments}
Graph->>Claude: rca_agent
Claude-->>Graph: RootCauseHypothesis {confidence: 0.88} ✓
Graph->>Claude: remediation_agent
Claude-->>Graph: RemediationPlan {3 steps + rollbacks}
Graph->>Slack: Post approval with [Approve] [Reject]
Note over Graph,DB: interrupt() — graph pauses, state checkpointed to PostgreSQL
Human->>API: POST /incidents/abc123/approve
API->>Graph: Command(resume={"approved": True})
Graph->>Graph: apply_remediation — execute all steps
Graph->>Claude: postmortem_agent
Claude-->>Graph: PostMortem {timeline, root_cause, action_items}
Graph->>DB: INSERT INTO postmortems
Graph->>Slack: Post post-mortem summary
```
### 项目结构
```
IRAS/
├── src/iras/
│ ├── api/
│ │ ├── app.py # FastAPI lifespan: init checkpointer → build graph
│ │ ├── background.py # Approval timeout monitor
│ │ └── routes/
│ │ ├── webhook.py # POST /webhook/alert
│ │ └── approval.py # POST /incidents/{id}/approve|reject
│ │
│ ├── graph/
│ │ ├── builder.py # Wire 9 nodes + conditional edges → compile
│ │ ├── checkpointer.py # AsyncPostgresSaver (singleton + asyncio.Lock)
│ │ ├── state.py # IncidentState TypedDict
│ │ └── nodes/
│ │ ├── ingestion.py
│ │ ├── triage.py # → Claude Haiku
│ │ ├── context_gathering.py # → Claude Haiku + tool calls
│ │ ├── rca.py # → Claude Sonnet + retry routing
│ │ ├── generate_plan.py # → Claude Sonnet + Slack notify
│ │ ├── approval.py # interrupt() — durable human checkpoint
│ │ ├── apply_remediation.py # Execute steps + rollback on failure
│ │ ├── escalation.py # PagerDuty + Slack
│ │ └── postmortem.py # → Claude Sonnet + persist to DB
│ │
│ ├── agents/ # One Pydantic AI agent per stage
│ ├── models/ # TriageResult · ContextBundle · RootCauseHypothesis
│ │ # RemediationPlan · RemediationStep · PostMortem
│ ├── tools/ # Elasticsearch · Loki · Prometheus · GitHub · Slack · PagerDuty
│ └── config/settings.py # Pydantic Settings — reads .env
│
├── tests/
│ ├── unit/ # Fully mocked
│ ├── integration/ # Live service tests (opt-in)
│ ├── e2e/ # Full graph runs with MemorySaver
│ └── stress/ # 47 adversarial + real-world scenarios
```
## 严重程度与升级
| 严重程度 | 含义 | 批准窗口 |
|---|---|---|
| P0 | 完全宕机 | 15 分钟,然后自动升级 |
| P1 | 严重降级 | 2 小时 |
| P2 | 部分降级 | 2 小时 |
| P3 | 警告/信息 | 2 小时 |
升级触发条件:RCA 置信度在最大重试后永远达不到阈值 · 人类拒绝计划 · 批准超时到期。
升级时:幂等的 PagerDuty 事件触发 + 带完整上下文的结构化 Slack 消息。故障报告始终运行——无论是否已解决。
## 配置
```
cp .env.example .env
```
| 变量 | 必填 | 描述 |
|---|---|---|
| `ANTHROPIC_API_KEY` | ✅ | Claude API 密钥 (`sk-ant-...`) |
| `POSTGRES_URL` | ✅ | `postgresql://user:pass@host:5432/db` |
| `SLACK_BOT_TOKEN` | ⬜ | 如果未设置则回退到模拟客户端 |
| `SLACK_ONCALL_CHANNEL_ID` | ⬜ | 用于值班警报的 Slack 频道 |
| `PAGERDUTY_INTEGRATION_KEY` | ⬜ | 如果未设置则回退到模拟客户端 |
| `PROMETHEUS_BASE_URL` | ⬜ | 如果未设置则回退到模拟客户端 |
| `ELASTICSEARCH_BASE_URL` | ⬜ | 选择一个日志后端 |
| `LOKI_BASE_URL` | ⬜ | 选择一个日志后端 |
| `LANGSMITH_API_KEY` | ⬜ | LangSmith 图追踪 |
| `LOGFIRE_TOKEN` | ⬜ | Logfire 代理追踪 |
| `RCA_CONFIDENCE_THRESHOLD` | ⬜ | 默认值:`0.7` |
| `RCA_MAX_ATTEMPTS` | ⬜ | 默认值:`3` |
| `APPROVAL_TIMEOUT_P0_MINUTES` | ⬜ | 默认值:`15` |
| `APPROVAL_TIMEOUT_DEFAULT_MINUTES` | ⬜ | 默认值:`120` |
## API 参考
### `POST /webhook/alert`
接受任何带有 `title` + `timestamp` 的 JSON。所有额外字段都会传递给 AI 代理。
```
{ "title": "High error rate on payment-service", "timestamp": "2026-05-03T10:30:00Z" }
```
```
{ "incident_id": "550e8400-...", "status": "processing" }
```
### `POST /incidents/{id}/approve`
### `POST /incidents/{id}/reject`
恢复暂停的图。批准路由到修复。拒绝路由到 PagerDuty 升级。
### `GET /health`
```
{ "status": "ok", "env": "development" }
```
## 可观测性
| 信号 | 工具 | 覆盖范围 |
|---|---|---|
| 图追踪 | LangSmith | 每个节点:输入、输出、时间、token 使用 |
| 代理追踪 | Logfire | 每个 LLM 调用:提示词、响应、工具调用、验证 |
| 结构化日志 | Python `logging` | 每个节点发出 `incident_id`、`node_name`、`timestamp` |
| 故障报告 | PostgreSQL | 完整记录,可按严重程度、时长、结果查询 |
## 部署
[](https://github.com/krishnashakula/IRAS/actions/workflows/ci.yml) [](https://python.org) [](https://fastapi.tiangolo.com) [](https://langchain-ai.github.io/langgraph/) [](https://ai.pydantic.dev) [](https://anthropic.com) [](https://coverage.readthedocs.io) [](https://pytest.org) [](LICENSE)
[**快速开始**](#-quick-start) · [**工作原理**](#-how-it-works) · [**我们不信任模型**](#-we-dont-trust-the-model) · [**架构设计**](#-architecture) · [**配置说明**](#-configuration) · [**贡献指南**](#-contributing)
**如果 IRAS 处理了您的凌晨 3 点事件,给它一个 ⭐**