OntologyAnalyst 指南¶
概述¶
ontology_analyst 是内置子代理,用于回答带有 Apache Ossie ontology 的数据源上的数据问题。它和 AskMetrics 一样只走语义层、不写裸 SQL,但还会读取 ontology,从中获得语义模型本身没有的四类信息:
- 主语概念:概念目录(Flight、Airport、FlightAlert……)从第一轮起就在上下文里,不用猜问题落在哪张表。
- 允许的过滤值:
FlightAlert.status这类属性带有封闭值域(OPEN、RESOLVED)。猜错的字面量(如'unresolved')会在编译前被 Dosi 拒绝,并返回允许值。 - 关系角色:航班通过
arrives_at(目的地)和departs_from(出发地)两种方式到达机场。代理会列出所有路径、查看 ontology 声明哪些等价,再按问题选择对应角色。 - 指标从哪里分组:指标从错误的概念分组时,Dosi 会给出正确的根路径,例如用
Delay.flight.arrives_at.code代替Flight.arrives_at.code。
OntologyAnalyst 还能返回明细记录,例如“列出飞往 JFK 的航班上未关闭的 P1 告警”,这是 AskMetrics 做不到的。
前置条件¶
- 数据源在
subject/semantic_models/<datasource>/下有可执行的语义模型(与 AskMetrics 相同)。唯一的例外是把模型内联在映射文档里的写法(见下文),这时不需要模型文件。 -
ontology 统一放在项目级的
subject/ontology/目录(与semantic_models/同级),并配上 Apache Ossie 映射文档(mapping document),把每个 ontology 绑定到语义模型:subject/ ├── semantic_models/ │ ├── aviation/ │ │ └── aviation.yaml # OSI 语义模型 │ └── airline_ops/ │ └── crews.yaml └── ontology/ ├── aviation.ontology.yaml # Ossie ontology ├── aviation-aviation.mapping.yaml # → ../semantic_models/aviation/aviation.yaml └── aviation-crews.mapping.yaml # → ../semantic_models/airline_ops/crews.yamlontology 不属于某一个数据源:一个 ontology 可以有多份映射文档,每份映射到一个语义模型文件,这些模型可以分属不同的数据源。因此
subject/ontology/只有一层,子目录不会被读取。每份映射文档用
semantic_model_ref.iri(相对映射文档所在目录的路径)指定模型。这个路径落在哪个数据源的模型目录里,就由哪个数据源加载这份映射,其他数据源忽略它。ontology_ref通过iri(相对映射文档所在目录)找到 ontology;没写iri时,按名字在目录里的 ontology 文档中查找。<ontology>.ontology.yaml/<ontology>-<model>.mapping.yaml的文件名只是方便阅读的约定。Datus 按内容区分文件、沿引用查找,从不依赖文件名。映射文档也可以把模型内联在
semantic_model_ref.document下。这样的映射是一份自包含的文档,不属于任何特定数据源:每个数据源都会加载它,并在该数据源的连接上执行。没有本地模型
iri的映射、iri指向本数据源目录里不存在的模型文件、两份映射指向同一个模型、没有任何映射引用的 ontology,以及目录里的其他文件,都会被报告出来,不会自动猜测。 -
安装的
dosi-engine需要提供 ontology API(Engine.describe/Engine.paths),并能加载映射文档(Engine支持mapping_path参数,osi-engine#303 及之后)。
只有 OntologyAnalyst 会加载 ontology,AskMetrics 和其他代理继续使用原模型。
缺少任何前置条件时,OntologyAnalyst 会在第一轮拒绝执行,说明缺了什么,并引导使用 ask_metrics;它不会在没有提示的情况下退化成普通指标问答。只有 subject/ontology/ 里有映射文档绑定了当前数据源的模型(或内联了模型)时,主代理才会提供 ontology_analyst 作为可委派的子代理。
快速上手:航空示例¶
本节用 Datus-agent 仓库自带的航空示例(Dosi aviation 场景)完整体验一遍。数据很小,每个答案都可以手工核对:
| 文件 | 内容 |
|---|---|
tests/data/semantic_models/aviation/aviation.yaml |
OSI 语义模型 aviation:9 张表,以及延误、告警、载客等指标 |
tests/data/ontology/aviation.ontology.yaml |
Ossie ontology:Flight、Airport、FlightAlert、Delay 等概念,以及它们之间的角色和值域 |
tests/data/ontology/aviation-aviation.mapping.yaml |
映射文档,把上面的 ontology 绑定到 semantic_models/aviation/aviation.yaml |
tests/data/semantic_models/aviation/seed.sql |
种子数据:13 个航班、延误和告警记录 |
1. 准备项目目录和数据库¶
Datus 以启动时的当前目录作为项目根目录,subject/ 也放在这个目录下。把下面的 DATUS_REPO 换成你本地 Datus-agent 仓库的路径:
export DATUS_REPO=~/src/Datus-agent
mkdir -p ~/ontology-demo && cd ~/ontology-demo
# 语义模型、ontology 和映射文档,按约定的目录放置
mkdir -p subject/semantic_models/aviation subject/ontology
cp $DATUS_REPO/tests/data/semantic_models/aviation/aviation.yaml subject/semantic_models/aviation/
cp $DATUS_REPO/tests/data/ontology/aviation{.ontology,-aviation.mapping}.yaml subject/ontology/
# 用种子数据生成 DuckDB 数据库(duckdb 随 Datus 一起安装)
python -c "import duckdb, pathlib; duckdb.connect('aviation.duckdb').execute(pathlib.Path('$DATUS_REPO/tests/data/semantic_models/aviation/seed.sql').read_text())"
完成后的目录结构:
~/ontology-demo/
├── aviation.duckdb
└── subject/
├── semantic_models/aviation/aviation.yaml
└── ontology/
├── aviation.ontology.yaml
└── aviation-aviation.mapping.yaml
aviation-aviation.mapping.yaml 通过 semantic_model_ref.iri: ../semantic_models/aviation/aviation.yaml 找到模型,因此归属 aviation 数据源;通过 ontology_ref.iri: ./aviation.ontology.yaml 找到 ontology。
单文件写法
映射文档也可以把 ontology 和模型都内联在 document: 下。这时不需要模型目录,每个数据源都会加载它。tests/data/ontology_inline/ 是一个小例子,自带 seed.sql。
2. 注册数据源¶
在 agent.yml(默认位于 ~/.datus/conf/agent.yml)中添加数据源。数据源名必须与目录名 aviation 一致:
3. 启动并切换到 OntologyAnalyst¶
也可以留在主对话中,主代理检测到该数据源有 ontology 后会自动委派;或者在单个问题末尾加 @Agent ontology_analyst。
4. 提问并核对答案¶
以下问题都按业务人员的口吻提出,不包含指标名或字段名。“考察点”一列说明 ontology 在其中起什么作用。
| 问题 | 预期答案 | 考察点 |
|---|---|---|
| 飞往 JFK 的航班里,有哪些未关闭的 P1 告警?给出告警号、类型、航班号和机场名。 | A007(WEATHER,SK202)和 A011(AIR_TRAFFIC,UA402),机场为 John F. Kennedy International Airport | 明细查询;两跳 + 到达角色;“未关闭”需要映射到值域里的 OPEN |
| 现在还有多少未解决的告警?按优先级分。 | P0 1 条、P1 3 条、P2 4 条,共 8 条 | “未解决”不是合法值,需要映射到 OPEN |
| 哪个目的地机场的延误航班最多?列出各目的地机场的延误航班数。 | LAX 3、JFK 2、SFO 2、SEA 1 | 指标要以 Delay 为根分组,经由 arrives_at 角色 |
| 按出发机场统计,哪个机场的延误航班最多?列出各出发机场的延误航班数。 | SFO 6、JFK 1、LAS 1 | 同一个指标换成 departs_from 角色。如果答案和上一题相同,说明角色混淆了 |
| 只看计划在 2026-08-14 和 2026-08-15 这两天起飞的航班,各目的地机场分别有多少延误航班? | JFK 1、LAX 1、SFO 1,不应出现 SEA | 时间轴来自 Delay.flight.scheduled_departure |
| 哪些延误的航班同时有告警?列出航班号、延误分钟数和告警数。 | SK202 95/2、SK103 60/2、SK201 55/1、SK102 42/2、UA303 30/1、UA401 29/1、UA302 25/1 | 两个子集合(延误、告警)挂在同一航班下,必须按航班分组,不能做明细 join(否则分钟数会被放大) |
每个问题建议开一个新会话(/clear),避免上一题的发现影响下一题。
5. 观察代理的过程¶
工具调用会显示在对话中,重点看下面几项:
describe_concept("FlightAlert")返回的FlightAlert.status带有domain: [OPEN, RESOLVED];find_concept_paths("Flight", "Airport")返回 4 条路径和 2 组等价关系,说明“机场”实际只有两种含义;- 如果代理先用了
'unresolved'这样的字面量,会收到value_not_in_domain和允许值列表,随后改用OPEN重试; - 如果代理用
Flight.arrives_at.code对delayed_flights分组,会收到invalid_relationship_path,candidates中给出Delay.flight.arrives_at.code,代理随后按它重试。
6. 常见问题¶
| 提示信息 | 原因和处理 |
|---|---|
ontology_analyst is unavailable: no ontology for datasource 'aviation' |
subject/ontology/ 里没有映射文档指向 subject/semantic_models/aviation/ 下的模型;检查各映射的 semantic_model_ref.iri、模型目录名与数据源名是否一致,以及是否在项目根目录启动 |
... could not be bound to a semantic model |
出现在 no ontology for datasource 之后;subject/ontology/ 是全项目共享的,列出的文件可能属于任意数据源。映射文档没有本地 semantic_model_ref.iri、iri 指向本数据源目录里不存在的模型文件或任何 subject/semantic_models/<datasource>/ 之外的路径、两份映射指向同一个模型、某个 ontology 没有任何映射引用,或者该文件既不是 ontology 也不是映射文档(语义模型应放在 subject/semantic_models/<datasource>/ 下);修正映射、为 ontology 补上映射,或把该文件移出目录 |
the installed dosi-engine binding cannot describe ontologies |
安装的 dosi-engine 版本过旧,升级到提供 Engine.describe / Engine.paths 的版本 |
... is an Ossie mapping document, and the installed dosi-engine cannot load one |
安装的 dosi-engine 不支持映射文档,请升级 |
主代理不委派给 ontology_analyst |
只有映射文档绑定了当前数据源的模型时才会提供;可以用 /agent ontology_analyst 或 @Agent ontology_analyst 显式指定 |
使用方式¶
直接在主对话中提问。数据源有 ontology 时,主代理会自动委派合适的问题:
显式路由单个问题:
连续提问:
Web/API 调用方使用 subagent_id: "ontology_analyst"。
工作方式¶
graph LR
A[问题] --> B[从目录选主语概念]
B --> C[describe_concept]
C --> D{有多种方式到达同一概念?}
D -->|是| E[find_concept_paths 按角色选择]
D -->|否| F{聚合还是明细?}
E --> F
F -->|聚合| G[query_metrics 以指标所在概念为根]
F -->|明细| H[select_records]
G --> I[Markdown 回答]
H --> I
FlightAlert.flight.arrives_at.name 这样的概念路径可以用在任何接受字段的地方:query_metrics 的维度和过滤条件,以及 select_records 的字段和过滤条件。Dosi 编译出的 SQL 与 dataset 写法相同。value_not_in_domain、invalid_relationship_path、unknown_concept 等拒绝会作为结构化工具结果返回,附带 candidates、hint、detail,代理据此重试。
默认工具¶
| 工具 | 用途 |
|---|---|
semantic_tools.describe_concept |
概念目录,或单个概念的属性、值域、链接、可分组指标和时间轴 |
semantic_tools.find_concept_paths |
两个概念之间的所有命名路径(最多 3 跳),附角色和等价关系 |
semantic_tools.select_records |
以某个概念为根的明细查询(默认 200 行,完整结果会缓存) |
semantic_tools.list_metrics |
列出可执行指标 |
semantic_tools.get_metric |
描述单个指标 |
semantic_tools.query_metrics |
查询指标值 |
semantic_tools.attribution_analyze |
跨维度解释指标变化 |
context_search_tools.search_metrics |
按措辞查找候选指标 |
配置¶
agent:
agentic_nodes:
ontology_analyst:
model: claude
max_turns: 30
# 启动时内联到提示词的概念数上限,超过后只列概念名
concept_catalog_prompt_limit: 60
自定义 OntologyAnalyst 代理¶
agent:
agentic_nodes:
flight_ops:
type: ontology_analyst
agent_description: "回答航班运营问题。"
tools: "semantic_tools.describe_concept,semantic_tools.find_concept_paths,semantic_tools.select_records,semantic_tools.query_metrics"
自定义代理会先查找自己的提示词模板(flight_ops_system_1.0.j2),找不到时回退到内置的 ontology_analyst_system 模板。
限制¶
以下限制来自 Dosi 的 ontology 支持:
- 路径搜索最多 3 跳。
- 存放在其他概念表上的概念(例如存放在机场表上的州)不能作为查询根。
- 值域只对
=和IN字面量做检查,不检查范围。 - n 元关系和行级
derived_by不会下沉到模型中。 - ontology 没有映射的模型字段(例如距离分段)无法通过概念路径访问,但仍可以使用
dataset.field写法。
AskMetrics 还是 OntologyAnalyst¶
| 场景 | 使用 |
|---|---|
| 数据源没有 ontology | ask_metrics |
| 有 ontology 的数据源上的 KPI 和趋势 | ontology_analyst |
| 答案取决于关系角色(出发地还是目的地) | ontology_analyst |
| 列出明细记录 | ontology_analyst |
| 在原始表上生成或修复 SQL | gen_sql |