第 8 章:结构化输出:让 AI 返回稳定 JSON

6 阅读2分钟

第 8 章:结构化输出:让 AI 返回稳定 JSON

本章目标

这一章解决一个常见问题:模型输出是自然语言,但应用经常需要稳定的结构化数据。

本章效果

右侧“结构化分析”面板展示了 /api/analyze-question 的结果。真实模型模式下,这部分由 withStructuredOutput 和 Zod Schema 约束。

问题结构化分析转存失败,建议直接上传图片文件

为什么需要结构化输出

比如我们想让模型判断用户问题类型:

{
  "intent": "policy_question",
  "keywords": ["报销", "发票"],
  "needHuman": false
}

如果只靠 Prompt 让模型“请返回 JSON”,很容易出现:

  • JSON 外面多一句解释
  • 字段缺失
  • 字段类型错误
  • 逗号或引号不合法

结构化输出就是为了解决这个问题。

用 Zod 定义输出 Schema

import * as z from "zod";

export const QuestionAnalysisSchema = z.object({
  intent: z.enum(["policy_question", "how_to", "troubleshooting", "other"]),
  keywords: z.array(z.string()),
  needHuman: z.boolean(),
  reason: z.string()
});

export type QuestionAnalysis = z.infer<typeof QuestionAnalysisSchema>;

绑定结构化输出

不同模型供应商对结构化输出支持程度不同。LangChain.js 提供统一能力时,可以这样组织:

const model = await createChatModel();
const structuredModel = model.withStructuredOutput(QuestionAnalysisSchema);

const analysis = await structuredModel.invoke([
  {
    role: "system",
    content: "你负责分析用户问题类型,并返回结构化结果。"
  },
  {
    role: "user",
    content: "报销需要什么发票?"
  }
]);

console.log(analysis.intent);

在知识库项目中的用途

结构化输出可以用在很多地方:

  • 判断问题意图
  • 提取关键词
  • 判断是否需要人工客服
  • 生成文档标签
  • 识别用户是否在要求越权信息
  • 判断答案是否足够可信

例如:

export const AnswerQualitySchema = z.object({
  supportedByContext: z.boolean(),
  confidence: z.enum(["low", "medium", "high"]),
  missingInfo: z.array(z.string()),
  suggestedFollowUp: z.string().optional()
});

前端渲染

结构化输出不一定直接展示给用户,但可以驱动 UI:

{analysis.needHuman && (
  <button>转人工处理</button>
)}

{analysis.keywords.map((keyword) => (
  <span key={keyword}>{keyword}</span>
))}

实战任务

完成:

  • 定义 QuestionAnalysisSchema
  • 创建 /api/analyze-question
  • 输入用户问题,返回意图、关键词、是否需要人工
  • 在 Chat 页面发送前调用一次分析接口

常见坑

不要把结构化输出当成绝对可靠。它比普通文本稳定,但仍然要做 Zod 校验、异常处理和兜底。

不要让结构化输出字段太复杂。字段越多,越容易错。

不要为了所有回答都强行 JSON 化。面向用户的长回答仍然适合自然语言。

本章小结

结构化输出让模型结果可以被程序消费。下一章进入工具调用,让模型开始调用真实函数。