ChatGPT 账单太高?这套降本方案和代码可以直接用
ChatGPT 如何降低成本|附源码
随着大模型能力不断提升,越来越多团队开始把 ChatGPT 或其他大语言模型接入到客服、内容生成、知识库问答、代码辅助、数据分析等业务中。刚开始使用时,很多人只关注“效果好不好”,但当调用量逐渐上升后,一个现实问题很快出现:成本越来越高。
尤其是在高并发、多轮对话、长文本输入、知识库检索、批量生成等场景下,如果不做成本优化,API 费用可能会快速增长。本文将从产品设计、Prompt 优化、模型选择、缓存策略、上下文压缩、RAG 检索优化、批处理、限流与监控等多个角度,系统讲解如何降低 ChatGPT 使用成本,并附上可直接参考的源码示例。
一、ChatGPT 成本主要来自哪里?
在优化成本之前,需要先搞清楚成本构成。
大多数大模型 API 的计费方式都与 Token 有关。简单来说,Token 可以理解为模型处理文本的基本单位。中文、英文、标点、空格都会被拆分成不同数量的 Token。
通常费用包括两部分:
-
输入 Token 成本
- 用户问题
- 系统提示词
- 历史对话上下文
- 知识库检索内容
- 工具调用说明
- 结构化输出要求
-
输出 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 成本优化可以概括为以下几条原则:
- 能不用模型就不用模型
- 能用小模型就不用大模型
- 能少传文本就少传文本
- 能缓存结果就缓存结果
- 能离线处理就不要实时处理
- 能结构化规则解决就不要交给大模型
- 能提前判断低价值请求就提前拦截
下面逐一展开。
四、策略一:缩短 Prompt,减少固定成本
Prompt 是每次请求都会消耗的输入成本。尤其是 System Prompt,如果写得很长,每次调用都会重复付费。
优化前示例
你是一个专业客服助手,你需要遵守以下规则:
1. 你必须使用中文回答;
2. 你必须礼貌;
3. 你不能编造信息;
4. 你需要参考以下业务背景;
5. 我们公司成立于……
6. 产品套餐包括……
7. 退款规则包括……
8. 发票规则包括……
……
这种方式虽然简单,但如果业务背景很长,成本会很高。
优化后思路
可以把 Prompt 拆成三部分:
- 固定规则:尽量压缩,保留必要规则;
- 动态知识:通过检索按需注入;
- 输出格式:只在需要时添加。
压缩后的 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 语义缓存示例
语义缓存的核心流程是:
- 将用户问题转换为向量;
- 在历史问题向量中查找相似问题;
- 如果相似度高于阈值,直接返回缓存答案;
- 如果未命中,则调用大模型,并把问题和答案写入缓存。
下面是一个简化版 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% 的普通请求走低成本模型,只把少量复杂请求交给高成本模型。
十、策略五:多轮对话做上下文裁剪和摘要
多轮对话是成本失控的高发场景。很多系统会把完整历史全部传给模型,这会导致每轮请求越来越贵。
更合理的做法是:
- 保留最近几轮对话;
- 将更早的历史压缩成摘要;
- 只传递与当前问题相关的历史;
- 对无关闲聊进行丢弃。
上下文结构示例
系统规则:简短固定 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,即检索增强生成,是知识库问答中非常常见的方案。它通常包括:
- 用户提问;
- 将问题向量化;
- 检索相关文档;
- 把文档片段放入 Prompt;
- 模型基于资料回答。
很多 RAG 系统成本高,是因为检索结果太多、片段太长、相关性不高。
RAG 降本建议
-
控制 Top K
- 不要一次塞入 20 个片段;
- 通常 3 到 5 个高质量片段即可。
-
片段长度适中
- 文档切片不要过大;
- 每片 300 到 800 字较常见。
-
加入重排序
- 先召回 20 条,再用 reranker 选前 3 条;
- 减少低相关内容进入 Prompt。
-
设置相似度阈值
- 如果检索结果相似度过低,直接回答“知识库未找到相关信息”;
- 不要强行调用大模型生成。
-
答案要短
- 知识库问答通常不需要长篇发挥。
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 使用量
没有监控,就无法判断优化是否有效。
你至少应该记录以下指标:
- 用户 ID;
- 请求时间;
- 使用模型;
- 输入 Token;
- 输出 Token;
- 总 Token;
- 是否命中缓存;
- 请求类型;
- 响应耗时;
- 估算成本。
源码六:记录 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 直接回答”,而是让系统先判断:这个请求是否真的需要调用模型、需要调用哪个模型、需要传多少上下文、需要输出多长答案。
十八、成本优化的优先级建议
如果你的项目已经上线,建议按以下顺序优化:
第一阶段:立刻见效
- 设置
max_tokens; - 缩短 System Prompt;
- 添加精确缓存;
- 添加请求长度限制;
- 用便宜模型替换部分任务。
第二阶段:明显降本
- 增加语义缓存;
- 多轮对话摘要;
- RAG 控制 Top K;
- 增加模型路由;
- 记录 Token 使用日志。
第三阶段:长期优化
- 建立成本看板;
- 分用户、分场景统计 ROI;
- 对高频任务做规则化;
- 离线批处理;
- 针对特定任务微调或蒸馏小模型。
十九、实际降本效果参考
不同业务场景的优化效果不同,但一般来说:
| 优化方式 | 可能节省成本 |
|---|---|
| 缩短 Prompt | 10% 到 40% |
| 设置输出长度 | 10% 到 30% |
| 精确缓存 | 5% 到 30% |
| 语义缓存 | 10% 到 50% |
| 模型路由 | 20% 到 70% |
| 上下文摘要 | 20% 到 60% |
| RAG 优化 | 15% 到 50% |
| 请求过滤与限流 | 5% 到 20% |
如果一个系统从未做过优化,综合成本降低 50% 以上并不罕见。
二十、总结
ChatGPT 降低成本不是简单地“换一个便宜模型”,而是一套系统工程。真正有效的方式是从请求入口到模型调用再到日志监控,形成完整闭环。
核心方法包括:
- 压缩 Prompt;
- 控制输出长度;
- 精确缓存和语义缓存;
- 多模型路由;
- 多轮对话摘要;
- 优化 RAG 检索;
- 拦截低价值请求;
- 设置限流和配额;
- 监控 Token 与成本;
- 对非实时任务进行异步批处理。
对于企业级应用来说,最理想的状态是:让大模型只处理真正值得处理的问题。简单问题交给规则,重复问题交给缓存,低复杂度任务交给小模型,复杂任务才交给强模型。这样既能保证用户体验,又能显著降低调用成本。
如果你正在开发基于 ChatGPT 的产品,建议不要等到账单失控后再优化,而是在系统设计阶段就加入缓存、路由、限流和监控。成本优化做得越早,后期收益越大。