Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/.vitepress/config/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { LocaleSpecificConfig, DefaultTheme } from 'vitepress'

const nav: DefaultTheme.NavItem[] = [
{ text: 'Guide', link: '/en/getting-started/quickstart', activeMatch: '/en/(getting-started|concepts|guides|architecture)/' },
{ text: 'Best Practices', link: '/en/best-practices/', activeMatch: '/en/best-practices/' },
{ text: 'Reference', link: '/en/reference/cli', activeMatch: '/en/reference/' },
{ text: 'Specs', link: '/en/spec/plan-schema-v1', activeMatch: '/en/spec/' },
{
Expand Down Expand Up @@ -51,6 +52,15 @@ const sidebar: DefaultTheme.SidebarItem[] = [
{ text: 'Agent Integration', link: '/en/guides/agent-integration' },
],
},
{
text: 'Best Practices',
collapsed: true,
items: [
{ text: 'Overview', link: '/en/best-practices/' },
{ text: 'Stable, domain-scoped identity', link: '/en/best-practices/stable-identity' },
{ text: 'Read the object graph with an AI agent', link: '/en/best-practices/agent-reads-the-object-graph' },
],
},
{
text: 'Architecture',
collapsed: true,
Expand Down
10 changes: 10 additions & 0 deletions docs/.vitepress/config/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { LocaleSpecificConfig, DefaultTheme } from 'vitepress'

const nav: DefaultTheme.NavItem[] = [
{ text: '指南', link: '/zh/getting-started/quickstart', activeMatch: '/zh/(getting-started|concepts|guides|architecture)/' },
{ text: '最佳实践', link: '/zh/best-practices/', activeMatch: '/zh/best-practices/' },
{ text: '参考', link: '/zh/reference/cli', activeMatch: '/zh/reference/' },
{ text: '规范', link: '/zh/spec/plan-schema-v1', activeMatch: '/zh/spec/' },
{
Expand Down Expand Up @@ -51,6 +52,15 @@ const sidebar: DefaultTheme.SidebarItem[] = [
{ text: 'Agent 集成', link: '/zh/guides/agent-integration' },
],
},
{
text: '最佳实践',
collapsed: true,
items: [
{ text: '概览', link: '/zh/best-practices/' },
{ text: '稳定、按域限定的身份', link: '/zh/best-practices/stable-identity' },
{ text: '用 AI Agent 读取对象图', link: '/zh/best-practices/agent-reads-the-object-graph' },
],
},
{
text: '架构',
collapsed: true,
Expand Down
71 changes: 71 additions & 0 deletions docs/en/best-practices/agent-reads-the-object-graph.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Read the object graph with an AI agent

Point an AI agent at the object graph and let the **model scope the reads** — discover entity sets, walk topology, and pull telemetry through model-generated query plans. Don't make the agent hand-write SPL, PromQL, or log DSL; the model already carries the structure it needs.

<video controls preload="metadata" style="width: 100%" src="https://unifiedmodel-assets.oss-accelerate.aliyuncs.com/QuickStart.mp4"></video>

*A 90-second run on the `quickstart-multidomain` pack: an agent installs the `umodel-query` skill, finds the `demo` workspace, reads `devops.service`, and pulls metrics and logs — without a hand-written query.*

## Why

The object graph encodes what an agent would otherwise have to guess: which entity sets exist, how they relate across domains, which datasets hang off an entity, and how to turn an entity into a concrete telemetry query. When the agent reads *through* the model — `.umodel` → `.entity` → `.topo` → `get_metrics` / `get_logs` — the model binds the entity id and storage backend into the returned query plan. Hand-written PromQL or index names drift from the model the moment topology or storage config changes; model-scoped reads do not.

## The flow

The worked example runs on [`quickstart-multidomain`](../../../examples/quickstart-multidomain) (the default `make quickstart` pack — five domains, 35 object types). Each read is one `umctl` command; the same SPL runs over MCP via `query_spl_execute`.

**1. Discover the model.** The `domain` + `name` pairs are the arguments every other read takes.

```bash
umctl query run demo ".umodel with(kind='entity_set') | project domain, name" -o json
```

**2. Read and search entities.** `devops.service` has four services; `checkout-service` is `degraded` (id `10000000000000000000000000000101`, reused below).

```bash
umctl query run demo ".entity with(domain='devops', name='devops.service', query='checkout') | project __entity_id__, display_name, status" -o json
```

**3. Walk cross-domain topology.** Find what the service `runs` on — a k8s workload in another domain. Filter graph-call output with `where`, not `with`.

```bash
umctl query run demo ".topo | graph-call getNeighborNodes('full', 1, [(:\"devops@devops.service\" {__entity_id__:'10000000000000000000000000000101'})]) | where __relation_type__ = 'runs'" -o json
```

**4. Find the datasets on the entity.** Read the dataset `domain` + `name` from the entity itself — don't scan `.umodel` for them.

```bash
umctl query run demo ".entity_set with(domain='devops', name='devops.service', ids=['10000000000000000000000000000101']) | entity-call list_data_set(['metric_set','log_set','event_set'], true)" -o json
```

**5. Pull telemetry as a plan.** `get_metrics` returns a `prometheus_promql` plan with the service id already bound; the caller executes it.

```bash
umctl query run demo ".entity_set with(domain='devops', name='devops.service', ids=['10000000000000000000000000000101']) | entity-call get_metrics('devops','devops.metric.service','request_count', step='30s')" -o json
```

## With the `umodel-query` skill

The [`umodel-query`](../../../skills/umodel-query) skill runs exactly this flow from natural language. Bring up the telemetry-backed stack (`sh examples/quickstart-multidomain/deploy/start.sh`), point the skill at `http://localhost:8080`, and ask:

> "Read checkout-service's request rate, error rate, p95 latency, and recent ERROR logs."

The agent discovers the service, walks to its datasets, and executes the returned metric and log plans — no PromQL or index names typed by hand. [`umodel-rca`](../../../skills/umodel-rca) adds root-cause analysis on top.

## Do / Don't

| Do | Don't |
|---|---|
| Let `get_metrics` / `get_logs` return the query plan (entity id + storage bound). | Hand-write PromQL or an Elasticsearch index name. |
| Read dataset `domain` + `name` from the entity via `list_data_set`. | Scan `.umodel` to guess dataset names. |
| Filter graph-call output with `where __relation_type__ = '…'`. | Expect a `with(...)` clause to filter graph-call output. |
| Resolve topology rows (entity ids) to names with `.entity … with(ids=[…])`. | Treat the raw ids in graph-call rows as display names. |

> In-memory mode (`make quickstart`) serves the model, entity, search, and topology reads; `get_metrics` / `get_logs` return plans with nothing to run them against. Use `deploy/start.sh` for end-to-end execution against seeded Prometheus / Elasticsearch.

## See also

- [Multi-domain quickstart pack](../../../examples/quickstart-multidomain)
- [Query Service](/en/guides/query-service)
- [Agent Integration](/en/guides/agent-integration)
- [UModel Agent Skills](../../../skills/README.md)
17 changes: 17 additions & 0 deletions docs/en/best-practices/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Best Practices

Single-topic practices for modeling, querying, and operating UModel. Each page is one practice: what to do, why it matters, and what to avoid.

> This section is growing — more practices will be added here over time.

## Modeling

- [Use stable, domain-scoped identity](./stable-identity)

## Querying

_More practices coming._

## Agent integration

- [Read the object graph with an AI agent](./agent-reads-the-object-graph)
21 changes: 21 additions & 0 deletions docs/en/best-practices/stable-identity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Use stable, domain-scoped identity

Identify entities by stable, domain-scoped fields — not by display names or volatile attributes.

## Why

An entity's ID is derived from its `primary_key_fields` through the entity set's `id_generator`. If identity depends on a display name or a value that changes (a label, a mutable status), the same real-world object produces a different ID over time — breaking topology edges, deduplication, and historical continuity. Stable identity keeps an entity the *same* entity across observations, documents, tests, and screenshots.

## Do / Don't

| Do | Don't |
|---|---|
| Choose `primary_key_fields` that are immutable and unique within the domain (e.g. a resource ARN, or a `cluster` + `namespace` + `name` tuple). | Use `display_name` or human-facing labels as identity. |
| Keep the entity set `name` stable and domain-scoped (`devops.service`). | Rename an entity set to change its meaning, or reuse one name across domains. |
| Reuse the same IDs across sample data, tests, and docs. | Regenerate IDs per environment. |

## See also

- [Model Authoring](/en/guides/model-authoring)
- [Entity Sets](/en/concepts/entity-sets)
- [Entity And Relation Writes](/en/guides/entity-relation-writes)
71 changes: 71 additions & 0 deletions docs/zh/best-practices/agent-reads-the-object-graph.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# 用 AI Agent 读取对象图

让 AI Agent 指向对象图,由**模型来界定读取范围**——发现实体集、遍历拓扑、并通过模型生成的查询计划拉取遥测。不要让 Agent 手写 SPL、PromQL 或日志 DSL;模型已经携带了它需要的结构。

<video controls preload="metadata" style="width: 100%" src="https://unifiedmodel-assets.oss-accelerate.aliyuncs.com/QuickStart.mp4"></video>

*一段 90 秒的演示,跑在 `quickstart-multidomain` 包上:Agent 安装 `umodel-query` 技能,找到 `demo` workspace,读取 `devops.service`,并拉取指标和日志——全程不手写一条查询。*

## 为什么

对象图把 Agent 否则只能靠猜的东西编码了下来:有哪些实体集、它们如何跨域关联、一个实体挂了哪些数据集、以及如何把实体变成一条具体的遥测查询。当 Agent *经由*模型读取——`.umodel` → `.entity` → `.topo` → `get_metrics` / `get_logs`——模型会把实体 id 和存储后端绑定进返回的查询计划里。手写的 PromQL 或索引名一旦拓扑或存储配置变化就会漂移;模型界定的读取不会。

## 流程

这个示例跑在 [`quickstart-multidomain`](../../../examples/quickstart-multidomain)(默认 `make quickstart` 包——五个域、35 个对象类型)。每步读取就是一条 `umctl` 命令;同样的 SPL 也能经 MCP 的 `query_spl_execute` 运行。

**1. 发现模型。** `domain` + `name` 这一对是其余每步读取的参数。

```bash
umctl query run demo ".umodel with(kind='entity_set') | project domain, name" -o json
```

**2. 读取并搜索实体。** `devops.service` 有四个服务;`checkout-service` 处于 `degraded`(id `10000000000000000000000000000101`,后面复用)。

```bash
umctl query run demo ".entity with(domain='devops', name='devops.service', query='checkout') | project __entity_id__, display_name, status" -o json
```

**3. 遍历跨域拓扑。** 找到该服务 `runs` 在什么上面——另一个域里的一个 k8s workload。用 `where` 过滤 graph-call 输出,不要用 `with`。

```bash
umctl query run demo ".topo | graph-call getNeighborNodes('full', 1, [(:\"devops@devops.service\" {__entity_id__:'10000000000000000000000000000101'})]) | where __relation_type__ = 'runs'" -o json
```

**4. 找到实体上的数据集。** 直接从实体本身读取数据集的 `domain` + `name`——不要去 `.umodel` 里扫。

```bash
umctl query run demo ".entity_set with(domain='devops', name='devops.service', ids=['10000000000000000000000000000101']) | entity-call list_data_set(['metric_set','log_set','event_set'], true)" -o json
```

**5. 以计划的形式拉取遥测。** `get_metrics` 返回一个 `prometheus_promql` 计划,服务 id 已经绑好;由调用方执行。

```bash
umctl query run demo ".entity_set with(domain='devops', name='devops.service', ids=['10000000000000000000000000000101']) | entity-call get_metrics('devops','devops.metric.service','request_count', step='30s')" -o json
```

## 配合 `umodel-query` 技能

[`umodel-query`](../../../skills/umodel-query) 技能用自然语言跑完全相同的流程。拉起带遥测后端的 stack(`sh examples/quickstart-multidomain/deploy/start.sh`),把技能指向 `http://localhost:8080`,然后问:

> “读一下 checkout-service 的请求速率、错误率、p95 延迟,以及最近的 ERROR 日志。”

Agent 会发现该服务、走到它的数据集、并执行返回的指标和日志计划——不用手敲 PromQL 或索引名。[`umodel-rca`](../../../skills/umodel-rca) 在此之上再加根因分析。

## 该做 / 不该做

| 该做 | 不该做 |
|---|---|
| 让 `get_metrics` / `get_logs` 返回查询计划(实体 id + 存储已绑定)。 | 手写 PromQL 或 Elasticsearch 索引名。 |
| 通过 `list_data_set` 从实体读取数据集的 `domain` + `name`。 | 去 `.umodel` 里扫来猜数据集名。 |
| 用 `where __relation_type__ = '…'` 过滤 graph-call 输出。 | 指望 `with(...)` 子句能过滤 graph-call 输出。 |
| 用 `.entity … with(ids=[…])` 把拓扑行(实体 id)解析成名称。 | 把 graph-call 行里的裸 id 当作展示名。 |

> 内存模式(`make quickstart`)支持模型、实体、搜索和拓扑读取;`get_metrics` / `get_logs` 返回的计划没有可执行的后端。需要端到端执行时,用 `deploy/start.sh` 跑起 seeded Prometheus / Elasticsearch。

## 相关文档

- [多域 quickstart 包](../../../examples/quickstart-multidomain)
- [Query Service](/zh/guides/query-service)
- [Agent 集成](/zh/guides/agent-integration)
- [UModel Agent 技能](../../../skills/README.zh-CN.md)
17 changes: 17 additions & 0 deletions docs/zh/best-practices/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# 最佳实践

围绕 UModel 建模、查询与运维的单主题实践。每一页就是一条实践:该做什么、为什么、以及要避免什么。

> 本章节会持续补充——更多实践将陆续加入。

## 建模

- [使用稳定、按域限定的身份](./stable-identity)

## 查询

_更多实践陆续加入。_

## Agent 集成

- [用 AI Agent 读取对象图](./agent-reads-the-object-graph)
21 changes: 21 additions & 0 deletions docs/zh/best-practices/stable-identity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 使用稳定、按域限定的身份

用稳定的、按 domain 限定的字段来标识实体——不要用显示名或易变属性。

## 为什么

实体 ID 由其 `primary_key_fields` 经 entity set 的 `id_generator` 生成。如果身份依赖显示名或会变的值(标签、可变状态),同一个现实对象在不同时间会算出不同 ID——破坏拓扑边、去重和历史连续性。稳定身份让一个实体在多次观测、文档、测试、截图中始终是*同一个*实体。

## 该做 / 不该做

| 该做 | 不该做 |
|---|---|
| 选域内不可变且唯一的 `primary_key_fields`(如资源 ARN,或 `cluster` + `namespace` + `name` 组合)。 | 用 `display_name` 或面向人的标签作身份。 |
| 保持 entity set 的 `name` 稳定、按域限定(`devops.service`)。 | 改名来改变含义,或跨域复用同一个名字。 |
| 在样例数据、测试、文档间复用同一批 ID。 | 每个环境重新生成 ID。 |

## 参见

- [模型编写](/zh/guides/model-authoring)
- [EntitySet 实体集](/zh/concepts/entity-sets)
- [实体与关系写入](/zh/guides/entity-relation-writes)
Loading