六维教程

结构化输出

大模型默认输出自然语言,给人看没问题,给程序用就麻烦了。要从一段话里提取字段、要模型决定调用哪个函数、要保证返回的数据能被代码直接解析,这些都需要结构化输出。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 教程到这里就结束了,从文本生成、流式输出、文本嵌入到结构化输出,覆盖了日常开发的主要用法。

上一篇 文本嵌入

上一篇
文本嵌入
下一篇
Cloudflare Workers 教程