Skip to content
A · AI 应用开发入门第 8 课⏱ 15 分钟

上线:成本、限流和错误重试

学完你能
  • 估算一个功能每月要花多少钱,并知道从哪几处省
  • 写出带超时和指数退避的重试,区分该重试和不该重试的错误
  • 做完结业项目:一个有后端的网页聊天机器人
Yui和Kai守护聊天机器人上线控制台
Yui和Kai守护聊天机器人上线控制台AI 生成配图

前面七课让功能「能跑」。上线以后要面对的是另一类问题:一个月花多少钱?用户多了会不会被限流?上游偶尔出错怎么办?Key 会不会泄露?

成本:先算清楚再上线 ​

用真实调用记录和长短滑杆估算费用
用真实调用记录和长短滑杆估算费用AI 生成配图

费用 = 输入 token × 输入单价 + 输出 token × 输出单价。估算一个功能的月成本:

每次调用的平均 token(看 usage) × 每天调用次数 × 30 × 单价

每次调用的实际花费在 HiveGPT 的「使用记录」里能看到,先在测试阶段跑几十次,取平均值再估算,比拍脑袋准得多。

省钱的几个方向,按效果排序:

  1. 控制上下文长度:多轮对话只带最近几轮(A2),RAG 只带最相关的几段(A5)。输入通常是大头。
  2. 限制输出:设置 max_completion_tokens,提示词里要求「简洁」「不超过 200 字」。
  3. 缓存:同样的问题(FAQ、固定说明)直接返回上次的结果,不调模型。
  4. 固定的前缀放前面:system 提示词和固定资料放在消息开头且保持不变,便于上游做提示词缓存。
  5. 按任务选模型:分类、抽取这类简单任务,不一定要用最强的模型。

错误:哪些该重试 ​

状态码含义处理
400请求本身有问题(参数、内容太长)不重试,修代码或提示用户
401 / 403Key 无效、停用或没有权限不重试,检查 Key 和分组
429请求太频繁等一会儿再重试
余额不足 / 额度用完按量分组余额用完,或 Key 设置的额度到了不重试,充值或调高额度后再试(看返回的 message)
500 / 502 / 503 / 504上游临时故障重试,间隔逐渐拉长
超时 / 连接断开网络问题重试

指数退避重试 ​

流量拥堵时用逐步变长等待来重试
流量拥堵时用逐步变长等待来重试AI 生成配图

每次失败后等待时间翻倍,再加一点随机数,避免大量客户端在同一时刻一起重试:

python
import os, random, time
from openai import OpenAI, APIStatusError, APIConnectionError, APITimeoutError

client = OpenAI(
    base_url="https://hivegpt.cn/v1",
    api_key=os.environ["HIVEGPT_API_KEY"],
    timeout=60,        # 单次请求最多等 60 秒
    max_retries=0,     # 关掉 SDK 自带重试,下面自己控制
)

RETRY_STATUS = {429, 500, 502, 503, 504}   # 余额不足这类错误重试也没用,看 e.message 提示用户

def chat(messages, attempts=4):
    for i in range(attempts):
        try:
            return client.chat.completions.create(model="gpt-5.5", messages=messages, max_completion_tokens=800)
        except (APIConnectionError, APITimeoutError):
            if i == attempts - 1:
                raise
        except APIStatusError as e:
            if e.status_code not in RETRY_STATUS or i == attempts - 1:
                raise
        time.sleep(min(2 ** i, 20) + random.random())   # 1s、2s、4s… 再加 0–1 秒随机

OpenAI SDK 自带重试

OpenAI(max_retries=2) 默认就会对连接错误、429 和 5xx 做退避重试。想要更细的控制(记录日志、区分余额不足)时,才像上面这样自己写。

