# 结构化输出

声明 JSON Schema，读取 StructuredOutput，并验证结果后再交给业务代码。

## 让结果交给程序使用

摘要可以作为文本展示，订单分类、风险清单和工作流分支则需要固定结构。使用 `WithOutputFormat` 声明 JSON Schema，再从最终结果的 StructuredOutput 读取数据。

Schema 描述预期结构，不代表事实已经经过业务验证。运行时和所选模型还必须支持该输出方式；配置被接受不等于每个任务都会成功得到对象。

## 声明输出结构

```go
options = options.WithOutputFormat(&client.OutputFormat{
    Type: "json_schema",
    Schema: map[string]any{
        "type": "object",
        "properties": map[string]any{
            "summary": map[string]any{"type": "string"},
            "needs_review": map[string]any{"type": "boolean"},
        },
        "required": []string{"summary", "needs_review"},
        "additionalProperties": false,
    },
})
```

任务提示应说明每个字段的业务含义，例如缺少材料时 needs_review 为 true。不要仅给 Schema 而省略判断依据，也不要把“必须输出 false”当作完成验收。

## 读取结果

```go
if result.IsError {
    return fmt.Errorf("结构化任务失败：%s %v", result.Subtype, result.Errors)
}
if result.StructuredOutput == nil {
    return fmt.Errorf("运行时没有返回结构化输出")
}
payload, err := json.Marshal(result.StructuredOutput)
if err != nil { return err }
var output struct {
    Summary string `json:"summary"`
    NeedsReview *bool `json:"needs_review"`
}
if err := json.Unmarshal(payload, &output); err != nil { return err }
if output.NeedsReview == nil {
    return fmt.Errorf("缺少 needs_review，不能进入后续流程")
}
if strings.TrimSpace(output.Summary) == "" {
    return fmt.Errorf("摘要为空，不能交付")
}
```

片段使用 encoding/json 和 strings。读取 StructuredOutput，不要假设 `Result` 文本就是 JSON，更不要通过截取第一对大括号解析混合回答。

## 验证结构与事实

反序列化到 Go struct 不会替你检查所有 Schema 约束，也不能证明记录 ID 真实存在。入库前应验证必填值、枚举范围、数量上限和数据归属。会触发后续操作的字段，还要经过宿主权限检查。

需要区分“字段缺失”和 false、0 等零值时，在验证层使用指针或先检查原始对象。不要把缺失的 needs_review 默认为 false 后自动放行。

## 失败与重试

| 结果 | 处理方式 |
| --- | --- |
| Go error | 按初始化、连接、取消或读取错误排查 |
| IsError 为 true | 保留 Subtype 和 Errors，不能消费为业务对象 |
| StructuredOutput 为空 | 报告缺失，不回退到随意解析自然语言 |
| JSON 可读但业务验证失败 | 标记需要复核，或提交明确的纠正任务 |

如果任务还执行了写入，重新生成输出前要先确认副作用是否已经发生。只重试结果提取与重做整个任务应是两条不同操作。详见[错误处理](/docs/sdk-errors)。
