Effect AI 的 @effect/ai-anthropic:Claude 4-6 系列原生结构化输出能力标记与实现解析
【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code
本篇文章围绕 effect-smol 仓库中@effect/ai-anthropic的一条 changeset(.changeset/pre/anthropic-4-6-structured-output.md)展开:它把 Claude 4-6 世代模型(claude-opus-4-6、claude-sonnet-4-6)在getModelCapabilities中标记为支持 Anthropic 原生结构化输出,使generateObject不再回退到“强制 JSON 工具”方案,而是直接请求output_config.format(json_schema)。读完本文,你将掌握结构化输出在 Effect AI 中的两条完整请求路径、能力检测表的判定规则、structuredOutputs覆盖配置的用法,以及对应的源码与测试验证方式。
一、问题背景:两条“结构化输出”路线
在 Anthropic 的 API 生态中,让模型输出符合 JSON Schema 的对象有两条路线:
- 原生约束解码(constrained decoding):通过消息请求中的
output_config.format: { type: "json_schema", schema: ... }字段,让服务端在解码阶段直接约束输出格式,稳定性和一致性最好。 - 强制 JSON 工具回退(forced JSON tool):把目标 Schema 包装成一个名为
objectName的工具,设置tool_choice: { type: "tool", name: objectName, disable_parallel_tool_use: true },用“必须调用该工具”的方式间接获得 JSON 输出。
@effect/ai-anthropic在generateObject时究竟走哪条路,取决于模型能力检测的结果。而本次 changeset 修复的问题正是:claude-opus-4-6与claude-sonnet-4-6已经在真实 API 上验证支持约束解码结构化输出,却因能力表缺失被误判为supportsStructuredOutput: false,导致本可享受原生能力的用户被迫走 JSON 工具回退路线。
二、能力检测核心:getModelCapabilities
检测逻辑集中在 AnthropicLanguageModel.ts 的getModelCapabilities函数中。它为每个 Claude 模型返回两个能力字段:
interface ModelCapabilities { readonly maxOutputTokens: number readonly supportsStructuredOutput: boolean }其判定采用“旧模型显式例外 + 新模型继承现代默认值”的策略(源码注释原文:“Legacy models are listed as exceptions so newly released models inherit modern defaults”):
模型匹配规则(modelId.includes(...)) | maxOutputTokens | supportsStructuredOutput |
|---|---|---|
claude-sonnet-4-5/claude-opus-4-5/claude-haiku-4-5 | 64000 | true |
claude-opus-4-1 | 32000 | true |
claude-sonnet-4-0/claude-sonnet-4-20250514/claude-3-7-sonnet | 64000 | false |
claude-opus-4-0/claude-opus-4-20250514 | 32000 | false |
claude-3-5-haiku | 8192 | false |
claude-3-* | 4096 | false |
其他(含claude-opus-4-6/claude-sonnet-4-6) | 128000 | true |
关键点在于最后一行else分支:claude-opus-4-6、claude-sonnet-4-6没有进入任何旧模型例外分支,因此落入“现代模型默认值”——supportsStructuredOutput: true,maxOutputTokens: 128000。这正是本次 changeset 生效的机制:不需要为 4-6 显式加一行判断,它们自动继承了现代默认值,同时 changeset 明确说明claude-opus-4-7/claude-opus-4-8也会按同样方式被归类,为后续Model枚举接入新模型预留了路径。
配套的版本演进记录见 CHANGELOG.md:rc.109 中引入“未知模型默认使用原生结构化输出与 128K 输出 token”策略,并新增structuredOutputs配置项用于覆盖能力检测;本次变更(PR #2357)在此基础上把 4-6 世代正式纳入原生结构化输出支持范围。
三、两条请求路径的源码实现
3.1 原生路径:getOutputFormat生成output_config
当模型能力为supportsStructuredOutput: true且调用方请求responseFormat.type === "json"时,getOutputFormat 会把用户提供的 Schema 转换成 Anthropic 的json_schema格式:
const getOutputFormat = Effect.fnUntraced(function*({ capabilities, options }) { if (options.responseFormat.type === "json" && capabilities.supportsStructuredOutput) { const jsonSchema = yield* tryJsonSchema(options.responseFormat.schema, "getOutputFormat") return { type: "json_schema", schema: jsonSchema as any } } return undefined })随后在 makeRequest 中组装请求体时,outputFormat被写入payload.output_config.format;若配置了推理成本档位(output_config.effort,取值"low" | "medium" | "high" | null)也会一并带上。只有output_config非空时才把它挂到请求体上:
const outputConfig: Mutable<typeof Generated.BetaCreateMessageParams.Encoded["output_config"]> = {} if (Predicate.isNotUndefined(outputFormat)) { outputConfig.format = outputFormat } if (Predicate.isNotUndefined(output_config?.effort)) { outputConfig.effort = output_config.effort } if (Object.keys(outputConfig).length > 0) { payload.output_config = outputConfig }3.2 回退路径:prepareTools强制 JSON 工具
当supportsStructuredOutput: false时,prepareTools 会走强制 JSON 工具方案:把 Schema 包装成input_schema,并强制模型以tool_choice调用它:
// Return a JSON response tool when using non-native structured outputs if (options.responseFormat.type === "json" && !capabilities.supportsStructuredOutput) { const input_schema = yield* tryJsonSchema(options.responseFormat.schema, "prepareTools") return { tools: [{ name: options.responseFormat.objectName, description: `${description}You MUST respond with a JSON object.`, input_schema: input_schema as any }], toolChoice: { type: "tool", name: options.responseFormat.objectName, disable_parallel_tool_use: true } } }3.3 附带影响:工具strict与 beta 头
supportsStructuredOutput还影响工具调用的两处行为(AnthropicLanguageModel.ts):
strict标记:原生结构化输出能力为true时,用户工具会带上strict字段,其值取toolStrict ?? config.strictJsonSchema ?? true(默认开启严格 JSON Schema 校验);能力为false时strict为undefined,不发送。- beta 请求头:能力为
true时,会在请求中加入anthropic-beta: structured-outputs-2025-11-13头(源码中通过betas.add("structured-outputs-2025-11-13")收集,最终拼入params["anthropic-beta"])。
四、配置覆盖:structuredOutputs与其他相关选项
能力检测是自动的,但@effect/ai-anthropic的模型Config(AnthropicLanguageModel.ts)允许显式覆盖。在 makeRequest 中:
const modelCapabilities = getModelCapabilities(config.model!) const capabilities = Predicate.isNotUndefined(config.structuredOutputs) ? { ...modelCapabilities, supportsStructuredOutput: config.structuredOutputs } : modelCapabilities即:只要配置了structuredOutputs,就以配置值为准,覆盖能力表结论。相关配置项汇总:
| 配置项 | 类型 | 作用 |
|---|---|---|
structuredOutputs | boolean \| undefined | 覆盖自动能力检测:显式声明模型是否支持原生结构化输出 |
strictJsonSchema | boolean \| undefined | 工具调用是否启用严格 JSON Schema 校验(默认true) |
disableParallelToolCalls | boolean \| undefined | 禁用模型用多个工具响应的能力 |
output_config.effort | "low" \| "medium" \| "high" \| null | 设置推理成本档位,随output_config一起发送 |
max_tokens | 可选 | 未显式设置时,使用能力表中的maxOutputTokens作为默认值 |
五、Model枚举与 4-7 / 4-8 的前瞻兼容
Generated.ts 中的ModelSchema 枚举当前已包含claude-opus-4-6、claude-sonnet-4-6,以及尚未进入能力表显式分支的claude-opus-4-7、claude-opus-4-8。由于能力检测采用“未知模型继承现代默认值”策略,当claude-opus-4-7/claude-opus-4-8通过该枚举接入时,将自动获得与 4-6 相同的supportsStructuredOutput: true与 128000 token 上限,无需再更新能力表——这正是 changeset 中“claude-opus-4-7/claude-opus-4-8are classified the same way for when the generatedModelenum picks them up”的含义。
六、测试验证:行为被完整固化
仓库在 AnthropicLanguageModel.test.ts 中用捕获 HTTP 请求体的方式固化了下述行为(注入 mockAnthropicClient,断言max_tokens与output_config.format.type):
| 测试场景 | 模型 / 配置 | 断言结果 |
|---|---|---|
| Claude 4.6 原生结构化输出 | claude-opus-4-6 | max_tokens为 128000,output_config.format.type为json_schema |
| 未知未来模型使用乐观默认值 | claude-sonnet-6-0 | max_tokens为 128000,原生json_schema路径 |
| 冻结旧模型例外 | claude-sonnet-4-20250514 | max_tokens为 64000,且不发送output_config(走工具回退) |
| 现代模型显式关闭 | claude-sonnet-6-0+{ structuredOutputs: false } | 不发送output_config,且请求体不含structuredOutputs字段 |
| 旧模型显式开启 | claude-sonnet-4-20250514+{ structuredOutputs: true } | 发送output_config.format.type为json_schema,请求体不含structuredOutputs字段 |
这些用例直接印证了 changeset 的核心结论:修复后claude-opus-4-6走原生output_config.format(json_schema)路径,而旧模型(如claude-sonnet-4-20250514)仍保持冻结的工具回退行为;同时structuredOutputs覆盖机制可以让开发者在任意模型上双向切换。
七、实际使用方式
安装(@effect/ai-anthropic随 Effect v4 以 rc 版本发布,安装说明见 packages/ai/anthropic/README.md):
npm install effect@rc @effect/ai-anthropic@rc在claude-opus-4-6上使用原生结构化输出,与测试用例写法一致:
import { Effect } from "effect" import { Schema } from "effect" import { AnthropicLanguageModel } from "@effect/ai-anthropic" const Person = Schema.Struct({ name: Schema.String, age: Schema.Number }) Effect.gen(function*() { const result = yield* LanguageModel.generateObject({ prompt: "Give me a person", schema: Person }).pipe( Effect.provide(AnthropicLanguageModel.model("claude-opus-4-6", { // 可选:显式覆盖能力检测 // structuredOutputs: true })) ) return result })运行后,发往/v1/messages的请求体将包含output_config.format.type === "json_schema",max_tokens默认取能力表中的 128000。
八、总结
本次 changeset 是一次典型的“能力表纠偏”修复:通过让 4-6 世代模型落入getModelCapabilities的现代默认分支,generateObject从强制 JSON 工具回退切换到 Anthropic 原生约束解码路径,同时借由“未知模型继承现代默认值 +structuredOutputs显式覆盖”的双保险策略,为 4-7 / 4-8 等后续模型的无缝接入铺平了道路。整套行为都有源码(AnthropicLanguageModel.ts)与测试(AnthropicLanguageModel.test.ts)双重背书,可作为理解 Effect AI 结构化输出能力检测机制的完整参考。
【免费下载链接】t3code项目地址: https://gitcode.com/GitHub_Trending/t3/t3code
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考