☰
airi 结构化输出实践指南:xsAI 的 generateObject、streamObject 与 tool()/rawTool() 使用全解
2026/10/15 17:28:24 网站建设 项目流程
  • 人工智能
  • AI Agent
  • MCP 服务
  • 工具调用
  • GUI 自动化
  • 语音
  • 数字人
  • 交互助手

【免费下载链接】airi

💖🧸 自托管、归你拥有的 Grok 风格 AI 伴侣与 waifu / 赛博生命灵魂容器,目标是接近 Neuro-sama 的高度;支持实时语音聊天、Minecraft 和 Factorio 游玩,支持 Web / macOS / Windows。

项目地址:https://gitcode.com/moeru-ai/airi
点击查看免费下载

本文以 airi 仓库中的 xsAI 技能参考文档(.agents/skills/xsai/references/structured-output.md)为核心骨架,系统讲解在 OpenAI 兼容工作流中如何产出「带类型校验的结构化输出」:何时用generateObject、何时用streamObject、如何用tool()/rawTool()声明工具、以及 Zod / Valibot / ArkType / Effect 等 Standard Schema 库如何经xsschema统一转换为 JSON Schema。读完本文,你将能独立判断结构化输出 API 的选型、补齐 schema 转换依赖,并理解 airi 在core-agent与stage-ui中真实的工具与 schema 落地方式,直接复制到自己的项目中运行。

一、Structured Output 两大 API:generateObject 与 streamObject

xsAI 将结构化输出收敛为两个核心 API,两者都依赖xsschema完成「用户侧 schema 库 → JSON Schema → 供应商可消费格式」的转换链路:

  • generateObject:一元(unary)结构化输出,返回一个经过校验的最终对象;
  • streamObject:增量(incremental)结构化输出,边生成边解析,适合需要渐进式渲染的 UI 或工作流。

选择依据很直接:用户要「一个确定、合法、校验过的对象」就用generateObject;用户要「随着 token 流入不断更新的部分结果」就用streamObject。两者的转换都发生在文本流开始之前,这也是streamObject被设计为 async 的原因——schema 必须先完成转换才能启动流。.agents/skills/xsai/SKILL.md中的 API 选择规则也印证了这一分工:generateObject对应「validated structured output」,streamObject对应「incremental object parsing」。

二、Schema 支持与转换:Standard Schema 生态 + xsschema

xsAI 通过xsschema支持Standard Schema风格的 schema 库,包括:

  • Zod
  • Valibot
  • ArkType
  • Effect

当用户需要「带类型的结构化输出」时,就使用这些库声明 schema。需要注意:部分 schema 厂商需要项目里额外安装一个「JSON Schema 转换器」包,否则xsschema无法把 schema 转成供应商能理解的 JSON Schema:

Schema 库需要的转换包
Zod v3zod-to-json-schema
Valibot@valibot/to-json-schema

仓库内的真实依赖印证了这一要求:packages/stage-ui/package.json 与 packages/core-agent/package.json 都同时依赖xsschema、zod与valibot(stage-ui 还有valibot),而 tool 定义统一来自@xsai/tool。

xsschema 在 airi 中的两条关键能力

  • toJsonSchema:把 Zod / Valibot 等 schema 转成 provider-neutral 的 JSON Schema。spark-command 工具实现 中createSparkCommandTool先用toJsonSchema(sparkCommandToolSchema)生成参数 schema,注释明确写着「Keep the generated JSON Schema provider-neutral. Each provider adapter converts unsupported schema forms before it sends the request」——即各 provider 适配器在发请求前还会再做一次兜底转换。
  • validate:在工具执行侧对输入做运行时校验。spark-notify 工具实现 同时导入toJsonSchema与validate,构建 notify 命令工具时对 schema 做双重处理。

三、generateObject:一次性返回校验过的最终对象

generateObject适用于「用户想要一个 validated final object」的场景。与让模型自由输出 JSON 相比,它最大的价值是schema 驱动 + 校验兜底:输出必须匹配 schema,否则无法通过校验。

重要选项

选项作用
schema结构化输出必须满足的 schema(Zod / Valibot / ArkType / Effect 之一)
schemaNameschema 的名称,用于供应商侧标识
schemaDescriptionschema 的描述,帮助模型理解输出结构
strict是否启用严格模式(开启后模型输出必须严格符合 schema,禁止多余字段)
output: 'array'(可选)让输出成为一个元素数组而非单个对象

