在打造 Gliding Horse(流马)这个 AI Agent 操作系统的过程中,我一直被一个问题困扰:如何让 Agent 真正“理解”它产出的每一条数据的含义,而不仅仅是生成文本?

LLM 擅长生成内容,但弱于遵守精确的结构化约束。一个 Agent 产出的 JSON-LD 文档可能缺少必填字段,或者引用了不存在的实体。在简单的单 Agent 场景里,这些问题可以人工兜底,但当一个工程由需求、设计、编码、测试等多个阶段的多个 Agent 协作完成时,数据一致性和语义正确性就成了生死线。

为了解决这个问题,我决定为流马装上一个“语义大脑”——一套完整的本体论系统。它不是事后校验,而是内建于内核的、基于 W3C 标准的语义推理与验证引擎。这篇文章将详细拆解这套系统的设计思路、核心架构、关键组件,以及它为整个平台带来的巨大提升。

一、为什么 Agent OS 需要本体论系统?

传统 Agent 框架大多把知识当作非结构化文本或 JSON 来传递。这带来三个致命问题:

  1. 弱约束:Prompt 里写“必须包含某个字段”,LLM 可能忽略;
  2. 不可推理:Agent 产出的数据之间没有逻辑推导能力,无法发现隐含的矛盾;
  3. 难以追溯:当任务失败,很难定位是哪一环节的数据出了问题。

Gliding Horse 从一开始就采用 JSON-LD 作为统一数据总线,所有数据都有全局 IRI 和语义类型。这套机制需要一个与之匹配的“编译器”和“类型检查器”——这就是本体论系统的用武之地。它提供:

  • SHACL 形状约束:像数据库的 CHECK 约束一样,强制数据完整性;
  • OWL 推理:通过逻辑推导,发现隐含的知识和矛盾;
  • 本体对齐与漂移检测:保持技能图谱和知识图谱的语义一致性;
  • 嵌入增强的语义检索:结合向量语义,让模糊搜索更精准。

二、设计思想:库级融合,共享图存储

市面上有不少本体工具,但大多以独立服务(如 MCP Server)的形式存在,需要进程间通信、序列化开销,且各自维护一份数据副本。在流马的设计中,我选择了库级融合(Library Fusion)的方案——将开源本体库 open-ontologies 直接作为 Rust 依赖嵌入内核,通过共享底层的 Arc<oxigraph::store::Store> 实现零拷贝的数据互通。

整个架构围绕一个新增的核心模块 OntologyEngine 展开,它与现有的记忆系统、上下文引擎、事件总线深度集成。

事件总线 (现有)

质量门禁 (现有)

记忆系统 (现有)

本体论系统 (新增)

双核心引擎

Agent 编排引擎

上下文管理引擎

OntologyEngine

SharedGraphStore (适配层)

L2 黑板 (Oxigraph 内存图)

L3 投影引擎

L0 持久化 (Oxigraph + Qdrant)

系统调用门

ToolGuard

EventBus

核心设计理念:本体论系统不引入额外的数据源,而是直接工作于现有的 L2 黑板(Oxigraph 内存图)之上。任何 Agent 写入的 JSON‑LD 数据,都能被本体引擎实时感知、校验和推理。上层应用(SA、PA、CA)通过 OntologyEngine 提供的 Rust API 直接调用推理、对齐等功能,零网络开销。

三、核心架构:OntologyEngine 及 SharedGraphStore

OntologyEngine 是整个本体论系统的“统一入口”,它将 open-ontologies 的各个组件封装在一起,并共享 Gliding Horse 的 Arc<Store>

pub struct OntologyEngine {
    graph: Arc<SharedGraphStore>,          // 共享图存储(适配层)
    db: StateDb,                          // 本体演化状态(SQLite)
    drift_detector: DriftDetector,        // 漂移检测器
    enforcer: Enforcer,                   // 设计模式强制器
    planner: Planner,                     // 本体生命周期规划器
    monitor: Monitor,                     // SPARQL 观察者
    alignment: AlignmentEngine,           // 本体对齐引擎
    lineage: LineageLog,                  // 血统审计日志
}

其中,SharedGraphStore 是对 Arc<oxigraph::store::Store> 的轻量级适配,使得 open-ontologies 的 ShaclValidatorReasoner 等能直接操作 GH 的图存储,无需额外的 Mutex 包裹(Oxigraph 的 Store 本身已是线程安全)。

pub struct SharedGraphStore {
    inner: Arc<Store>,
}

impl SharedGraphStore {
    pub fn from_arc(store: Arc<Store>) -> Self { Self { inner: store } }
    // 代理到 inner 的 SPARQL、插入、遍历等方法
}

