---
title: "把截图变成 Agent 能直接用的类型化 JSON"
description: "用 Clairic 的 extract() 把截图转成类型化 JSON 字段，带置信度，缺失的值返回 null 而不是编造。"
canonical: "https://system-one.dev/zh-CN/use-cases/screenshot-to-json"
language: "zh-CN"
---

# 把截图变成 Agent 能直接用的类型化 JSON

**要点**\n\n1. Clairic 的 `extract()` 最多接收 4 张 PNG、JPEG 或 WebP 图片（base64 内嵌），按你定义的字段逐个返回类型化的值。
2. 每个字段都有 `value`；标量字段还可能带 `confidence` 和 `pNull`，图片里确定不了的值返回 `null`。
3. 把这些字段交给 Jev 或 Clef，就能决定下一步，比如工单分给哪个团队。

想把截图转成 JSON，调用 Clairic 的 `extract()` 就行：图片用 base64 传进去，`state` 留空字符串，再定义好要哪些字段。每个字段返回一个类型化的值，截图里没有的信息返回 `null`。

![报错截图经 Clairic 转成类型化 JSON 字段](TODO-IMG: 封面。左：模拟的 App 报错截图；箭头；右：product / error_time / impact 三行 JSON。设计制作，仅用模拟数据。)

## 为什么 Agent 需要结构化输入

截图对程序来说只是一堆像素，路由逻辑没法直接拿它写 `if`。Agent 真正要的是产品名、时间、时长这类有明确类型的值。

