先跑通单条请求再上批量 / Gemini 3.5 Flash-Lite 的接入顺序

文章导读
把 Gemini 3.5 Flash-Lite 接进业务时,最容易出问题的地方往往在批量脚本开跑之后,而不在模型本身。密钥没读到、请求体字段名写错、循环里没控制并发,这三种问题混在同一堆报错里,逐个排查很费时间。比较稳妥的顺序是:先让一条请求跑通并把响应字段打印清楚,再用十条样本暴露频率类问题,最后按检查点分批放量。这个顺序与使用哪个模型无关,但对 Flash-Lite 这类常用于轻量、大批量调用
📋 目录
  1. A 写一个只发一条请求的最小脚本骨架
  2. B 把响应完整打印并确认字段名与预期一致
  3. C 放到十条小批量并统计成功与失败
  4. D 按检查点表分批放量并设定停止条件
A A

把 Gemini 3.5 Flash-Lite 接进业务时,最容易出问题的地方往往在批量脚本开跑之后,而不在模型本身。密钥没读到、请求体字段名写错、循环里没控制并发,这三种问题混在同一堆报错里,逐个排查很费时间。比较稳妥的顺序是:先让一条请求跑通并把响应字段打印清楚,再用十条样本暴露频率类问题,最后按检查点分批放量。这个顺序与使用哪个模型无关,但对 Flash-Lite 这类常用于轻量、大批量调用的模型更重要,因为用错字段名或误判限流错误,在批量阶段会被放大许多倍。

接入 Gemini 3.5 Flash-Lite 建议分三步:单条请求跑通、十条小批量、按检查点分批放量。单条阶段只验证三件事——环境变量里的密钥被正确读取、请求体结构能被接口接受、响应中你准备解析的字段确实存在。十条阶段关注错误码分布和超时,不关注平均耗时。放量阶段每批之间停下来看错误码有没有变化,一旦出现鉴权类错误或连续限流类错误就退回上一批。这套顺序的价值是把配置问题、请求格式问题和频率问题分开定位;接口字段名和鉴权方式仍需以官方文档为准。

写一个只发一条请求的最小脚本骨架

这一步的目标不是把功能写完,也不是搭框架,而是让“配置读取”和“请求组装”这两件事在代码里物理分开。配置项全部走环境变量,代码里不出现密钥字符串,这样后面换环境、换密钥时不用改代码,排查时也能先确认变量到底有没有被读到。

# 先在 shell 里设置,再运行脚本;不要把密钥写进代码或提交到仓库
export GEMINI_API_KEY="你的密钥"
export GEMINI_BASE_URL="官方文档给出的接口根地址"
export GEMINI_MODEL="Flash-Lite 对应的模型标识"

下面是一个只发一条请求的骨架。请求体字段按常见形态写出,字段名、层级和鉴权头请对照官方文档核对一次再替换,不同接入方式(原生接口或兼容层)路径可能不同。

import os, json, time, requests

API_KEY  = os.environ.get("GEMINI_API_KEY", "")
BASE_URL = os.environ.get("GEMINI_BASE_URL", "")
MODEL    = os.environ.get("GEMINI_MODEL", "")

def build_payload(prompt: str) -> dict:
    # 请求体只在这里组装,批量阶段复用同一个函数
    return {
        "contents": [
            {"role": "user", "parts": [{"text": prompt}]}
        ],
        "generationConfig": {"temperature": 0.2, "maxOutputTokens": 256},
    }

def send_one(prompt: str) -> dict:
    if not (API_KEY and BASE_URL and MODEL):
        raise SystemExit("缺少环境变量:先确认 GEMINI_API_KEY / GEMINI_BASE_URL / GEMINI_MODEL")
    url = f"{BASE_URL.rstrip('/')}/{MODEL}:generateContent"
    headers = {
        "Content-Type": "application/json",
        "x-goog-api-key": API_KEY,   # 鉴权头名称以官方文档为准
    }
    t0 = time.time()
    resp = requests.post(url, headers=headers,
                         data=json.dumps(build_payload(prompt)), timeout=30)
    return {
        "status_code": resp.status_code,
        "elapsed_ms": int((time.time() - t0) * 1000),
        "raw_text": resp.text,
    }

if __name__ == "__main__":
    r = send_one("用一句话说明什么是幂等")
    print("HTTP", r["status_code"], "耗时", r["elapsed_ms"], "ms")
    print("原始响应:", r["raw_text"][:2000])

成功响应至少要打印这几项:HTTP 状态码、耗时、原始响应文本。不要一上来就 print 解析后的文本,先看到原始 JSON,才能判断是字段层级错了还是内容确实为空。

把响应完整打印并确认字段名与预期一致

