用稳定接口构建游戏工单分类器
文章以 Node.js 游戏客服工单分类为例,建议固定标签和 JSON 输出契约,将模型调用封装为可替换适配器。通过统一评测集比较至少三家提供商,并配套解析、重试和 token 用量统计。
AI 深度解读
给游戏客服工单接上 GPT,最容易完成的是 API 调用,最容易埋坑的是调用之后:分类标签能否直接用于分流,异常输出怎么处理,更换模型会不会牵动所有消费者。作者的结论是,先固定分类契约,再把模型调用放进适配器,并让评测跟着代码一起交付。
这套方案适合已经用 Node.js 跑队列任务的团队。它提供了一条从演示走向上线的工程路径,但没有给出供应商实测排名;而且标题写着 Node.js,示例实际使用 Python,阅读时需要分清设计建议与可直接运行的代码。
先约定分类结果,再决定接哪个模型
作者最初尝试过直接写提示词,再拆分模型返回的字符串。退款金额、玩家名字、换行和类似 JSON 的文本,很快就会让这种解析方式变得脆弱。
他们建议把系统收敛成几个部件:分类器、解析器、重试策略,以及一套固定评测集。对客服队列来说,不需要先搭完整 Agent。
分类结果包含三个字段:
category负责主分类,限定为billing、account、bug、abuse、other。tags补充细节,例如refund、login、crash。needs_human表示是否需要人工介入,无法可靠判断的工单应当允许升级处理。
分类接口首先要服务下游决策,JSON 只是承载这个决策的格式。
例如,一张工单同时提到结账崩溃、重复扣款和订单待处理。只判断成 billing 或 bug,看起来都能解释,但无法完整指导客服。原文建议保留 billing,附加 duplicate-charge 和 crash,再按升级规则决定 needs_human。
这里还缺一个业务约定:多个意图同时出现时,哪个分类优先。这个规则应该由团队写清楚,不能让每个模型自行理解。
输入也要有边界。作者将工单放在分隔符之间,把字段规则放在工单之外,提醒模型将玩家文本作为数据处理。玩家粘贴的「忽略你的规则」不应该获得指令权限。不过,分隔符只是提示组织方式,原文没有证明它能可靠抵御提示注入。
示例给出了调用边界,还没补齐上线兜底
作者用环境变量切换模型和服务地址,避免供应商配置散落到业务代码里。下面是原文完整示例,它解决的是请求、解析和限流重试问题:
import json
import os
import time
from openai import APIStatusError, OpenAI
ALLOWED_CATEGORIES = {"billing", "account", "bug", "abuse", "other"}
def classify_ticket(ticket_text: str) -> dict:
client = OpenAI(
api_key=os.environ["INFRAI_API_KEY"],
base_url=os.environ["INFRAI_BASE_URL"],
)
model = os.environ["OPENAI_MODEL"]
prompt = f"""Classify this gaming customer-support ticket.
Return one JSON object with exactly these keys:
category: one of billing, account, bug, abuse, other
tags: an array of short lowercase strings
needs_human: a boolean
Ticket text begins after the delimiter and ends before it:
---
{ticket_text}
---
"""
for attempt in range(4):
try:
response = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": "Return valid JSON only."},
{"role": "user", "content": prompt},
],
temperature=0,
)
result = json.loads(response.choices[0].message.content)
if result["category"] not in ALLOWED_CATEGORIES:
raise ValueError("model returned an unknown category")
if not isinstance(result["tags"], list):
raise ValueError("model returned non-list tags")
if not isinstance(result["needs_human"], bool):
raise ValueError("model returned a non-boolean needs_human")
return result
except APIStatusError as error:
if error.status_code != 429 or attempt == 3:
raise
retry_after = error.response.headers.get("retry-after")
delay = float(retry_after) if retry_after else 2**attempt
time.sleep(delay)
raise RuntimeError("classification did not complete")
重点看 range(4) 和 attempt == 3:最多尝试 4 次,只有 HTTP 429 进入这里的重试逻辑;优先读取 retry-after,缺失时按 2**attempt 等待。其他 API 错误继续抛给调用方。
temperature=0 也不能替代校验。代码通过 json.loads 解析内容,再检查分类、标签数组和布尔值,但没有验证每个标签都是小写字符串,也没有拒绝多余字段。提示词要求「恰好三个字段」,程序并未完整落实这一要求。
无效 JSON、缺失字段等问题同样没有进入兜底流程。作者在交付建议中提出死信路径,也就是把处理失败的任务暂存下来供排查或补偿,但示例没有实现它。
对于 Node.js worker,原文建议使用 openai npm 包,通过 baseURL 和 API key 配置对应边界,没有提供 JavaScript 实现,也没有指定包版本。
此外,模型重试和业务去重是两回事。示例没有写入业务状态,因此作者没有加入幂等键;真实队列在保存分类、创建人工任务或发送回复之前,应以工单 ID 去重,避免一次重试产生两次交接。
能不能换供应商,要让同一套工单来回答
作者建议先用一个模型跑通,再把同一套工单回放到至少三家供应商:OpenAI GPT、Anthropic Claude、Google Gemini;若考虑 OpenAI-compatible 网关,也要一起测试。
兼容请求格式,并不代表行为一致。Claude 使用 Messages API,Gemini 有自己的 SDK 和内容模型;网关可以减少适配代码,却仍需验证 JSON 遵循程度、拒绝行为、token 统计和限流处理。具体模型与区域是否可用,也必须核实。
这里有真实取舍:统一到共同能力范围,可能舍弃供应商原生的结构化输出特性;大量使用原生特性,则需要维护更多适配逻辑。如果业务依赖最新原生功能或原生工具调用,作者明确反对强行通过网关解决。
判断依据应当来自固定评测集。原文建议先准备 50 条经人工审核、带版本的工单,覆盖重复投诉、多意图、愤怒表达、粘贴日志,以及应当返回 needs_human: true 的案例。每次结果同时记录提示词版本。
50 条只是控制实验规模的起点,没有准确率保证。作者也没有提供这套评测集的实际结果。
需要观察的指标包括分类准确率、标签精确率、无效 JSON 比例、人工升级召回率、延迟,以及输入和输出 token。人工升级召回率关心的是:本来应该交给人的工单,有多少被成功识别出来。
错误的代价并不对称。账户被盗被分进普通故障队列,可能比普通崩溃误转人工严重得多。因此要看分类混淆矩阵,并逐条检查分歧,不能只看整体完全匹配率。
token 成本也应在上线前估算。作者建议使用可用的 token 计数能力,记录供应商提供的响应统计,删掉重复规则、限制工单长度,并在最新消息足够时省去完整对话。原文没有提供统一的计数 API,也没有给出具体成本数据。
我的判断
我认可这篇把分类契约和评测放在供应商选择之前。对于客服分流,稳定字段、明确升级规则和失败处理,比一次演示中标签看起来多准确更有价值。
但它是一套工程经验建议,没有对照实验支撑「哪家更好」或「兼容网关更省」这样的结论。文中还出现了「五字段行为契约」的表述,与实际列出的三个字段不一致,不能据此猜出另外两个字段。
相比直接拼提示词、拆字符串,这套方案的边界更清楚;相比供应商原生结构化输出,它给出的代码校验仍然偏弱。两者也可以配合使用:适配器负责调用原生能力,业务侧继续验证统一契约,具体收益由评测决定。
另一个边界应当保留:工单分类不等于内容审核。原文明确说,这套设计不能证明语音转写、各区域实时语音会话或专用 moderation endpoint 可用。把分类器改成审核系统,需要重新定义目标和评测标准。
能拿来做什么
- 在 Node.js 客服 worker 中先固定
category、tags、needs_human,补上多意图分类优先级和转人工规则,再让队列消费者依赖这个统一结果。 - 把供应商调用收进适配器,并在应用侧检查字段、枚举和标签元素类型;将无效输出送入明确的失败处理路径,用工单 ID 约束后续业务操作。
- 上线前整理 50 条人工标注工单,记录提示词版本,优先检查漏转人工和高代价误分类,再结合延迟、token 用量比较候选模型。
- 首发可以只接一个模型,但让第二家供应商持续参与评测。是否切换应依据解析和人工升级门槛,不能只看接口是否兼容。