跳至主要内容
System One

一次调用,抽取发票和小票上的字段

查看示例

![发票文本和小票照片经 Clairic 转成类型化字段](TODO-IMG: 封面。模拟发票文本 + 模拟小票照片 → 字段卡片(invoice_number、total、tax…)。设计制作。)

要抽取的字段

字段类型 / 工厂函数说明
invoice_number / order_numbertext / textField()单据号,可带 span
开票日期、due_datedate / dateField()日历日期
purchase_timedatetime / dateTimeField()日期 + 时间
total、subtotal、tax、tip、unit_pricenumber / numberField({ decimals: 2 })decimals 取值 0–6
quantitynumber / numberField({ integer: true })整数
contacts / 明细名称list / listField({ items: 'text' })items 必填:text、number 或 date
days_to_payestimate / estimateField({ min, max })返回 median、p10、p90 等

文本发票示例(INV-2291)

下面是 Clairic SDK 文档里的发票示例。先装 SDK:

bashnpm 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

保存为 extract-invoice.mts,在 .env 里设置 SYSTEM_ONE_API_KEY:

tsimport {
  createSystemOne, defineFields, textField, numberField, dateField, listField, estimateField,
} 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;
if (!apiKey) throw new Error('Set SYSTEM_ONE_API_KEY.');

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

const fields = defineFields({
  invoice_number: textField({ instructions: 'The invoice id printed on the document.' }),
  due_date: dateField({ instructions: 'Issue date plus the net payment days.' }),
  total: numberField({ decimals: 2, instructions: 'The total amount due.' }),
  contacts: listField({ items: 'text' }),
  days_to_pay: estimateField({ min: 0, max: 120, step: 1 }),
});

const result = await client.extract({
  model: 'clairic',
  state: [
    'INVOICE INV-2291',
    'From: Northwind Supply',
    'To: Contoso Ltd',
    'Issued: 2026-09-02, net 30',
    'Total due: 1080.00 including shipping',
    'Contact: Dana Ruiz, Priya Shah',
  ].join('\n'),
  fields,
  referenceDate: '2026-10-19',
  options: { includeQuantiles: false },
});

const invoice: string | null = result.answers.invoice_number.value;
console.log({
  model: result.model,
  invoice,
  dueDate: result.answers.due_date.value,
  total: result.answers.total.value,
  contacts: result.answers.contacts.value,
  daysToPay: result.answers.days_to_pay.median,
  truncated: result.truncated,
  usage: result.usage,
});

运行:node --env-file=.env extract-invoice.mts。每运行一次就会产生一次计费请求。

contacts 是 list 类型,返回联系人数组;days_to_pay 是 estimate 类型,读它的 median 即可,p10、p90 也会一并返回。

![Clairic 对发票 INV-2291 的输出:发票号、到期日、金额](TODO-IMG: 真实运行 extract-invoice.mts 的终端截图,API key 打码为 sk-****。)

试用发票抽取 demo

歪斜、模糊的小票照片

官方博客里有个例子:一张拍歪了、字迹也不清楚的小票照片,Clairic 从中取出了订单号、购买时间、数量、单价、小计、税费和小费。小票上本来就没有折扣,所以折扣一项返回 null。

照片版代码如下(保存为 extract-receipt.mts),每次请求最多可以传 4 张图:

tsimport { readFile } from 'node:fs/promises';
import {
  createSystemOne, defineFields, textField, numberField, dateTimeField,
  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 JPEG path.');

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

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

const fields = defineFields({
  order_number: textField({ instructions: 'The order or receipt number.' }),
  purchase_time: dateTimeField({ instructions: 'Date and time of purchase.' }),
  quantity: numberField({ integer: true }),
  unit_price: numberField({ decimals: 2 }),
  subtotal: numberField({ decimals: 2 }),
  tax: numberField({ decimals: 2 }),
  tip: numberField({ decimals: 2 }),
  discount: numberField({ decimals: 2, instructions: 'Discount amount, only if the receipt shows one.' }),
});

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

for (const [name, answer] of Object.entries(result.answers)) {
  console.log(name, answer.value, answer.confidence, answer.pNull);
}

小票上没有折扣时,discount 返回 null。正式上线前,请用自己拍的照片测一遍,并给 confidence、pNull 设好阈值。

![拍歪的小票照片和 Clairic 字段,折扣返回 null](TODO-IMG: 左:模拟的拍歪、模糊小票照片(无折扣行);右:真实运行 extract-receipt.mts 的输出,discount 为 null,key 打码。)

referenceDate 与账期计算

referenceDate(HTTP 里写作 reference_date)用来确定没写年份的日期属于哪一年,比如只印了月和日的小票。

到期日可以直接算。发票示例里,due_date 字段的 instructions 写的是 "Issue date plus the net payment days.",Clairic 会根据 2026-09-02 和 net 30 推出到期日。

没有的字段返回 null

pNull 超过 null 阈值(默认 0.5)时,value 返回 null,guess 里可能还有值。阈值可以用 options.nullThreshold 统一调,也可以按字段单独设。

发票信息提取 demo 里就有这种情况:应付合计写着"待确认"时,金额返回 null,发票号照常取出。

计费

Clairic 按输入大小每次请求收一次费,不按字段数计,详见 Clairic 模型页。新账号注册即送 100 积分。

试一下 demo

同样的发票任务,可以直接在发票信息提取 demo 里试(需登录)。想直接调 API 的话,POST https://system-one.dev/v1/systemone 的请求体如下:

json{
  "model": "clairic",
  "state": "INVOICE INV-2291\nIssued: 2026-09-02, net 30\nTotal due: 1080.00",
  "reference_date": "2026-10-19",
  "fields": {
    "invoice_number": { "type": "text" },
    "total": { "type": "number", "decimals": 2 }
  }
}

认证、幂等和请求头见 API 文档。在重放记录仍可用期间,用同一个 key 重试同一操作不会重复扣费。

![system-one.dev 上的 Clairic 发票抽取 demo](TODO-IMG: /zh-CN/demo/invoice-extraction 页面真实截图,字段与结果区。)

获取 API Key

相关阅读

获取 API Key

核验日期:2026-10-22

常见问题

照片拍歪了、模糊了能读吗?

可以从拍歪、字迹不清的照片里抽取字段;官方例子就从这样一张小票里取出了订单号、购买时间、数量、单价、小计、税费和小费。请用自己的照片测试,并检查 confidence 和 pNull。

可以传哪些输入?

文本、JSON,以及最多 4 张以 base64 内嵌的图片(PNG、JPEG 或 WebP)。

发票上没有某个字段怎么办?

pNull 超过 null 阈值时返回 null,所以没有的折扣会保持 null,不会被填上。

怎么计费?

按输入大小每次请求收一次费:每 4KB 一块,每块 2 积分,抽取多少字段都一样。详见 Clairic 模型页。

referenceDate 有什么用?

用来确定没写年份的日期属于哪一年,比如只印了月和日的小票。