上一篇 下一篇 分享链接 返回 返回顶部

ChatGPT 账单太高?这套降本方案和代码可以直接用

发布人:慈云数据-客服中心 发布时间:17小时前 阅读量:6

ChatGPT 如何降低成本|附源码

随着大模型能力不断提升,越来越多团队开始把 ChatGPT 或其他大语言模型接入到客服、内容生成、知识库问答、代码辅助、数据分析等业务中。刚开始使用时,很多人只关注“效果好不好”,但当调用量逐渐上升后,一个现实问题很快出现:成本越来越高

尤其是在高并发、多轮对话、长文本输入、知识库检索、批量生成等场景下,如果不做成本优化,API 费用可能会快速增长。本文将从产品设计、Prompt 优化、模型选择、缓存策略、上下文压缩、RAG 检索优化、批处理、限流与监控等多个角度,系统讲解如何降低 ChatGPT 使用成本,并附上可直接参考的源码示例。


一、ChatGPT 成本主要来自哪里?

在优化成本之前,需要先搞清楚成本构成。

大多数大模型 API 的计费方式都与 Token 有关。简单来说,Token 可以理解为模型处理文本的基本单位。中文、英文、标点、空格都会被拆分成不同数量的 Token。

通常费用包括两部分:

  1. 输入 Token 成本

    • 用户问题
    • 系统提示词
    • 历史对话上下文
    • 知识库检索内容
    • 工具调用说明
    • 结构化输出要求
  2. 输出 Token 成本

    • 模型生成的回答
    • JSON 结果
    • 解释说明
    • 推理文本

因此,降低成本的本质就是:

在不明显降低效果的前提下,减少不必要的输入 Token、输出 Token,以及高价模型调用次数。


二、常见导致成本升高的原因

很多项目成本高,并不是因为业务本身必须消耗这么多 Token,而是因为实现方式不够精细。

1. System Prompt 过长

有些项目会在每次请求中塞入大量系统提示词,例如角色设定、规则说明、业务背景、输出格式、示例等。短期看方便,长期看成本很高。

如果一个系统提示词有 2000 Token,每天调用 10 万次,那么仅系统提示词就会消耗 2 亿输入 Token。

2. 多轮对话无限拼接

不少聊天系统会把用户所有历史消息都传给模型。刚开始几轮没问题,但对话一长,Token 会线性增长。

例如:

  • 第 1 轮:500 Token
  • 第 5 轮:3000 Token
  • 第 20 轮:15000 Token

如果没有上下文裁剪或摘要机制,成本会非常高。

3. 检索内容过多

在知识库问答场景中,很多系统会从向量数据库中检索出大量文档片段,然后全部塞进 Prompt。实际上,模型可能只需要其中一小部分。

4. 所有任务都使用最强模型

并不是所有任务都需要使用最强模型。例如:

  • 意图识别
  • 文本分类
  • 简单摘要
  • 敏感词判断
  • 关键词提取
  • 问题改写

这些任务通常可以交给更便宜的小模型,只有复杂推理和高价值生成任务才使用更强模型。

5. 没有缓存

很多用户的问题是重复的,例如:

  • “你们的退款政策是什么?”
  • “如何重置密码?”
  • “支持开发票吗?”
  • “套餐有什么区别?”

如果每次都请求模型,就会造成明显浪费。


三、降低成本的核心思路

ChatGPT 成本优化可以概括为以下几条原则:

  1. 能不用模型就不用模型
  2. 能用小模型就不用大模型
  3. 能少传文本就少传文本
  4. 能缓存结果就缓存结果
  5. 能离线处理就不要实时处理
  6. 能结构化规则解决就不要交给大模型
  7. 能提前判断低价值请求就提前拦截

下面逐一展开。


四、策略一:缩短 Prompt,减少固定成本

Prompt 是每次请求都会消耗的输入成本。尤其是 System Prompt,如果写得很长,每次调用都会重复付费。

优化前示例

你是一个专业客服助手,你需要遵守以下规则:
1. 你必须使用中文回答;
2. 你必须礼貌;
3. 你不能编造信息;
4. 你需要参考以下业务背景;
5. 我们公司成立于……
6. 产品套餐包括……
7. 退款规则包括……
8. 发票规则包括……
……

这种方式虽然简单,但如果业务背景很长,成本会很高。

优化后思路

可以把 Prompt 拆成三部分:

  1. 固定规则:尽量压缩,保留必要规则;
  2. 动态知识:通过检索按需注入;
  3. 输出格式:只在需要时添加。

