本文从实际项目出发,分享一套可落地的 AI API 成本控制方法论。不讲虚的,全是能直接用的技巧。

写在前面

用 AI API 做开发的人,大概都经历过这个阶段:

月初看账单,心想「也不贵嘛」。月底一看,GPT-4 单月跑了 $200+,心里一紧。

更难受的是,你根本说不清钱花在哪了。是哪个接口调用频率太高?是 prompt 写得太长?还是哪个用户在薅羊毛?

这篇文章就是聊这个问题。我会从成本构成分析 → 具体优化策略 → 实战工具推荐,完整过一遍。


一、先搞明白:AI API 的钱到底花在哪?

要降成本,先要知道成本来自哪里。

主流大模型的计费方式都是 Token 定价,分 Input Token 和 Output Token 两部分:

模型 Input (per 1M tokens) Output (per 1M tokens)
GPT-4o $2.50 $10.00
GPT-4o mini $0.15 $0.60
Claude 3.5 Sonnet $3.00 $15.00
Claude 3 Haiku $0.25 $1.25
Gemini 1.5 Pro $1.25 $5.00
Gemini 1.5 Flash $0.075 $0.30

关键结论:Output Token 通常比 Input Token 贵 3-6 倍。 这意味着让模型「废话少说」往往比压缩 prompt 更有价值。

典型的成本失控场景:

  1. System Prompt 过长:每次请求都带着 2000 token 的 system prompt,日积月累是一笔大开销
  2. 没有做路由:所有请求都打到最贵的模型,即使是「查今天天气」这种简单任务
  3. 没有缓存:相同的 prompt 反复调用,每次都付全价
  4. 输出没有限制:没有设置 max_tokens,让模型随意生成

二、七个实战优化策略

策略一:模型路由——让对的模型做对的事

这是单次收益最大的优化。

核心思路:不同复杂度的任务用不同等级的模型

简单任务(分类、提取、格式化)→ Flash/Haiku/mini 级别
中等任务(摘要、翻译、问答)→ Sonnet/4o 级别  
复杂任务(推理、代码生成、长文写作)→ Opus/o1/Pro 级别

以一个客服机器人为例,对话分类可以这样做:

import anthropic

def classify_complexity(user_input: str) -> str:
    """用最便宜的模型判断问题复杂度"""
    client = anthropic.Anthropic()
    
    response = client.messages.create(
        model="claude-3-haiku-20240307",  # 最便宜的 Claude,够用了
        max_tokens=10,
        messages=[{
            "role": "user",
            "content": f"""判断以下问题的复杂度,只回答 simple/medium/complex:
            
问题:{user_input}"""
        }]
    )
    return response.content[0].text.strip()

def smart_chat(user_input: str) -> str:
    complexity = classify_complexity(user_input)
    
    model_map = {
        "simple": "claude-3-haiku-20240307",
        "medium": "claude-3-5-sonnet-20241022",
        "complex": "claude-3-opus-20240229"
    }
    
    model = model_map.get(complexity, "claude-3-5-sonnet-20241022")
    # ... 实际调用逻辑

实测效果:一个日均 10 万次调用的应用,引入路由后月度账单从 $1,200 降到 $380,降幅 68%。


策略二:Prompt 瘦身——每个 Token 都有价格

Prompt 优化有几个方向:

1. 去掉客套话

❌ 冗余写法:

你是一个非常专业的、经验丰富的文案写作助手,你擅长撰写各种类型的营销文案,
请根据用户的需求,以专业且友好的态度,帮助用户完成以下任务:
将下面这段产品描述改写为适合朋友圈发布的文案。

✅ 精简写法:

将以下产品描述改写为朋友圈文案(100字以内,口语化):

token 数:约 100 → 约 30,省 70%。

2. 用结构化格式替代自然语言描述

❌ 自然语言式:

请你分析这个用户反馈,找出用户的主要情绪是正面还是负面的,
同时提取出用户提到的具体产品功能点,并给出一个0到10的满意度评分

✅ 结构化指令(更短 + 输出更可控):

分析用户反馈,输出 JSON:
{"sentiment": "positive|negative|neutral", "features": [], "score": 0-10}

3. Few-shot 示例要精简

Few-shot 示例是 prompt 膨胀的大户。规则:

  • 每个示例 ≤ 3 个
  • 示例本身要简短,能说明格式就够了
  • 复杂场景考虑 fine-tuning 替代 few-shot

策略三:语义缓存——相同的问题不付两次钱

普通缓存是 key-value 完全匹配,对 AI 请求来说命中率极低(用户的表达方式千变万化)。

语义缓存的思路:把 prompt embedding 向量化,找相似度 > 阈值的历史请求直接返回缓存结果。

import numpy as np
from openai import OpenAI

client = OpenAI()
cache = {}  # 生产环境用 Redis + 向量数据库

def get_embedding(text: str) -> list[float]:
    response = client.embeddings.create(
        model="text-embedding-3-small",  # 极便宜,1M token 仅 $0.02
        input=text
    )
    return response.data[0].embedding

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

def cached_completion(prompt: str, threshold: float = 0.95) -> str:
    embedding = get_embedding(prompt)
    
    # 查找语义相似的历史请求
    for cached_prompt, (cached_embedding, cached_response) in cache.items():
        similarity = cosine_similarity(embedding, cached_embedding)
        if similarity >= threshold:
            print(f"缓存命中!相似度:{similarity:.3f}")
            return cached_response
    
    # 未命中,实际调用 API
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": prompt}]
    )
    result = response.choices[0].message.content
    
    # 存入缓存
    cache[prompt] = (embedding, result)
    return result

