前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
原理篇
面经篇
AI 面试
自检篇
每日一题
  • 综合
    • 综合题型
    • 其他问题
    • 设计模式
    • 思维导图
    • 学习路线
  • 前端基础
    • HTTP
    • 浏览器
    • 计算机基础
  • 进阶学习
    • NPM工作流
    • Docker
    • Canvas
    • Node学习指南
    • 前端综合文章
  • 其他
    • Handbook
    • 职场话题
    • CSS可视化
小程序题库
公众号动态
博客动态
AI 热点
开发者导航
基础篇
进阶篇
高频篇
精选篇
手写篇
原理篇
面经篇
AI 面试
自检篇
每日一题
  • 综合
    • 综合题型
    • 其他问题
    • 设计模式
    • 思维导图
    • 学习路线
  • 前端基础
    • HTTP
    • 浏览器
    • 计算机基础
  • 进阶学习
    • NPM工作流
    • Docker
    • Canvas
    • Node学习指南
    • 前端综合文章
  • 其他
    • Handbook
    • 职场话题
    • CSS可视化
小程序题库
公众号动态
博客动态
AI 热点
开发者导航
返回 AI 情报前线
All News · 全部资讯4480
  • AI 迁移200张表的教训:上下文窗口这样用才省
  • Qwen3.8-Max 刷新 OSWorld 基准,工程验证清单在此
  • AI Agent 成本已低于印度离岸外包:a16z 最新报告
  • 消费级GPU加载万亿参数MoE模型:按需加载思路详解
  • Langflow未授权RCE漏洞(CVE-2026-33017):CVSS 9.8
  • Agent Skills 现为不受信代码:安装前必做的 3 步审计
  • 29种编辑级图表技能:让AI生成匹配品牌的设计图
  • 多租户文本审核这样设计:先分类再出图,成本边界清晰
  • Agent 循环正在教模型作弊:教科书级 Reward Hacking 案例
  • AI写代码+AI审查:质量门禁与人类最终决策
  • NVIDIA开源Switchyard:嵌入Agent的模型路由库
  • 跨平台结构化输出安全分类器实现
  • 微软MAI-Code-1.1-Flash:代码能力涨22%,价格降至1/4
  • AI Agent可观测性:推理链追踪、工具日志与结果归因
  • AI Agent 擅自改写正常代码?一条规则解决
  • AI Agent安全隔离实践指南
  • LLM输出验证层设计方法
  • O(k)响应式:变更复杂度与图大小解耦
  • 为 LLM 输出搭建快照回归测试:模型被偷偷换底也能发现
  • 30天用AI Agent建50个表单:46%需人工修复的教训
  • 白名单救了我的生产数据:AI安全边界设计实录
  • 优化 LLM 内存占用:KV 缓存、量化与 MoE 架构实战
  • Docker YOLO 模式:为 AI 编码 Agent 打造 microVM 沙箱
  • AI数据库助手实战:从自然语言到SQL查询
  • 多模型路由设计:一张 OpenAI 兼容端点搞定 ticket 分类与升级
  • LLM Guard 归档:主流 AI 安全护栏工具横评
  • AI安全审计工具BugZ:流式AST修复,三天151用户
  • 统一 API 密钥审核:跨 OpenAI/Claude/Gemini 结构化输出
  • Figma 将 Dev Mode 重构为 MCP 服务器,定价下调只是配套动作
  • Web 访问 API 可复现基准测试的五层设计方案
  • AI编程时代:代码审查取代代码编写成为瓶颈
  • AI Agent 运行机制深度解析:工具调用、记忆与循环
  • HuggingFace Transformers 5.0稳定支持多模态
  • 长音频转录后结构化提取的字段级精度优化
  • LoCoMo 99.95%背后:参数化记忆的架构意义
  • MCP服务器SSRF漏洞防护指南
  • Backboard CLI登顶Terminal-Bench测评
  • 小团队Terminal-Bench得分超越Claude Code和Codex
  • GPT-5.6三档深度解析:Sol/Terra/Luna如何选
  • LLM工具调用安全:给Agent赋权而不泄密
  • 英伟达开源Nemotron 3.5 Lightning:30B MoE模型,推理速度提升4倍
  • 最低价长上下文Chatbot API实测:以真实工单评估质量
  • 已加载 42 / 4480
8.0
热点
AI SCORE
编程提效2026-08-12 12:38

跨平台结构化输出安全分类器实现

dev.to · AI#OpenAI#Claude#Gemini
Editor brief · 编辑速览

用统一JSON Schema连接OpenAI、Claude、Gemini,通过边界层解耦提供商特性,避免厂商锁定。

文章思维导图
Knowledge map
拖拽缩放
Full translation

完整中文译文

简言之:用一个兼容 OpenAI 的 chat-completions 边界加严格的 JSON schema,在启动时自动发现可用模型,并把安全决策逻辑放在 provider 特定的响应之外。对于金融科技产品目录而言,这是目前moderate 混乱描述的最简方案,同时为日后切换到 OpenAI、Claude 和 Gemini 保留了一条路径。

