结构化输出
大模型默认输出自然语言,给人看没问题,给程序用就麻烦了。要从一段话里提取字段、要模型决定调用哪个函数、要保证返回的数据能被代码直接解析,这些都需要结构化输出。Workers AI 提供两种方式,JSON 模式约束输出格式,工具调用让模型触发外部动作。这篇讲清楚两种方式的用法和区别,以及怎么保证输出能被程序稳定消费。
为什么需要结构化输出
假设要从一段用户评论里提取情感和评分,直接让模型回答,输出可能是这样
这条评论的情感是正面,评分 4 分。
程序拿到这个字符串还得自己解析,正则匹配、字符串切割,模型换个说法就可能解析失败。如果能让模型直接返回下面这种结构,程序拿到就能用
{ "sentiment": "正面", "score": 4 }
两种输出的对比
| 对比项 | 自然语言输出 | 结构化输出 |
|---|---|---|
| 给谁用 | 给人看 | 给程序消费 |
| 解析方式 | 正则、字符串切割 | JSON.parse 直接解析 |
| 稳定性 | 模型换说法就可能失败 | 字段名和类型固定 |
| 下游处理 | 需要额外清洗 | 直接取字段 |
| 适用场景 | 聊天、文案 | 数据提取、函数调用、自动化流程 |
Workers AI 提供两种结构化输出能力,JSON 模式和工具调用,分别对应不同场景。
两种方式对比
| 对比项 | JSON 模式 | 工具调用 |
|---|---|---|
| 核心目的 | 约束输出格式为指定 JSON | 让模型决定调用哪个外部函数 |
| 触发方式 | response_format 参数 | tools 参数 |
| 模型主动权 | 被动按格式填数据 | 主动决定要不要调用、调哪个 |
| 典型场景 | 信息提取、分类 | 查询数据库、调用 API、执行动作 |
| 返回内容 | 数据本身 | 函数名和参数,需要再执行 |
简单说,要数据用 JSON 模式,要动作用工具调用。
JSON 模式
JSON 模式通过 response_format 参数告诉模型按指定的 JSON Schema 输出。Schema 用 JSON Schema 规范描述,定义有哪些字段、什么类型、哪些必填。
export default {
async fetch(request, env) {
const response = await env.AI.run("@cf/meta/llama-3.1-8b-instruct", {
messages: [
{
role: "system",
content: "从用户评论中提取情感和评分,评分 1 到 5",
},
{
role: "user",
content: "这个产品很好用,速度快质量好,我给 5 分。",
},
],
response_format: {
type: "json_schema",
json_schema: {
type: "object",
properties: {
sentiment: {
type: "string",
enum: ["正面", "中性", "负面"],
},
score: {
type: "integer",
},
summary: {
type: "string",
},
},
required: ["sentiment", "score"],
},
},
});
// response.response 是一个 JSON 字符串,需要 parse
const data = JSON.parse(response.response);
return Response.json(data);
},
};
返回结果
{
"sentiment": "正面",
"score": 5,
"summary": "产品速度快质量好"
}
response_format 的结构说明
| 字段 | 含义 |
|---|---|
| type | 固定填 json_schema |
| json_schema | 一个标准的 JSON Schema 对象 |
| properties | 定义每个字段的名称和类型 |
| required | 列出哪些字段必须出现 |
| enum | 限制字段只能取枚举值里的一个 |
JSON Schema 支持的常用类型
| 类型 | 对应 JSON 值 | 示例用途 |
|---|---|---|
| string | 字符串 | 名称、描述 |
| integer | 整数 | 评分、数量 |
| number | 小数 | 价格、比例 |
| boolean | 布尔值 | 是否、有无 |
| array | 数组 | 列表、标签 |
| object | 对象 | 嵌套结构 |
注意模型返回的 response.response 是一个字符串,内容是 JSON 文本,需要 JSON.parse 才能变成对象。模型有时候会在 JSON 外面加 markdown 代码块标记,parse 前最好做一下清洗,去掉可能的 json 和 包裹。
JSON 模式的限制
JSON 模式不保证百分百严格遵循 schema。模型在复杂场景下可能漏字段、类型不对。Workers AI 官方文档明确说,极端情况下会返回 JSON Mode couldn't be met 错误,代码里要处理这种情况。
JSON 模式的几个限制
| 限制项 | 说明 |
|---|---|
| 不保证严格遵循 | 复杂 schema 模型可能填错 |
| 不支持流式 | JSON 模式和 stream 不能同时用 |
| 模型范围有限 | 只有部分模型支持,选模型时要确认 |
| 嵌套层级 | 太深的嵌套结构模型容易出错 |
支持 JSON 模式的部分模型
| 模型 ID | 参数量 |
|---|---|
| @cf/meta/llama-3.1-8b-instruct | 8B |
| @cf/meta/llama-3.1-8b-instruct-fast | 8B |
| @cf/meta/llama-3.1-70b-instruct | 70B |
| @cf/meta/llama-3.3-70b-instruct-fp8-fast | 70B |
| @hf/nousresearch/hermes-2-pro-mistral-7b | 7B |
选模型时到 Workers AI 模型页面确认是否支持 JSON 模式,不支持会报错。
工具调用
工具调用(Tool Calling)也叫函数调用,让模型根据用户意图决定调用哪个预定义的函数,并返回函数名和参数。和 JSON 模式的区别在于,模型是主动判断要不要调用,而不是被动填数据。
一个典型流程是这样的。用户问北京天气怎么样,模型判断需要调用查天气的函数,返回函数名 get_weather 和参数 { city: "北京" },你的代码拿到后真正去调用天气 API,再把结果喂回给模型生成最终回答。
export default {
async fetch(request, env) {
const { question } = await request.json();
// 第一步,让模型决定调用哪个工具
const response = await env.AI.run(
"@hf/nousresearch/hermes-2-pro-mistral-7b",
{
messages: [{ role: "user", content: question }],
tools: [
{
name: "get_weather",
description: "查询指定城市的天气",
parameters: {
type: "object",
properties: {
city: {
type: "string",
description: "城市名称",
},
},
required: ["city"],
},
},
],
}
);
// 检查模型是否决定调用工具
if (response.tool_calls && response.tool_calls.length > 0) {
const toolCall = response.tool_calls[0];
const args = toolCall.arguments;
// 真正执行函数
let toolResult;
if (toolCall.name === "get_weather") {
toolResult = { city: args.city, temp: 25, condition: "晴" };
}
// 第二步,把工具结果喂回给模型生成最终回答
const finalResponse = await env.AI.run(
"@hf/nousresearch/hermes-2-pro-mistral-7b",
{
messages: [
{ role: "user", content: question },
{ role: "assistant", content: JSON.stringify(toolCall) },
{ role: "tool", content: JSON.stringify(toolResult) },
],
tools: [
{
name: "get_weather",
description: "查询指定城市的天气",
parameters: {
type: "object",
properties: {
city: { type: "string", description: "城市名称" },
},
required: ["city"],
},
},
],
}
);
return Response.json({ answer: finalResponse.response });
}
// 模型没调用工具,直接返回回复
return Response.json({ answer: response.response });
},
};
tools 数组里每个工具的字段说明
| 字段 | 含义 |
|---|---|
| name | 函数名,模型用它来调用 |
| description | 函数做什么的描述,模型靠这个判断要不要调用 |
| parameters | 参数的 JSON Schema,和 JSON 模式的 schema 一样 |
| required | 哪些参数必填 |
description 写得越清楚,模型判断越准。它不知道这个函数能干嘛就不会调用,所以描述要具体。
工具调用的消息流
工具调用涉及多轮消息,角色比普通对话多一个 tool。理解消息流是关键。
| 轮次 | 角色 | 内容 | 作用 |
|---|---|---|---|
| 1 | user | 用户的问题 | 触发模型思考 |
| 1 | assistant | 模型返回的 tool_calls | 模型决定调用哪个函数 |
| 2 | tool | 函数执行的结果 | 把真实数据喂给模型 |
| 2 | assistant | 模型基于工具结果生成的回答 | 最终返回给用户的回复 |
第一步模型可能不调用工具,直接回答。也可能调用多个工具,tool_calls 数组里会有多项。第二步把每个工具的结果以 tool 角色消息发回去,模型再综合所有信息生成最终回答。
assistant 消息里放的是模型上一步返回的 tool_calls 对象,要 JSON.stringify 转成字符串。tool 消息里放的是你执行函数后的结果,同样转成字符串。模型会理解这两条消息的对应关系。
信息提取实战
用一个完整例子把 JSON 模式用在实际场景里。用户输入一段产品评论,提取情感、评分、优缺点,返回结构化数据存进数据库。
export default {
async fetch(request, env) {
const { review } = await request.json();
const response = await env.AI.run("@cf/meta/llama-3.3-70b-instruct-fp8-fast", {
messages: [
{
role: "system",
content: "从产品评论中提取结构化信息,优点和缺点为数组",
},
{ role: "user", content: review },
],
response_format: {
type: "json_schema",
json_schema: {
type: "object",
properties: {
sentiment: {
type: "string",
enum: ["正面", "中性", "负面"],
},
score: {
type: "integer",
description: "1 到 5 的评分",
},
pros: {
type: "array",
items: { type: "string" },
},
cons: {
type: "array",
items: { type: "string" },
},
},
required: ["sentiment", "score", "pros", "cons"],
},
},
});
let data;
try {
data = JSON.parse(response.response);
} catch {
return Response.json(
{ error: "模型输出无法解析为 JSON" },
{ status: 500 }
);
}
// 这里可以把 data 存进 D1 或 KV
return Response.json({ extracted: data });
},
};
几个实践要点。
schema 里的 description 字段很重要,模型靠它理解每个字段该填什么。score 加上 “1 到 5 的评分” 比光写 type integer 效果好。
pros 和 cons 用 array 类型加 items 限制元素为 string,模型会输出字符串数组,程序直接遍历就行。
parse 要包 try catch。即使开了 JSON 模式,模型偶尔也会输出不规范内容,兜底处理比直接崩溃好。
JSON 模式还是工具调用
两种方式解决不同问题,选错了代码会复杂很多。判断标准很简单,要数据还是要动作。
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 从文本提取字段 | JSON 模式 | 只是要数据,不需要执行外部动作 |
| 用户提问触发查数据库 | 工具调用 | 模型要判断查不查、查什么 |
| 评论情感分类 | JSON 模式 | 输出固定分类标签 |
| 根据问题调用不同 API | 工具调用 | 模型决定调哪个 API、传什么参数 |
| 把非结构化文本转表格数据 | JSON 模式 | 纯数据转换 |
| 智能助手根据指令执行操作 | 工具调用 | 需要触发实际动作 |
一句话总结,输出的内容如果只是给程序读的数据,用 JSON 模式。如果需要模型决定调哪个外部函数再执行,用工具调用。两者也可以配合,工具调用的结果最终再走 JSON 模式格式化输出。
小结
结构化输出让大模型从聊天工具变成可编程的组件。JSON 模式用 response_format 传 JSON Schema,约束模型按指定格式输出,适合信息提取和分类。工具调用用 tools 数组定义可用函数,模型主动判断调用时机并返回参数,适合需要触发外部动作的场景。两种方式让模型输出能被代码稳定消费,是从演示走向生产的关键一步。Workers AI 教程到这里就结束了,从文本生成、流式输出、文本嵌入到结构化输出,覆盖了日常开发的主要用法。
上一篇 文本嵌入