保护 Key 和你的钱包 ​

  • Key 只放在服务端:浏览器、App 安装包里的 Key 一定会被人拿到。网页通过你自己的后端调用模型。
  • 一个应用一个 Key:在「API 密钥」页给每个项目单独建 Key,出问题只停这一个;可以给 Key 设置额度上限,超了自动停。
  • 给你的用户限流:每个用户每分钟、每天能调多少次,在你的后端控制,防止被人刷接口。
  • 限制输入长度:用户发来几万字也照单全收,账单会很难看。

结业项目:网页聊天机器人 ​

把前面学的串起来:一个有流式输出、能多轮对话的网页聊天机器人。结构是浏览器 → 你的后端 → HiveGPT,Key 只在后端。

python
# app.py —— pip install fastapi uvicorn openai
import os
from fastapi import FastAPI
from fastapi.responses import FileResponse, StreamingResponse
from openai import OpenAI
from pydantic import BaseModel

client = OpenAI(base_url="https://hivegpt.cn/v1", api_key=os.environ["HIVEGPT_API_KEY"], timeout=60)
app = FastAPI()

class Chat(BaseModel):
    messages: list[dict]

@app.post("/api/chat")
def chat(body: Chat):
    history = [m for m in body.messages if m.get("role") in ("user", "assistant")][-10:]   # 只带最近 10 条
    messages = [{"role": "system", "content": "你是一个友好、简洁的助手。"}] + history

    def stream():
        resp = client.chat.completions.create(model="gpt-5.5", messages=messages, stream=True, max_completion_tokens=800)
        for chunk in resp:
            if chunk.choices and chunk.choices[0].delta.content:
                yield chunk.choices[0].delta.content

    return StreamingResponse(stream(), media_type="text/plain; charset=utf-8")

@app.get("/")
def index():
    return FileResponse("index.html")
html
<!-- index.html -->
<div id="log"></div>
<form id="f"><input id="q" placeholder="说点什么…" autocomplete="off"><button>发送</button></form>
<script type="module">
const messages = []
const log = document.getElementById('log')
document.getElementById('f').onsubmit = async (e) => {
  e.preventDefault()
  const q = document.getElementById('q')
  messages.push({ role: 'user', content: q.value })
  log.insertAdjacentHTML('beforeend', '<p><b>我:</b></p>')
  log.lastElementChild.append(q.value)
  q.value = ''
  const p = document.createElement('p')
  log.append(p)
  const res = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ messages }) })
  const reader = res.body.getReader()
  const decoder = new TextDecoder()
  let text = ''
  for (;;) {
    const { done, value } = await reader.read()
    if (done) break
    text += decoder.decode(value, { stream: true })
    p.textContent = text          // 用 textContent,不要把模型输出当 HTML 插入
  }
  messages.push({ role: 'assistant', content: text })
}
</script>

本地运行:export HIVEGPT_API_KEY=sk-...,然后 uvicorn app:app --reload,打开 http://localhost:8000。

在这个基础上,可以把 A5 的 RAG 接进来做成「某某问答机器人」,或者把 A6 的工具加进去。做完以后:

  • 把代码推到 GitHub 或 Gitee(不要提交 Key,用 .gitignore 排除 .env),把仓库链接作为结业项目;
  • 也可以把后端部署到你的服务器上,提交可访问的网址。

静态网页托管放不了后端

HiveGPT「我的网站」托管的是静态网页(HTML / CSS / JS),没有后端,不能放 Key。可以用它发布项目介绍页或演示截图,聊天功能要放在你自己的后端上。

小结 ​

  • 上线前用「使用记录」里的真实数据估算成本;上下文长度和输出长度是最大的两个开关。
  • 429 和 5xx 用指数退避重试,400 / 401 不重试;每个请求都要有超时。
  • Key 只放后端,一个应用一个 Key,对自己的用户限流。

这是 A 路线的最后一课。完成全部测验和检查点、提交结业项目后,就可以到路线页领取结业证书。

代码示例在页面里运行时使用 HiveGPT 的模型接口。延伸阅读来自 JavaGuide(Apache-2.0),版权归原作者。