【Agent Harness】Gliding Horse 根因分析引擎:从“头痛医头”到“三维会诊”

0 阅读7分钟

Gliding Horse 根因分析引擎:从“头痛医头”到“三维会诊”

摘要:本文深入解析 Gliding Horse 根因分析引擎的设计哲学与架构演进,展示如何通过 GraphBackend 抽象层统一图遍历、快照与特征提取能力,构建跨越执行面、结构面与语义面的三维融合诊断系统。文章涵盖从模块重构到系统集成的完整实践,适合关注 AI 可观测性、多 Agent 系统稳定性与智能运维的开发者阅读。

关键词:根因分析、GraphBackend、三维诊断、多 Agent 系统、知识图谱、贝叶斯网络、故障定位、可观测性、AI 运维、系统稳定性

在多 Agent 协作的复杂系统中,定位一个故障的根因有多难?

Agent 报了一个“文件写入失败”。是权限问题?是依赖的数据库连接超时?还是上游任务变更了目录结构?传统做法要么靠人类翻日志,要么让 LLM 做一次性事后分析,效果堪忧。Gliding Horse 给出的答案是:把根因分析变成一个持续运行的三维诊断引擎,让系统自己回答“为什么会出错”以及“还会有什么会出错”。

本文将拆解这套根因分析体系的设计哲学、架构演进和最终成果。它不再是单一的规则匹配或贝叶斯模型,而是一个跨越执行面、结构面和语义面的融合诊断系统,依托我们全新引入的 GraphBackend 抽象层,统一了图遍历、快照和特征提取能力,使得根因分析可以同时审视代码调用链、技能依赖图和实体关系网。


一、从“三座孤岛”到“一片大陆”

在优化之前,Gliding Horse 的根因分析能力其实已经相当丰富,但它们各自为政:

  • 执行层 RootCauseEngine:通过 HookManager 捕获错误,执行 5-why 模式匹配,生成证据链和防御建议。
  • 结构层 CausalEngine:基于贝叶斯网络,利用 petgraph 构建的技能依赖图计算错误传播概率。
  • 语义层 KnowledgeGraphStore:存储所有实体的 RDF 关系,支持 SPARQL 查询和 BFS 邻居遍历。

问题是:CausalEngine 紧紧绑定着 SkillGraphStore,只看得见技能图谱里的依赖边;语义层有丰富的代码调用、实体归属等关系,却从未被根因分析使用。三套系统共享同一个 @id 命名空间,却无法联合“会诊”。

我们需要一个桥梁。于是,GraphBackend 抽象层诞生了。


二、GraphBackend 抽象层:让根因分析“有统一的图”

classDiagram
    class GraphBackend {
        <<interface>>
        +list_all_nodes() Vec~String~
        +get_outgoing_edges(iri) Vec~out~
        +get_incoming_edges(iri) Vec~in~
        +bfs(seeds, depth, filter) Vec~String~
        +node_count() usize
    }
    class SnapshotBackend {
        <<interface>>
        +snapshot() Vec~Node~
        +apply_snapshot(nodes) Result
    }
    class FeatureGraph {
        <<interface>>
        +neighbors(iri, dir) Vec~String~
        +degree(iri, dir) usize
        +all_nodes() Vec~String~
    }

    class PetgraphBackend {
        -graph: DiGraph
    }
    class SparqlBackend {
        -store: KnowledgeGraphStore
    }

    GraphBackend <|.. PetgraphBackend
    GraphBackend <|.. SparqlBackend
    FeatureGraph <|.. PetgraphFeatureGraph
    FeatureGraph <|.. SparqlFeatureGraph
  • PetgraphBackend:包装了 SkillGraphStore 的内部 petgraph,为 CausalEngine 提供轻量内存图遍历。
  • SparqlBackend:包装了 KnowledgeGraphStore,通过 SPARQL 查询将 Oxigraph 中任意命名图的边关系暴露为统一的图遍历接口。这意味着,代码 AST(graph:code)、工具调用结果(graph:tool-result)、甚至 PDCA 执行记录(system:plan/exec/review/decision)都能被同一个 bfs 方法探索。

同时,SnapshotBackend 让版本快照不再只针对技能图谱,可以对任意命名图创建快照和 diff;FeatureGraph 让 GNN 特征提取器能够从不同数据源抽取结构特征。这三个 trait 将高级特性从“技能图谱”的孤岛中彻底解耦,变为系统级的通用能力。


