Repository navigation
Custom Response Models
sungaowei edited this page Oct 16, 2026
·
2 revisions
English Version | 简体中文
SystemOneAs[T] 允许你将评估结果直接解码到自定义的 Go 结构体中,让编译器在编译期掌握每个业务问题答案的精确数据类型:
type TicketAnalysis struct {
Model string `json:"model"`
Usage typesafe.Usage `json:"usage"`
Tone typesafe.ChoiceAnswer `json:"tone"` // 自动从 answers["tone"] 提取并提升
Billing *typesafe.NoulAnswer `json:"billing"` // 可选字段:使用指针表示可能不存在
Target *typesafe.BoxAnswer `json:"target"` // 支持 Box 目标检测答案
Summary string `json:"summary,omitempty"`
}
ticket, err := typesafe.SystemOneAs[TicketAnalysis](ctx, client, state, typesafe.Questions{
"tone": typesafe.NewChoice(map[string]any{"calm": nil, "angry": nil}),
"billing": typesafe.NewNoul("该内容是否与账单相关?"),
"target": typesafe.NewBox("图片中的目标物体"),
}, typesafe.WithCallImages(true), typesafe.WithModel(typesafe.ModelJevPreview))
if err != nil {
return err
}
fmt.Printf("tone=%s billing=%.2f\n", ticket.Tone.Choice, ticket.Billing.Noul)
if ticket.Target != nil {
fmt.Printf("box: Top=%.2f Bottom=%.2f\n", ticket.Target.Box.Top, ticket.Target.Box.Bottom)
}注意:
SystemOneAs是包级泛型函数而非Client的方法,因为 Go 语言规范不允许在结构体的方法上声明独立的类型参数。
泛型结构体 T 可以声明顶层信封字段(model、usage、answers),并且针对每一个具体问题,都可以按问题名称声明对应的答案结构体字段。
每个同名字段都会自动从 answers 字典中提取对应条目并赋值。
- 真实的信封字段(如
model、usage)优先级高于同名问题,绝不会被意外覆盖; - 本版本未建模或未知的答案类型会被静默跳过,与
client.SystemOne规则保持一致。
若需要动态遍历或保留以问题名称为键的完整答案集合,可以在结构体中声明 Answers typesafe.Answers \json:"answers"``:
type GenericAnalysis struct {
Model string `json:"model"`
Usage typesafe.Usage `json:"usage"`
Answers typesafe.Answers `json:"answers"`
}
analysis, err := typesafe.SystemOneAs[GenericAnalysis](ctx, client, state, questions)
if err != nil {
return err
}
for name, answer := range analysis.Answers {
switch typed := answer.(type) {
case *typesafe.NoulAnswer:
fmt.Printf("%s: p(true)=%.2f\n", name, typed.Noul)
case *typesafe.ChoiceAnswer:
fmt.Printf("%s: %s (置信度 %.2f)\n", name, typed.Choice, typed.Confidence)
case *typesafe.ScoreAnswer:
fmt.Printf("%s: 评分=%.2f\n", name, typed.Score)
case *typesafe.BoxAnswer:
fmt.Printf("%s: 边界框=[%.2f, %.2f, %.2f, %.2f]\n",
name, typed.Box.Top, typed.Box.Bottom, typed.Box.Left, typed.Box.Right)
}
}typesafe.Answers 是 SDK 提供的专用字典类型,它在反序列化时能根据 JSON 中的 "type" 字段("noul"、"choice"、"score"、"box")动态构建正确的具体结构体指针。普通的 map[string]typesafe.Answer 无法做到这一点,因为接口类型无法得知该实例化哪一个具体实现。
在 T 的顶层字段中,所有字段默认均为必需字段,除非:
- 该字段可自然表示“缺省”(如指针
*T、map、slice、interface等,未出现时为nil); - 字段被打上了
json:",omitempty"或json:",omitzero"标签。
未打标签的非导出私有字段或匿名嵌入字段不会被强校验。
若服务端返回的响应中缺失了必填的答案字段,SDK 会抛出 *typesafe.APIResponseValidationError,明确指出缺失字段的路径(validationErr.FieldPath),防止程序因反序列化出零值而做出错误的业务判断(例如将缺失的概率字段作为 0.0 判定)。
var validationErr *typesafe.APIResponseValidationError
if errors.As(err, &validationErr) {
log.Printf("响应缺失必填字段: %s", validationErr.FieldPath)
}- 指南
- Guides