cadneowl/chaos-loop
GitHub: cadneowl/chaos-loop
一个基于多 Agent AI 的闭环混沌工程系统,从代码分析、故障注入、根因诊断到自动生成修复 PR,填补了传统混沌工具在注入后缺乏行动闭环的空白。
Stars: 1 | Forks: 0
# chaos — 闭环、安全感知的混沌工程
[](https://github.com/cadneowl/chaos-loop/actions/workflows/ci.yml)
[](pyproject.toml)
[](pyproject.toml)
[](LICENSE)
Orchestrator(`orchestrator/loop.py`)运行一个确定性的状态
机。每次转换都持久化到 SQLite,以便在运行中途崩溃时可以
恢复。*"每个人都在自己的车道上。状态机不接受请求。"*
LLM 是系统的认知表层;它们也是会消耗金钱、泄露数据、产生幻觉且可能陷入死循环的表层。每次调用基于 LLM 的 Agent 都会通过一个 **meta-harness** 包装器(`agents/_harness.py`),它为 Orchestrator 提供了一个集中的地方,以便跨所有五个 Agent 强制执行控制属性。*"请出示许可证。支出报告。审计日志。继续前行。"*
该包装器是一个 `__getattr__` 代理 — 调用 `harness.instrument(name,
agent)` 会返回一个透明的替代品,满足与底层 Agent 相同的 Protocol。同步属性读取会穿透;协程方法会被插桩。被包装的 Agent 是**只读的**:`__setattr__` 会抛出异常,因此行为不当的调用者无法改变被包装实例上的状态。
包装器在每次异步方法调用时执行的操作:
| 关注点 | Harness 如何处理 |
|---|---|
| **可观测性** | 记录入口 / 出口 / 持续时间 / 错误 / 一行式的输入 + 输出摘要 — 成功时为 INFO 级别,抛出异常时为 WARNING 级别。 |
| **审计追踪** | 构建一个 `AgentInvocation` 记录并将其附加到 `Harness.invocations`。Orchestrator 在每次 SQLite 保存之前将完整列表附加到 `ExperimentRecord.agent_invocations`,因此即使运行中途崩溃,也会留下取证追踪。 |
| **成本归因** | 将每个任务的 `ContextVar` 设置为当前的 `AgentInvocation`。当 Agent 的策略调用 `complete_with_tools` 时,该函数会读取 ContextVar 并将调用的 `response_cost` 计入触发它的 invocation — 无需跨三个构造函数传递 harness 引用。 |
| **预算执行** | 在闭环的每一步之后,Orchestrator 会汇总 `harness.invocations` 中的 `inv.spend_usd`,将其持久化在记录上,在达到 `soft_cap_usd` 时记录一次日志,在达到 `hard_cap_usd` 或 `wall_clock_seconds` 时以 `BUDGET_EXCEEDED` 中止。 |
| **错误传播** | 异常总是被传播。Harness 会为审计日志记录错误字符串,但**绝不**吞没异常。一个失败的 Agent 会导致整个实验失败 — 不允许静默的部分成功。 |
| **禁止修改** | `_Wrapped.__setattr__` 阻止了对代理本身的写入;被包装的 Agent 在概念上是冻结的视图。 |
端到端的 LLM 成本流如下所示:
```
ExperimentRunner.run()
└─ awaits harness_wrapped_tester.hypothesize(req) ← proxy
└─ ContextVar.set(AgentInvocation)
└─ awaits inner_tester.hypothesize(req)
└─ awaits ClaudeHypothesizer.generate(...)
└─ awaits complete_with_tools(...)
└─ for each turn:
└─ litellm.acompletion()
└─ cost = response.usage.cost
└─ record_llm_spend(cost)
└─ inv.spend_usd += cost ← ContextVar
└─ records AgentInvocation in Harness.invocations
└─ budget.spent_usd = sum(inv.spend_usd for inv in harness.invocations)
└─ if budget.hard_exceeded(): abort
```
这在实践中为何重要:
- **Orchestrator 从不直接看到 LLM 调用**。它只通过 Protocol 接口看到
Agent 的调用。Harness 是边界控制。
- **添加新 Agent** 不需要更改任何 harness 代码 — 只需
`harness.instrument("new-agent", instance)`,它就会获得相同的可观测性 / 审计 / 成本 / 预算执行。
- **即使 Agent 调用多个 LLM,控制属性依然成立。** ContextVar 模式意味着 N 次嵌套的 `complete_with_tools` 调用都会将其成本计入启动外部 Agent 方法的那个 invocation。
- **测试可以完全脱离 Harness**(`Fixture*` 实现本来就不使用 LLM),或者独立使用它来验证调用日志是否符合预期。`tests/test_harness.py` 这两种情况都测试了。
本地 Ollama 运行报告的成本为 0(LiteLLM 没有自托管端点的定价数据),因此预算路径在免费层设置中会优雅地空操作 — 可观测性 + 审计追踪属性依然成立。
## 安装说明
### 前置条件
- **Python 3.11+**(推荐 3.13;与开发环境一致)
- **Docker**(用于 Chaos Mesh 的 `kind` 测试集群以及任何镜像扫描)
- **kubectl**(1.27+ 版本;兼容 `client-go` v0.29)
- **kind**(仅当你想要一个本地 k8s 集群时需要;替代方案:现有集群 + Chaos Mesh 安装)
- **(可选) Anthropic API 密钥**,用于 `--profile llm` / `--profile hybrid` 配合 Claude — 在 https://console.anthropic.com 注册
- **(可选) Ollama**,用于本地 LLM — https://ollama.com
### 1. 克隆并安装 Python 依赖
```
git clone chaos && cd chaos
python -m venv .venv
source .venv/bin/activate # POSIX
# .\.venv\Scripts\Activate.ps1 # Windows PowerShell
pip install -e ".[dev]"
```
验证安装:
```
chaos --help # the orchestrator's `chaos run` CLI
python -m pytest tests/ -q # should print "411 passed, 1 skipped"
```
### 2. 启动 kind 集群 + 安装 Chaos Mesh
适用于 Linux / macOS:
```
kind create cluster --name chaos-dev --wait 60s
helm repo add chaos-mesh https://charts.chaos-mesh.org && helm repo update
kubectl create ns chaos-mesh
helm install chaos-mesh chaos-mesh/chaos-mesh \
--namespace=chaos-mesh \
--version 2.7.2 \
--set chaosDaemon.runtime=containerd \
--set chaosDaemon.socketPath=/run/containerd/containerd.sock \
--wait --timeout=4m
kubectl get pods -n chaos-mesh # all should be Running
```
适用于 Windows + WSL2 (Ubuntu):上述所有内容都在 WSL 内运行。然后将
kubeconfig 暴露给 Windows,以便 Windows 上的 Python 可以与集群通信:
```
mkdir "$env:USERPROFILE\.kube" -Force
wsl -d Ubuntu-24.04 -- cat ~/.kube/config |
Out-File -Encoding utf8 "$env:USERPROFILE\.kube\config-kind"
$env:KUBECONFIG = "$env:USERPROFILE\.kube\config-kind"
```
使用本仓库的渲染器验证 Chaos Mesh:
```
python scripts/validate_renderers.py --context kind-chaos-dev
# 应输出:所有 7 个渲染器已通过真实的 Chaos Mesh 验证。
```
### 3. (可选) 安装 Ollama 用于本地 LLM
```
# Linux / macOS
curl -fsSL https://ollama.com/install.sh | sh
ollama pull qwen2.5-coder:14b # ~9 GB
ollama serve # default: http://localhost:11434
```
### 4. (可选) 配置可观测性后端
为了让 `--profile llm` 能从日志/指标中进行真正的根因分析 (RCA),需要将 Agent 指向
Loki + Prometheus:
```
export PROM_URL=http://prometheus.example/api/v1
export LOKI_URL=http://loki.example
```
没有它们闭环依然可以运行 — 只是 Diagnostician 收集的证据范围会窄一些。
## 配置说明
Orchestrator 和 Agent 通过三个层级进行配置,按优先级排序如下:
1. `chaos run` 上的 **CLI 标志位**(最高)
2. **环境变量**
3. `agents/_factory.py` 中的 **`AgentConfig` 默认值**
| CLI 标志 | 环境变量 | 默认值 | 用途 |
|---|---|---|---|
| `--profile` | — | `static` | 策略组合: `static` / `hybrid` / `llm` |
| `--dry-run` | — | 关闭 | 使用模拟 Agent(无 LLM,无集群) |
| `--prom-url` | `PROM_URL` | 未设置 | Prometheus 基础 URL |
| `--loki-url` | `LOKI_URL` | 未设置 | Loki 基础 URL |
| `--target-repo-path` | `TARGET_REPO_PATH` | 未设置 | 目标仓库的本地检出路径 |
| `--kubeconfig` | `KUBECONFIG` | `~/.kube/config` | kubeconfig 的路径 |
| `--kube-context` | `KUBE_CONTEXT` | 当前 context | 在 kubeconfig 中使用的 context |
| `--model` | `CHAOS_LLM_MODEL` | `claude-opus-4-7` | LiteLLM 模型标识符 |
| `--api-base` | `CHAOS_LLM_API_BASE` | 提供商默认值 | 覆盖 LLM API 基础 URL |
| `--db` | — | `~/.local/share/chaos/experiments.sqlite` | SQLite 存储路径 |
此外,实验计划(YAML)本身也是一种配置 — 参见
[experiments/examples/](experiments/examples/) 中带有完整注释的示例。
### 模型选择
`--model` 会直接传递给 LiteLLM。裸名称通过前缀路由到提供商:
| 裸名称模式 | 提供商 | 示例 |
|---|---|---|
| `claude-*`, `anthropic-*` | Anthropic | `claude-opus-4-7` |
| `gpt-*`, `o1-*`, `o3-*` | OpenAI | `gpt-4o` |
| `qwen*`, `llama*`, `mistral*`, `deepseek*`, `phi*` | Ollama | `qwen2.5-coder:14b` |
| 其他 | 使用显式的 `provider/model` 形式 | `groq/llama-3.1-70b` |
对于 Ollama(和其他自托管端点),你还需要
`--api-base http://localhost:11434`。
### 调优闭环
| 调优项 | 位置 | 备注 |
|---|---|---|
| 基线漂移的 Z-score 阈值 | `agents/tester/agent.py` `_BASELINE_SHIFT_Z_THRESHOLD` | 默认 3.0(约 0.3% 假阳性)。越低越敏感。 |
| 基线运行次数 | `TesterRequest.baseline_run_count` | 默认 5。越多 = 分布越紧凑,基线耗时越长。 |
| 触发操作的最低置信度 | `agents/fixer/policy.py` `DEFAULT_MIN_CONFIDENCE` | 默认 0.5。Fixer 提议代码编辑而非仅输出 DOC_ONLY 的阈值。 |
| 路径黑名单 | `agents/fixer/policy.py` `PathDenylist` | Fixer 拒绝触碰的文件。默认包括 `.github/`、`infra/`、`secrets/`。 |
| LLM 工具循环最大轮次 | `Claude*` 策略构造函数 `max_turns=25` | 每次 LLM 调用。越大 = 探索越多,成本越高。 |
| LLM 单次调用预算 | `Claude*` 策略构造函数 `max_budget_usd=3.0` | 单个工具循环内的本地熔断器。 |
| 检测器集合 | `agents/tester/detectors/__init__.py` `default_detectors()` | 增加 / 删除模式。 |
| 故障目录 | `agents/chaos/faults/_meta.py` `CATALOGUE` | Orchestrator 接受哪些故障。 |
| 禁止的集群子字符串 | 每个计划的 `SafetyConstraints.forbidden_cluster_substrings` | 默认 `("prod", "production", "live", "main")`。 |
## 操作系统
### 运行实验
```
# 确定性的,无 LLM,无需 API key。
chaos run experiments/examples/01-redis-network-loss.yaml --profile static
# 使用 Claude。
ANTHROPIC_API_KEY=sk-ant-... chaos run experiments/examples/01-redis-network-loss.yaml --profile hybrid
# 使用本地 Ollama。
chaos run experiments/examples/01-redis-network-loss.yaml \
--profile hybrid \
--model ollama/qwen2.5-coder:14b \
--api-base http://localhost:11434
# 使用 mock agents 完整 dry-run —— 无 cluster,无 API,无其他依赖。
chaos run experiments/examples/01-redis-network-loss.yaml --dry-run
```
输出的内容是 JSON 格式的 `ExperimentRecord`:状态机演进过程,
每次 Agent 调用及其输入/输出摘要 + 花费、混沌时间线、诊断以及修复建议。
### 验证计划但不运行
```
chaos validate experiments/examples/01-redis-network-loss.yaml
```
检查项:schema 验证,每个 `fault.name` 存在于目录中,集群黑名单,爆炸半径。不执行任何 I/O 操作。
### 列出 + 检查过往实验
```
chaos list # 20 most recent
chaos list --limit 100
chaos show # full JSON record
```
### 中止正在运行的实验
```
chaos abort # one
chaos abort --all # everything in a non-terminal state
```
**注意:** `chaos abort` 会更新存储;它**不会**删除集群资源。请使用 Chaos Agent 的清理路径或:
```
kubectl delete chaos \
-l chaos.kosta.dev/experiment-id=
```
### 列出故障目录
```
chaos list-faults # everything
chaos list-faults --category network
chaos list-faults --requires-approval # which faults need explicit approval
```
### 验证集群 + Chaos Mesh 集成
```
# 对每个故障 renderer 进行 Server-side dry-run apply:
python scripts/validate_renderers.py --context kind-chaos-dev
# 真实的 KubernetesClusterIO 往返测试 (apply / get / list / delete):
python scripts/smoke_kubernetes_cluster_io.py --context kind-chaos-dev
# 针对真实 nginx 目标的 Live chaos 测试(观察 pods 实际被 kill):
python scripts/smoke_live_chaos.py --context kind-chaos-dev
# 完整的 ClaudeChaosAgent 生命周期测试:
python scripts/smoke_chaos_agent.py --context kind-chaos-dev
```
每个冒烟测试脚本都接受 `--kubectl ""`(shlex 分割)用于
非简单调用(例如,从 Windows 调用 WSL)。参见各自的 `--help`。
## 诊断 UI
[`ui/`](ui/) 中附带了一个只读的 Web UI。它以 WAL 快照模式打开 Orchestrator 的 SQLite 存储(绝不阻塞写入者),并将每个实验渲染为六个标签页 — 概述、时间线(交错显示调用和混沌事件)、LLM 遥测(支出 / token 数 / 按 Agent 细分)、诊断(带有置信度标记的排序假设列表)、修复建议(操作 + draft-PR 链接)和原始 JSON。基于 NestJS 11 + Angular 21 standalone 构建,默认单机和仅限本地访问;通过 bearer-token 模式可以开放给团队共享使用。
```
cd ui && pnpm install
pnpm --filter @chaos/ui-server start:dev # http://127.0.0.1:3000
pnpm --filter @chaos/ui-web start # http://localhost:4200
```
完整的设置、每个标签页的截图,以及如何将其连接到真实的 Chaos Mesh 集群:[**ui/README.md**](ui/README.md)。
## 测试状态
```
$ python -m pytest tests/ -q
458 passed, 1 skipped in 2.55s
$ python -m mypy agents/ shared/ orchestrator/
Success: no issues found in 61 source files
$ python -m ruff check .
All checks passed!
```
按领域划分的覆盖率(`pytest --cov`):
| 领域 | 覆盖率 |
|---|---|
| `shared/contracts.py` | 99% |
| `orchestrator/safety.py` | 95% |
| `orchestrator/loop.py` | 82%+(变更后) |
| `agents/_factory.py`, `_harness.py`, `_llm.py`, `_retry.py` | 92–96% |
| `agents/chaos/*` | 74–100%(cluster.py 91% — kubernetes-client 路径) |
| `agents/tester/detectors/*` | 89–100% |
| `agents/diagnostician/diagnoser.py` | 78% |
| `agents/fixer/strategy.py` | 79% |
| **项目总计** | **81%+** |
实际的集群集成由scripts/` 中的四个脚本进行验证(不属于单元测试运行的一部分;它们需要一个真实的集群 + Chaos Mesh)。
## 仓库结构
```
chaos/
├── shared/ Pydantic contracts — the inter-agent interface (most important file)
├── orchestrator/ Deterministic loop, safety gates, budget tracking, SQLite store, typer CLI
├── agents/
│ ├── _factory.py build_real_agents(profile=…): wire agents per static/hybrid/llm
│ ├── _harness.py Meta-harness: invocation log, contextvar for LLM cost attribution
│ ├── _llm.py Universal LiteLLM tool-loop runner (Anthropic / Ollama / OpenAI)
│ ├── _json.py Shared JSON-from-LLM extraction (used by all three strategies)
│ ├── _retry.py Async retry helper used by Loki + Prometheus HTTP backends
│ ├── _mocks.py Mock agents for `chaos run --dry-run`
│ ├── tester/ baseline + verify + hypothesize; detectors live here too
│ ├── chaos/ Chaos Mesh CRD renderers + ClusterIO Protocol (Fake + Kubernetes impls)
│ ├── security/ Scanner runner + Trivy (more scanners in M4.1)
│ ├── diagnostician/ RCA agent: Loki + Prom + code tools; Static/Hybrid/Claude Diagnoser
│ └── fixer/ Draft-PR agent: decision tree + Static/Hybrid/Claude FixerStrategy
├── experiments/ Plan YAMLs + run artifacts
├── tests/ 411 unit tests (mock-based; live cluster tested via scripts/)
├── scripts/ Live-cluster smoke tests + renderer validator
├── ui/ Read-only diagnostic web UI: NestJS server + Angular SPA
└── docs/ Deeper architecture / safety / modes / comparison / roadmap docs
```
## 延伸阅读
- [docs/CAST.md](docs/CAST.md) — 完成所有工作的七个角色(包含画像和属性面板)
- [docs/MODES.md](docs/MODES.md) — `static` / `hybrid` / `llm` 三分法详解
- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) — Agent 规范 + 深入理解状态机
- [docs/SAFETY.md](docs/SAFETY.md) — 爆炸半径 + 中止条件 + 审批
- [docs/SUPPRESSION.md](docs/SUPPRESSION.md) — 静默处理那些允许系统继续产生但你已决定不采取行动的发现
- [docs/SECURITY_CHAOS.md](docs/SECURITY_CHAOS.md) — 安全混沌工程集成
- [docs/COMPARISON.md](docs/COMPARISON.md) — 现有技术概貌(ChaosEater、Harness、Litmus 等)
- [docs/ROADMAP.md](docs/ROADMAP.md) — 里程碑 + 剩余工作
- 各 Agent 的 README: [tester](agents/tester/README.md) · [chaos](agents/chaos/README.md) · [security](agents/security/README.md) · [diagnostician](agents/diagnostician/README.md) · [fixer](agents/fixer/README.md)
- [ui/README.md](ui/README.md) — 基于 SQLite 存储的只读诊断 UI,包含每个标签页的截图和真实集群连接指南
## 许可证
Apache 2.0 — 参见 [LICENSE](LICENSE)。
Orchestrator(`orchestrator/loop.py`)运行一个确定性的状态
机。每次转换都持久化到 SQLite,以便在运行中途崩溃时可以
恢复。*"每个人都在自己的车道上。状态机不接受请求。"*
LLM 是系统的认知表层;它们也是会消耗金钱、泄露数据、产生幻觉且可能陷入死循环的表层。每次调用基于 LLM 的 Agent 都会通过一个 **meta-harness** 包装器(`agents/_harness.py`),它为 Orchestrator 提供了一个集中的地方,以便跨所有五个 Agent 强制执行控制属性。*"请出示许可证。支出报告。审计日志。继续前行。"*
该包装器是一个 `__getattr__` 代理 — 调用 `harness.instrument(name,
agent)` 会返回一个透明的替代品,满足与底层 Agent 相同的 Protocol。同步属性读取会穿透;协程方法会被插桩。被包装的 Agent 是**只读的**:`__setattr__` 会抛出异常,因此行为不当的调用者无法改变被包装实例上的状态。
包装器在每次异步方法调用时执行的操作:
| 关注点 | Harness 如何处理 |
|---|---|
| **可观测性** | 记录入口 / 出口 / 持续时间 / 错误 / 一行式的输入 + 输出摘要 — 成功时为 INFO 级别,抛出异常时为 WARNING 级别。 |
| **审计追踪** | 构建一个 `AgentInvocation` 记录并将其附加到 `Harness.invocations`。Orchestrator 在每次 SQLite 保存之前将完整列表附加到 `ExperimentRecord.agent_invocations`,因此即使运行中途崩溃,也会留下取证追踪。 |
| **成本归因** | 将每个任务的 `ContextVar` 设置为当前的 `AgentInvocation`。当 Agent 的策略调用 `complete_with_tools` 时,该函数会读取 ContextVar 并将调用的 `response_cost` 计入触发它的 invocation — 无需跨三个构造函数传递 harness 引用。 |
| **预算执行** | 在闭环的每一步之后,Orchestrator 会汇总 `harness.invocations` 中的 `inv.spend_usd`,将其持久化在记录上,在达到 `soft_cap_usd` 时记录一次日志,在达到 `hard_cap_usd` 或 `wall_clock_seconds` 时以 `BUDGET_EXCEEDED` 中止。 |
| **错误传播** | 异常总是被传播。Harness 会为审计日志记录错误字符串,但**绝不**吞没异常。一个失败的 Agent 会导致整个实验失败 — 不允许静默的部分成功。 |
| **禁止修改** | `_Wrapped.__setattr__` 阻止了对代理本身的写入;被包装的 Agent 在概念上是冻结的视图。 |
端到端的 LLM 成本流如下所示:
```
ExperimentRunner.run()
└─ awaits harness_wrapped_tester.hypothesize(req) ← proxy
└─ ContextVar.set(AgentInvocation)
└─ awaits inner_tester.hypothesize(req)
└─ awaits ClaudeHypothesizer.generate(...)
└─ awaits complete_with_tools(...)
└─ for each turn:
└─ litellm.acompletion()
└─ cost = response.usage.cost
└─ record_llm_spend(cost)
└─ inv.spend_usd += cost ← ContextVar
└─ records AgentInvocation in Harness.invocations
└─ budget.spent_usd = sum(inv.spend_usd for inv in harness.invocations)
└─ if budget.hard_exceeded(): abort
```
这在实践中为何重要:
- **Orchestrator 从不直接看到 LLM 调用**。它只通过 Protocol 接口看到
Agent 的调用。Harness 是边界控制。
- **添加新 Agent** 不需要更改任何 harness 代码 — 只需
`harness.instrument("new-agent", instance)`,它就会获得相同的可观测性 / 审计 / 成本 / 预算执行。
- **即使 Agent 调用多个 LLM,控制属性依然成立。** ContextVar 模式意味着 N 次嵌套的 `complete_with_tools` 调用都会将其成本计入启动外部 Agent 方法的那个 invocation。
- **测试可以完全脱离 Harness**(`Fixture*` 实现本来就不使用 LLM),或者独立使用它来验证调用日志是否符合预期。`tests/test_harness.py` 这两种情况都测试了。
本地 Ollama 运行报告的成本为 0(LiteLLM 没有自托管端点的定价数据),因此预算路径在免费层设置中会优雅地空操作 — 可观测性 + 审计追踪属性依然成立。
## 安装说明
### 前置条件
- **Python 3.11+**(推荐 3.13;与开发环境一致)
- **Docker**(用于 Chaos Mesh 的 `kind` 测试集群以及任何镜像扫描)
- **kubectl**(1.27+ 版本;兼容 `client-go` v0.29)
- **kind**(仅当你想要一个本地 k8s 集群时需要;替代方案:现有集群 + Chaos Mesh 安装)
- **(可选) Anthropic API 密钥**,用于 `--profile llm` / `--profile hybrid` 配合 Claude — 在 https://console.anthropic.com 注册
- **(可选) Ollama**,用于本地 LLM — https://ollama.com
### 1. 克隆并安装 Python 依赖
```
git clone 标签:AI智能体, AI风险缓解, Chaos Mesh, CISA项目, DevSecOps, DLL 劫持, LLM, PyRIT, Python, SRE, Unmanaged PE, 上游代理, 云资产清单, 人工智能, 代码分析, 偏差过滤, 凭证管理, 多智能体系统, 大语言模型, 子域名突变, 安全测试, 开源框架, 微服务可靠性, 持续集成, 攻击性安全, 故障注入, 无后门, 模块化设计, 混合策略, 混沌工程, 用户模式Hook绕过, 站点可靠性工程, 系统弹性, 自动化修复, 自动生成PR, 自定义请求头, 请求拦截, 逆向工具, 逆向工程, 闭环测试