5. 结构化输出

让 AI 返回格式固定的 JSON 数据,而不是自由文本。在数据处理、信息提取等场景非常有用。

请参考3.3部分 “generateText vs streamText 的选择” 的例子

5.2 提取数组数据

// 从非结构化文本中提取多条数据
import { generateText, Output } from 'ai'
import { createDeepSeek } from '@ai-sdk/deepseek'
import { z } from 'zod'
import 'dotenv/config'

const deepseek = createDeepSeek({
    apiKey: process.env.DEEPSEEK_API_KEY,
});

async function main() {
    // 从非结构化文本中提取多条数据
    const result = await generateText({
        model: deepseek('deepseek-chat'),
        output: Output.object({
            schema: z.object({
                tasks: z.array(z.object({
                    title: z.string(),
                    owner: z.string(),
                    priority: z.enum(['高', '中', '低']),
                    deadline: z.string().optional(),
                })),
            }),
        }),
        prompt: `
    从以下会议记录中提取所有待办任务:
    
    "张三需要在周五之前完成用户调研报告,这个优先级很高。
    李四负责更新产品文档,不急,下周前完成就好。
    王五要修复登录页面的 bug,今天必须搞定。"
  `,
    })

    console.log(result.output.tasks);
}
main();

模型会根据您设置的想要的数据格式的字段名含义进行内容提取并填充。如果您的字段名明显没有什么意义,那么可以增加描述对需要提取的数据的意义进行一个补充说明。比如可以这样设置:

{
    title: z.string(),
    // 对想提取的信息进行一个描述
    xxxx: z.string().describe('责任人'),
    priority: z.enum(['高', '中', '低']),
    deadline: z.string().optional(),
}

不过经测试,字段名的意义优先级好像是大于 describe()中的描述的(可能描述仅是一个辅助建议,当它与字段名的常见语义矛盾时,模型通常会选择相信字段名的常见含义)。