Node.js接入模型时,用Schema守住输出契约
文章以平台文本摘要与审核报告为例,建议在模型请求边界声明JSON Schema,并在结果进入下游队列前解析、校验。Schema由应用持有,供应商适配层保持轻量,字段扩展配套新增评估用例。
AI 深度解读
市场平台想用 AI 整理举报内容,难点往往在模型返回之后:结果能否稳定入库,审核页面能否直接渲染,换模型会不会把队列消费者弄坏。作者的主张是,把 JSON Schema 放到模型调用边界,再由应用校验返回值,别只靠提示词要求「输出合法 JSON」。
这篇值得读的地方,是把结构化输出放回了生产流程:接口适配、错误重试、字段迁移和质量评测必须一起考虑。不过,标题虽然写着 Node.js,可运行示例实际使用 Python,也没有给出性能或准确率对照数据。
合法 JSON,为什么还不能直接交给审核页面
作者建议先把举报摘要收敛成三个字段:summary 保存摘要,key_points 保存待核查的要点,entities 保存实体列表,每个实体包含 name 和 type。
这套结构对应的是明确的消费方式。举报里出现卖家、商品和买家的原话,审核页面可以根据实体数组展示对象,根据要点列表提示审核员检查哪些主张。前端不必再从一整段自然语言里猜测信息的位置。
但能被 JSON.parse 解析,只证明文本符合 JSON 语法。缺少 entities、把摘要写成对象、多返回一个未知字段,都可能是合法 JSON,却不符合应用约定。
JSON 解析成功,只代表语法过关,不代表数据可以交给下游。
原文的 Schema 在顶层要求三个字段全部存在,并通过 additionalProperties: False 拒绝额外属性;实体对象同样要求 name、type,且不允许多余属性。
这个约束也有边界。原文虽然希望摘要简短,但 Schema 只要求 summary 是字符串,没有限制长度,也没有要求数组非空。字段类型过关,并不等于产品要求全部满足。
后续增加 severity 或策略分类,也不能只改一处字段定义。作者特别提醒:旧 worker 如果把缺失的严重程度默认为「低」,一次字段迁移就会悄悄变成审核决策。因此,Schema 版本应与结果一起保存,新增字段要配套评测用例,并明确旧消费者如何处理。
请求可以共用,供应商行为仍要分别验证
可迁移性的关键,是由应用持有 Schema,让供应商适配器只处理请求差异、认证和返回值归一化。
原文通过 OpenAI-compatible 接口调用模型。下面这段配置把结构要求直接放进请求,代码保留原样:
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "moderation_summary",
"schema": SUMMARY_SCHEMA,
"strict": True,
},
},
要看的是 schema 引用应用维护的 SUMMARY_SCHEMA,以及 strict: True。不过,这些参数能否按预期生效,仍要验证具体供应商和模型的支持情况。
完整示例使用 requests 发起 HTTP POST,请求地址为 {base_url}/chat/completions。INFRAI_BASE_URL、INFRAI_API_KEY、SUMMARY_MODEL 都来自环境变量,认证使用 Authorization: Bearer。这些配置便于切换服务,但换掉地址和模型名,只完成了连接层的迁移。
OpenAI 的 Schema 响应、Anthropic 的 tool call、Gemini 的 JSON mode,是不同的接口机制。即使最后都能得到相同对象,它们的拒答方式、失败条件和响应表现也未必一致。
作者建议让候选服务运行同一批固定用例,比较解析成功率、字段有效性、拒答行为、延迟和 token 用量。审核任务持续进入队列,结构再漂亮,太慢或成本过高也不适合上线。
文章对 Infrai 的介绍带有明确的选型倾向:公开、自描述的发现接口提供请求与响应 Schema、可运行示例;平台宣称覆盖 20 个模块、295 条路由,并统一密钥和账单。这些能减少接入工作,但文章没有给出与其他方案的实测对照。作者也承认,当模型表现、区域控制或现有运维工具更重要时,直连供应商可能更合适。
校验、重试和人工兜底,要组成完整路径
模型返回后,作者设置了两道关卡:先解析 JSON,再用请求中的同一份 Schema 校验对象。
下面这段负责从响应中取出内容,并在返回结果前执行校验:
body = response.json()
content = body["choices"][0]["message"]["content"]
result = json.loads(content)
validate(instance=result, schema=SUMMARY_SCHEMA)
return result
要注意最后两行的顺序:validate 通过之后才 return。解析或校验失败时,不应再用正则从自然语言里补字段,而应进入明确的失败路径。
作者建议记录供应商、模型、请求 ID 和 Schema 版本,并将校验失败与 HTTP 失败分开统计。前者可能需要调整契约、适配器或模型,后者更可能涉及限流、认证或服务可用性。
重试策略也很具体:最多尝试 4 次,单次请求设置 timeout=30;遇到 HTTP 429,优先读取 Retry-After,没有该响应头时按 2 ** attempt 指数退避。其他非成功响应抛出错误,包含状态码和响应正文,便于查看 4xx 原因。预算耗尽后,应记录失败并转入人工审核兜底,不能让报告无限等待。
这里需要区分设计建议和示例实际完成的工作。代码展示了有限重试和格式校验,但没有实现失败记录、人工分流,也没有捕获网络异常、JSON 解析异常或校验异常。Retry-After 还被直接转成浮点数,没有展示日期形式的处理。
作者认为这次摘要调用不产生业务写入副作用,因此没有加入客户端幂等键;如果重试覆盖发布或入队,就需要重新设计,避免重复操作。
更关键的是,结构校验无法判断摘要是否忠实、审核含义是否正确。评测集还要覆盖空举报、多实体、多语言,以及要求模型忽略审核任务的文本。本文也不假定存在专用 moderation endpoint:文本和图片审核依赖 chat model 与 JSON Schema 兜底,策略标签及安全评测由应用负责。
我的判断
我认同把 Schema 当作应用契约。对前端和 Node 服务来说,稳定字段能减少展示层防御性分支,也让供应商切换的影响更容易定位。相比只在提示词里要求 JSON,这个方案增加了可执行的运行时检查。
但它是一套工程建议,不是已被对照实验验证的最佳实践。作者没有公布校验失败率、审核准确率、延迟或成本数据,也没有提供 Node.js 实现。平台路由多、密钥统一,只能说明接入便利,不能证明某个模型更适合审核任务。
格式正确和审核正确,必须分别验收。
普通团队还要考虑迁移成本:如果消费者已经积累很多字段和默认值,把 Schema 收紧并不只是改请求参数。应先弄清现有数据、worker 和页面实际依赖什么,再决定哪些字段必须拒绝、哪些允许兼容。
能拿来做什么
- 给 Node 摘要服务定义一份由应用维护的 Schema,从
summary、key_points、entities起步;将返回内容视为未知数据,完成解析和运行时校验后,再交给存储与审核页面。 - 在保存归一化结果时一并保存 Schema 版本,并分别记录 HTTP 失败与校验失败,方便判断该修接口配置还是输出契约。
- 切换模型或供应商前,用同一批举报样本检查格式、内容含义、拒答、延迟和 token 用量,避免仅凭几条正常响应决定迁移。
- 给
429设置有限重试和明确的人工兜底;如果流程还包括入队或发布,单独设计写入重试,防止生成重试引发重复业务操作。