> ## Documentation Index
> Fetch the complete documentation index at: https://avala.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 序列元数据模式

> 每个序列上的结果标签、技术有效性的报告方式，以及如何读取、写入和导出两者

每个序列都带有两类彼此独立的元数据：

* **技术有效性。** 这段录制是否可用？Avala 根据验收标准和质量目标报告它。参见下文的“技术有效性”一节。
* **行为结果。** 人或机器人做了什么？任务是否成功？Avala 将其存储为该序列的**结果标签**。

一个序列可以在技术上有效，同时展示一次失败。失败的序列往往最有用。两个维度保持分开，因此筛选干净数据时不会丢掉有用的失败，筛选成功序列时也不会掩盖损坏的传感器。

## 结果标签

每个序列最多有一个当前结果标签。每次写入都会记录一个新版本。较早的版本仍可在标签历史中读取。

### 示例

下面是一次带有滑落和恢复的人类示范的标签，即 `GET .../sequences/<sequence-uid>/outcome/` 返回的内容：

```json theme={null}
{
  "uid": "<outcome-uid>",
  "sequence_uid": "<sequence-uid>",
  "version": 2,
  "is_current": true,
  "outcome": "mistake_and_recovery",
  "progress": 1.0,
  "quality": 4,
  "speed": 3,
  "subtasks": [
    { "label": "Load the plate into the lower rack", "start_ts": 0.0, "end_ts": 19.8, "outcome": "mistake_and_recovery" },
    { "label": "Pull out the lower rack", "start_ts": 0.0, "end_ts": 6.2, "outcome": "expert_success" },
    { "label": "Place the wet plate in a free slot", "start_ts": 6.2, "end_ts": 19.8, "outcome": "mistake_and_recovery" }
  ],
  "hand_actions": {
    "left": [
      { "start_ts": 0.0, "end_ts": 6.2, "action": "pulling the lower rack out by its front edge", "object": "dishwasher rack", "verb": "pull", "contact": true },
      { "start_ts": 6.2, "end_ts": 19.8, "action": "holding the rack steady", "object": "dishwasher rack", "verb": "hold", "contact": true }
    ],
    "right": [
      { "start_ts": 6.2, "end_ts": 9.1, "action": "carrying the wet plate to the rack", "object": "plate", "verb": "carry", "contact": true },
      { "start_ts": 9.1, "end_ts": 10.4, "action": "the plate slips out of the grasp", "object": "plate", "verb": null, "contact": false },
      { "start_ts": 10.4, "end_ts": 14.7, "action": "regrasping the plate by its rim", "object": "plate", "verb": "grasp", "contact": true },
      { "start_ts": 14.7, "end_ts": 19.8, "action": "placing the plate in a free slot", "object": "plate", "verb": "place", "contact": true }
    ]
  },
  "mistake_type": "slip_during_place",
  "recovery_type": "regrasp",
  "failure_stage": "place",
  "autonomy_level": "human_demonstration",
  "model_version": "",
  "evaluation_membership": "train",
  "leakage_groups": {
    "location": "loc_8f2a",
    "object": "obj_set_31",
    "mechanism": "dishwasher_pull_rack_v2",
    "operator": "op_5c1e",
    "environment_family": "kitchen_galley"
  },
  "source": "human",
  "source_metadata": {},
  "labeled_by": "<user-uid>",
  "confidence": null,
  "created_at": "2026-09-30T17:42:11.204000Z",
  "updated_at": "2026-09-30T17:42:11.204000Z"
}
```

这些值用于说明标签的结构。它们不描述真实的交付。

### 字段

所有时间戳都是距序列开始的秒数。