这样,整个系统中只有一个物理图存储,推理、校验、检索都基于同一份实时数据

四、关键组件设计详解

4.1 SHACL 门禁 —— 让每次写入都“过安检”

Gliding Horse 的所有 Agent 产出都是以 JSON‑LD 形式写入 L2 黑板的。我们在 Blackboard::write_node() 中植入了一个可配置的 SHACL 校验门禁:

Oxigraph StoreShaclValidatorBlackboardAgentOxigraph StoreShaclValidatorBlackboardAgentalt[校验通过 (或 Warn 模式)][校验失败 (Block 模式)]write_node(JSON‑LD)validate(shape)ok / warning log写入三元组violationsCoreError::ValidationFailed

我们定义了一系列 SHACL 形状,用于约束核心 Agent 概念:

  • ExecutionEvent:必须包含 executedBy(执行者)、hasTimestamp(时间戳)且类型为 xsd:dateTime
  • Task:每个任务至多一个 assignedTo,子任务必须引用合法的 Task 实例;
  • PDCACycle:阶段数 4‑7 个;
  • Skill:必须有 karma 权重(浮点数)。
:ExecutionEventShape a sh:NodeShape ;
    sh:targetClass gh:Event ;
    sh:property [
        sh:path gh:executedBy ;
        sh:minCount 1 ; sh:maxCount 1 ;
        sh:class gh:Agent ;
    ] ;
    sh:property [
        sh:path gh:hasTimestamp ;
        sh:datatype xsd:dateTime ;
    ] .

效果:违反形状的数据可在写入前被硬性阻止(Block 模式),确保流入持久层的每一条三元组都是“合规公民”。即使是 LLM 产生幻觉漏掉的字段,SHACL 也能兜底。

下面是用 Rust 定义 SHACL 形状并集成到 Blackboard::write_node 中的完整实战代码:

// ============================================================
// 1. 使用 open-ontologies 的 ShaclValidator 定义形状
// ============================================================
use oxigraph::store::Store;
use open_ontologies::shacl::{ShaclValidator, ValidationReport, Severity};
use std::sync::Arc;

/// 加载并注册 SHACL 形状到验证器
fn load_shacl_shapes(store: &Store) -> ShaclValidator {
    let shapes_turtle = r#"
        @prefix sh: <http://www.w3.org/ns/shacl#> .
        @prefix gh: <http://gliding-horse.io/ontology/> .
        @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

        # ExecutionEvent 形状:必须包含执行者和时间戳
        :ExecutionEventShape a sh:NodeShape ;
            sh:targetClass gh:Event ;
            sh:property [
                sh:path gh:executedBy ;
                sh:minCount 1 ; sh:maxCount 1 ;
                sh:class gh:Agent ;
            ] ;
            sh:property [
                sh:path gh:hasTimestamp ;
                sh:datatype xsd:dateTime ;
                sh:minCount 1 ;
            ] .

        # Task 形状:至多一个 assignedTo,子任务必须引用合法 Task
        :TaskShape a sh:NodeShape ;
            sh:targetClass gh:Task ;
            sh:property [
                sh:path gh:assignedTo ;
                sh:maxCount 1 ;
                sh:class gh:Agent ;
            ] ;
            sh:property [
                sh:path gh:subTaskOf ;
                sh:nodeKind sh:IRI ;
                sh:class gh:Task ;
            ] .

        # PDCACycle 形状:阶段数 4-7 个
        :PDCACycleShape a sh:NodeShape ;
            sh:targetClass gh:PDCACycle ;
            sh:property [
                sh:path gh:phaseCount ;
                sh:minInclusive 4 ;
                sh:maxInclusive 7 ;
                sh:datatype xsd:integer ;
            ] .

        # Skill 形状:必须有 karma 权重(浮点数)
        :SkillShape a sh:NodeShape ;
            sh:targetClass gh:Skill ;
            sh:property [
                sh:path gh:karma ;
                sh:datatype xsd:float ;
                sh:minCount 1 ;
            ] .
    "#;

    // 将形状定义解析并加载到验证器
    ShaclValidator::from_turtle(shapes_turtle, store)
        .expect("SHACL 形状定义解析失败,请检查 Turtle 语法")
}

// ============================================================
// 2. 集成到 Blackboard::write_node 方法中
// ============================================================
use open_ontologies::shacl::ValidationMode;

/// 黑板写入配置
#[derive(Clone)]
pub struct WriteConfig {
    /// SHACL 校验模式:Off / Warn / Block
    pub shacl_mode: ValidationMode,
    /// 推理配置
    pub reasoning_profile: ReasoningProfile,
}

