← 返回博客

API 末日实验:当所有 API 都崩溃时,你的 Agent 能自己进化吗?

我们构建了一个 AI Agent 混沌工程实验。Rotifer Agent 自动切换 Gene 维持 83.3% 数据源可用性,而对照组 Agent 跌至 33.3%。全程零人工干预。

API 末日实验:当所有 API 都崩溃时,你的 Agent 能自己进化吗?

API Apocalypse 演示 — Rotifer Agent vs Baseline

API 会崩。它们不打招呼就改响应格式,把你限流到崩溃,凌晨三点直接下线。每个开发者都写过周一还正常、周五就挂掉的代码——因为某个第三方端点决定把 temperature 字段改成嵌套到 weather.temp_celsius 里。

通常的修复流程:人类发现问题 → 更新解析器 → 部署 → 祈祷不要再出别的问题。

如果 Agent 能自己修复呢?我们做了一个实验来验证这个想法。


实验设计:API Apocalypse

我们创建了一个可控混沌环境——三个模拟天气 API 数据源在 30 秒内渐进式降级:

时间Source A (JSON)Source B (XML)Source C (CSV)
0–6s正常正常正常
6s格式变更正常正常
12s已变更格式变更正常
18s已变更已变更格式变更
24s限流 (429)已变更已变更
28s限流中完全下线 (503)已变更

两个 Agent 在同一环境中竞争:

两个 Agent 每 2 秒从三个源聚合天气数据。当某个源崩溃时,Baseline Agent 对该源永久失败;Rotifer Agent 尝试替代 Gene。


核心创新:Fitness 排序 Failover

Rotifer Agent 为每个 domain 维护一个 Gene 池。每个 Gene 有一个运行时 Fitness 分数——成功时增加,失败时降低。当活跃 Gene 失败时,Agent 执行:

  1. 惩罚失败 Gene 的 Fitness
  2. 按 Fitness 降序排列同 domain 的剩余 Gene
  3. 逐个尝试,直到某个候选 Gene 成功
  4. 将成功的候选 Gene 提升为活跃 Gene
// 主 Gene 失败时,按 Fitness 排名尝试替代方案
const candidates = this.getGenesByDomain(domain)
.filter(g => g.name !== activeName);
for (const candidate of candidates) {
try {
const result = await candidate.express(input);
this.updateFitness(candidate, true);
this.activeGenes.set(domain, candidate.name);
recovered = true;
break;
} catch {
this.updateFitness(candidate, false);
}
}

这不是重试逻辑。这是选择压力。失败的 Gene 不会被盲目重试——它们的 Fitness 排名下降,被能处理新现实的替代方案取代。


实验结果

t │ ROTIFER(自动切换) │ BASELINE(固定代码)
─────┼─────────────────────────────-┼──────────────────────
0s │ ✓ ✓ ✓ 23.5°C │ ✓ ✓ ✓ 23.5°C
6s │ ✓ ✓ ✓ 23.4°C │ ✓ ✓ ✓ 23.4°C
8s │ ✓⟳ ✓ ✓ 23.3°C │ ✗ ✓ ✓ 23.2°C
14s │ ✓ ✓⟳ ✓ 23.1°C │ ✗ ✗ ✓ 23.0°C
20s │ ✓ ✓ ✓⟳ 22.9°C │ ✗ ✗ ✗ NO DATA
28s │ ✗ ✓ ✓ 23.0°C │ ✗ ✗ ✗ NO DATA

⟳ 标记展示了自动 Gene 切换——Rotifer Agent 检测到解析失败并在同一个 tick 内完成切换。

正面对决

指标RotiferBaseline
数据源可用性83.3%33.3%
数据连续性100%(始终至少有 1 个源可用)50%(t=90s 后丢失所有源)
自动 Gene 切换3 次N/A
人工干预0 次需要手动修复

Rotifer Agent 维持了 2.5 倍的数据源可用性,同时零人工干预。当 Source A 在 t=24s 被限流时,两个 Agent 都丢失了它——网络层故障不是解析 Gene 能修复的。但到那个时候,Baseline Agent 已经因无法处理格式变更而丢失了全部三个源。


这跟重试逻辑有什么本质区别

重试循环是重复同一个操作,期待不同的结果。Rotifer Agent 做的事情有本质不同:

它切换的是实现方式,不是尝试次数。

json-v1(期待 { temperature: 23.5 })遇到 { weather: { temp_celsius: 23.5 } } 时,重试 json-v1 永远不会成功。但 json-v2 恰好是为这种格式设计的。Agent 不知道 API 为什么变了——它只观察到 json-v1 的 Fitness 下降而 json-v2 成功,于是提升了 json-v2

这就是生物学类比:生物体不调试它们的环境。它们携带遗传变异,选择压力提升有效的那些。


Gene Fitness 动态

实验结束后,Fitness 分数清晰地讲述了整个故事:

weather-source-a-v1 █████████████████░░░ 0.850 (15✓ 1✗)
weather-source-a-v2 ████████████████████ 1.000 (45✓ 0✗)
weather-source-b-v1 █████████████████░░░ 0.850 (30✓ 1✗)
weather-source-b-v2 ████████████████████ 1.000 (45✓ 0✗)
weather-source-c-v1 █████████████████░░░ 0.850 (45✓ 1✗)
weather-source-c-v2 ████████████████████ 1.000 (45✓ 0✗)

每个 v1 Gene 作为初始活跃解析器,在稳定期表现良好。当 API 格式变更时,v1 Gene 失败一次,Fitness 受到惩罚,v2 Gene 被提升。v2 Gene 随后无故障运行直到实验结束或源完全下线。

没有配置文件被更新。没有人类被呼叫。Agent 自行适应了。


自己试试

完整实验是自包含的,可以复现:

Terminal window
git clone https://github.com/rotifer-protocol/rotifer-playground.git
cd rotifer-playground
npm install
npx tsx experiments/api-apocalypse/run.ts

30 秒演示版(为录屏优化):

Terminal window
npx tsx experiments/api-apocalypse/demo.ts

一切都在本地运行——模拟服务器、Gene、Agent、指标采集。无需外部依赖、API 密钥或云服务。


这证明了什么(以及没证明什么)

本实验证明了基于 Fitness 的 Gene 选择 + 自动 failover 机制可以在 API 中断期间维持服务连续性,无需人工干预,实现比固定代码方案 2.5 倍的数据源可用性提升。

本实验没有声称进化式方法普遍优于传统工程。场景是刻意限定的——单 Agent、本地执行、已知故障模式 + 预构建的替代 Gene。

下一步的诚实问题是:如果 Agent 必须在运行时发现或生成新 Gene(而不仅仅是从预注册池中选择),会发生什么?那是我们为 v0.9 规划的多 Agent HLT(水平逻辑迁移)实验——当 P2P 网络使跨 Agent Gene 共享成为可能的时候。


为什么这对 Rotifer 之外也有意义

每个依赖第三方 API 的生产系统都面临同样的问题。当前业界的答案是防御性工程:超时、重试、断路器、降级值。这些必要但被动——它们管理故障,不进化越过故障。

本实验展示的替代方案是携带遗传变异——为同一能力维护多种实现策略,让运行时性能决定哪个领先。这是一个在任何规模下都有效的模式,从单个 API 解析器到自主 Agent 舰队。

代码开源。运行它,打破它,扩展它。


Rotifer Protocol — Code as Gene, Evolution as Runtime