| 字段 | 类型 | 含义 |
| - | - | - |
| `uid` | string | 此标签版本的 ID。只读。 |
| `sequence_uid` | string | 标签所属的序列。只读。 |
| `version` | integer | 标签版本，从 1 开始。每次写入加一。只读。 |
| `is_current` | boolean | 当前版本为 `true`。只读。 |
| `outcome` | string | 行为结果。必填。参见下文的“结果值”。 |
| `progress` | number or null | 任务进度，从 0 到 1。 |
| `quality` | integer or null | 执行质量，从 1 到 5。 |
| `speed` | integer or null | 执行速度，从 1 到 5。 |
| `subtasks` | list | 带标签的时间范围。参见下文的“子任务”。 |
| `hand_actions` | object | 按手划分的动作流：`left` 和 `right`。参见下文的“手部动作”。 |
| `mistake_type` | string | 您为错误类型设定的标记。最多 128 个字符。未设置时为空。 |
| `recovery_type` | string | 您为恢复类型设定的标记。最多 128 个字符。未设置时为空。 |
| `failure_stage` | string | 您为失败发生的阶段设定的标记。最多 128 个字符。未设置时为空。 |
| `autonomy_level` | string | 由谁控制。参见下文的“自主级别”。未设置时为空。 |
| `model_version` | string | 驱动机器人的策略或模型版本。最多 255 个字符。人类示范时为空。 |
| `evaluation_membership` | string | `train`、`held_out_eval` 或 `none`。未设置时为空。参见下文的“评估成员资格”。 |
| `leakage_groups` | object | 用于保持数据划分干净的组 ID。参见下文的“泄漏组”。 |
| `source` | string | 标签的写入方：`human`、`model` 或 `imported`。默认为 `human`。 |
| `source_metadata` | object | 导入来源信息。参见下文的“来源元数据”。 |
| `labeled_by` | string or null | 写入该版本的用户的 UID。仅返回给可以编辑该数据集的调用方。只读。 |
| `confidence` | number or null | 模型置信度，从 0 到 1。仅当 `source` 为 `model` 时接受。 |
| `created_at`, `updated_at` | string | 该版本的时间戳。只读。 |

`mistake_type`、`recovery_type` 和 `failure_stage` 是自由文本。请使用计划的失败分类体系中的标记，以便序列能清晰地分组。

### 结果值

| 结果 | 含义 |
| - | - |
| `expert_success` | 任务完成。执行流畅，达到专家速度。 |
| `slow_success` | 任务完成。执行正确，明显慢于专家速度。 |
| `partial_success` | 任务的一部分完成。未达到最终目标状态。 |
| `mistake_and_recovery` | 出现错误，例如滑落、抓空或放错位置。执行者在没有外部帮助的情况下恢复并完成任务。 |
| `intervention` | 有人介入纠正或完成任务。 |
| `unsafe` | 序列中包含不安全的动作或状态。 |
| `aborted` | 尝试在任务完成前停止，且没有恢复。 |
| `failure` | 尝试结束，任务未达成。 |
| `novel_strategy` | 任务以一种有效但不同于常用策略的方式完成。 |
| `inefficient_strategy` | 任务完成，但有可避免的多余动作、重复尝试或绕路。 |

每个子任务可以带有取自同一列表的自身结果。

### 自主级别

| 值 | 含义 |
| - | - |
| `human_demonstration` | 由人执行任务。 |
| `teleoperation` | 由人远程操控机器人。 |
| `autonomous` | 机器人按其策略运行。 |
| `shared_autonomy` | 策略与人共同控制。 |
| `intervention` | 在序列进行中，人从策略手中接管控制。 |

### 子任务

每个子任务的形式为 `{label, start_ts, end_ts, outcome}`。

| 字段 | 规则 |
| - | - |
| `label` | 子任务的自然语言描述。必填。1 到 500 个字符。 |
| `start_ts`, `end_ts` | 有限数值，大于或等于 0。`end_ts` 不小于 `start_ts`。 |
| `outcome` | 一个结果值，或 `null`。 |

一个标签最多包含 256 个子任务。列表必须按 `start_ts` 排序。任意两个子任务要么互不相交，要么一个完全包含另一个，例如一个阶段及其步骤。首尾相接视为不相交。部分重叠会被拒绝。当两个子任务同时开始时，先列出外层的那个。

