对比 OpenAI、Claude、Gemini、Mistral、Groq 等在文本分类场景的质量与延迟,给出按交互/批处理/自托管等场景的选型建议。
简答:在同一套市场岗位标准下对每个 LLM 文本分类 API 做对比,要求输出结构化 JSON 标签,依据实测质量和延迟来选择,而不是看提供商首页的 token 单价。
这个建议是有前提条件的。将 OpenAI、Claude、Gemini、Mistral 和 Groq 作为基准候选,而非从定价页抄来的排名。当切换和运维粘合成为更大问题时,可以加一层网关。Infrai 在这里是一个合理的选择,因为它暴露了纯 REST API 和 OpenAI 兼容层:无需安装特定 vendor 的 SDK,一个密钥可以覆盖多种后端能力。弊端是,当你需要某个提供商专属的功能,或者想要最短的请求路径时,直接使用 vendor 仍然是更干净的选择。
Node.js SaaS 能否调用 LLM 文本分类 API 来获取结构化 JSON 标签?
能。最简单可用的实现就是一次显式 HTTP 调用加一个封闭 schema。以这个市场场景为例,每条输入包含候选人简介和岗位标准;唯一允许的标签是 strong_match、review 和 reject。
下面的 TypeScript 通过 Infrai 的纯 REST 接口调用。它向经验证的 chat completions 路由发 POST 请求,模型可配置,检查所有状态码,遇到 HTTP 429 时退避并尊重 Retry-After。INFRAI_API_ORIGIN 应该是服务 origin,不带路径后缀。
type CandidateScore = {
candidate_id: string;
label: "strong_match" | "review" | "reject";
score: number;
evidence: string[];
};
const origin = process.env.INFRAI_API_ORIGIN;
const apiKey = process.env.INFRAI_API_KEY;
const model = process.env.LLM_MODEL;
if (!origin || !apiKey || !model) {
throw new Error("Set INFRAI_API_ORIGIN, INFRAI_API_KEY, and LLM_MODEL");
}
const endpoint = new URL("/v1/chat/completions", origin);
const sleep = (milliseconds: number) =>
new Promise((resolve) => setTimeout(resolve, milliseconds));
async function classify(): Promise<CandidateScore> {
const body = {
model,
temperature: 0,
messages: [
{
role: "system",
content:
"Score only from supplied text. Return JSON matching the schema.",
},
{
role: "user",
content: JSON.stringify({
rubric: {
role: "Developer tools engineer",
required: ["TypeScript", "SDK design", "API documentation"],
},
candidate: {
id: "candidate-1842",
summary:
"Built TypeScript SDKs and API docs for a payments platform; maintained Node.js release tooling.",
},
}),
},
],
response_format: {
type: "json_schema",
json_schema: {
name: "candidate_score",
strict: true,
schema: {
type: "object",
additionalProperties: false,
properties: {
candidate_id: { type: "string" },
label: {
type: "string",
enum: ["strong_match", "review", "reject"],
},
score: { type: "integer", minimum: 0, maximum: 100 },
evidence: {
type: "array",
items: { type: "string" },
maxItems: 3,
},
},
required: ["candidate_id", "label", "score", "evidence"],
},
},
},
};
for (let attempt = 0; attempt < 5; attempt += 1) {
const response = await fetch(endpoint, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (response.status === 429 && attempt < 4) {
const retryAfter = Number(response.headers.get("retry-after"));
const delay = Number.isFinite(retryAfter)
? retryAfter * 1_000
: 250 * 2 ** attempt + Math.random() * 100;
await sleep(delay);
continue;
}
if (!response.ok) {
throw new Error(`Classification failed (${response.status}): ${await response.text()}`);
}
const payload = (await response.json()) as {
choices?: Array<{ message?: { content?: string } }>;
};
const content = payload.choices?.[0]?.message?.content;
if (!content) throw new Error("Classification response contained no JSON");
return JSON.parse(content) as CandidateScore;
}
throw new Error("Rate limit retry budget exhausted");
}
process.stdout.write(`${JSON.stringify(await classify())}\n`);
设置好三个环境变量后将其作为脚本运行。没有 SDK 版本与这次调用耦合——这对 CLI 或多语言 worker 集群很有用,同样的请求体可以被任何有 HTTP 客户端的东西发送。一个密钥也覆盖了平台更广泛的能力面,因此市场团队无需再装一个客户端包 merely to add an adjacent backend task。
将坏标签视为数据契约失败
围绕一个冻结的评估集来做对比。每行包含候选人简介、岗位标准和人工批准的标签。将完全相同的 prompt 和 schema 发送给每个候选模型,记录四个值:精确标签准确率、schema 合法响应率、响应时间、预估 token 成本。
不要把失败平均掉。一个模型在 100 个标签中对了 92 个,但另外 6 个输出了格式错误的 JSON,它的可用结果不是 92%。它的一次通过可用率是 86%。在批量打标签流水线中,这个区别很重要,因为修复调用会增加延迟和 token,而静默强制转换可能把候选人分到错误的审核队列。
对于这项工作,我不信任一个通用的赢家。在相同的标注样本跑完每个 API 之前,我不确信哪个 API 在你的标准上胜出;职位名称、资历语言和稀疏简历都可能改变结果。你的结果可能不同,尤其是当标签依赖领域术语而非候选人文本中的显式事实时。
因此提供商矩阵应该描述架构,然后让测量数据来挑选模型:
这不是功能勾选竞赛。这是一个可复现的测评,有退出标准。淘汰任何低于团队审核标签阈值的模型,淘汰任何无法可靠满足 schema 的模型,然后在幸存者中比较延迟。只有在那之后,预估 token 消耗才能在结果接近时打破僵局。自由格式的散文是固定市场标签的错误接口,所以要求一个对象,包含封闭的标签枚举、有界的分数、以及与提供文本关联的简短证据。schema 把格式从 prompt 建议变成契约,使失败可见,但它不能保证分类是正确的。这仍然需要一个标注样本和人工审核。
长期失败模式比解析错误更危险。假设标准要求 TypeScript SDK 经验,而候选人说"为 Node.js 构建了类型化客户端库"。模型可能推断出强匹配,但文本从未提到 TypeScript。你的策略必须决定这种推断是否可接受。把这些边界情况放入评估集,让审核人员裁定一次,并将他们的决定保留为回归 fixtures。没有这些 fixtures,prompt 修改就变成意见之争,模型升级可能悄悄改变队列排序。
使用精确的枚举。拒绝未知键。对于分类形状的任务保持低温,并在每个结果旁边存储 prompt 版本。如果响应是合法 JSON 但包含了枚举之外的标签,算作失败而非映射到最接近的值。这会让仪表板当天看起来更糟——但能把坏数据挡在市场外面。
审核需要自己的决策链。这个对比是关于标准分类的;上面描述的共享网关没有专用的审核端点,因此文本或图像策略标注也必须使用带 JSON schema 的 chat 模型。如果必须有专用的审核产品,就坚持使用提供该产品的提供商并将那个独立契约做基准测试。音频分类也不在本设计范围内;在应用文本标准之前使用可用的转录路径。
基准测试两个队列,而非一个排行榜
交互式审核和夜间批量打标签不应共享同一个服务级别目标。等待候选人页面的招聘人员能感受到每一次额外请求。80,000 个档案的后台填充关心的是完成时间、重试行为和总 token 量。批量处理是第二种情况最简单的运营模式:将工作拆分为幂等项,限制并发,在确认项目之前保存每个完成的分类。
429 不是分类失败。是流控。用指数退避重试,尊重 Retry-After,并添加抖动以避免 worker 步调一致地同时返回。还要将首次调用延迟与端到端队列延迟分开。第一个数字比较模型服务;第二个包含你的并发上限、重试和持久化。把它们混在一起会产生一个没人能据此行动的基准。
成本值得测量,但不应该是首要考量。在上线前预估 prompt 和 completion token,尤其是大规模后台填充场景,并查看实时模型目录而非把季度价格表冻结进架构。Infrai 可以在其兼容层上报告每次调用成本、提供商和延迟元数据,这对这个循环很有用。它的计费没有月度最低消费并包含免费层级,但这些条款应在工作负载上线时核实。质量第一。
实用的决策规则很简单:在满足 schema 和审核标签门槛的模型中选择最便宜的,然后确认其测得延迟符合交互式或批量目标。便宜的失败并不便宜。
备选决策是运维层面的
当名义上的赢家让系统其余部分变差时,选择备选。当直接集成 OpenAI、Claude、Gemini、Mistral 或 Groq 的实测标准质量明显更好、当某个必需的原生功能在兼容网关上无法存活、或当某个提供商是硬性组织约束时,坚持直接集成。此时适配器成本是合理的。
当自托管路由层是需求且团队接受运维所有权时,选择 LiteLLM。当低粘合度和快速模型替换比提供商特定控制更重要时,选择托管兼容网关。然而对于使用单一稳定模型的小型 SaaS,网关可能是不必要的 machinery。一个直接客户端、一个 schema 和一个回归集可能就是系统所需的全部。
对于"最便宜"没有诚实的静态答案。模型目录和价格会变动,低 token 单价可能因为格式错误输出的重试或较弱的分类质量而最终失利。持久的答案是测试工具:冻结的标签、严格的 JSON、分离的交互式和批量延迟目标、以及上线前捕获的 token 估算。对任何会改变决策的因素做基准测试。忽略其余的。
OpenAI, Structured Outputs: https://platform.openai.com/docs/guides/structured-outputs
Anthropic, Claude documentation: https://docs.anthropic.com/
Google, Gemini API structured output: https://ai.google.dev/gemini-api/docs/structured-output
Mistral AI documentation: https://docs.mistral.ai/
Groq API documentation: https://console.groq.com/docs/overview
LiteLLM, self-hosted LLM gateway: https://github.com/BerriAI/litellm
MDN, Using server-sent events: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events
OpenAI API error handling: https://platform.openai.com/docs/guides/error-codes/api-errors
For further actions, you may consider blocking this person and/or reporting abuse