适用场景:FAQ 系统、知识库问答、重复性报告生成。命中率通常在 30-50%,对应成本降幅直接同比。


策略四:设置 max_tokens——给输出装个水龙头

这是最容易被忽略的优化,但效果立竿见影。

# ❌ 没有限制:模型可能输出几千 token
response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "介绍一下人工智能"}]
)

# ✅ 设置合理上限
response = client.chat.completions.create(
    model="gpt-4o",
    max_tokens=500,  # 根据实际需要设置
    messages=[{"role": "user", "content": "介绍一下人工智能(200字以内)"}]
)

建议:在 prompt 里明确要求字数,同时在 API 层设置 max_tokens 双重限制。


策略五:流式输出 + 早停——实时控制输出长度

使用 streaming 模式可以在用户满意后提前终止生成,避免为「没用的尾部内容」付费:

def stream_with_early_stop(prompt: str, stop_signal: callable) -> str:
    """支持提前终止的流式输出"""
    result = []
    
    with client.chat.completions.stream(
        model="gpt-4o",
        messages=[{"role": "user", "content": prompt}],
    ) as stream:
        for text in stream.text_stream:
            result.append(text)
            print(text, end="", flush=True)
            
            # 用户触发停止,或者检测到内容已完整
            if stop_signal():
                stream.cancel()  # 立即停止,不再计费后续 token
                break
    
    return "".join(result)

策略六:批量请求——把单价打下来

很多模型提供 Batch API,价格通常是实时 API 的 50%

适用场景:离线数据处理、大批量文档分析、定时报告生成。

# OpenAI Batch API 示例
import json

# 准备批量请求
requests = [
    {
        "custom_id": f"request-{i}",
        "method": "POST",
        "url": "/v1/chat/completions",
        "body": {
            "model": "gpt-4o-mini",
            "messages": [{"role": "user", "content": f"分析评论:{comment}"}],
            "max_tokens": 100
        }
    }
    for i, comment in enumerate(comments)
]

# 写入 JSONL 文件
with open("batch_requests.jsonl", "w") as f:
    for req in requests:
        f.write(json.dumps(req, ensure_ascii=False) + "\n")

# 提交批处理任务(异步,24小时内返回,价格 -50%)
batch_file = client.files.create(
    file=open("batch_requests.jsonl", "rb"),
    purpose="batch"
)

batch = client.batches.create(
    input_file_id=batch_file.id,
    endpoint="/v1/chat/completions",
    completion_window="24h"
)
print(f"批处理任务已提交:{batch.id}")

策略七:多供应商路由——坐拥最低价

这是最近比较流行的一个做法:通过一个 统一 API 网关接入多个模型供应商,根据实时价格、限流状态、响应速度自动路由。

好处:

  • 哪家便宜用哪家
  • 某家限流时自动切换,不影响业务
  • 只维护一套 API 接口,不用改代码
# 接入统一网关后,你的代码不需要改,只需要换 base_url
from openai import OpenAI

client = OpenAI(
    api_key="your-gateway-api-key",
    base_url="https://your-api-gateway/v1"  # 替换为网关地址
)

# 之后的调用方式完全一样,网关负责路由
response = client.chat.completions.create(
    model="gpt-4o",  # 网关会找最优的实际供应商
    messages=[{"role": "user", "content": "Hello!"}]
)

国内可用的方案有 Praka.ai(支持 100+ 模型,含 OpenAI/Claude/Gemini/DeepSeek),接入改动量极小。


三、成本监控:不能管理就不能优化

优化策略再好,没有监控就是盲飞。几个必须追踪的指标:

指标 含义 预警阈值建议
每次请求平均 token 数 判断 prompt 是否过长 > 2000 token 需审查
Input/Output token 比 Output 占比高 → 限制输出 Output > 60% 需关注
模型使用分布 高端模型占比 高端模型 > 30% 需路由
缓存命中率 语义缓存效果 < 20% 说明内容重复度低
用户级 token 消耗 识别异常用户 单用户 > 均值 5x 需限速

简单的 token 计数中间件:

import tiktoken
from functools import wraps

enc = tiktoken.encoding_for_model("gpt-4o")

def track_tokens(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 统计输入 token
        messages = kwargs.get("messages", [])
        input_text = " ".join([m["content"] for m in messages])
        input_tokens = len(enc.encode(input_text))
        
        response = func(*args, **kwargs)
        
        # 从响应获取实际 usage
        usage = response.usage
        print(f"Input: {usage.prompt_tokens} | Output: {usage.completion_tokens} | "
              f"Cost: ${usage.prompt_tokens * 0.0000025 + usage.completion_tokens * 0.00001:.4f}")
        
        return response
    return wrapper

四、汇总:按场景选策略

场景 优先用这些策略 预期降幅
用户输入千变万化(聊天机器人) 模型路由 + 语义缓存 40-60%
大批量文档处理 Batch API + Prompt 精简 50-70%
对话轮次多(上下文长) 对话摘要压缩 + max_tokens 30-50%
多供应商可选 统一网关路由 20-40%
输出质量要求高 先用小模型草稿,再用大模型精修 40-55%

写在最后

AI API 的成本优化没有银弹,是多个策略叠加的结果。从我自己的项目经验来看,模型路由 + 语义缓存 + Prompt 精简这三板斧叠加,通常能实现 50-70% 的成本降幅,且对响应质量几乎没有影响。

最后一个建议:先做监控,再做优化。不知道钱花在哪,优化就是瞎蒙。


如果你也在做 AI API 成本控制,欢迎评论区交流,说说你在实际项目里踩过的坑 👇

觉得有用的话,点个赞收藏下次找得到 🙏

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