压缩后的 Prompt 示例

你是客服助手。要求:
- 用中文简洁回答;
- 仅依据提供资料;
- 不确定时说明无法确认;
- 不编造。

很多时候,几百字的规则可以压缩到几十字,效果差异并不大,但成本会明显降低。


五、策略二:控制输出长度

输出 Token 通常比输入 Token 更贵,因此控制回答长度非常重要。

可以在 Prompt 中明确要求:

请在 150 字以内回答。

或者:

只输出 JSON,不要解释。

对于分类、判断、提取类任务,应该避免让模型输出长篇解释。

示例:低成本分类 Prompt

判断用户问题属于哪一类,只输出类别名:
类别:退款、发票、账号、套餐、其他

用户问题:{{question}}

输出:

退款

这比让模型解释“为什么属于退款类”要便宜很多。


六、策略三:使用缓存减少重复调用

缓存是降低成本最直接、最有效的方法之一。

对于重复问题、相似问题、固定知识问答,可以使用缓存。缓存可以分为两类:

1. 精确缓存

完全相同的问题直接返回历史答案。

2. 语义缓存

问题不完全相同,但语义相近,也可以复用答案。

例如:

  • “怎么申请退款?”
  • “我想退款怎么办?”
  • “退款流程是什么?”

这几个问题语义相近,可以命中同一个缓存结果。

下面给出一个基于 Node.js 的精确缓存示例。


七、源码一:Node.js 精确缓存示例

/**
 * 简单 ChatGPT 成本优化示例:精确缓存
 * 运行前:
 * npm install express openai
 */

import express from "express";
import OpenAI from "openai";
import crypto from "crypto";

const app = express();
app.use(express.json());

const client = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

// 内存缓存,生产环境建议使用 Redis
const cache = new Map();

function hashText(text) {
  return crypto.createHash("sha256").update(text.trim()).digest("hex");
}

async function askLLM(question) {
  const response = await client.chat.completions.create({
    model: "gpt-4o-mini",
    messages: [
      {
        role: "system",
        content: "你是客服助手。用中文简洁回答,不确定则说明无法确认。",
      },
      {
        role: "user",
        content: question,
      },
    ],
    temperature: 0.2,
    max_tokens: 300,
  });

  return response.choices[0].message.content;
}

app.post("/chat", async (req, res) => {
  try {
    const { question } = req.body;

    if (!question) {
      return res.status(400).json({ error: "question is required" });
    }

    const key = hashText(question);

    if (cache.has(key)) {
      return res.json({
        source: "cache",
        answer: cache.get(key),
      });
    }

    const answer = await askLLM(question);

    cache.set(key, answer);

    return res.json({
      source: "llm",
      answer,
    });
  } catch (error) {
    console.error(error);
    return res.status(500).json({ error: "server error" });
  }
});

app.listen(3000, () => {
  console.log("Server running on http://localhost:3000");
});

这个示例非常简单,但在 FAQ、客服、运营助手等场景中,精确缓存就能节省相当一部分成本。

不过,精确缓存只能处理完全一致的问题。如果用户换一种说法,缓存就无法命中。此时可以使用语义缓存。


八、源码二:Python 语义缓存示例

语义缓存的核心流程是:

  1. 将用户问题转换为向量;
  2. 在历史问题向量中查找相似问题;
  3. 如果相似度高于阈值,直接返回缓存答案;
  4. 如果未命中,则调用大模型,并把问题和答案写入缓存。

下面是一个简化版 Python 示例。

"""
语义缓存示例
运行前:
pip install openai numpy
"""

import os
import numpy as np
from openai import OpenAI

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

cache = []


def cosine_similarity(a, b):
    a = np.array(a)
    b = np.array(b)
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))


def get_embedding(text):
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=text
    )
    return response.data[0].embedding


def ask_llm(question):
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {
                "role": "system",
                "content": "你是客服助手。用中文简洁回答,只回答确定的信息。"
            },
            {
                "role": "user",
                "content": question
            }
        ],
        temperature=0.2,
        max_tokens=300
    )
    return response.choices[0].message.content


def semantic_chat(question, threshold=0.88):
    query_embedding = get_embedding(question)

    best_item = None
    best_score = 0

    for item in cache:
        score = cosine_similarity(query_embedding, item["embedding"])
        if score > best_score:
            best_score = score
            best_item = item

    if best_item and best_score >= threshold:
        return {
            "source": "semantic_cache",
            "score": best_score,
            "answer": best_item["answer"]
        }

    answer = ask_llm(question)

    cache.append({
        "question": question,
        "embedding": query_embedding,
        "answer": answer
    })

    return {
        "source": "llm",
        "score": None,
        "answer": answer
    }