### 手部动作

`hand_actions` 包含两个独立的流：`left` 和 `right`。每一项描述一只手在一个时间范围内所做的事。

| 字段 | 规则 |
| - | - |
| `start_ts`, `end_ts` | 有限数值，大于或等于 0。`end_ts` 不小于 `start_ts`。 |
| `action` | 动作的自然语言描述。必填。1 到 500 个字符。 |
| `object` | 手所操作的物体。可选。最多 255 个字符。 |
| `verb` | 描述该动作的简短动词。可选。最多 64 个字符。 |
| `contact` | 手接触物体时为 `true`，未接触时为 `false`，未知时为 `null`。只接受 JSON 布尔值。 |

* 在同一只手内，各项按 `start_ts` 排序，且不得重叠。一项在上一项结束时或之后开始。首尾相接没有问题。
* 两只手之间可以自由重叠。
* 每只手最多 3,600 项，即按每秒一个标签计算的一小时。
* `left` 和 `right` 是仅有的两个键。两者都可以省略。省略的手存储为空列表。
* 空的 `object` 或 `verb` 存储为 `null`。每个存储的项都包含全部六个字段。

### 序列时长

当 Avala 知道序列的时长时，每个子任务的 `end_ts` 和每个手部动作的 `end_ts` 都不得超过时长加 1 秒。多出的这一秒是为按每秒一个写入的标签留出的余量。当序列恰好有一个已提取且记录了长度的 MCAP 文件时，Avala 知道其时长。对于其他序列，会跳过此项检查。

### 评估成员资格

`evaluation_membership` 把序列放入一个划分：

| 值 | 含义 |
| - | - |
| `train` | 该序列可用于训练。 |
| `held_out_eval` | 该序列属于一个留出评估集。请将其排除在训练之外。 |
| `none` | 该序列不属于任何一个划分。 |

Avala 为每个评估组中的序列设置 `held_out_eval`。这些序列从不出现在训练发布版本中。参见[评估](/docs/zh/fleet-learning/evaluation.md)。

### 泄漏组

泄漏组防止近似重复的数据同时落在训练集和评估集两侧。`leakage_groups` 接受以下键，每个键对应一个最多 255 个字符的组 ID：

| 键 | 归为一组的序列共享 |
| - | - |
| `location` | 同一个住所、场地或房间。 |
| `object` | 同一套实物物体。 |
| `mechanism` | 同一台电器或同一个机构单元。 |
| `operator` | 同一名采集员或遥操作员。 |
| `environment_family` | 同类环境和布局，例如狭长型厨房。 |

其他任何键都会被拒绝。每个键都是可选的。

Avala 构建评估组时，确保评估组中的任何组值都不会出现在任何训练发布版本中。您可以用同样的方式划分自己的数据：按组留出，绝不按随机序列留出。Avala 的组 ID 是假名化的，因此您可以分组和划分，而无需知道涉及的人员或地点。

### 来源元数据

`source_metadata` 记录导入的标签来自哪里，例如 `{"importer": "...", "run_id": "...", "task": "...", "log_path": "..."}`。

* 它是一个扁平对象，最多 20 个键。
* 每个键为 1 到 64 个字符。
* 每个值是最多 512 个字符的字符串、有限数值或布尔值。
* 嵌套对象、列表和 `null` 值会被拒绝。

### 写入方式

* 一次写入会整体替换当前标签，并记录一个新版本。您省略的任何可选字段都存储为未设置，不会从上一个版本复制。
* 写入需要数据集的编辑权限。读取所需的权限与读取该序列相同，包括公开数据集。
* 除非 `source` 为 `model`，否则 `confidence` 会被拒绝。
* 读取没有标签的序列会返回 `404`。

## 技术有效性

技术有效性不属于结果标签。Avala 根据验收引擎和质量目标报告它，它没有自己的 JSON 模式。

**验收标准。** 采集活动中的每次采集都会得到一个机器判定：`accept`、`quarantine` 或 `reject`。判定依据以下标准：

