跳到主要内容
非官方简体中文翻译,内容版权归 Mastra 所有,遵循 Apache-2.0 许可。

Structured output

结构化输出让 Agent 返回符合 schema 定义形状的对象,而不是文本。schema 告诉模型应生成哪些字段,模型确保最终结果符合该形状。通过 response.object 访问经过校验、带类型的结果。

何时使用结构化输出
何时使用结构化输出的直接链接

当你需要 Agent 返回数据对象而不是文本时,使用结构化输出。明确定义的字段可以更轻松地提取 API 调用、UI 渲染以及应用逻辑所需的值。

定义 schemas
定义 schemas的直接链接

Agents 可以通过 Standard JSON Schema(Zod、Valibot、ArkType 等)或 JSON Schema 定义预期输出来返回结构化数据。推荐使用 Zod 等库,因为它们提供 TypeScript 类型推断和运行时校验;当你需要语言无关的格式时,JSON Schema 很有用。

使用 Zod 定义 output 形状:

import { z } from 'zod'

const response = await testAgent.generate('Help me plan my day.', {
structuredOutput: {
schema: z.array(
z.object({
name: z.string(),
activities: z.array(z.string()),
}),
),
},
})

console.log(response.object)

完整配置选项参见 .generate()。

示例输出:

[
{
"name": "Morning Routine",
"activities": ["Wake up at 7am", "Exercise", "Shower", "Breakfast"]
},
{
"name": "Work",
"activities": ["Check emails", "Team meeting", "Lunch break"]
},
{
"name": "Evening",
"activities": ["Dinner", "Relax", "Read a book", "Sleep by 10pm"]
}
]

流式传输结构化输出
流式传输结构化输出的直接链接

流式传输也支持结构化输出。最终的结构化对象可在 stream.fullStream 中获得,流完成后也可通过 stream.object 获取。文本流 chunks 仍会发出,但包含自然语言文本而非结构化数据。

import { z } from 'zod'

const stream = await testAgent.stream('Help me plan my day.', {
structuredOutput: {
schema: z.array(
z.object({
name: z.string(),
activities: z.array(z.string()),
}),
),
},
})

for await (const chunk of stream.fullStream) {
if (chunk.type === 'object-result') {
console.log('\n', JSON.stringify(chunk, null, 2))
}
process.stdout.write(JSON.stringify(chunk))
}

console.log(await stream.object)

for await (const chunk of stream.textStream) {
process.stdout.write(chunk)
}

结构化 Agent
结构化 Agent的直接链接

当主 Agent 不擅长生成结构化输出时,你可以为 structuredOutput 提供一个 model。此时,Mastra 会在底层使用第二个 Agent 从主 Agent 的自然语言响应中提取结构化数据。这会产生两次 LLM 调用:一次生成响应,另一次将响应转换为结构化对象,会增加一些延迟和成本,但能提高复杂结构化任务的准确性。

import { z } from 'zod'

const response = await testAgent.generate('Analyze the TypeScript programming language.', {
structuredOutput: {
schema: z.object({
overview: z.string(),
strengths: z.array(z.string()),
weaknesses: z.array(z.string()),
useCases: z.array(
z.object({
scenario: z.string(),
reasoning: z.string(),
}),
),
comparison: z.object({
similarTo: z.array(z.string()),
differentiators: z.array(z.string()),
}),
}),
model: 'openai/gpt-5.6-sol',
},
})

console.log(response.object)

组合工具和结构化输出
组合工具和结构化输出的直接链接

当 Agent 同时配置了工具和结构化输出时,某些模型可能不支持将两者一起使用。这是底层模型 API 的限制,而不是 Mastra 本身的问题。

如果在启用结构化输出后工具未被调用,或在组合两者时收到错误,请尝试以下变通方案之一。

变通方案
变通方案的直接链接

当模型不支持工具和结构化输出同时使用时,你有三种选择:

  1. 使用 jsonPromptInjection:设为 'auto' 可在支持时选择原生结构化输出,否则使用内联 prompt 注入;也可以选择明确的注入模式
  2. 使用独立的结构化模型:向 structuredOutput 传入 model,使用第二个 LLM 进行结构化
  3. 使用 prepareStep:在不同步骤中分别处理工具和结构化输出

每种方法详见下文。

LLM 结构化输出支持
LLM 结构化输出支持的直接链接

由于 API 差异,各 LLM 对结构化输出的支持各不相同。以下章节介绍针对不完全支持结构化输出、或无法将其与工具组合的模型的变通方案。

jsonPromptInjection
jsonpromptinjection的直接链接

默认情况下,Mastra 使用 response_format API 参数将 schema 传给模型提供商。设置 jsonPromptInjection: 'auto' 可让 Mastra 根据其模型能力数据选择模式。对于支持的模型,Mastra 使用原生结构化输出;对于不支持的模型或没有能力数据的模型,则使用内联 prompt 注入。