if __name__ == "__main__":
    while True:
        q = input("请输入问题:")
        result = semantic_chat(q)
        print(result)

生产环境中,建议将向量存入专业向量数据库,例如 Milvus、Qdrant、Weaviate、Pinecone,或支持向量检索的 PostgreSQL pgvector。


九、策略四:多模型路由,便宜任务用便宜模型

降低成本的关键之一是:不要所有请求都调用最贵模型。

可以设计一个模型路由器,根据任务复杂度选择不同模型。

示例路由规则

任务类型 推荐模型策略
简单分类 小模型
敏感词判断 规则或小模型
FAQ 问答 缓存 + 小模型
知识库问答 小模型或中等模型
复杂推理 强模型
高价值内容生成 强模型
代码审查 中等或强模型

源码三:模型路由示例

import os
from openai import OpenAI

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))


def classify_task(question: str) -> str:
    """
    简化版任务分类。
    生产环境可结合规则、小模型或意图识别模型。
    """
    simple_keywords = ["退款", "发票", "密码", "套餐", "价格", "登录"]
    complex_keywords = ["分析", "方案", "架构", "代码", "优化", "推理"]

    if any(k in question for k in complex_keywords):
        return "complex"

    if any(k in question for k in simple_keywords):
        return "simple"

    return "normal"


def choose_model(task_type: str) -> str:
    if task_type == "simple":
        return "gpt-4o-mini"
    elif task_type == "normal":
        return "gpt-4o-mini"
    else:
        return "gpt-4o"


def chat(question: str):
    task_type = classify_task(question)
    model = choose_model(task_type)

    max_tokens = 300 if task_type != "complex" else 1000

    response = client.chat.completions.create(
        model=model,
        messages=[
            {
                "role": "system",
                "content": "你是专业助手。请用中文回答,尽量简洁。"
            },
            {
                "role": "user",
                "content": question
            }
        ],
        temperature=0.3,
        max_tokens=max_tokens
    )

    return {
        "task_type": task_type,
        "model": model,
        "answer": response.choices[0].message.content
    }

通过模型路由,可以让 80% 的普通请求走低成本模型,只把少量复杂请求交给高成本模型。


十、策略五:多轮对话做上下文裁剪和摘要

多轮对话是成本失控的高发场景。很多系统会把完整历史全部传给模型,这会导致每轮请求越来越贵。

更合理的做法是:

  1. 保留最近几轮对话;
  2. 将更早的历史压缩成摘要;
  3. 只传递与当前问题相关的历史;
  4. 对无关闲聊进行丢弃。

上下文结构示例

系统规则:简短固定 Prompt

历史摘要:
用户之前询问过退款政策,已说明支持 7 天内退款;用户购买的是专业版套餐。

最近对话:
用户:我上周买的套餐还能退吗?
助手:如果在 7 天内通常可以申请退款,请提供订单号确认。

当前问题:
用户:那我应该在哪里提交申请?

这样比传完整历史便宜很多。

源码四:对话历史摘要示例

import os
from openai import OpenAI

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))


def summarize_history(messages):
    text = "\n".join([f"{m['role']}:{m['content']}" for m in messages])

    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {
                "role": "system",
                "content": "请将以下对话压缩成不超过150字的中文摘要,保留用户关键信息、结论和待办事项。"
            },
            {
                "role": "user",
                "content": text
            }
        ],
        temperature=0,
        max_tokens=200
    )

    return response.choices[0].message.content


def build_messages(summary, recent_messages, current_question):
    messages = [
        {
            "role": "system",
            "content": "你是客服助手。依据上下文回答,中文简洁,不确定则说明。"
        }
    ]

    if summary:
        messages.append({
            "role": "system",
            "content": f"历史摘要:{summary}"
        })

    messages.extend(recent_messages)

    messages.append({
        "role": "user",
        "content": current_question
    })

    return messages

摘要本身也需要调用模型,但它可以显著降低长对话后续每一轮的成本,适合对话轮次较多的场景。


十一、策略六:优化 RAG,减少无效知识注入

RAG,即检索增强生成,是知识库问答中非常常见的方案。它通常包括:

  1. 用户提问;
  2. 将问题向量化;
  3. 检索相关文档;
  4. 把文档片段放入 Prompt;
  5. 模型基于资料回答。

