Perun-Engineering/sre-on-call

GitHub: Perun-Engineering/sre-on-call

基于 AWS Bedrock AgentCore 的多 Agent 系统,自动调查 Slack/Discord 上的基础设施告警并生成结构化故障分析报告。

Stars: 2 | Forks: 0

# sre-on-call 一个多 agent 机器人,可自动调查跨 Slack 和 Discord 的基础设施警报。当有警报发布到频道时,系统会对其进行确认,将调查工作并行分发给专门的 agent,并以线程回复的形式发布结构化的故障分析。 ## 架构 ``` Slack/Discord webhook │ ▼ Lambda Adapter ── (signature verify, dedup, classify, ack) ──▶ chat platform │ │ bedrock-agentcore.invoke_agent_runtime (JSON-RPC 2.0 / A2A) ▼ Master Agent ── investigate_alert tool ──▶ Orchestrator │ ┌──────────────────────┼──────────────────────┐ ▼ ▼ ▼ Slack Scanner Discord Scanner CloudWatch Logs EKS (4 specialized agents, configured via config.yaml) ``` - **Lambda Adapter** — 接收 Slack/Discord webhook,验证签名,通过 DynamoDB 进行去重,**对提及进行分类**(在分发之前抑制非警报的闲聊 — 见下文),然后调用 Master Agent 运行时。 - **Master Agent** — 编排调查过程:分发给专门的 agent,强制执行截止时间,发布故障报告。见 [`agents/master/README.md`](agents/master/README.md)。 - **专门的 agent** — 每个数据源一个。每个都有自己的 README: - [Slack Scanner](agents/slack_scanner/README.md) — Slack 频道历史关联。 - [Discord Scanner](agents/discord_scanner/README.md) — Discord 频道历史关联。 - [CloudWatch Logs](agents/cloudwatch_logs/README.md) — Logs Insights 查询。 - [EKS](agents/eks/README.md) — Kubernetes 集群状态。 一个 Prometheus agent (`agents/prometheus/`) 已提交,但**未部署,也不在编排器的分发任务中** — 它没有在 `config.yaml` 中列出,也没有相关的 terraform 配置。 有关完整的文档索引(部署、测试、架构、设计规范),请参见 [`docs/README.md`](docs/README.md)。 所有 agent 均运行在 AWS Bedrock AgentCore Runtime 上,通过 A2A 协议(基于运行时 `/invocations` endpoint 的 JSON-RPC 2.0)进行通信,并使用带有 Claude Haiku 4.5 的 Strands Agents SDK。每个 agent 的工具接口都以声明式描述:[`config.yaml`](config.yaml) 列出了 agent 启用的技能和 MCP server,每个技能都是 `agents//skills//` 下的一个 `SKILL.md` 包,而 `shared.a2a_factory` 是唯一的入口点,负责加载配置、解析技能、打开 MCP 连接并启动 A2A server。 ## 项目结构 ``` ├── config.yaml # Per-agent skills + MCP servers (single source of truth) ├── lambda_adapter/ # Lambda webhook ingestion │ ├── handler.py # Lambda entry point │ ├── intake.py # Dedup + classification gate + master agent invocation │ ├── classifier.py # Alert-vs-chatter classification (heuristics + optional LLM) │ └── dedup.py # DynamoDB deduplication store ├── agents/ │ ├── master/ # Master orchestration agent │ │ ├── tools.py # investigate_alert (single tool, fire-and-forget) │ │ ├── orchestrator.py # InvestigationOrchestrator: fan-out + deadlines │ │ ├── report_formatter.py # Incident report assembly │ │ ├── skills//SKILL.md # Skill bundles (frontmatter -> tool symbol) │ │ ├── tests/test_tools.py # Per-agent unit tests │ │ └── agent_card.json │ ├── slack_scanner/ # tools.py + skills/ + tests/ + agent_card.json │ ├── discord_scanner/ # same layout │ ├── cloudwatch_logs/ # same layout (also wires the aws_docs MCP) │ ├── eks/ # same layout (network_mode: VPC) │ └── prometheus/ # Not deployed; not in config.yaml ├── shared/ # Cross-agent utilities │ ├── models.py # AlertContext, AgentResult, Finding, AgentFailure, AgentMetadata, CommandRequest │ ├── constants.py │ ├── a2a_factory.py # Loads config + skills + MCPs; A2AServer + uvicorn + /ping │ ├── a2a_protocol.py # JSON-RPC envelope build/extract helpers │ ├── agent_telemetry.py # Per-agent metadata footer (model, tokens, cost) │ ├── config.py # ProjectConfig (Pydantic) + loader for config.yaml │ ├── skill_loader.py # SKILL.md parser + tool-symbol resolver │ ├── mcp_loader.py # Context-managed MCPConnections handle │ ├── platforms/ # ChatPlatform per chat platform (Slack, Discord) │ │ ├── __init__.py # Protocol, WebhookEvent tagged union, deliver_with_retry, registry │ │ ├── slack.py # SlackChatPlatform: signature, parse, ack, deliver │ │ └── discord.py # DiscordChatPlatform: signature, parse, ack, deliver │ ├── channel_scan.py # Shared channel-scanning algorithm │ ├── channel_utils.py │ ├── report_renderer.py # MarkupDialect-driven section renderer (Slack mrkdwn, Discord MD) │ ├── secrets.py # Secrets Manager ARN -> plaintext resolver (cached) │ ├── time_utils.py # Investigation window + ISO timestamp helpers │ ├── tool_result.py │ ├── experiment.py │ ├── experiment_store.py │ ├── experiment_results_store.py │ └── trace_store.py # S3 + DDB per-investigation trace archive (fail-open) ├── tests/ # Cross-cutting / shared unit tests │ ├── integration/ # Handler, orchestrator, A2A factory, synthetic webhook │ └── property/ # Hypothesis property-based tests ├── modules/sre-on-call/ # Reusable module (no provider/backend) │ ├── versions.tf # Provider requirements only │ ├── variables.tf # Inputs (incl. config_path, source_root) │ ├── networking.tf # EKS-VPC reference + agent SG │ ├── ecr.tf # ECR repos for the 5 agent images │ ├── dynamodb.tf # Dedup table │ ├── dynamodb_experiments.tf # A/B experiment tables │ ├── secrets.tf # Slack/Discord secret containers │ ├── lambda.tf # Lambda function + URL │ ├── iam.tf # Lambda + agent IAM roles │ ├── iam_agentcore.tf # AgentCore-specific IAM │ ├── agentcore.tf # 5 aws_bedrockagentcore_agent_runtime resources │ ├── traces.tf # S3 trace bucket + DDB index + KMS CMK + IAM grants │ └── observability.tf # CloudWatch alarms + SNS topic for AgentCore ├── examples/complete/ # Reference root: provider + backend + module call │ ├── main.tf # provider + module "sre_on_call" │ ├── outputs.tf # Re-exports module outputs │ └── moved.tf # State re-keying for the old flat root ├── scripts/ │ ├── build_and_push_agents.sh # Build 5 linux/arm64 images and push to ECR │ ├── hydrate_secrets.sh # Push Slack/Discord secret values │ ├── enable_observability.sh # One-time CloudWatch Transaction Search enablement │ └── synthetic_slack_webhook.py # Send a signed synthetic alert to the Lambda URL ├── docs/ │ ├── README.md # Docs index │ ├── deployment.md # Build, deploy, scoped testing │ ├── testing.md # Synthetic + real Slack alert procedures │ ├── architecture.d2 # Source for architecture.svg │ ├── architecture.svg │ ├── icons/ # AWS + vendor icons used by the diagram │ └── superpowers/ # Living design specs and implementation plans ├── CONTEXT.md # Domain vocabulary └── pyproject.toml ``` ## 前置条件 - **Python 3.12+** - **Terraform >= 1.5**(仅用于基础设施部署) - 支持 `linux/arm64` 的 **Docker buildx**(AgentCore 运行时需要 arm64) - **AWS CLI**,并具有针对目标账户的 SSO 或静态凭证 ## 安装说明 ``` git clone cd sre-on-call python3.12 -m venv .venv source .venv/bin/activate pip install -e ".[dev]" python -c "from shared.models import AlertContext; print('OK')" ``` ## 运行测试 ``` pytest # full suite pytest -v # verbose pytest agents/eks/tests/test_tools.py # one agent's unit tests pytest tests/integration/test_orchestrator.py # one integration test pytest tests/property/ # property-based tests only ``` 当前数量:**收集 582 个**,582 个通过。(尽管该 agent 未部署,Prometheus 测试依然运行并通过。) ### 测试布局 - `agents//tests/test_tools.py` — 针对 tool 接口的单 agent 单元测试。 - `tests/` — 针对共享模块(config、skill loader、MCP loader、channel utils、telemetry、dedup、parser、signature、time utils、report formatter、A/B experiments、postmortem command)的跨领域单元测试。 - `tests/integration/` — 主编排器、Lambda handler、A2A factory 以及 synthetic-webhook 签名往返测试。 - `tests/property/` — 针对 parser、signature verifier、dedup、time utils、channel utils、report formatter 和 CloudWatch Logs 查询 helper 的 Hypothesis 基于属性的测试。 ## 基础设施部署 完整流程(构建镜像 → ECR 仓库 → terraform apply → 密钥填充)请参见 **[docs/deployment.md](docs/deployment.md)**。概要如下: 1. 配置 AWS profile 和所需的 Terraform 变量。 2. 执行 `terraform apply -target=module.sre_on_call.aws_ecr_repository.agents` 创建仓库。 3. 执行 `./scripts/build_and_push_agents.sh ` 构建并推送 5 个 agent 镜像。 4. 执行 `terraform apply -var "agent_image_tag="` 部署其余部分。 5. 执行 `./scripts/hydrate_secrets.sh` 填充 Slack/Discord 的密钥值。 ### 所需的 Terraform 变量 ``` # examples/complete/terraform.tfvars eks_cluster_name = "eks-uat" # existing cluster the EKS agent inspects agent_container_registry = ".dkr.ecr..amazonaws.com" ``` 可选: | 变量 | 默认值 | 用途 | |----------|---------|---------| | `aws_region` | `us-east-1` | | | `environment` | `dev` | 资源名称前缀 | | `project_name` | `sre-on-call` | 资源名称前缀 | | `agent_image_tag` | `latest` | 在 apply 时固定镜像标签 | | `model_id` | `us.anthropic.claude-haiku-4-5-20251001-v1:0` | 所有 agent 使用的 Bedrock 模型 | | `lambda_memory_size` | `256` | | | `lambda_timeout` | `30` | | ### 配置 Slack 完整的 Slack App + bot 设置请参见 **[docs/testing.md](docs/testing.md)**。简版如下: 1. 将 Event Subscriptions URL 设置为已部署的 Lambda 函数 URL。 2. 仅订阅 **`app_mention`** bot 事件。接入 [分类门控](#alert-classification-gate) 会抑制明显的非警报闲聊,但订阅更广泛的事件仍然会浪费分类器的工作 — 请保持仅订阅 `app_mention`。 3. Bot 权限范围:`app_mentions:read`, `chat:write`, `channels:history`。 4. 使用真实的 Bot Token (`xoxb-…`) 和 Signing Secret 填充密钥。 ## 警报分类门控 并非每个 `@bot` 提及都是警报 — 一句随意的“谢谢!”不应触发 全面的 agent 分发。Lambda 接入层会在调度前对每个新提及进行分类: - **Tier 1 — 启发式** (`lambda_adapter/classifier.py`,纯粹且确定性): 扫描警报特征标记(严重性关键词、Alertmanager/Grafana 格式化、dashboard/console 链接),以及相反的明显闲聊 (问候、确认、单纯的提及)。高置信度的判定在此阶段胜出。 - **Tier 2 — LLM**(可选,`CLASSIFIER_LLM_ENABLED=true`):一次 Bedrock Converse 轮次(默认为 Haiku)判定 Tier 1 无法定夺的消息。 - **手动覆盖**:在提及中包含 **`investigate`** 一词,可强制 进行调查,无需考虑分类结果。 该门控是 **fail-open** 的 — 一条模糊的消息、一个禁用/出错的 LLM,或 任何意外错误默认都会采取*调查*行动,因此真正的呼叫绝不会 被静默丢弃。被门控拦截的提及会收到一条单行的线程内提示,而不是 进行分发。使用 `ALERT_CLASSIFICATION_ENABLED=false` 禁用整个门控 (Terraform:`enable_alert_classification = false`)。 ## 测试 请参见 **[docs/testing.md](docs/testing.md)**。三种方式: - **合成警报** — `scripts/synthetic_slack_webhook.py` 构建一个正确签名的 `app_mention` payload 并 POST 到 Lambda URL。适用于快速冒烟测试。 - **真实的 Slack 警报** — 邀请 bot 加入频道并使用 `@bot …` 触发端到端的调查。 - **`/sre-snapshot` 快照** — 使用带有 `--command /sre-snapshot` 的同一脚本(或在 Slack 注册后于任何频道中运行 `/sre-snapshot`)。发布集群状态、按摄入量排名靠前的 log group 以及聊天平台可达性的顶层快照。 ## 环境变量 这些由 Terraform 在 Lambda 函数和 AgentCore 运行时中设置;通常你不需要手动设置它们。 | 变量 | 组件 | 描述 | |----------|-----------|-------------| | `SLACK_SIGNING_SECRET` | Lambda | Secrets Manager 中保存 Slack signing secret 的 ARN | | `SLACK_BOT_TOKEN` | Lambda, Master, Slack Scanner | Secrets Manager 中保存 Slack bot OAuth token 的 ARN | | `DISCORD_PUBLIC_KEY` | Lambda | Secrets Manager 中保存 Discord 应用程序公钥的 ARN | | `DISCORD_BOT_TOKEN` | Lambda, Master, Discord Scanner | Secrets Manager 中保存 Discord bot token 的 ARN | | `DEDUP_TABLE_NAME` | Lambda | DynamoDB 去重表名 | | `ALERT_CLASSIFICATION_ENABLED` | Lambda | 将非警报的提及拦截在分发之外(默认为 `true`;kill-switch) | | `CLASSIFIER_LLM_ENABLED` | Lambda | 为模糊的提及启用 Tier 2 LLM 分类器(默认为 `false`) | | `CLASSIFIER_MODEL_ID` | Lambda | 用于 Tier 2 分类器的 Bedrock 模型(回退至 `MODEL_ID`,然后是 Haiku) | | `EXPERIMENTS_TABLE_NAME` | Lambda | DynamoDB A/B 实验配置表名 | | `MASTER_AGENT_RUNTIME_ARN` | Lambda | 主 agent 的 AgentCore 运行时 ARN | | `TRACES_BUCKET_NAME` | Lambda, Master | 用于每次调查追踪归档的 S3 bucket(可选 — 未设置则禁用追踪) | | `TRACES_TABLE_NAME` | Lambda, Master | 用于追踪归档查找的 DynamoDB 索引表 | | `SLACK_SCANNER_AGENT_RUNTIME_ARN` | Master | Slack Scanner 的 AgentCore 运行时 ARN | | `DISCORD_SCANNER_AGENT_RUNTIME_ARN` | Master | Discord Scanner 的 AgentCore 运行时 ARN | | `CLOUDWATCH_LOGS_AGENT_RUNTIME_ARN` | Master | CloudWatch Logs 的 AgentCore 运行时 ARN | | `EKS_AGENT_RUNTIME_ARN` | Master | EKS 的 AgentCore 运行时 ARN | | `MODEL_ID` | 所有 agent | Bedrock 模型 ID 或跨区域推理配置 | | `EKS_CLUSTER_NAME` | EKS agent | EKS agent 检查的集群 | | `A2A_PORT` / `A2A_HOST` | 所有 agent | A2A server 绑定端口 (9000) / 主机 (0.0.0.0) | 对于 agent 作为普通 HTTP A2A server(而非在 AgentCore 上)运行的本地开发工作,每个 `*_AGENT_RUNTIME_ARN` 都会回退到对应的 `*_AGENT_URL`(例如 `EKS_AGENT_URL=http://localhost:9005`)。如果未设置,编排器将使用 `localhost` 默认值。
标签:AWS Bedrock, EKS, Slack, SRE, 偏差过滤, 告警分诊, 多智能体, 请求拦截, 运维, 逆向工具