单条请求返回 200 并不代表可以收工。批量脚本出错,很大一部分原因是按错的字段路径取文本:把列表当成对象、把多段 parts 只取第一段、把被拦截导致的空文本当成接口故障。所以在单条阶段先把要解析的字段逐个打印出来核对。

先跑通单条请求再上批量 / Gemini 3.5 Flash-Lite 的接入顺序

需要确认的字段清单(名称以实际响应为准,逐项打印出来核对):

  • 候选结果数组是否存在、长度是否为 1,批量时是否可能返回多个候选。
  • 第一候选下内容里的分段数组:你取文本的路径到底是几层,是列表还是字符串。
  • 结束原因字段:正常结束、达到最大输出长度、内容被安全策略拦截,会落到不同取值上,后者文本为空是预期行为,不是接口故障。
  • 用量字段:输入与输出 token 计数是否存在、名称是什么,后面算成本要用。
  • 错误对象:失败时通常有状态码、错误类型和错误消息三个层级,批量日志里要能把它们分开记录。

字段缺失或类型不符时的排查方向:先打印完整原始响应确认不是解析层级写错;再确认请求体里用于控制输出的参数是否被正确传入;如果文本为空但结束原因提示被拦截,调整输入内容而不是改代码;如果响应结构与文档示例对不上,检查是否经过了网关、代理或兼容层改写,这类中间层经常会重排字段结构。

放到十条小批量并统计成功与失败

十条的规模足够暴露频率类问题,又不至于在出问题时产生明显成本。这一步只做串行或很低并发,目的是拿到错误码分布,而不是衡量性能。

十条样本的挑选原则:不要十条都用同一句话。建议覆盖短输入、明显较长的输入、含特殊字符或多语言的输入、接近你真实业务形态的输入各若干条;每条样本单独记录返回结果。如果要做畸形请求测试(比如故意删掉一个必填字段),单独跑一条,不要混在这十条里,否则成功率的含义会被污染。

results = []
for i, prompt in enumerate(samples, 1):
    r = send_one(prompt)
    try:
        ok = r["status_code"] == 200
    except Exception:
        ok = False
    results.append({"序号": i, "状态码": r["status_code"],
                    "耗时ms": r["elapsed_ms"], "成功": ok,
                    "错误摘要": "" if ok else r["raw_text"][:200]})

for row in results:
    print(row)
print("成功", sum(1 for x in results if x["成功"]), "/", len(results))

记录表建议按下面几列填,一行一条请求,失败原因要归类而不是抄整段报错:

先跑通单条请求再上批量 / Gemini 3.5 Flash-Lite 的接入顺序
序号状态码耗时(ms)成功/失败失败原因分类备注
1200—成功—短输入
2——失败鉴权类密钥未读到
3——失败请求格式类字段名写错
4——失败频率/限流类连续快速请求
5——失败超时客户端超时设得过短

失败原因分类统一用四到五类即可:鉴权类、请求格式类、频率限流类、超时类、内容被拦截。分类的价值在于指向不同的修改位置——鉴权类改环境变量,请求格式类改 build_payload,频率类改放量节奏,超时类改 timeout 和重试策略。

按检查点表分批放量并设定停止条件

放量的核心是让每一步都可回退。不要从十条直接跳到全量,中间留出能停下来观察的批次,并且在下一次加量前把上一批的记录看完。

批次规模建议并发每批要看的指标通过条件
第 1 批十条串行状态码、错误分类、字段是否可解析无鉴权类和格式类错误
第 2 批三十条左右低并发或串行加间隔错误码分布是否变化、是否出现限流类错误不集中在同一类
第 3 批一百条左右逐步提到目标并发的一半以下超时占比、限流类错误、内容被拦截的比例错误可被现有重试策略吸收
第 4 批接近目标的量目标并发同上,并观察是否出现持续性错误错误类型与第 3 批基本一致

触发暂停的具体条件建议写死在脚本或观察流程里,出现任意一条就停下当前批次:出现鉴权类错误(通常说明密钥失效或被替换,继续跑没有意义);连续多条限流类错误;超时条数明显多于上一批;响应文本解析失败条数不为零;出现单条请求耗时远高于同批其他请求且无法归因。这些条件都是可配置项,具体阈值需要结合你的业务容忍度和官方给出的配额说明确定,不要照搬别人的数字。

暂停后的回退动作:先把当前批次降到上一批的规模和并发重跑,确认错误是否消失;如果错误在低并发下仍出现,回到十条阶段复现,定位是配置变化还是请求体变化;把这一批的请求样本、原始响应和错误分类存一份快照,后续加量前对照,避免同一个错误反复出现。重试策略也要在这一步明确:哪些错误码适合重试、重试几次、间隔多久,写进配置而不是散在代码里,否则放量后很难解释某一批的耗时为什么异常。