很多 RAG 系统成本高,是因为检索结果太多、片段太长、相关性不高。

RAG 降本建议

  1. 控制 Top K

    • 不要一次塞入 20 个片段;
    • 通常 3 到 5 个高质量片段即可。
  2. 片段长度适中

    • 文档切片不要过大;
    • 每片 300 到 800 字较常见。
  3. 加入重排序

    • 先召回 20 条,再用 reranker 选前 3 条;
    • 减少低相关内容进入 Prompt。
  4. 设置相似度阈值

    • 如果检索结果相似度过低,直接回答“知识库未找到相关信息”;
    • 不要强行调用大模型生成。
  5. 答案要短

    • 知识库问答通常不需要长篇发挥。

RAG Prompt 示例

你是知识库问答助手。只能根据资料回答,不得编造。
如果资料不足,请回答:当前资料中未找到相关信息。

资料:
{{context}}

问题:
{{question}}

请用不超过 200 字回答。

十二、策略七:对低价值请求做拦截

不是所有请求都值得调用大模型。例如:

  • 空问题;
  • 乱码;
  • 明显恶意刷接口;
  • 超长无意义文本;
  • 重复提交;
  • 不在业务范围内的问题。

这些请求应该在进入模型之前被拦截。

源码五:简单请求过滤

function validateQuestion(question) {
  if (!question || typeof question !== "string") {
    return { valid: false, reason: "问题不能为空" };
  }

  const q = question.trim();

  if (q.length < 2) {
    return { valid: false, reason: "问题过短" };
  }

  if (q.length > 2000) {
    return { valid: false, reason: "问题过长,请精简后再提交" };
  }

  const repeatedChar = /(.)\1{20,}/;
  if (repeatedChar.test(q)) {
    return { valid: false, reason: "问题包含大量重复字符" };
  }

  return { valid: true };
}

这个过滤逻辑很简单,但可以挡掉大量无效请求。对于商业系统,还应该结合用户 ID、IP、频率、套餐额度等进行限流。


十三、策略八:设置 max_tokens 和 temperature

很多人调用模型时不设置 max_tokens,这会让模型有机会输出很长内容,导致费用不可控。

建议根据业务类型设置不同上限:

场景 max_tokens 建议
分类 10 到 50
关键词提取 50 到 100
简短客服回答 200 到 400
摘要 200 到 600
内容创作 800 到 2000
复杂分析 1000 以上

同时,temperature 越高,输出越发散,可能更长、更不稳定。对于客服、知识库、分类任务,建议使用较低 temperature,例如 0 到 0.3。


十四、策略九:监控 Token 使用量

没有监控,就无法判断优化是否有效。

你至少应该记录以下指标:

  1. 用户 ID;
  2. 请求时间;
  3. 使用模型;
  4. 输入 Token;
  5. 输出 Token;
  6. 总 Token;
  7. 是否命中缓存;
  8. 请求类型;
  9. 响应耗时;
  10. 估算成本。

源码六:记录 Token 用量

def call_and_log(client, model, messages, max_tokens=500):
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        max_tokens=max_tokens,
        temperature=0.2
    )

    usage = response.usage

    log_item = {
        "model": model,
        "prompt_tokens": usage.prompt_tokens,
        "completion_tokens": usage.completion_tokens,
        "total_tokens": usage.total_tokens,
        "answer": response.choices[0].message.content
    }

    print("usage log:", log_item)

    return log_item

实际项目中,应将日志写入数据库或监控系统,例如 PostgreSQL、ClickHouse、Elasticsearch、Prometheus 等。

通过监控,你可以发现:

  • 哪些用户消耗最多;
  • 哪些接口最贵;
  • 哪些 Prompt 太长;
  • 哪些场景缓存命中率低;
  • 哪些模型调用可以降级。

十五、策略十:批处理与异步任务

对于不要求实时返回的任务,可以采用批处理或异步处理。

例如:

  • 批量生成商品描述;
  • 批量摘要文章;
  • 离线清洗标签;
  • 周报月报生成;
  • 文档预处理。

这些任务不一定要用户点击后立即调用模型,可以放入任务队列,在低峰期处理,或者使用更便宜的模型批量执行。

常见架构如下:

用户提交任务
    ↓
写入任务队列
    ↓
后台 Worker 异步处理
    ↓
保存结果
    ↓
用户查询或通知用户

这样既能平滑峰值,又能避免重复调用。


十六、推荐的低成本架构

