这篇文章的原文已经是简体中文,因此无需翻译。我按照您的要求(口语化、自然流畅、保留产品名和技术术语英文原文)对全文做了整理校对,以下是定稿版本:
Gemini 3.5 Flash 进阶用法:从入门到精通
一个真实的问题
上个月我在做一个客服工单自动分类的功能。每天大概有两千条工单进来,之前的规则引擎只能处理六成左右,剩下全靠人工分流。我需要一个又快又便宜、还能理解中文口语化表达的模型。试了一圈之后,我把宝押在了 Gemini 3.5 Flash 上——原因很简单:它便宜、延迟低,而且长上下文能力对处理冗长的工单历史记录特别友好。
但真正用起来,我才发现“能调通”和“用好”之间差得很远。这篇文章就把我从入门到稳定上线的完整过程记录下来,包括踩过的坑。
第一步:跑通基础调用
先说最基本的部分。用 Python SDK 调用大概是这样:
from google import genai
client = genai.Client(api_key="YOUR_KEY")
response = client.models.generate_content(
model="gemini-3.5-flash",
contents="把这条工单分类为:退款、物流、账户、其他\n\n工单内容:我买的东西半个月了还没到,客服也联系不上"
)
print(response.text)
# 输出:物流
这一步很顺利。但很快我就遇到了第一个问题:输出不稳定。有时返回“物流”,有时返回“该工单应归类为物流类别”。这对下游程序来说是灾难。
第二步:用结构化输出锁死格式
解决方案是 JSON Mode 加上 schema 约束:
import json
schema = {
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": ["退款", "物流", "账户", "其他"]
},
"urgency": {
"type": "string",
"enum": ["高", "中", "低"]
},
"reason": {"type": "string"}
},
"required": ["category", "urgency"]
}
response = client.models.generate_content(
model="gemini-3.5-flash",
contents=prompt,
config={
"response_mime_type": "application/json",
"response_schema": schema,
"temperature": 0.1
}
)
result = json.loads(response.text)
这里有两个细节值得强调:
- enum 是救命稻草。与其在 prompt 里写“只能输出这四类之一”,不如直接在 schema 里枚举,模型物理上就出不了圈。
- temperature 一定要降。分类任务我用 0.1,实测比默认值的一致性高出一大截。别迷信“创意任务才调温度”这种说法——任何需要稳定输出的场景都应该压低。
第三步:System Instruction 才是提示词的主战场
我早期犯的错误是把所有指令都塞进用户消息里。后来改成 system instruction 之后,效果明显更稳:
SYSTEM_PROMPT = """你是一个电商客服工单分类器。
规则:
1. 涉及钱的问题优先归"退款",即使同时提到物流
2. 只提到收货问题的归"物流"
3. 用户情绪激动且涉及资金损失时,urgency 设为"高"
4. 不确定时归"其他",不要猜
few-shot 示例:
工单:买错了想换个尺码 -> category: 其他, urgency: 低
工单:你们是不是骗子?我要投诉退款!-> category: 退款, urgency: 高
"""
一个意外发现:Flash 对 few-shot 示例的敏感度比我想象的高。加了三五个示例后,边界案例的准确率从大约 85% 提到了 93% 左右(基于我人工标注的 200 条测试集)。
第四步:长上下文的正确用法
Flash 的长上下文窗口是我选它的核心理由。工单里经常附带整段聊天记录,有时还很长。我最初的写法是把聊天记录直接堆进 prompt——能用,但两个问题:慢、贵。
改进方案是两段式处理:
# 第一步:一次性上传长文档,拿到引用
doc = client.files.upload(file="chat_history_1024.txt")
# 第二步:只传引用做推理
response = client.models.generate_content(
model="gemini-3.5-flash",
contents=[doc, "总结用户的核心诉求,不超过50字"],
)
对于重复处理同一份长文档的场景(比如同一个工单被多个流程处理),files API 能省不少 token。另一个技巧是让模型先做摘要,再拿摘要做分类——摘要任务对精度要求低,可以放更激进的 temperature。
第五步:批量处理降成本
两千条工单一条条调用,既慢又浪费。Batch API 的价格大约是实时调用的一半:
# 把所有请求打包成一个 JSONL 文件提交
jobs = []
for ticket in tickets:
jobs.append({
"contents": [{"parts": [{"text": f"{SYSTEM_PROMPT}\n\n{ticket}"}]}]
})
代价是结果不是实时的,通常要等几分钟到一小时。我的做法是:夜间批量处理存量工单,实时调用只留给新进来的。成本直接砍掉了约 40%。
第六步:别忽略流式输出
前端展示等待中状态时,非流式调用体验很差。改成流式很简单:
for chunk in client.models.generate_content_stream(
model="gemini-3.5-flash",
contents="给用户写一封物流延误的道歉信"
):
print(chunk.text, end="", flush=True)
对分类这种短输出场景意义不大,但只要是生成类文案,一定要用流式——用户感知的等待时间能从十几秒降到一两秒。
踩过的坑总结
- 不要在 prompt 里用“请务必”“一定要”这种情绪化措辞,Flash 对结构性约束(schema、枚举)的反应远好于语气强调。
- 上下文越长,中间内容的权重越低。关键指令放在 prompt 开头或结尾,别埋在几千字的聊天记录中间。实测把分类规则从中间挪到开头,准确率又提了两个点。
- 错误重试必须有。高峰期偶尔会遇到限流,用指数退避重试,配 tenacity 库五分钟就搞定。
- 别完全信任模型对“其他”类的判断。我后来加了一个兜底:模型置信度低(通过让模型输出 confidence 字段模拟)的工单直接转人工。
诚实评价
用三个月下来,我的整体感受是:Flash 这类模型在结构化、高并发的中等难度任务上性价比极高,我的工单分类场景完全可以胜任。但它不是没有短板——对于需要多步推理的复杂投诉(比如涉及赔偿金额计算、多轮责任认定的),Flash 偶尔会给出看似合理实则错误的答案,这类工单我最终路由给了更大的模型处理。
我的建议是:把 Flash 当作你的默认主力,但一定要设计一个“升级路径”——低置信度的请求自动转给更强的模型。这样既省钱,又不牺牲质量。
如果您需要的是将这篇文章翻译成其他语言(如英文、日文),或者您手上有英文原版需要翻成中文,请告诉我,我可以马上处理。