/// 黑板写入结果
#[derive(Debug)]
pub enum WriteResult {
    /// 写入成功
    Accepted,
    /// 校验警告(Warn 模式下写入成功但记录警告)
    AcceptedWithWarning(Vec<String>),
    /// 校验失败被阻止(Block 模式下)
    Rejected(Vec<String>),
}

impl Blackboard {
    /// 写入节点,经过 SHACL 门禁校验
    pub fn write_node(
        &self,
        json_ld: &str,           // 待写入的 JSON-LD 数据
        shapes_graph_iri: &str,  // SHACL 形状图的 IRI
        config: &WriteConfig,
    ) -> Result<WriteResult, CoreError> {
        // 步骤 1:将 JSON-LD 解析为三元组并暂存到临时图
        let temp_graph = self.store
            .parse_graph(json_ld, "application/ld+json")
            .map_err(|e| CoreError::ParseError(format!("JSON-LD 解析失败: {}", e)))?;

        // 步骤 2:执行 SHACL 校验
        let validator = load_shacl_shapes(&self.store);
        let report: ValidationReport = validator.validate_with_shapes_graph(
            &temp_graph,
            shapes_graph_iri,
        )?;

        // 步骤 3:根据校验结果和配置模式决定行为
        let violations: Vec<String> = report
            .results()
            .iter()
            .filter(|r| r.severity() == Severity::Violation)
            .map(|r| format!(
                "[{}] 路径: {} | 消息: {}",
                r.severity(),
                r.path().map(|p| p.to_string()).unwrap_or_default(),
                r.message().unwrap_or("无详细信息")
            ))
            .collect();

        match config.shacl_mode {
            ValidationMode::Off => {
                // 关闭校验,直接写入
                self.store.load_graph(temp_graph, None)?;
                Ok(WriteResult::Accepted)
            }
            ValidationMode::Warn => {
                if violations.is_empty() {
                    self.store.load_graph(temp_graph, None)?;
                    Ok(WriteResult::Accepted)
                } else {
                    // 记录警告但允许写入
                    tracing::warn!(
                        "SHACL 校验警告 (Warn 模式): {} 条违规",
                        violations.len()
                    );
                    for v in &violations {
                        tracing::warn!("  -> {}", v);
                    }
                    self.store.load_graph(temp_graph, None)?;
                    Ok(WriteResult::AcceptedWithWarning(violations))
                }
            }
            ValidationMode::Block => {
                if !violations.is_empty() {
                    tracing::error!(
                        "SHACL 校验失败 (Block 模式): {} 条违规,写入被阻止",
                        violations.len()
                    );
                    return Err(CoreError::ValidationFailed(violations));
                }
                self.store.load_graph(temp_graph, None)?;
                Ok(WriteResult::Accepted)
            }
        }
    }
}

/// 自定义错误类型
#[derive(Debug, thiserror::Error)]
pub enum CoreError {
    #[error("JSON-LD 解析错误: {0}")]
    ParseError(String),
    #[error("SHACL 校验失败: {0:?}")]
    ValidationFailed(Vec<String>),
    #[error("推理引擎错误: {0}")]
    ReasoningError(String),
}

代码要点说明

  • load_shacl_shapes() 函数将 Turtle 格式的形状定义解析为 ShaclValidator 实例,支持 ExecutionEventTaskPDCACycleSkill 四种核心形状;
  • Blackboard::write_node() 方法实现了完整的校验流程:解析 JSON-LD → 执行 SHACL 校验 → 根据 ValidationMode(Off/Warn/Block)决定是否写入;
  • Block 模式下,任何 Severity::Violation 都会导致写入被拒绝并返回详细的违规信息,Agent 可据此修正数据后重试。

4.2 OWL 推理 —— 从显式声明中挖掘隐性知识

Agent 产出的 JSON‑LD 通常只显式声明最直接的属性,但 Gliding Horse 的 Agent 本体(如 SupervisorAgentDoAgent)本身是一个 OWL 类层次。

SupervisorAgent rdfs:subClassOf Agent
DoAgent rdfs:subClassOf Agent
Agent rdfs:subClassOf ExecutorEntity
ExecutorEntity gh:hasRole min 1

单纯依靠 SHACL 校验,无法自动推断出 SupervisorAgent 也需要 hasRole 属性,除非显式声明。而我们通过 OWL‑RL 推理引擎,对写入后的数据进行物化(materialization),将推断出的三元组写入一个专门的命名图 <urn:gh:inferred>

// Blackboard::write_node() 中的调用
if config.reasoning_profile != Off {
    Reasoner::run(&graph, "owl-rl", true)?;
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