最小可运行示例

以 Valibot 为例(需要项目里安装@valibot/to-json-schema):

import { env } from 'node:process' import { generateObject } from '@xsai/generate-object' import * as v from 'valibot' const { object } = await generateObject({ apiKey: env.OPENAI_API_KEY!, baseURL: 'https://api.openai.com/v1/', messages: [ { content: 'Extract the event information.', role: 'system', }, { content: 'Alice and Bob are going to a science fair on Friday.', role: 'user', }, ], model: 'gpt-4o', schema: v.object({ date: v.string(), name: v.string(), participants: v.array(v.string()), }), })

返回的object已通过 schema 校验,可以直接当作类型安全的 JS 对象使用。可运行示例集 明确建议:凡是需要结构化数据的场景,优先用generateObject而不是让模型自由输出 JSON,因为后者无法保证字段齐全、类型正确。

四、streamObject:增量解析的结构化输出

streamObject适用于「UI 或工作流能从部分结构化输出中受益」的场景,例如打字机式的对象填充、边生成边校验的表单。它之所以是 async,是因为schema 转换发生在文本流开始之前——转换完成才会真正发起流式请求。

两种结果形状

模式结果流适用场景
对象模式(默认)partialObjectStream需要渐进式更新的单个对象
数组模式(output: 'array')elementStream需要逐元素消费的结果列表

最小可运行示例

import { env } from 'node:process' import { streamObject } from '@xsai/stream-object' import * as v from 'valibot' const { partialObjectStream } = await streamObject({ apiKey: env.OPENAI_API_KEY!, baseURL: 'https://api.openai.com/v1/', messages: [ { content: 'Extract the event information.', role: 'system', }, { content: 'Alice and Bob are going to a science fair on Friday.', role: 'user', }, ], model: 'gpt-4o', schema: v.object({ date: v.string(), name: v.string(), participants: v.array(v.string()), }), }) for await (const partialObject of partialObjectStream) { console.log(partialObject) }

对象模式下,每次迭代拿到一个「部分填充」的对象,前端可以实时渲染;需要逐条结果时切换到output: 'array'并消费elementStream。

五、Tool helpers:tool() 与 rawTool() 的选择

除了输出对象,结构化 schema 还广泛用于「工具(function calling)」定义。xsAI 提供两个工具构造器:

  • tool():配合 Standard Schema 库(Zod、Valibot 等)使用。适用于项目里已经有 schema 库的情况,声明式地定义name、description、parameters与execute。
  • rawTool():直接使用原始 JSON Schema。适用于项目里已经有一份 JSON Schema,或者希望与 schema 库零耦合的情况。

SKILL.md 补充了一条中间规则:只有仓库已经在用纯Tool对象形态、或者需要规避tool()的异步初始化时,才直接用普通Tool对象。

airi 中的 tool() 实战:MCP 与 Home Assistant

  • MCP 桥接工具 用tool()定义了builtIn_mcpListTools与builtIn_mcpCallTool两个 MCP 桥接工具:前者参数为z.object({}).strict()(空对象 + 严格模式),后者用z.object({ name: z.string(), arguments: z.string() })把 MCP 参数包成 JSON 字符串传入——注释解释了为何用z.string()而非z.unknown():z.unknown()会生成没有type键的{},部分供应商无法接受。
  • Home Assistant 工具 同样用tool()+ Zod 定义实体查询工具,并在execute内对service_data做JSON.parse前的字符串与对象形态校验(parseServiceData),形成「schema 声明 + 运行时双重校验」。

airi 中的 rawTool() 实战:spark 命令

spark-command 工具实现 展示了完整的rawTool()用法:

const parameters = await toJsonSchema(sparkCommandToolSchema) return [ rawTool({ name: 'builtIn_emitSparkCommand', description: 'Send a spark:command to one or more frontend-connected modules or sub-agents.', parameters, execute: async (rawPayload) => { const payload = rawPayload as z.infer<typeof sparkCommandToolSchema> // ... 归一化并转发为 WebSocketEvents['spark:command'] options.sendSparkCommand(command) }, }), ]

流程是:先用 Zod 声明sparkCommandToolSchema(spark-notify schema 中是z.object({...})的复合 schema),再toJsonSchema转成 provider-neutral 的 JSON Schema,最后交给rawTool。spark-notify 工具实现 沿用了同一模式,并额外用validate做运行时校验。

strict-tool-schema:airi 对 tool schema 的强约束测试

airi 对工具 schema 的要求比默认更严格。strict-tool-schema 断言 提供 Vitest 自定义断言toSatisfyStrictToolSchema,强制以下规则:

  • 只要出现properties,就必须同时提供required;
  • required必须覆盖properties中的每一个键(既不能缺、也不能多);
  • 出现properties时additionalProperties必须为false;
  • 递归检查嵌套items与anyOf/oneOf/allOf分支。

这直接呼应了structured-output.md中strict选项的意图——在 OpenAI 兼容的 function calling 场景里,schema 不严格会导致供应商 400 或模型自由发挥。web-search 工具实现 的注释也说明:严格 OpenAI 兼容的供应商会拒绝required中缺失properties键的 tool schema,所以可选参数被建模为「required 且允许 null」而非.optional()。

六、供应商兼容性兜底:anyOf 折叠与 nullable 归一化

schema 转换到 JSON Schema 后,不同供应商对anyOf/ nullable 的接受度不同。airi 的provider-inference为此实现了适配层:

  • tool-schema 兼容性实现 的collapseToolSchemaPrimitiveAnyOf(schema)会把「纯原始类型组成的 anyOf 分支」折叠成 JSON Schema 的type数组(例如anyOf: [{ type: 'string' }, { type: 'null' }]→{ type: ['string', 'null'] }),用于仅在供应商拒绝标准 anyOf 形式之后的兜底;对象与数组联合仍保留为anyOf。对应测试见 tool-schema 测试。
  • spark-notify schema 的normalizeNullableAnyOf则对 schema 做递归的 nullable 归一化,与collapseToolSchemaPrimitiveAnyOf配合,让同一个工具 schema 能安全穿越 azure-openai 适配器 等对 schema 形式敏感的自定义供应商适配器。

这条链路完整回答了「为什么说 schema 转换发生在请求之前、且各 provider 还要再兜底一次」:toJsonSchema负责标准转换,适配器负责供应商差异,validate负责运行时正确性。

七、推荐规则速查

把structured-output.md的三条推荐规则与 SKILL.md、可运行示例集、包选择指南 综合如下:

  1. 优先generateObject,而不是让模型输出自由格式 JSON——校验保证结构正确,字段不会缺失或变形;
  2. 当 UI 或工作流能从部分结构化输出中受益时,用streamObject——增量解析、渐进渲染;
  3. 除非用户明确需要原始 JSON Schema,否则优先tool()——schema 库带来的类型安全与描述能力通常更有价值;
  4. 包体敏感时优先@xsai/generate-object、@xsai/stream-object、@xsai/tool等细粒度包,而不是xsai全家桶(@xsai/tool同时导出tool与rawTool);
  5. 所有示例都应显式给出baseURL与model;托管供应商的apiKey在 Node.js 用process.env、浏览器用localStorage注入,绝不硬编码。

八、扩展阅读路径

  • xsAI 技能入口与总体选型规则:API 选择、约束与定位
  • 结构化输出参考文档:本文核心骨架
  • 可运行示例集:文本生成、流式工具、generateObject / streamObject 完整代码
  • 包选择指南:细粒度包与 umbrella 包取舍
  • 文本、流式与工具循环:generateText/streamText与stopWhen工具循环
  • spark-command 工具实现:rawTool+toJsonSchema实战
  • spark-notify 工具实现:rawTool+toJsonSchema+validate实战
  • web-search 工具实现:strict schema 与 required-nullable 建模
  • MCP 桥接工具:tool()定义实例
  • tool schema 兼容性兜底:anyOf折叠实现与其测试
  • strict tool schema 断言:required / additionalProperties 强约束测试
  • 人工智能
  • AI Agent
  • MCP 服务
  • 工具调用
  • GUI 自动化
  • 语音
  • 数字人
  • 交互助手

【免费下载链接】airi

💖🧸 自托管、归你拥有的 Grok 风格 AI 伴侣与 waifu / 赛博生命灵魂容器,目标是接近 Neuro-sama 的高度;支持实时语音聊天、Minecraft 和 Factorio 游玩,支持 Web / macOS / Windows。

项目地址:https://gitcode.com/moeru-ai/airi
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询