| 标准 | 检查内容 |
| - | - |
| `capture_health` | MCAP 文件通过校验，且采集活动要求的通道都存在。 |
| `frame_rate` | 摄像头帧率在整个片段中保持稳定，没有持续的崩溃。 |
| `completeness` | 时长接近采集活动要求的时长。 |
| `framing` | 在检测器观察的时间里，手可见的时间足够长。 |
| `shake` | 在片段的大部分时间里，摄像头没有抖动。 |

每项标准报告 `pass`、`fail`、`insufficient_evidence` 或 `skipped`。未通过的标准会记录一个原因代码，例如 `acceptance_frame_rate_drop` 或 `hands_not_visible`。随后由审核员确认或推翻该判定。原因代码列在[验收小时](/docs/zh/fleet-learning/accepted-hour.md)中。

使用 MCP 工具读取验收结果：`get_result_acceptance` 用于单次采集，`get_campaign_acceptance_summary` 用于一个采集活动。`list_capture_submissions` 包含每次采集的机器验收摘要。

**质量目标。** 项目可以为质量指标设定阈值，例如验收率、拒收率和问题率。当某个指标越过阈值时，Avala 会发出警报。使用 MCP 工具 `list_quality_targets` 读取它们。

不属于机器标准的计划检查，例如标定、同意和时间戳同步，会作为拒收原因在[覆盖报告](/docs/zh/fleet-learning/coverage.md)中报告。参见[验收小时](/docs/zh/fleet-learning/accepted-hour.md)。

## 读取和写入结果标签

REST 端点位于 `https://api.avala.ai/api/v1` 之下：

| 方法和路径 | 作用 |
| - | - |
| `GET /datasets/{owner}/{slug}/sequences/{sequence_uid}/outcome/` | 一个序列的当前标签。未标注时返回 `404`。 |
| `PUT /datasets/{owner}/{slug}/sequences/{sequence_uid}/outcome/` | 记录一个新的当前标签。 |
| `GET /datasets/{owner}/{slug}/sequences/{sequence_uid}/outcome/history/` | 标签的每个版本，最新的在前。 |
| `GET /datasets/{owner}/{slug}/sequence-outcomes/` | 数据集中的当前标签，分页返回。 |

数据集列表接受筛选条件 `outcome`、`evaluation_membership`、`source` 和 `autonomy_level`。每个条件接受一个值或逗号分隔的列表，序列匹配列表中的任意一个值即可。`model_version` 精确匹配一个版本。未知的筛选值返回 `400`。

序列列表 `GET /datasets/{owner}/{slug}/sequences/` 以同样的形式接受 `outcome` 和 `evaluation_membership`，返回当前标签匹配的序列。

### 写入标签

```bash cURL theme={null}
curl -X PUT \
  "https://api.avala.ai/api/v1/datasets/your-org/kitchen-rollouts/sequences/<sequence-uid>/outcome/" \
  -H "X-Avala-Api-Key: $AVALA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "outcome": "failure",
    "progress": 0.4,
    "subtasks": [
      { "label": "Place the plate in the lower rack", "start_ts": 0.0, "end_ts": 21.5, "outcome": "failure" }
    ],
    "mistake_type": "slip_during_place",
    "failure_stage": "place",
    "autonomy_level": "autonomous",
    "model_version": "your-policy@2026-09-20",
    "evaluation_membership": "held_out_eval",
    "source": "imported",
    "source_metadata": { "importer": "your-eval-harness", "run_id": "r42", "task": "load_dishwasher" }
  }'
```

```python Python theme={null}
from avala import Client

client = Client()  # reads AVALA_API_KEY

label = client.sequence_outcomes.set(
    "your-org",
    "kitchen-rollouts",
    "<sequence-uid>",
    outcome="failure",
    progress=0.4,
    subtasks=[
        {"label": "Place the plate in the lower rack", "start_ts": 0.0, "end_ts": 21.5, "outcome": "failure"},
    ],
    mistake_type="slip_during_place",
    failure_stage="place",
    autonomy_level="autonomous",
    model_version="your-policy@2026-09-20",
    evaluation_membership="held_out_eval",
    source="imported",
    source_metadata={"importer": "your-eval-harness", "run_id": "r42", "task": "load_dishwasher"},
)
print(label.version, label.outcome)
```

