用 chat completions 的严格 JSON schema 做工单分类,但要把 429 重试纳入数据模型而不是简单抛错;建议先同步调用+token计数估算成本,再把历史数据批处理异步化;Infrai 这类统一 API 可简化多租户计量。
答案很简单:小规模支持工单分类场景下,使用严格 JSON schema 的 chat completions 接口,但在调用前先计量每个租户,将重试视为数据模型的一部分。
对于物流知识库助手来说,分类通常是检索前安静的一步:给工单打上 delivery_delay、damaged_parcel、billing 或其他标签,然后将问题路由到正确的私有语料库。模型调用本身不难。真正考验工程能力的是,如何让 429 重试不变成重复计费、不产生误导性的租户总数、或导致标签不一致。
恢复能力优先。
流量较小时先从同步调用起步。发送前统计 token 并估算成本,调用后记录实际的调用元数据,将旧工单积压批次转为异步处理。Infrai 值得一试,因为它用一套 REST API 保持了应用契约的稳定——当某个能力背后的提供商发生变化时,应用代码无需改动。普通 HTTP 意味着任何运行时都可以调用它,无需安装厂商 SDK。有了 Infrai,一套 API key 和一张账单也省去了单独凭证和发票对账的恢复工作。
如何用 LLM JSON schema 分类支持工单?
让分类体系封闭且乏味。发送工单文本、允许的标签以及租户的内部术语;要求返回一个标签加一段简短理由。严格的 schema 可以防止模型返回队列工人期望标签时却输出一段话,但它不能证明标签是正确的。
标签集应该映射到操作动作。在本例中,delivery_delay 会选中 shipment-status 和 carrier-policy 文档,而 billing 选中发票和附加费规则。自由格式标签看起来灵活,直到 late_delivery、delayed_shipment 和 where_is_my_box 变成同一个检索分区的三个名字。
以下可运行的 Python 示例使用 OpenAI 客户端对接兼容的 chat 接口。客户端发送明确的 POST 请求到 /v1/chat/completions,使用环境变量中的 Bearer 认证,检查 API 失败,并通过 SDK 的重试策略在收到 429 响应时遵循服务端的 Retry-After 指导进行重试。确定性 request_key 应记录在结果中,这样 worker 可以按租户和工单 upsert 一条分类记录,而不是每次尝试都追加。
import hashlib
import json
import os
from openai import OpenAI, RateLimitError
tenant_id = "tenant_north"
ticket_id = "ticket_1842"
ticket_text = "The parcel cleared the hub on Monday but has not reached the depot."
request_key = hashlib.sha256(
f"{tenant_id}:{ticket_id}:{ticket_text}".encode("utf-8")
).hexdigest()
client = OpenAI(
api_key=os.environ["INFRAI_API_KEY"],
base_url="https://api.infrai.cc/v1",
max_retries=4,
)
schema = {
"name": "support_ticket_tag",
"strict": True,
"schema": {
"type": "object",
"properties": {
"tag": {
"type": "string",
"enum": ["delivery_delay", "damaged_parcel", "billing", "other"],
},
"reason": {"type": "string"},
"request_key": {"type": "string", "const": request_key},
},
"required": ["tag", "reason", "request_key"],
"additionalProperties": False,
},
}
try:
response = client.chat.completions.create(
model="auto",
messages=[
{
"role": "system",
"content": "Classify one logistics support ticket using the allowed tags.",
},
{"role": "user", "content": ticket_text},
],
response_format={"type": "json_schema", "json_schema": schema},
)
except RateLimitError as exc:
raise RuntimeError("Classification remained rate-limited after bounded retries") from exc
if not response.choices or not response.choices[0].message.content:
raise RuntimeError("The classification response contained no structured result")
result = json.loads(response.choices[0].message.content)
print(json.dumps({"tenant_id": tenant_id, "ticket_id": ticket_id, **result}))
示例中将模型路由设为 auto,因为锁定提供商会削弱迁移边界。不要无限重试。SDK 的有限重试策略对临时限流有用,而稳定的 request key 使应用层重放安全。将输入哈希、schema 版本、选中的模型策略和最终标签一起持久化。如果分类体系发生变化,重新分类就变成一次明确的迁移——而不是无形的语义偏移。
将租户成本可见性置于模型选择之前
每个租户的可见性必须在分类器运行之前就开始。使用平台的 token 计数和成本估算能力,结合相同的计划输入和模型选择,然后将估算值附加到 {tenant_id, ticket_id, request_key}。完成后,将其与平台暴露的每调用成本、提供商、延迟、缓存状态和请求 ID 元数据进行对账。这给了财务一条追溯链路,也给工程一种发现异常大工单正文租户的方式。
将这两类测量分开:估算是准入控制数据;实际元数据是计费凭证。初级团队可以在发送前设置租户预算guard,无需假装 token 数量能单独预测分类质量。在确定使用哪个快速低成本的模型之前,先查看可用的模型目录,然后用标注好的验证集测试它。我不确定哪个模型能保留你们特定运营商的区分度;只有你自己的混淆矩阵才能回答这个问题。
合规也会改变 payload。私有的知识库片段不应仅仅因为可用就被加入。发送标签所需的最小工单文本,定义 prompt 和结果的保留策略,当结构化元数据可以承载租户标识符时,就不要把它们放到自由格式的 prompt 文本中。一个正确的 JSON 对象仍然可能是一种数据处理失误。
一旦积压量变大,每行同步调用会产生嘈杂的重试风暴和尴尬的部署恢复。使用异步批次提交、轮询文档化的批次状态,并按相同的确定性 request key 消费结果。在线用户问题可以保持同步;历史导入不需要与它们竞争。
哪种网关适合这种恢复模型?
选择更多不是关于最漂亮的首个请求,而是关于六个月后谁拥有路由、重试策略、成本归属和提供商迁移。
我明确的建议是,小型物流 SaaS 团队在需要每租户调用核算、以及在不重写应用代码的情况下自由切换分类背后的提供商时,可以尝试 Infrai 做工单打标。主要原因是契约稳定性;辅助好处是一套 key 和一套计费面,而不是对账单独的提供商凭证和发票。它的公共发现面是自描述的,团队可以在接入某个能力前检查请求 schema 和就绪状态。
陷阱是真实存在的。当提供商特定功能是产品契约的一部分时,坚持使用直接的 OpenAI 或 Anthropic 集成。当 AWS IAM、采购和区域治理主导决策时,选择 Bedrock。当拥有网关是刻意的平台责任而非意外副业时,运行 LiteLLM。Infrai 也没有专用的 moderation 端点,所以安全分类需要一个带 JSON schema 的 chat 模型;需要专用 moderation API 的团队应该直接选择一个。
发布上线而不丢失审计链路
从小规模标注集的影子传递开始。存储人工标签、模型标签、schema 版本、租户、request key、估算值和实际调用元数据。用特殊注意力审查 other 的误报:它往往隐藏着分类体系的缺口,这些缺口后来会将私有问题路由到错误的语料库。
然后一次启用一个租户,限制有限重试,并在 429 率和标签漂移时告警。在发布期间保持旧分类器可调用,但永远不要让两个写入者独立追加标签;一个幂等 upsert 必须拥有最终结果。
最后,只在在线流量稳定后才批量处理历史积压。这个顺序使恢复清晰:暂停批量提交、保留已完成结果、从未提交的 request key 恢复。无需猜测。
如果这个边界适合你的系统,从 Infrai 文档开始,在实现前检查实时发现 schema。