Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型"答错一句话",而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gemini API 的 Structured Outputs 能按 JSON Schema 生成结果;本篇用 Python 把退款、账号和普通咨询分流,并在模型输出越界时拒绝自动流转。你会得到一条能复制、能超时、能降级的最小链路。

为什么不直接解析一段自然语言

结构化输出的作用是规定"输出长什么样",不是保证事实正确。Schema 让字段和枚举更稳定;业务规则仍要由服务端负责。本文把 priority 限在 low、normal、high,把 queue 限在三个队列。模型若返回未知值,程序转到人工队列。当前官方文档把它用于抽取、分类和 Agent 工作流,并通过 response_format 的 application/json 与 schema 配置。
#mermaid-svg-CjF8aEJ6kPGLcEw9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CjF8aEJ6kPGLcEw9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .error-icon{fill:#552222;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .marker.cross{stroke:#333333;}#mermaid-svg-CjF8aEJ6kPGLcEw9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CjF8aEJ6kPGLcEw9 p{margin:0;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .cluster-label text{fill:#333;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .cluster-label span{color:#333;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .cluster-label span p{background-color:transparent;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .label text,#mermaid-svg-CjF8aEJ6kPGLcEw9 span{fill:#333;color:#333;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .node rect,#mermaid-svg-CjF8aEJ6kPGLcEw9 .node circle,#mermaid-svg-CjF8aEJ6kPGLcEw9 .node ellipse,#mermaid-svg-CjF8aEJ6kPGLcEw9 .node polygon,#mermaid-svg-CjF8aEJ6kPGLcEw9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .rough-node .label text,#mermaid-svg-CjF8aEJ6kPGLcEw9 .node .label text,#mermaid-svg-CjF8aEJ6kPGLcEw9 .image-shape .label,#mermaid-svg-CjF8aEJ6kPGLcEw9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .rough-node .label,#mermaid-svg-CjF8aEJ6kPGLcEw9 .node .label,#mermaid-svg-CjF8aEJ6kPGLcEw9 .image-shape .label,#mermaid-svg-CjF8aEJ6kPGLcEw9 .icon-shape .label{text-align:center;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .node.clickable{cursor:pointer;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .arrowheadPath{fill:#333333;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CjF8aEJ6kPGLcEw9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CjF8aEJ6kPGLcEw9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CjF8aEJ6kPGLcEw9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .cluster text{fill:#333;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .cluster span{color:#333;}#mermaid-svg-CjF8aEJ6kPGLcEw9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CjF8aEJ6kPGLcEw9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .icon-shape,#mermaid-svg-CjF8aEJ6kPGLcEw9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .icon-shape p,#mermaid-svg-CjF8aEJ6kPGLcEw9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .icon-shape .label rect,#mermaid-svg-CjF8aEJ6kPGLcEw9 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CjF8aEJ6kPGLcEw9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CjF8aEJ6kPGLcEw9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CjF8aEJ6kPGLcEw9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 通过
失败/超时
工单文本
Gemini JSON Schema
解析JSON
枚举与业务规则
进入对应队列
人工复核队列

环境准备

使用 Python 3.10+。安装依赖:python -m pip install google-genai。把密钥放入环境变量:export GEMINI_API_KEY='你的密钥'。模型名示例为文档中的 gemini-3.8-flash;你的账号可用模型与配额仍需在控制台确认。保存为 ticket_router.py,运行 python ticket_router.py。示例未在本次任务中实际调用线上 API;本地仅完成语法检查,目标环境安装依赖后应再作一次无敏感数据的联调。

python 复制代码
import json
import os
from google import genai

ALLOWED_QUEUES = {"refund", "account", "general"}
ALLOWED_PRIORITY = {"low", "normal", "high"}
SCHEMA = {"type": "object", "properties": {
    "queue": {"type": "string", "enum": sorted(ALLOWED_QUEUES)},
    "priority": {"type": "string", "enum": sorted(ALLOWED_PRIORITY)},
    "reason": {"type": "string"}},
    "required": ["queue", "priority", "reason"]}

def safe_route(raw: str) -> dict:
    try:
        data = json.loads(raw)
    except json.JSONDecodeError:
        return {"queue": "human_review", "reason": "JSON无法解析"}
    if data.get("queue") not in ALLOWED_QUEUES:
        return {"queue": "human_review", "reason": "未知队列"}
    if data.get("priority") not in ALLOWED_PRIORITY:
        return {"queue": "human_review", "reason": "未知优先级"}
    return data

def classify(ticket: str) -> dict:
    if not os.getenv("GEMINI_API_KEY"):
        return {"queue": "human_review", "reason": "缺少GEMINI_API_KEY"}
    client = genai.Client()
    try:
        result = client.interactions.create(
            model="gemini-3.8-flash", input=ticket,
            response_format={"type": "text", "mime_type": "application/json",
                             "schema": SCHEMA}, timeout=20)
        return safe_route(result.output_text)
    except Exception as exc:
        return {"queue": "human_review", "reason": f"调用失败: {type(exc).__name__}"}

if __name__ == "__main__":
    print(classify("订单重复扣款,请尽快退款"))
    assert safe_route('{"queue":"refund","priority":"high","reason":"重复扣款"}')["queue"] == "refund"

代码分两层:SCHEMA 提前缩小模型输出空间;safe_route 再做独立验证,不能因为 API 承诺 JSON 就省略它。timeout=20 是为了不让一个慢请求占住 Web 工作线程;生产环境还应设置 HTTP 客户端级超时、重试次数和熔断。genai.Client() 从环境读取密钥,不要把密钥写进仓库。

预期输出(有密钥且调用成功时)类似 {'queue': 'refund', 'priority': 'high', 'reason': '...'};没配密钥则是人工复核。最常见的坑有:一,Schema 只约束格式,不会自动核验订单真伪;二,枚举改了却没同步服务端白名单;三,超时后盲目重试,造成同一工单被重复创建;四,把模型理由直接展示给用户而泄露内部规则。

适合把"先分给谁处理"自动化,不适合直接批准退款或关闭投诉。工程化时,把原始文本、模型版本、schema 版本、路由结果和人工改判一起记录;每周抽样计算错分率。5分钟实践:新增 security 队列,并写一个断言确保模型返回 delete_database 时永远进入 human_review。

你的工单里,哪一个字段一旦分类错就不该自动流转?

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


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

相关推荐
xingyuzhisuan1 小时前
一文读懂星宇智算 StarVerse-AI 无限画布,AI 视频场景的空间扩展方案
人工智能·计算机视觉
Zzj_tju1 小时前
蒸馏推理过程,传递的是能力还是表达习惯?
人工智能·机器学习·自然语言处理
码点滴1 小时前
接了五家大模型以后,业务代码开始失控:LiteLLM 如何把复杂度收进一个入口?
人工智能
临床数据科学和人工智能兴趣组1 小时前
字符串转换为句子风格
大数据·数据库·人工智能·r语言·r语言-4.2.1
TomEval1 小时前
【测AI】第12篇:NLP / CV / 语音 —— 三大 AI 方向速通
人工智能·python·自然语言处理·aigc·测试用例
Gu_WenYun1 小时前
基金被套后,如何调整投资策略
人工智能·金融
SEO_juper1 小时前
给网页做可引用性评分:判断内容够不够让 AI 引用(GEO 工具,附代码)
开发语言·人工智能·爬虫·python·seo·独立站
甲维斯1 小时前
戒断Claude之后,被GLM5.3蠢哭了!
人工智能
龙孚信息1 小时前
搜索算法的保质期,正在被AI刷新
人工智能·chatgpt