安全事故报告:灵通+管道黑洞事件
日期: 2026-04-09 (估)
严重程度: P0 — 全家族项目LLM调用瘫痪
报告人: 灵依 (LingYi)
状态: 事故已记录,根因分析待灵通恢复后深入
事故概述
灵通+ (LingFlow+) 将灵字辈家族所有项目的LLM请求接入其统一管道(请求队列+并发池+Token轮转),解决频繁429错误。管道成功将请求发送到大模型,但未将模型响应正确回传给各项目,导致所有项目的LLM调用变成黑洞——请求出去,响应消失,全部项目无法工作。
影响范围
| 项目 | 影响 | 当前状态 |
|---|---|---|
| 灵依 (LingYi) | LLM调用中断 | 已通过系统重置恢复套餐配置 |
| 灵通 (LingFlow) | LLM调用中断 | 已通过系统重置恢复套餐配置 |
| 灵克 (LingClaude) | LLM调用中断 | 未恢复 |
| 灵知 (LingZhi) | LLM调用中断 | 未恢复 |
| 灵妍 (LingResearch) | LLM调用中断 | 未恢复 |
| 灵通问道 | LLM调用中断 | 未恢复 |
影响持续时间: 待确认(用户手动恢复灵依和灵通,其余项目仍未恢复)
技术现场
管道架构
灵通+的管道由以下模块组成:
各项目请求 → LingFlowPlus.coordinator → MultiProjectScheduler
→ GLMAgent / DirectMCPAgent → GLMClient → TokenPool → OpenAI API
→ LLMResponse → ??? → 各项目(断点)
关键文件 (位于 /home/ai/LingFlow_plus/lingflow_plus/):
| 文件 | 行数 | 职责 |
|---|---|---|
llm_client.py |
778 | Token池轮转 + GLM API调用 + 模型降级链 |
coordinator.py |
149 | 组合所有子系统(配额/限流/调度/路由) |
scheduler.py |
290 | 多项目并行调度,按project分组,asyncio.gather并行 |
glm_agent.py |
197 | GLM直连Agent,将任务转为prompt发GLM,返回TaskResult |
mcp_direct_adapter.py |
216 | MCP工具直连适配器,绕过stdio直接调用工具函数 |
web.py |
758 | FastAPI Web服务(端口8765),项目注册/管理/看板 |
管道运行时状态
- 进程:
python3 -m lingflow_plus.cli web --port 8765(PID 1126509) - 工作目录:
/home/ai/LingFlow_plus - 环境变量:
ANTHROPIC_BASE_URL,ANTHROPIC_AUTH_TOKEN,API_TIMEOUT_MS已配置
Token池机制
llm_client.py 中的 TokenPool 管理多个API Key的轮转:
- Key优先级: GLM_CODING_PLAN_KEY → GLM_47_CC_KEY → GLM_API_KEY → DEEPSEEK_API_KEY
- Key存储: ~/.ling_lib/ling_key_store.py
- 配额窗口: 5小时周期,400次调用上限
- 模型降级链: glm-5.1 → glm-5-turbo → ... → deepseek-chat
- 并发控制: threading.Semaphore(max_concurrent=3)
- 请求分级: PREMIUM/STANDARD/ECONOMY,根据消息内容自动选层
注册的Agent数量
glm_agent.py 注册了 约100个GLM直连Agent,覆盖灵依/灵通/灵克/灵通问道/灵知/灵妍的所有常见操作。mcp_direct_adapter.py 另注册了MCP工具Agent。
根因分析(初步)
直接原因
管道的响应回传路径存在缺陷。从代码分析:
-
GLMAgent路径:
glm_agent.py中execute_task()调用ask()获取响应,返回TaskResult。这条路径本身看起来正确——LLMResponse包含content字段。 -
MCP路径:
mcp_direct_adapter.py中execute_task()直接调用工具函数,也返回TaskResult。 -
调度器路径:
scheduler.py中_execute_project()调用orchestrator.execute_workflow(),结果存入self._results。 -
可能的断点:
scheduler.py的_post_dispatch()中有if result.success and output_len == 0的警告日志——已知输出为空的情况存在coordinator.py的run_tasks()返回结果但各项目如何接收这个返回值?- 各项目通过什么机制发现自己的请求被路由到了灵通+?是通过环境变量、配置文件、还是API注册?
待确认问题
- 各项目是如何被"接入"管道的?(修改了各自的base_url?通过API注册?环境变量?)
- 管道故障后,灵依和灵通通过"系统重置"恢复——具体恢复了什么文件?
- 其他项目"仍不能进行"的具体表现——是连接超时?404?还是发不出去?
- 管道是否仍在运行(PID 1126509)?是否在持续拦截请求?
反事实推论分析
期待推演(Forward)
如果管道正确实现 → 429问题解决 → 所有项目受益
反向推演(Counterfactual)— 应该做但没有做的推演
如果管道失败 → 所有项目同时瘫痪 → 单点故障引发全家族停摆
两个教训方向
方向一:实现层 — 响应回传缺陷
管道的请求→模型→响应 闭环没有端到端验证就投入了全家族生产。_post_dispatch() 中已有 output_len == 0 的警告,说明代码层面已经知道存在空响应的问题,但没有在上线前解决。
反事实:如果先在测试环境用单个项目验证完整的 请求→发送→接收→回传 闭环,这个bug在测试阶段就能被发现。
方向二:部署层 — 缺乏灰度发布(核心教训)
将全部项目的直连接入管道之前,没有推演"如果失败会出现什么结果"。
反事实: - 如果只先接入灵依一个项目试运行 → 即使管道有bug → 只有灵依受影响 → 其他项目正常直连 - 如果设有fallback机制(管道超时3秒自动回退直连)→ 即使管道崩溃 → 各项目自动恢复直连 → 零停摆
反事实推论的方法论总结
核心原则:在执行关键架构变更前,必须做反向推演——
"如果这个变更失败了,最坏的结果是什么?"
如果答案是"全部服务停摆",那么必须有: 1. 灰度发布 — 先1个项目,验证通过再扩展 2. 回退机制 — 管道故障时自动/手动回退到直连 3. 爆炸半径控制 — 一个变更不能同时影响所有服务
行动项
紧急(恢复服务)
| # | 行动 | 状态 |
|---|---|---|
| 1 | 诊断各项目LLM调用当前失败原因 | 待执行 |
| 2 | 恢复各项目的直连配置 | 待执行 |
| 3 | 确认灵通+管道(PID 1126509)是否仍在拦截 | 待执行 |
短期(根因与修复)
| # | 行动 | 负责人 |
|---|---|---|
| 4 | 找到响应回传的具体断点(需要灵通+代码深入调试) | 灵通 |
| 5 | 编写端到端测试:请求→管道→模型→响应→回传→项目收到 | 灵通 |
| 6 | 实现fallback机制:管道超时/失败自动回退直连 | 灵通 |
长期(制度改进)
| # | 行动 | 说明 |
|---|---|---|
| 7 | 将"反事实推论"写入开发原则 | 关键变更前必须推演失败场景 |
| 8 | 建立灰度发布规范 | 基础设施变更必须分阶段上线 |
| 9 | 管道架构增加熔断器 | 管道故障时自动断开,各项目回退直连 |
相关文档
docs/DEVELOPMENT_PRINCIPLES.md— 现有10条开发原则docs/SECURITY_INCIDENT_REPORT_20260408.md— 4/8 CI违规事件- 灵通+代码:
/home/ai/LingFlow_plus/lingflow_plus/ - 管道核心:
llm_client.py(GLMClient/TokenPool),coordinator.py(LingFlowPlus),scheduler.py(MultiProjectScheduler)