import { z } from 'zod'

const response = await testAgent.generate('Help me plan my day.', {
structuredOutput: {
schema: z.array(
z.object({
name: z.string(),
activities: z.array(z.string()),
}),
),
jsonPromptInjection: 'auto',
},
})

console.log(response.object)

需要覆盖基于能力的选择时,可使用明确的模式:

  • false 或省略:使用提供商的原生结构化输出。
  • 'inline':将 schema 指令添加到最新一条用户消息中。
  • true 或 'system':将 schema 指令添加到系统消息中。
  • 'auto':当模型支持时使用原生结构化输出,否则使用内联 prompt 注入。
Gemini 2.5 与工具

Gemini 2.5 模型不支持在同一次 API 调用中组合 response_format(结构化输出)和函数调用(工具)。如果你的 Agent 带有工具,并且对 Gemini 2.5 模型使用 structuredOutput,必须设置 jsonPromptInjection: true,以避免错误 Function calling with a response mime type: 'application/json' is unsupported。

const response = await agentWithTools.generate('Your prompt', {
structuredOutput: {
schema: yourSchema,
jsonPromptInjection: true, // Required for Gemini 2.5 when tools are present
},
})

使用 instructions 减少注入的 tokens
使用-instructions-减少注入的-tokens的直接链接

Prompt 注入会在每次模型调用中嵌入序列化后的 JSON schema,对于大型 schema,每个步骤可能耗费数千个 tokens。当 jsonPromptInjection 生效时,设置 instructions 可注入你自己的精简描述,而不是 schema:

const response = await testAgent.generate('Summarize this ticket.', {
structuredOutput: {
schema: ticketSchema,
jsonPromptInjection: 'system',
instructions:
'Reply with a JSON object: "summary" (one paragraph), "priority" ("low" | "high"), "tags" (up to 5 strings). No markdown.',
},
})

响应仍会根据 schema 校验。改变的只是注入的文本。由于模型不再看到完整 schema,遵循程度取决于你的 instructions 对预期字段描述得有多明确。请确保字段列表完整。未提供 instructions 时,仍和以前一样使用生成的 schema 指令;在使用原生结构化输出(无注入)时,该字段不起作用。

使用独立的结构化模型
使用独立的结构化模型的直接链接

当向 structuredOutput 属性提供 model 时,Mastra 会使用一个独立的内部 Agent 处理结构化输出。主 Agent 负责所有步骤(包括工具调用),结构化输出模型只负责生成结构化输出。

const response = await testAgent.generate('Tell me about TypeScript.', {
structuredOutput: {
schema: yourSchema,
model: 'openai/gpt-5.6-sol',
},
})

如果你希望结构化模型也能看到当前对话历史,请在提供 model 的同时设置 useAgent: true。Mastra 会复用父 Agent 和该独立结构化模型,并在有线程可用时附加只读 memory 上下文。

const response = await testAgent.generate('Return my profile as structured data.', {
memory: {
thread: 'thread-123',
resource: 'user-123',
},
structuredOutput: {
schema: z.object({
favoriteColor: z.string(),
hometown: z.string(),
petName: z.string(),
}),
model: 'openai/gpt-5.6-sol',
useAgent: true,
},
})

如果你希望独立结构化模型只基于当前响应工作,不继承先前的对话记忆,请不要设置 useAgent。

使用 prepareStep 的多步骤方法
使用-preparestep-的多步骤方法的直接链接

对于不支持工具和结构化输出同时使用的模型,你可以使用 prepareStep 在不同步骤中分别处理。

const result = await agent.stream('weather in vancouver?', {
prepareStep: async ({ stepNumber }) => {
if (stepNumber === 0) {
return {
model: 'openai/gpt-5.6-sol',
tools: {
weatherTool,
},
toolChoice: 'required',
}
}
return {
model: 'openai/gpt-5.6-sol',
tools: undefined,
structuredOutput: {
schema: z.object({
temperature: z.number(),
humidity: z.number(),
windSpeed: z.number(),
}),
},
}
},
})

处理错误
处理错误的直接链接

当 schema 校验失败或独立结构化模型失败时,你可以使用 errorStrategy 控制错误处理方式。默认的 strict 策略会抛出错误,而 warn 会记录警告并继续。fallback 策略返回通过 fallbackValue 提供的值,结果会报告 usedFallbackValue: true,以便你区分替换对象和真实答案。

const response = await testAgent.generate('Tell me about TypeScript.', {
structuredOutput: {
schema: z.object({
summary: z.string(),
keyFeatures: z.array(z.string()),
}),
errorStrategy: 'fallback',
fallbackValue: {
summary: 'TypeScript is a typed superset of JavaScript',
keyFeatures: ['Static typing', 'Compiles to JavaScript', 'Better tooling'],
},
},
})

console.log(response.object)
console.log(response.usedFallbackValue) // true when the fallback value was substituted