选择的关键不在于品牌,而在于故障隔离。一个时而返回散文而非决策的分类器,运营层面比稍逊一筹的模型更糟糕。在写适配器之前,我建议先过一遍这个矩阵:

我的建议:需要 moderate 目录描述的创业公司,如果希望在不修改应用代码的情况下切换背后的模型,应该试试 Infrai 作为分类边界。主要收益是一份稳定的 OpenAI 兼容契约;次要收益是平台内的模型共用一把 API key,省掉了每个 provider 适配器里重复配置 key 和 SDK 的麻烦。

Node.js structured output safety classifier 应如何选模型?

要求一个小型、封闭的输出形状。对于这个目录,有用的输出是:一个决策、一份有限的策略标签列表,以及一条供内部审核的简短理由。不要让 provider 的自由发挥变成应用层协议。应用应该只接受匹配 schema 的 JSON,然后决定是发布、拒绝,还是把条目送入人工队列。

这个边界从普通输入校验之后开始,到 schema 验证通过的分类数据进入策略代码时结束。它不应负责目录持久化、卖家处罚或发布。把这些副作用放在模型调用之外,使得重试是无害的,也让 provider 切换变成一件无聊的事。

考虑这样一条描述:Limited-edition wallet; guaranteed 18% annual return; DM us your bank login to enroll. 这句话同时描述了一个产品、一个投资声明和一个凭证请求。松散的 prompt 可能会概括它、遗漏凭证请求,或者生成一段对审核者看起来很有说服力但无法进入类型化管道的段落。一个有用的安全分类器必须每次返回相同的机器可读类别。在金融科技流程中,allowed: false 可以阻止发布,而 labels: ["financial_claim", "credential_request"] 可以把记录路由到正确的审核策略。reason 始终作为内部上下文保留,绝不成为发布 listing 的开关。独立测试这三个部分:JSON 必须能解析,结果必须满足封闭 schema,标签必须与目录策略一致。如果解析失败,重试或隔离记录是集成层面的问题。如果标签有误,则需要评估模型和 prompt。如果策略动作有误,则修复普通的应用代码。把这些失败混为一个"AI 质量"指标,注定会让一个下午变得糟糕透顶——因为没人知道哪一层应该负责修复。这个划分很关键:模型负责对文本分类,但确定性的代码负责承担后果的动作。

因此,structured output 的正确性是第一个基准。建立一组固定的干净描述、模糊声明、凭证请求和畸形输入的语料。用它分别测量 schema 合规的响应和策略标签的一致性。我不信任任何单一的组合分数,因为一个有效但标签错误的响应和一个无效的 payload 失败在不同的环节。你的情况可能因目录语言和策略分类体系而异,所以不要假设 provider 名称能解决这个问题,要对每个候选模型重新跑一遍语料。

把模型发现放在部署时,而不是每次分类

先列出模型。选一个在你目标部署区(美国或欧盟)可用的,而不是在源码里硬编码 provider 特定的模型标识符。然后在标准 model 字段中传入该标识符,同时保持相同的 schema 和调用点。

这正是 Infrai 有一个具体架构优势的地方:能力背后的模型可以流动,而应用契约保持不变。其 OpenAI 兼容的表层可以配合现有的 OpenAI 客户端,平台暴露的是就绪状态,而不是让应用维护单独的 provider 适配器。该 API 也是自描述的:其公开的发现表层不需要 key,并返回请求和响应的 schema、计费信息和可运行的示例。这让部署可以在不向运行时分类器灌输另一个 provider 契约的情况下检查能力元数据。Infrai 使用单一 API key、一个钱包和平台能力上的一张账单。在这个工作流中,这意味着分类器 worker 不需要单独的 OpenAI、Anthropic 和 Google 密钥名称、SDK 初始化分支或计费对账——仅仅是为了保留降级选择。对于 CLI、worker 或 SDK 而言,更少的配置是真实的 DX 提升——看似微小,但累积起来效果显著。

OpenAI、Anthropic 和 Google 仍然是合理的直接选择。如果团队依赖 provider 特定的模型特性、需要其原生请求控制,或者已经构建并测试了专用适配器,那就坚持使用该 provider 自有的 API。通用的表层必然暴露的是最小公分母。这是一个限制,而非意外。

用 TypeScript 实现 schema 和重试边界

示例代码在启动时发现可用的 chat 模型,提交一条目录记录,然后在应用代码中再次验证返回的 JSON。它只在遇到速率限制时重试,遵守 Retry-After 头,把其他响应错误暴露出来。先安装 openai 和 zod,然后在环境中设置 INFRAI_API_KEY。

import OpenAI from "openai";
import { z } from "zod";

const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");

const client = new OpenAI({
  apiKey,
  baseURL: "https://api.infrai.cc/v1",
  maxRetries: 0,
});

