结构化输出 vs 工具使用 vs 预填充:2026年从Claude获取JSON
长期以来,强制模型输出JSON的技巧是在助手轮次中预填充一个左花括号。但这个技巧在当前版本的Claude模型上已经失效了,会返回400错误。如果你还在使用这种方法,一旦升级模型版本,代码就会崩溃。本文将介绍获取可靠结构化输出的现代方法,以及如何在替代预填充的两种方案中做出选择。
问题所在
旧的模式:
// 在 Opus 4.6+、Fable 5 上错误:预填充最后助手轮次现在会返回400
messages: [
{ role: "user", content: "提取名称。" },
{ role: "assistant", content: '{"name": "' }, // 强制JSON起始
]
我第一次在Opus 4.8上尝试时,这段代码就返回了400。Opus 4.6、4.7、4.8和Fable 5不再支持最后助手轮次的预填充。替代方案取决于预填充原本要实现的目的。
方案1:结构化输出(当你需要特定格式时)
如果预填充是为了强制JSON schema,直接的替代方案是使用 output_config.format。SDK会自动根据你的schema验证响应,配合Zod还能免费获得类型安全:
import { z } from "zod";
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";
const Finding = z.object({
vulnerability: z.string(),
severity: z.enum(["low", "medium", "high", "critical"]),
function_name: z.string(),
line: z.number(),
});
const response = await client.messages.parse({
model: "claude-opus-4-8",
max_tokens: 16000,
messages: [{ role: "user", content: `查找漏洞:
${contract}` }],
output_config: { format: zodOutputFormat(Finding) },
});
// 类型安全且经过验证。解析失败时为null。
console.log(response.parsed_output?.severity);
输出保证匹配schema,否则 parsed_output 为null。无需正则表达式,无需在try/catch中写JSON.parse,也无需担心"模型又加了markdown代码块"。
需要注意一些schema限制:数值约束(如最小值/最大值)不会在服务端强制执行(SDK会移除这些约束并在客户端验证),不支持递归schema,每个对象都需要设置 additionalProperties: false。Zod辅助函数会帮你处理大部分问题。
方案2:工具调用(当JSON是操作的副作用时)
如果你真正想要的是让模型使用结构化参数执行某个操作,那么工具调用是更合适的选择。典型的场景是分类:你需要一个标签,而这个标签就是工具的输入。
const response = await client.messages.create({
model: "claude-opus-4-8",
max_tokens: 1024,
tools: [{
name: "classify_severity",
description: "记录发现的严重程度分类。",
strict: true,
input_schema: {
type: "object",
properties: {
severity: { type: "string", enum: ["low", "medium", "high", "critical"] },
},
required: ["severity"],
additionalProperties: false,
},
}],
tool_choice: { type: "tool", name: "classify_severity" },
messages: [{ role: "user", content: `分类:
${finding}` }],
});
使用 strict: true 可以保证参数完全符合schema。将 tool_choice 强制指定为特定工具,意味着模型必须调用它。
如何选择
决定性的问题是:JSON是最终答案,还是某个操作的参数?
- JSON是我要解析并直接使用的结构化对象? → 使用结构化输出(
output_config.format)。 - 模型是循环中的一个步骤,JSON需要传递给下一个工具? → 使用工具调用。
- 我要从文本中提取字段并存入记录? → 使用结构化输出。
- 我在构建一个需要在多个操作中选择的智能体? → 使用工具调用。
对于我的审计工具,报告就是最终答案,所以我使用结构化输出。对于决定运行哪个分析流程的路由逻辑,决策本身是一个操作,所以我使用工具调用。
其他预填充技巧的替代方案
预填充不仅用于JSON,以下是完整的映射关系:
- 强制分类标签:使用带enum的工具,或结构化输出。
- 跳过开场白(如"以下是摘要:"):在系统提示中写明"直接回复,无需开场白"。
- 规避不当拒绝:现在通常不需要;当前模型的拒绝行为更加合理,直接提示即可。
- 继续被中断的回复:在用户轮次中放入续写内容,如"你之前的回复以X结尾,请从这里继续"。
总结
预填充技巧利用的是模型续写文本的方式。而现代功能通过在API层面约束输出格式来实现目标,既更可靠,也不会因模型升级而失效。如果你还在预填充花括号,那么只需一次模型字符串的更改,就会遇到400错误。请改用结构化输出或工具调用,别再和格式化器较劲了。