```typescript TypeScript theme={null}
import Avala from "@avala-ai/sdk";

const avala = new Avala(); // reads AVALA_API_KEY

const label = await avala.sequenceOutcomes.set("your-org", "kitchen-rollouts", "<sequence-uid>", {
  outcome: "failure",
  progress: 0.4,
  subtasks: [{ label: "Place the plate in the lower rack", startTs: 0.0, endTs: 21.5, outcome: "failure" }],
  mistakeType: "slip_during_place",
  failureStage: "place",
  autonomyLevel: "autonomous",
  modelVersion: "your-policy@2026-09-20",
  evaluationMembership: "held_out_eval",
  source: "imported",
  sourceMetadata: { importer: "your-eval-harness", run_id: "r42", task: "load_dishwasher" },
});
console.log(label.version, label.outcome);
```

```bash CLI theme={null}
avala sequence-outcomes set your-org kitchen-rollouts <sequence-uid> \
  --outcome failure \
  --progress 0.4 \
  --subtasks-file subtasks.json \
  --mistake-type slip_during_place \
  --failure-stage place \
  --autonomy-level autonomous \
  --model-version your-policy@2026-09-20 \
  --evaluation-membership held_out_eval \
  --source imported \
  --source-metadata '{"importer": "your-eval-harness", "run_id": "r42", "task": "load_dishwasher"}'
```

TypeScript SDK 使用 camelCase 选项名，并发送 API 的 snake\_case 字段。`sourceMetadata` 中的键按原样发送。在 CLI 中，`--hand-actions-file` 接受一个内容为 `{"left": [...], "right": [...]}` 的 JSON 文件，`--leakage-group KEY=ID` 可以重复使用。

### 读取标签

```bash cURL theme={null}
# Current label of one sequence
curl "https://api.avala.ai/api/v1/datasets/your-org/kitchen-rollouts/sequences/<sequence-uid>/outcome/" \
  -H "X-Avala-Api-Key: $AVALA_API_KEY"

# Every version, newest first
curl "https://api.avala.ai/api/v1/datasets/your-org/kitchen-rollouts/sequences/<sequence-uid>/outcome/history/" \
  -H "X-Avala-Api-Key: $AVALA_API_KEY"

# Failed or aborted held-out sequences
curl "https://api.avala.ai/api/v1/datasets/your-org/kitchen-rollouts/sequence-outcomes/?outcome=failure,aborted&evaluation_membership=held_out_eval" \
  -H "X-Avala-Api-Key: $AVALA_API_KEY"

# The sequences themselves, filtered by their current label
curl "https://api.avala.ai/api/v1/datasets/your-org/kitchen-rollouts/sequences/?outcome=mistake_and_recovery,intervention" \
  -H "X-Avala-Api-Key: $AVALA_API_KEY"
```

```python Python theme={null}
from avala import Client

client = Client()

label = client.sequence_outcomes.get("your-org", "kitchen-rollouts", "<sequence-uid>")
versions = client.sequence_outcomes.history("your-org", "kitchen-rollouts", "<sequence-uid>")

page = client.sequence_outcomes.list(
    "your-org",
    "kitchen-rollouts",
    outcome=["failure", "aborted"],
    evaluation_membership="held_out_eval",
    model_version="your-policy@2026-09-20",
)
for item in page:
    print(item.sequence_uid, item.outcome, item.progress)
```

