Python + Gemini JSON Schema 实现可校验的工单路由

客服消息里同时出现"扣款""登录不上""发票",规则越堆越难维护。通俗答案是:让大语言模型(LLM)理解自然语言,但绝不能直接把它的一段自由文本送进工单系统。把输出限制为 JSON,再用本地规则复核,才能把"会聊天的模型"变成可靠的分类协作者。你会学到 JSON Schema、超时、失败回退和人工复核这四个可迁移到任何自动化场景的基本功。

为什么关键词方案迟早失控

关键词只能匹配字面。用户说"银行卡被扣了两次",不含"退款"也许仍应进入支付队列;"想问电子发票"又不该误判为支付故障。模型擅长处理这种语义差异,但模型输出本质上是不确定的:可能多解释一句,可能给未知队列,也可能在网络波动时根本没有结果。因此职责要拆开:模型给候选意图;Schema 约束形状;程序检查允许值、置信度和敏感升级条件;不通过就转人工,而不是猜。

Gemini 的 generateContent 接口支持通过 responseMimeType 与 responseJsonSchema 请求 JSON 输出。Schema 不是事实校验器:它只能规定字段、类型和枚举,不能保证"支付"这个判断真的正确。生产系统还应保留抽样标注集,观察各队列的误分率。

flowchart LR U[用户消息] --> P[Python 组装提示与 Schema] P --> G[Gemini generateContent] G --> J[JSON 解析] J --> V{枚举/置信度/敏感词校验} V -->|通过| Q[创建目标队列工单] V -->|失败| H[人工复核队列]

环境准备

需要 Python 3.10+,本例只使用标准库,无须安装第三方依赖。到 Google AI Studio 创建密钥后设置环境变量;不要把密钥写进源码或提交到 Git。

bash 复制代码
export GEMINI_API_KEY='你的密钥'
python3 route_ticket.py '银行卡重复扣款,客服一直没回复'

完整代码

保存为 route_ticket.py。示例对 HTTP 设置 20 秒超时;即使接口返回合法 JSON,也会拒绝低置信度与升级关键词。模型名使用官方生成内容页面示例中的 gemini-3.8-flash;上线前请在目标账号核对可用模型与配额。

python 复制代码
import json
import os
import sys
import urllib.error
import urllib.request

ALLOWED = {"payment", "account", "invoice", "human_review"}
SCHEMA = {
    "type": "object",
    "properties": {
        "queue": {"type": "string", "enum": sorted(ALLOWED)},
        "confidence": {"type": "number"},
        "reason": {"type": "string"},
    },
    "required": ["queue", "confidence", "reason"],
    "additionalProperties": False,
}

def call_model(message: str) -> dict:
    key = os.environ.get("GEMINI_API_KEY")
    if not key:
        raise RuntimeError("缺少 GEMINI_API_KEY 环境变量")
    body = {
        "contents": [{"parts": [{"text": (
            "将消息路由到 payment/account/invoice/human_review。"
            "不确定时选 human_review。消息:" + message)}]}],
        "generationConfig": {
            "responseMimeType": "application/json",
            "responseJsonSchema": SCHEMA,
            "temperature": 0,
        },
    }
    url = ("https://generativelanguage.googleapis.com/v1beta/models/"
           "gemini-3.8-flash:generateContent?key=" + key)
    request = urllib.request.Request(
        url, data=json.dumps(body).encode(),
        headers={"Content-Type": "application/json"}, method="POST")
    with urllib.request.urlopen(request, timeout=20) as response:
        payload = json.load(response)
    text = payload["candidates"][0]["content"]["parts"][0]["text"]
    return json.loads(text)

def safe_route(message: str) -> dict:
    try:
        result = call_model(message)
        risky = any(word in message for word in ("盗刷", "泄露", "起诉"))
        if (result.get("queue") not in ALLOWED or
                not isinstance(result.get("confidence"), (int, float)) or
                result["confidence"] < 0.80 or risky):
            return {"queue": "human_review", "reason": "本地安全门禁"}
        return result
    except (KeyError, ValueError, urllib.error.URLError, TimeoutError) as exc:
        return {"queue": "human_review", "reason": f"调用或解析失败: {exc}"}

if __name__ == "__main__":
    print(json.dumps(safe_route(" ".join(sys.argv[1:])), ensure_ascii=False))

逐段看懂这段程序

SCHEMA 把队列锁为四个枚举值,并要求三个字段全部出现;temperature: 0 让相同输入更稳定,但不是正确性的保证。urlopen(..., timeout=20) 防止网络连接无限等待。最关键的是 safe_route:它将 API 失败、字段缺失、低置信度和敏感投诉统一降级到人工队列。这样即使供应商限流或模型响应异常,也不会让订单卡在半自动状态。

成功时可能输出 {"queue":"payment","confidence":0.94,"reason":"重复扣款"};没有密钥时则直接指出环境变量缺失。本次任务已在本机用 python3 -m py_compile 做过语法检查,但未实际调用线上 API,不能把示例输出当作模型实测结果。

最常见的三个坑

  1. 把 Schema 当真相:它只保证结构,不保证分类。先让高风险类别只进人工复核。
  2. 不处理空候选 :安全拦截、配额耗尽都可能没有 candidates[0];这里会回退人工,生产环境还应记录请求 ID。
  3. 在提示中塞入规则机密:提示可能进入日志。仅发送完成任务所需的最少字段,并对日志脱敏。

什么时候适用,什么时候不要用

适合:队列有限、错误可复核、能接受数秒延迟的售后分单、表单归类、内容标签。不要用于直接退款、封号、授信或医疗结论;这些场景应由确定性规则和有资质的人做最终决定。工程化时,把 Schema 版本、提示版本和人工纠正结果写入审计表,每周用纠正样本回放评估;队列变更采用灰度发布。

5 分钟实践题

新增 shipping 队列,并设计一条"地址泄露"的测试消息:它即使语义像物流咨询,也必须被本地敏感词门禁转到 human_review。你会如何记录这次覆盖的理由?

你们的自动分单最怕模型答错,还是最怕它返回了不能解析的格式?

关注「蜗牛聊AI」,一起看懂技术变化背后的真正机会。


本文首发于 java4u.cn,转载请注明出处。

相关推荐
知几蜗牛41 分钟前
Python 标准库调用 Audio Transcriptions API 的超时与异常处理
人工智能
znx9391 小时前
因子分析:量化交易的底层核心与盈利逻辑基石
人工智能·python·机器学习·期魔方
沉默王二1 小时前
轻量开源版 Muse 来了!CopilotKit 开源 OpenMuse,Personal Agent 的工程细节全摊开了
人工智能·openai·agent
Dawson Zhu1 小时前
《Agentic Design Patterns》第 10 章导读:模型上下文协议(MCP)
人工智能·语言模型·架构·aigc·agi
IvorySQL1 小时前
VACUUM FULL 之后 ROWID 就废了? IvorySQL 兼容性实测
数据库·人工智能·ai·postgresql·开源
GPUStack1 小时前
一张 A800 80GB,跑通 Qwen-Image-2.1:GPUStack 部署、生成与图像编辑实战
人工智能·开源·github·vllm·大模型部署·gpustack
高洁011 小时前
AI软件工程:大模型赋能软件研发全流程革新
人工智能·深度学习·机器学习·transformer·tornado
吴文周1 小时前
让大模型在本地持续进化:YoungAi 如何用 Sidecar 和后训练重新思考本地 AI
人工智能