三、三维修正根因分析引擎

有了统一的图接口,我们的 FusedRootCause 引擎就可以同时从三个维度审视一个故障:

flowchart TB
    subgraph Fused[&#34;FusedRootCause 引擎&#34;]
        direction LR
        L0[&#34;执行面<br/>RootCauseEngine 5-why 追溯&#34;]
        L1[&#34;结构面<br/>CausalEngine 依赖传播推断&#34;]
        L2[&#34;语义面<br/>SparqlBackend RDF 语义遍历&#34;]
    end

    Fused --> RESULT[&#34;三维加权融合诊断&#34;]

    L0 -->|“谁调了谁?”| TraceChain
    L1 -->|“谁依赖谁?”| CausalInferences
    L2 -->|“谁相关于谁?”| RdfContext

    TraceChain & CausalInferences & RdfContext --> FUSION[加权融合]
    FUSION --> REPORT[FusedRootCauseResult]
  1. 执行面:当 HookManager 捕获到 TaskErrorRootCauseEngine 立即执行传统的 5-why 回溯,沿着调用链向上追踪,生成 TraceChain 和证据链,置信度基于证据完整性计算。
  2. 结构面CausalEngine 以故障实体为种子,在 GraphBackend(可以是技能图或 RDF 全图)上运行 BFS,利用贝叶斯后验概率计算每个关联节点的因果贡献,输出 CausalInference 列表。
  3. 语义面SparqlBackend 对故障 IRI 进行深度为 3 的语义邻居探索,获取它在知识图谱中的所有上下文——属于哪个任务、操作了哪些实体、受哪些规则约束等,生成 RdfSemanticContext

最终,三个维度的结果通过加权融合(执行面 0.4,结构面 0.35,语义面 0.25)生成最终根因报告 FusedRootCauseResult,包含主要故障 IRI、总体置信度、各维度的贡献因子以及针对性行动建议。这种“三维会诊”远比任何单一维度的分析更可靠。


四、模块改造:从硬绑定到依赖注入

为了让这套融合引擎落地,我们对相关模块进行了低侵入性的重构:

  • CausalEngine:构造函数从接受 Arc<SkillGraphStore> 改为接受 Arc<dyn GraphBackend>。在技能故障场景,注入 PetgraphBackend;在跨领域分析时,注入 SparqlBackend,无需改动引擎内部逻辑。
  • TimelineStore:原本绑死技能快照,现在 create_snapshot 改为接收 &dyn SnapshotBackend 参数,可以对任意图存储生成时间线快照,用于跨版本对比。
  • FeatureExtractor:同样改为依赖 Arc<dyn FeatureGraph>,使得提取节点度、中心性等图特征时,可以从不同存储后端读取。

旧版 skill_graph/types.rs 中的 CausalEventCausalChain 被标记为弃用,统一迁移到 causal/types.rs,消除类型冗余。整个重构保持向后兼容,原有测试全部通过。


下面是一个具体的 Rust 代码示例,展示 CausalEngine 如何通过依赖注入 GraphBackend 来灵活切换后端:

use std::sync::Arc;

// 1. 定义 GraphBackend trait(抽象层)
pub trait GraphBackend: Send + Sync {
    fn bfs(&self, seeds: &[String], depth: usize) -> Vec<String>;
    fn get_outgoing_edges(&self, iri: &str) -> Vec<String>;
    // ... 其他图操作方法
}

// 2. 两个具体实现
pub struct PetgraphBackend {
    // 包装 SkillGraphStore 内部的 petgraph
    graph: petgraph::Graph<String, ()>,
}

impl GraphBackend for PetgraphBackend {
    fn bfs(&self, seeds: &[String], depth: usize) -> Vec<String> {
        // 在内存 petgraph 上执行 BFS 遍历
        // 适用于技能依赖图的快速分析
        vec![] // 简化示意
    }
    fn get_outgoing_edges(&self, iri: &str) -> Vec<String> {
        vec![]
    }
}

pub struct SparqlBackend {
    store: Arc<KnowledgeGraphStore>,
}

impl GraphBackend for SparqlBackend {
    fn bfs(&self, seeds: &[String], depth: usize) -> Vec<String> {
        // 通过 SPARQL 查询在 Oxigraph 上执行 BFS
        // 可遍历代码 AST、工具调用结果等任意命名图
        vec![] // 简化示意
    }
    fn get_outgoing_edges(&self, iri: &str) -> Vec<String> {
        vec![]
    }
}

// 3. CausalEngine 通过依赖注入接收 GraphBackend
pub struct CausalEngine {
    backend: Arc<dyn GraphBackend>,
}

impl CausalEngine {
    /// 构造函数接受任意实现了 GraphBackend 的后端
    pub fn new(backend: Arc<dyn GraphBackend>) -> Self {
        Self { backend }
    }

    /// 根因推断:利用注入的后端执行 BFS 与贝叶斯后验计算
    pub fn infer_root_cause(&self, fault_iri: &str) -> Vec<CausalInference> {
        let neighbors = self.backend.bfs(&[fault_iri.to_string()], 3);
        // 对邻居节点计算贝叶斯后验概率...
        vec![]
    }
}

// 4. 使用示例:根据场景注入不同的后端
fn main() {
    // 场景 A:技能依赖故障 → 使用轻量内存图
    let petgraph_backend = Arc::new(PetgraphBackend { /* ... */ });
    let engine_for_skills = CausalEngine::new(petgraph_backend);

    // 场景 B:跨领域分析 → 使用 RDF 知识图谱
    let sparql_backend = Arc::new(SparqlBackend {
        store: Arc::new(KnowledgeGraphStore::new()),
    });
    let engine_for_cross_domain = CausalEngine::new(sparql_backend);

    // 两种场景共用同一套 infer_root_cause 逻辑,无需修改引擎内部代码
    let result_a = engine_for_skills.infer_root_cause("task:file-write-error");
    let result_b = engine_for_cross_domain.infer_root_cause("task:file-write-error");
}

关键点CausalEngine 不再关心具体是哪种图存储,只依赖 Arc<dyn GraphBackend> 接口。注入 PetgraphBackend 时获得轻量内存遍历性能,注入 SparqlBackend 时获得全量 RDF 语义探索能力——一行构造函数调用即可切换后端,引擎内部逻辑完全不变

五、系统集成:从被动记录到主动诊断

融合引擎完全嵌入 Hook 系统,形成闭环:

sequenceDiagram
    participant HM as HookManager
    participant RE as RootCauseEngine
    participant CE as CausalEngine
    participant KG as KnowledgeGraphStore

    HM->>RE: TaskError 事件
    RE->>RE: trace_backward() [5-why]
    RE->>CE: infer_root_cause(fault_iri)
    CE->>CE: GraphBackend.bfs() + posterior
    CE-->>RE: Vec
    RE->>KG: get_neighbors(fault_iri, depth=3)
    KG-->>RE: RDF 语义子图
    RE->>RE: fuse_confidence()
    RE-->>HM: FusedRootCause
    HM->>L0: 写入诊断报告 (JSON-LD)

诊断报告以 JSON-LD 节点存入 L0,关联原任务 IRI,后续 SA 进行相似任务编排时会参考历史根因;Batch Agent “失败模式挖掘” 也会消费这些报告,提炼出通用失败模式并挂载到技能图谱。这实现了根因分析的 “一次诊断,全局受益”


六、带来的提升与平台效果

  • 准确率提升:融合三维证据后,根因定位的置信度显著高于单维度,避免了“只看调用链以为是参数错误,实际是上游数据源变更”的片面判断。
  • 跨领域通用:无论是技能依赖错误、代码缺陷、还是环境资源冲突,都能在统一框架内分析,因为图遍历后端可灵活切换。
  • 可解释性强:每份诊断报告都附带三个维度的证据分项,人类审计时可以清晰回溯 AI 的推理过程。
  • 系统自愈基础:根因引擎输出的 recommended_actions 已经可以直接喂给 AA(决策 Agent)进行自动修复或回滚。

在流马内部,这套根因分析系统已经从“特定模块的附属工具”升级为系统级的诊断服务,为多 Agent 复杂任务的长稳运行提供了坚实保障。它让我们从“记录错误”迈向了“理解错误”,最终走向“预防错误”。

Gliding Horse 已在 GitHub 开源:github.com/doiito/glid…