```typescript TypeScript theme={null}
import Avala from "@avala-ai/sdk";

const avala = new Avala();

const label = await avala.sequenceOutcomes.get("your-org", "kitchen-rollouts", "<sequence-uid>");
const versions = await avala.sequenceOutcomes.history("your-org", "kitchen-rollouts", "<sequence-uid>");

const page = await avala.sequenceOutcomes.list("your-org", "kitchen-rollouts", {
  outcome: ["failure", "aborted"],
  evaluationMembership: "held_out_eval",
  modelVersion: "your-policy@2026-09-20",
});
for (const item of page.items) {
  console.log(item.sequenceUid, item.outcome, item.progress);
}
```

```bash CLI theme={null}
avala sequence-outcomes get your-org kitchen-rollouts <sequence-uid>
avala sequence-outcomes history your-org kitchen-rollouts <sequence-uid>
avala sequence-outcomes list your-org kitchen-rollouts \
  --outcome failure,aborted \
  --evaluation-membership held_out_eval
```

数据集列表每页最多返回 100 个标签。传入上一页的 `cursor` 即可继续读取。

### 通过 MCP 服务器读取标签

MCP 服务器读取结果标签，但不写入。

* `get_sequence_outcome` 接受 `owner`、`slug` 和 `sequenceUid`，返回当前标签。
* `list_sequence_outcomes` 接受 `owner` 和 `slug`，以及筛选条件 `outcome`、`evaluationMembership`、`source`、`autonomyLevel` 和 `modelVersion`，还有 `limit` 和 `cursor`。

两个工具默认返回简短形式。传入 `detail: "full"` 可包含子任务、手部动作、错误和恢复标记、泄漏组、来源元数据和标注者。连接方法参见[与 AI 一起使用 Avala](/docs/zh/getting-started/mcp.md)。

## 通过 LeRobot 导出结果标签

LeRobot 导出器可以把每个序列的当前结果标签写在数据集旁边。一个 Avala 序列成为一个 LeRobot episode。

```bash CLI theme={null}
avala lerobot export your-org/kitchen-rollouts \
  --repo-id your-hf-user/kitchen-rollouts \
  --output ./lerobot-out \
  --include-outcomes
```

```python Python theme={null}
from avala import Client
from avala.lerobot import export_dataset

export_dataset(
    Client(),
    "your-org",
    "kitchen-rollouts",
    repo_id="your-hf-user/kitchen-rollouts",
    output_dir="./lerobot-out",
    include_outcomes=True,
)
```

标签写入 `meta/avala_sequence_outcomes.jsonl`：每行一个 JSON 对象，每个已保存的 LeRobot episode 对应一行。LeRobot 自身的 episode 表没有存放这些元数据的位置，因此它是一个单独的文件。每行包含以下键：

| 键 | 值 |
| - | - |
| `episode_index` | LeRobot episode 索引。 |
| `avala_sequence_uid` | 该 episode 所来自的 Avala 序列。 |
| `outcome` | 结果值；序列没有标签时为 `null`。 |
| `outcome_version` | 被导出的标签版本。 |
| `progress`, `quality`, `speed` | 与标签中相同。 |
| `subtasks`, `hand_actions` | 与标签中相同。时间戳是距 episode 开始的秒数。 |
| `mistake_type`, `recovery_type`, `failure_stage`, `autonomy_level`, `model_version`, `evaluation_membership` | 与标签中相同，空值以 `null` 代替。 |
| `leakage_groups` | 与标签中相同。 |
| `outcome_source`, `outcome_source_metadata`, `outcome_confidence` | 标签的 `source`、`source_metadata` 和 `confidence`。 |

没有标签的序列会得到一行，其中只有 `episode_index`、`avala_sequence_uid` 和 `"outcome": null`。

## 失败与恢复通道

可恢复的失败作为独立的通道交付：即结果为 `mistake_and_recovery` 或 `intervention` 的序列。每个序列的子任务和手部动作标出失败和恢复发生的位置。

该通道在发布版本中作为单独的切片交付。您可以在其上训练、为其加权或将其排除。您也可以用 `?outcome=mistake_and_recovery,intervention` 自行构建同样的集合。不安全的序列不属于该通道。
