Go 调用大模型时结构化 JSON 仍要校验:Schema、拒答和截断响应的最小防线
接口已经要求大模型返回 JSON,线上却仍然出现了“能解析、不能入库”的记录:分类字段拼写错误、数组内容被截断,甚至模型直接返回了拒答文本。处理这类问题时,Go 服务不该只调用一次 json.Unmarshal 就把结果直接传给数据库,而要把响应状态、JSON 结构和业务语义分成三道独立的检查环节。
严格 JSON Schema 主要解决「数据形状是否符合事先约定」的问题,不会帮你判断请求是否触发拒答、输出内容是否完整,也不保证字段里的业务数值完全正确;Go 侧至少要保留状态检查、结构解析和业务校验三层防线。
要点速览
- 先判断响应是否正常结束,以及是否存在拒答或空内容的情况。
json.Unmarshal只负责校验 JSON 语法和字段类型,不负责枚举值合法性与内容长度校验。- 业务校验失败时保留原始请求 ID,禁止半成品数据直接写入存储。
- 流式响应要等完成事件或明确的结束状态返回后,再拼接得到最终的完整 JSON。
先分清:Schema 约束和业务规则不是一回事
结构化输出能让模型更稳定地贴合开发者给出的 JSON Schema 要求,例如要求返回 title、priority 和 labels 三个字段。官方说明也明确提到,严格 Schema 只覆盖标准 JSON Schema 的一部分规则;请求可能因为安全原因返回拒答,也可能在达到输出长度上限前被截断,字段值本身仍可能不符合业务要求。
放到 Go 服务的实现逻辑里,可以先画出一条清晰的边界:
- 响应层:校验 HTTP 状态、请求 ID、完成状态、拒答相关字段。
- 结构层:校验 JSON 是否完整,字段类型是否和预先定义的 Go 结构体匹配。
- 业务层:校验优先级范围、标签数量、标题长度和所有业务必填字段。

最小实现:用 Go 结构体挡住半成品数据
先定义专属的业务对象,不要用 map[string]any 接住所有返回字段。结构体可以把类型错误尽早暴露出来,也方便后续串联对应的业务规则检查。
package review
import (
"encoding/json"
"errors"
"fmt"
"strings"
)
type Review struct {
Title string `json:"title"`
Priority int `json:"priority"`
Labels []string `json:"labels"`
}
func ParseReview(raw []byte) (Review, error) {
var out Review
if len(strings.TrimSpace(string(raw))) == 0 {
return out, errors.New("empty model content")
}
if err := json.Unmarshal(raw, &out); err != nil {
return out, fmt.Errorf("invalid review json: %w", err)
}
if err := out.Check(); err != nil {
return out, err
}
return out, nil
}
func (r Review) Check() error {
title := strings.TrimSpace(r.Title)
if title == "" || len([]rune(title)) > 80 {
return errors.New("title is empty or too long")
}
if r.Priority 3 {
return fmt.Errorf("priority %d is outside 1..3", r.Priority)
}
if len(r.Labels) > 5 {
return errors.New("too many labels")
}
return nil
}
这里的关键不是写了多少条校验规则,而是校验失败时绝不返回「看起来还能用」的对象。调用方拿到错误后,要把请求 ID、模型状态和原始响应写入隔离日志,交给人工或者后续补偿流程处理。
把响应状态检查放在 JSON 解析之前
很多线上误判都来自检查顺序错误:服务看到一段字符串能被正常解析,就当成模型返回的最终结果。更稳妥的做法是先读接口的外层响应字段,再把最终文本交给 ParseReview。
type ModelEnvelope struct {
RequestID string `json:"id"`
Status string `json:"status"`
Refusal string `json:"refusal"`
Content string `json:"content"`
}
func AcceptEnvelope(e ModelEnvelope) error {
if e.Refusal != "" {
return fmt.Errorf("model refusal: %s", e.Refusal)
}
if e.Status != "completed" {
return fmt.Errorf("response is not completed: %s", e.Status)
}
if strings.TrimSpace(e.Content) == "" {
return errors.New("completed response has no content")
}
return nil
}
completed 只是示例状态名,实际接入时要以你所用接口的官方响应字段为准,不要把某一家 API 的字段名直接当成所有模型服务的通用协议。
流式返回:最后一段分片不等于完整 JSON
流式接口会把内容拆成多段事件逐步返回。中途断开时,前半段内容可能已经长得很像完整 JSON,但缺少结尾的闭合括号;如果代码在每个分片到达时都尝试入库,截断的数据就会混入正常业务记录。
正确的处理边界是:分片内容只写入内存缓冲,收到明确的完成信号后再调用一次 ParseReview 做统一解析。如果连接断开、完成状态缺失或者内容长度超过业务阈值,直接进入失败处理分支。对流式 JSON 做「每个分片独立解析」通常没有实际意义,因为字符串、数组和转义字符都有可能跨分片拆分。