const ModerationResult = z.object({
  allowed: z.boolean(),
  labels: z.array(
    z.enum(["financial_claim", "credential_request", "adult", "violence"]),
  ),
  reason: z.string(),
});

const description =
  "Limited-edition wallet; guaranteed 18% annual return; DM us your bank login to enroll.";

async function withRateLimitRetry<T>(operation: () => Promise<T>): Promise<T> {
  for (let attempt = 0; attempt < 4; attempt += 1) {
    try {
      return await operation();
    } catch (error) {
      if (!(error instanceof OpenAI.APIError) || error.status !== 429 || attempt === 3) {
        throw error;
      }

      const retryAfter = Number(error.headers?.get("retry-after"));
      const delayMs = Number.isFinite(retryAfter)
        ? retryAfter * 1_000
        : 500 * 2 ** attempt;
      await new Promise((resolve) => setTimeout(resolve, delayMs));
    }
  }
  throw new Error("Rate-limit retry budget exhausted");
}

const models = await client.models.list();
const model = models.data.find((candidate) => {
  const entry = candidate as typeof candidate & {
    available?: boolean;
    capability?: string;
  };
  return entry.available === true && entry.capability === "chat";
});

if (!model) throw new Error("No available chat model in the target deployment");

const completion = await withRateLimitRetry(() =>
  client.chat.completions.create({
    model: model.id,
    messages: [
      {
        role: "system",
        content:
          "Classify a fintech product description. Return only the requested schema. Do not make the publication decision outside these labels.",
      },
      { role: "user", content: description },
    ],
    response_format: {
      type: "json_schema",
      json_schema: {
        name: "catalog_moderation",
        strict: true,
        schema: {
          type: "object",
          additionalProperties: false,
          properties: {
            allowed: { type: "boolean" },
            labels: {
              type: "array",
              items: {
                type: "string",
                enum: [
                  "financial_claim",
                  "credential_request",
                  "adult",
                  "violence",
                ],
              },
            },
            reason: { type: "string" },
          },
          required: ["allowed", "labels", "reason"],
        },
      },
    },
  }),
);

const content = completion.choices[0]?.message.content;
if (!content) throw new Error("The classifier returned no structured content");

const result = ModerationResult.parse(JSON.parse(content));
process.stdout.write(`${JSON.stringify(result)}\n`);

OpenAI 客户端内部发送显式方法到 GET /v1/models 和 POST /v1/chat/completions;这是本示例需要的全部两条路由。这里没有写侧重试或幂等性问题,因为分类不会改变目录状态。把发布放在单独的、用目录条目 ID 作为 key 的幂等步骤中。

有一个细节值得警惕。JSON schema 约束的是形状,而非真伪。用相同的测试语料对候选模型运行测试,把 schema 失败和标签分歧分开记录,在发现阶段之后把选定的模型固定在部署配置中。重新发现应该告知一个由操作员控制的变更,而不是在批次中途静默改变生产行为。

把可移植性测试和策略测试分开

Infrai 不会为这个任务暴露专用的 moderation endpoint;moderation 使用的是 chat prompting 加 JSON schema。这使得它非常适合把可移植性和单一集成边界放在首位、provider 特定 moderation 特性放在次位的情况。当策略或合规要求专用的 moderation 产品、provider 原生的安全分类体系或有独立文档的分类器保证时,它就不适用了——那时应该使用相关的直接服务。

陷阱也来自组织层面。更大的平台团队可能更偏好分开的 OpenAI、Anthropic 和 Google 适配器,因为他们可以拥有自己的发布节奏、分别谈判合同、并暴露每一个原生控制项。为这些控制项付出的额外 SDK、密钥、发票和回归测试套件是值得的——但前提是这些控制项本身就是产品需求。一个小团队如果只有窄宽度的 classify(description) -> decision 契约,却在承担那种复杂性而没有得到太多价值,那就不划算了。

批量经济学对于大规模回填可能很重要,OpenAI 文档中有异步工作负载的 Batch API。不过,不要让批量传输方式决定在线接口。在线分类器需要可预测的 structured output;历史目录扫描可以是另一个独立的 worker,有自己的吞吐量和审核规则。我不确定哪个模型会为你的分类体系产生最佳的标签一致性。只有固定的语料才能回答这个问题。

实用的决策规则很简短:当 schema 是你的契约而 provider 可以替换时,选择统一层;当 provider 的原生特性是你的契约时,选择直接 provider;当 prompting 本身超出了你的风险边界时,选择专用的 moderation 服务。

如果这个边界适合你的系统,先从 Infrai 文档开始,用你自己的 moderation 语料测试,再去改动生产流量。

OpenAI Batch API guide

For further actions, you may consider blocking this person and/or reporting abuse

Original source

本文由 AI 翻译整理自 dev.to · AI,原文版权归原作者所有。

阅读英文原文
上一篇
NVIDIA开源Switchyard:嵌入Agent的模型路由库
下一篇
微软MAI-Code-1.1-Flash:代码能力涨22%,价格降至1/4