一个较成熟的低成本 ChatGPT 应用架构可以设计为:

用户请求
  ↓
请求校验与限流
  ↓
意图识别
  ↓
缓存查询
  ↓
是否需要知识库检索
  ↓
模型路由
  ↓
调用 LLM
  ↓
结果缓存
  ↓
Token 日志与成本统计
  ↓
返回用户

在这个流程中,真正调用大模型只是其中一步。越多请求能在前面被缓存、规则、检索、限流处理掉,总成本就越低。


十七、一个完整的成本优化伪代码

下面给出一个更完整的流程示例,便于理解整体思路。

def handle_user_question(user_id, question):
    # 1. 请求校验
    valid_result = validate_question(question)
    if not valid_result["valid"]:
        return valid_result["reason"]

    # 2. 限流
    if is_rate_limited(user_id):
        return "请求过于频繁,请稍后再试。"

    # 3. 精确缓存
    cached = exact_cache_get(question)
    if cached:
        return cached

    # 4. 语义缓存
    semantic_cached = semantic_cache_get(question)
    if semantic_cached and semantic_cached["score"] > 0.9:
        return semantic_cached["answer"]

    # 5. 意图识别
    intent = classify_intent(question)

    # 6. 简单问题走规则
    rule_answer = rule_based_answer(intent, question)
    if rule_answer:
        return rule_answer

    # 7. 需要知识库则检索
    context = ""
    if intent in ["faq", "policy", "product"]:
        docs = search_knowledge_base(question, top_k=3)
        if not docs:
            return "当前资料中未找到相关信息。"
        context = build_context(docs)

    # 8. 选择模型
    model = choose_model(intent, question)

    # 9. 构造短 Prompt
    messages = build_prompt(question, context)

    # 10. 调用模型
    answer = call_llm(model, messages, max_tokens=300)

    # 11. 写入缓存
    exact_cache_set(question, answer)
    semantic_cache_set(question, answer)

    # 12. 记录成本
    log_usage(user_id, model, question, answer)

    return answer

这个流程的目标不是“每个请求都让 ChatGPT 直接回答”,而是让系统先判断:这个请求是否真的需要调用模型、需要调用哪个模型、需要传多少上下文、需要输出多长答案。


十八、成本优化的优先级建议

如果你的项目已经上线,建议按以下顺序优化:

第一阶段:立刻见效

  1. 设置 max_tokens
  2. 缩短 System Prompt;
  3. 添加精确缓存;
  4. 添加请求长度限制;
  5. 用便宜模型替换部分任务。

第二阶段:明显降本

  1. 增加语义缓存;
  2. 多轮对话摘要;
  3. RAG 控制 Top K;
  4. 增加模型路由;
  5. 记录 Token 使用日志。

第三阶段:长期优化

  1. 建立成本看板;
  2. 分用户、分场景统计 ROI;
  3. 对高频任务做规则化;
  4. 离线批处理;
  5. 针对特定任务微调或蒸馏小模型。

十九、实际降本效果参考

不同业务场景的优化效果不同,但一般来说:

优化方式 可能节省成本
缩短 Prompt 10% 到 40%
设置输出长度 10% 到 30%
精确缓存 5% 到 30%
语义缓存 10% 到 50%
模型路由 20% 到 70%
上下文摘要 20% 到 60%
RAG 优化 15% 到 50%
请求过滤与限流 5% 到 20%

如果一个系统从未做过优化,综合成本降低 50% 以上并不罕见。


二十、总结

ChatGPT 降低成本不是简单地“换一个便宜模型”,而是一套系统工程。真正有效的方式是从请求入口到模型调用再到日志监控,形成完整闭环。

核心方法包括:

  • 压缩 Prompt;
  • 控制输出长度;
  • 精确缓存和语义缓存;
  • 多模型路由;
  • 多轮对话摘要;
  • 优化 RAG 检索;
  • 拦截低价值请求;
  • 设置限流和配额;
  • 监控 Token 与成本;
  • 对非实时任务进行异步批处理。

对于企业级应用来说,最理想的状态是:让大模型只处理真正值得处理的问题。简单问题交给规则,重复问题交给缓存,低复杂度任务交给小模型,复杂任务才交给强模型。这样既能保证用户体验,又能显著降低调用成本。

如果你正在开发基于 ChatGPT 的产品,建议不要等到账单失控后再优化,而是在系统设计阶段就加入缓存、路由、限流和监控。成本优化做得越早,后期收益越大。

目录结构
全文