三个容易漏掉的边界场景
拒答不是格式错误
拒答内容可能是完全合法的字符串,甚至外层响应状态仍然显示正常。它应该单独标记为「模型未提供有效业务结果」,不要伪装成 JSON 解析失败,也不要直接拿默认空对象继续往下走流程。
截断不是低优先级告警
达到输出上限时,模型可能只返回 JSON 的前半段内容。就算这段残缺内容偶然能被解析,也要结合完成状态和响应元数据综合判断;没有明确的结束信号,就不要把结果视为可正常落库的有效数据。
合法值不代表正确值
{"priority":2} 在语法和类型上都没有问题,但它是否符合当前工单的风险等级,仍要由业务规则或者后置人工审核决定。严格 Schema 不能替代实际业务逻辑的事实核对。
落库前的最后一道校验门
实际服务可以把三个阶段的错误统一封装成可观测事件,但不要把它们混成一个笼统的「AI 调用失败」。建议至少记录 request_id、接口状态、完成状态、解析错误类型、业务校验错误和重试次数这些维度的信息。入库动作只接收已经通过 AcceptEnvelope 与 ParseReview 的对象。
如果业务允许配置重试,重试条件也要做限制:网络中断、明确的临时服务错误可以发起重试;拒答、字段语义冲突和连续截断这类场景更适合直接送入隔离队列。重试前要保留完整的原始响应,方便后续定位问题根源是模型侧、提示词侧还是调用层出了问题。
相关问题
JSON Schema 严格模式能保证字段内容真实吗?
不能。它只能约束结构和类型,字段值仍可能不符合业务事实,需要额外加规则校验、数据库核对或者人工审核环节。
能不能只用 map[string]any 接模型返回结果?
临时做技术探索可以,生产链路不建议。结构体加显式校验的模式更容易发现字段改名、类型变化与空值这类隐性问题。
流式输出什么时候可以写入数据库?
收到接口定义的完成信号、拼接出来的完整内容通过 JSON 解析,并且所有业务规则校验全部通过之后。任一条件缺失都应该先把数据做隔离处理。
模型返回拒答时应该重试吗?
先确认拒答原因。安全类拒答不适合盲目重复请求;如果是临时网络或服务波动错误,再依据接口状态和预设重试策略处理。
小结
Go 接入大模型时,把 Schema 当成第一道护栏就够了,后面仍需要响应状态检查、完整性判断和业务语义校验。代码量不需要很大,但每道门都要有明确的失败处理逻辑:不返回半成品数据、不吞掉原始响应、不把合法 JSON 误当成完全正确的业务数据。
浏览器长任务怎么排查:用 PerformanceObserver 定位 50ms+ 主线程卡顿
- 上一篇
- 浏览器长任务怎么排查:用 PerformanceObserver 定位 50ms+ 主线程卡顿
- 下一篇
- Go 1.26.5 和 1.25.12 发布:crypto/tls、os 修复后,生产服务怎么判断升级窗口
-
- 科技周边 · 人工智能 | 17分钟前 | openai api · 结构化输出 · AI工程 · Pydantic JSON Schema Structured Outputs
- Structured Outputs让模型结果贴合 JSON Schema的实现方法
- 191浏览 收藏
-
- 科技周边 · 人工智能 | 2小时前 | 人工智能 · 结构化输出 · JSON解析 · 模型输出 · 接口容错 · 大模型结构化输出 模型输出JSON缺字段 JSON兜底解析 JSON Schema校验 AI接口异常处理
- 模型输出 JSON 缺字段时如何设计兜底解析
- 272浏览 收藏
-
- 科技周边 · 人工智能 | 4小时前 | 人工智能 · 显存管理 · 推理优化 · 本地推理 KV Cache batch size
- 本地推理 KV cache 和 batch size 如何做取舍
- 251浏览 收藏
-
- 科技周边 · 人工智能 | 5小时前 | API · 性能优化 · ai · AI 提示词缓存 Responses API Prompt Caching
- AI 提示词缓存如何按稳定前缀组织请求
- 357浏览 收藏
-
- 科技周边 · 人工智能 | 6小时前 |
- 分类评测集不均衡时如何比较 macro 与 micro 指标
- 473浏览 收藏
-
- 科技周边 · 人工智能 | 7小时前 |
- Agent 工具返回文件路径时如何限制工作区范围
- 381浏览 收藏
-
- 科技周边 · 人工智能 | 9小时前 |
- AI 流式响应中的 finish_reason 如何决定持久化时机
- 195浏览 收藏
-
- 科技周边 · 人工智能 | 10小时前 |
- LoRA adapter 合并后 tokenizer 配置如何核对
- 162浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 42次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 137次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 73次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 38次使用
-
- CMMLU
- 深入了解CMMLU中文评估基准,涵盖67个学科主题,提供数据集下载、Zero-shot/Five-shot评估方法及排行榜,助力优化中文语言模型性能。
- 24次使用
-
- 接口返回 200 但前端仍报错怎么办:从响应格式到跨域一步步排查
- 2026-06-14 332浏览
-
- Go map 并发写 panic 怎么办:从共享 map 到可控写入路径
- 2026-06-30 123浏览
-
- golang生成JSON以及解析JSON
- 2023-01-17 329浏览
-
- Go如何实现json字符串与各类struct相互转换
- 2023-01-07 377浏览
-
- Go Excelize API源码阅读SetSheetViewOptions示例解析
- 2022-12-24 485浏览