[Clairic 模型页](https://system-one.dev/zh-CN/models/clairic)里的「Agent 前置步骤」说的就是这件事：先把关键值结构化，再让 Agent 根据这些值做路由或判断。

## 定义字段（defineFields）

用 `defineFields` 加字段工厂函数描述你要的东西。本例用到 `textField`、`dateTimeField` 和 `durationField({ unit: 'minutes' })`，每个字段指什么，写在 `instructions` 里。

字段名需符合 `[A-Za-z_][A-Za-z0-9_.-]{0,63}`。

| 字段 | 类型 | 含义 |
|---|---|---|
| `product` | text | 截图里报错的产品名 |
| `error_time` | datetime | 报错发生的日期时间 |
| `impact` | duration（分钟） | 影响持续了多久 |

## 发送图片（ImageInput，state 留空）

`ImageInput` 有两种写法：`{ mediaType, base64 }`，或者 base64 data URL。传了图片，`state` 就可以是 `''`。远程图片 URL 会被拒绝，所以要先读文件再内嵌。

安装 SDK：

```bash
npm install @system-one-ai/core@0.8.0 @system-one-ai/adapter-system-one@0.7.0 @system-one-ai/transport-fetch@0.8.0
```

保存为 `screenshot-to-json.mts`，在 `.env` 里设置 `SYSTEM_ONE_API_KEY`：

```ts
import { readFile } from 'node:fs/promises';
import {
  createSystemOne, defineFields, textField, dateTimeField, durationField,
  type ImageInput,
} from '@system-one-ai/core';
import { systemOneAdapter } from '@system-one-ai/adapter-system-one';
import { createFetchTransport } from '@system-one-ai/transport-fetch';

const apiKey = process.env.SYSTEM_ONE_API_KEY;
const imagePath = process.argv[2];
if (!apiKey || !imagePath) throw new Error('Set SYSTEM_ONE_API_KEY and supply a PNG path.');

const images: readonly ImageInput[] = [{
  mediaType: 'image/png',
  base64: (await readFile(imagePath)).toString('base64'),
}];

const client = createSystemOne({
  adapter: systemOneAdapter,
  transport: createFetchTransport(),
  apiKey,
  timeoutMs: 30_000,
  maxRetries: 0,
});

const fields = defineFields({
  product: textField({ instructions: 'The product or service named in the error screenshot.' }),
  error_time: dateTimeField({ instructions: 'When the error happened, as shown in the screenshot.' }),
  impact: durationField({ unit: 'minutes', instructions: 'How long the problem has lasted.' }),
});

const result = await client.extract({
  model: 'clairic',
  state: '',
  images,
  fields,
});

console.log({
  model: result.model,
  product: result.answers.product.value,
  errorTime: result.answers.error_time.value,
  impactMinutes: result.answers.impact.value,
  impactPNull: result.answers.impact.pNull,
  truncated: result.truncated,
  usage: result.usage,
});
```

运行：`node --env-file=.env screenshot-to-json.mts ./error.png`。注意每运行一次，就会产生一次计费请求。

![作为 Clairic 图片输入的示例报错截图](TODO-IMG: 模拟报错截图，有产品名和时间，不含影响时长。设计制作。)

[查看 Clairic SDK 文档](https://docs.system-one.dev/zh-CN/docs/sdk/clairic) *（CTA 按钮）*

## 读结果：value、confidence、pNull、span

只有 `value` 是一定会返回的。标量字段在模型给出时，还会带 `guess`、`confidence` 和 `pNull`。`pNull` 超过 null 阈值（默认 0.5）时，`value` 为 `null`，`guess` 里则可能还有值。

输出大致长这样（示意，不是真实结果）：

```json
{
  "model": "clairic",
  "product": "<product name from the screenshot>",
  "errorTime": "<datetime string>",
  "impactMinutes": null,
  "impactPNull": "<0–1, above the null threshold>",
  "truncated": false,
  "usage": { "inputTokens": "<n>", "outputTokens": "<n>", "totalTokens": "<n>" }
}
```

示例截图里没写影响了多久，所以 `impact` 返回 `null`，而不是随便猜个数（`guess` 里可能仍有值）。建议给 `confidence`、`pNull` 设好阈值，达不到的转人工。`result.model` 可能和你请求的模型不同，两个都记下来；`truncated` 一定是布尔值，`usage` 里是 token 用量。

![extract() 的终端输出，显示每个字段的 value 和 pNull](TODO-IMG: 真实运行 screenshot-to-json.mts 的终端截图，API key 打码为 sk-****。)

| 返回键 | 何时出现 | 含义 |
|---|---|---|
| `value` | 总会有 | 类型化的值，或 `null` |
| `guess` / `confidence` / `pNull` | 标量字段，模型返回时 | 猜测值、置信度、输入确定不了该值的概率 |
| `span` | text 字段，有 `state` 文本时 | 该值在 `state` 里的起止字符位置 |
| `mean` / `median` / `p10` / `p90` / `pBelow` / `pAbove` | 仅 estimate、forecast | 估计值的分布 |

## 串联 Jev、Clef 做决策

模型页里的「工单信息整理」就是这个思路：Clairic 取出产品名、报错时间、影响时长，再交给 Jev 或 Clef 判断分给哪个团队。接在上面的代码后面：

```ts
import { choice } from '@system-one-ai/core';

const decision = await client.evaluate({
  model: 'jev',
  state: {
    product: result.answers.product.value,
    error_time: result.answers.error_time.value,
    impact_minutes: result.answers.impact.value,
  },
  questions: {
    team: choice('Choose the team that should handle this error report.', {
      billing: 'Payments and refunds',
      support: 'Technical issues',
      review: 'More information is needed',
    }),
  },
});

console.log(decision.answers.team.choice);
```

`extract()` 不传 `questions`，`evaluate()` 不传 `fields`。决策这一步拿到的是结构化的值，而不是原图。这个模式的完整说明见[先抽取再决策](https://system-one.dev/zh-CN/blog/clairic-vs-jev-clef)，决策模型见 [Jev](https://system-one.dev/zh-CN/models/jev)。

![从截图到 Clairic 字段再到 Jev 分派决策的流程](TODO-IMG: 流程图：截图 → Clairic extract() → 类型化字段 → Jev evaluate() → 团队。设计制作。)

## 图片限制

| 限制 | 值 |
|---|---|
| 每次请求图片数 | 最多 4 张 |
| 格式 | PNG、JPEG、WebP |
| 单张大小 | 4MB |
| 图片合计 | 8MB |
| 整个 JSON 请求体 | 不超过 13MB |
| 传法 | 只能 base64，不收远程 URL |

Clairic 按输入大小每次请求收一次费，不按字段数计，费率见 [Clairic 模型页](https://system-one.dev/zh-CN/models/clairic)。

[获取 API Key](https://system-one.dev/zh-CN/signup) *（CTA 按钮）*

[获取 API Key](https://system-one.dev/zh-CN/signup) *（CTA 按钮，文末）*

核验日期：2026-10-22

## 常见问题

### Clairic 支持哪些图片格式？

PNG、JPEG 和 WebP，每次请求最多 4 张，单张不超过 4MB，合计不超过 8MB。

### 可以传图片 URL 吗？

不行。图片只能以 base64 内嵌，写成 data URL 或 `{ mediaType, base64 }`，远程 URL 会被拒绝。

### 读不出来时返回什么？

pNull 超过 null 阈值（默认 0.5）时，该字段的 value 返回 null。guess 里可能还有值，你可以决定要不要展示，或转人工确认。

### 一次能传几张图？

一次 extract() 请求最多 4 张。有图片时，state 可以是空字符串。

### 和直接让大模型输出 JSON 有什么区别？

Clairic 不生成文本，而是每个字段返回一个类型化的值；输入确定不了的值返回 null，标量字段还带 confidence 和 pNull。详见[《LLM 抽取 vs Clairic》](https://system-one.dev/zh-CN/blog/clairic-vs-llm-extraction)。

### 抽取的字段越多越贵吗？

不会。Clairic 按输入大小每次请求收一次费，不按字段数计。费率见 Clairic 模型页。
