第七章:生产环境 — 02 成本优化#

LLM API 成本可以优化 10x 以上。本章覆盖所有主要的降本手段。

成本优化全景图#

LLM 成本 = 请求数量 × 每次 token 数 × 单价

降本策略:
1. 降低单价     → 选择更便宜的模型、批量折扣
2. 减少 token   → 压缩提示词、控制输出长度
3. 减少请求数   → 语义缓存、结果复用
4. 智能路由     → 简单任务用小模型
5. 并行处理     → 减少等待时间(间接降本)

本章目标: 掌握每种策略的实现方式和预期节省比例。

import json
import os
import time
import hashlib
import asyncio
import litellm
litellm.drop_params = True
from dotenv import load_dotenv

try:
    import tiktoken
    TIKTOKEN_AVAILABLE = True
except ImportError:
    TIKTOKEN_AVAILABLE = False
    print("tiktoken 未安装,将用字符数估算 token:pip install tiktoken")

try:
    import numpy as np
    NUMPY_AVAILABLE = True
except ImportError:
    NUMPY_AVAILABLE = False
    print("numpy 未安装:pip install numpy")

load_dotenv()
MODEL = os.getenv("LLM_MODEL", "gpt-4o-mini")
litellm.set_verbose = False

print(f"使用模型: {MODEL}")
print("成本优化工具包初始化完成")
# gpt-5/o系列不支持自定义temperature值,统一用安全wrapper
def _c(**kw):
    _m = kw.get('model', MODEL)
    if any(_m.startswith(p) for p in ('openai/gpt-5','openai/o1','openai/o3','openai/o4')):
        kw.pop('temperature', None)
    return litellm.completion(**kw)
使用模型: openai/gpt-5-mini
成本优化工具包初始化完成

Section 1:Token 成本核算#

首先要知道自己花了多少钱,才能针对性地优化。

# 各模型价格表(2024年,USD/1M tokens)
MODEL_PRICES = {
    "gpt-4o": {"input": 2.50, "output": 10.00},
    "gpt-4o-mini": {"input": 0.15, "output": 0.60},
    "claude-3-5-sonnet": {"input": 3.00, "output": 15.00},
    "claude-3-haiku": {"input": 0.25, "output": 1.25},
    "gemini-1.5-flash": {"input": 0.075, "output": 0.30},
    "gemini-1.5-pro": {"input": 1.25, "output": 5.00},
    "llama-3-8b-self-hosted": {"input": 0.0, "output": 0.0, "server": 2.0},  # GPU $2/hr
}


def cost_estimate(model: str, prompt_tokens: int, completion_tokens: int) -> dict:
    """
    估算 LLM API 调用成本
    
    Args:
        model: 模型名称
        prompt_tokens: 输入 token 数
        completion_tokens: 输出 token 数
    
    Returns:
        dict with cost breakdown in USD
    """
    if model not in MODEL_PRICES:
        # 尝试从 litellm 获取
        try:
            cost = litellm.completion_cost(
                completion_response=None,
                model=model,
                prompt=" " * prompt_tokens,
                completion=" " * completion_tokens
            )
            return {"total_usd": cost, "source": "litellm"}
        except:
            return {"error": f"未知模型: {model}"}
    
    prices = MODEL_PRICES[model]
    input_cost = prompt_tokens / 1_000_000 * prices["input"]
    output_cost = completion_tokens / 1_000_000 * prices["output"]
    total = input_cost + output_cost
    
    return {
        "model": model,
        "prompt_tokens": prompt_tokens,
        "completion_tokens": completion_tokens,
        "input_cost_usd": round(input_cost, 6),
        "output_cost_usd": round(output_cost, 6),
        "total_usd": round(total, 6),
        "source": "manual"
    }


# 场景:1M 请求/天,每次 300 prompt + 200 completion tokens
DAILY_REQUESTS = 1_000_000
PROMPT_TOKENS = 300
COMPLETION_TOKENS = 200

print(f"场景:每天 {DAILY_REQUESTS:,} 次请求,每次 {PROMPT_TOKENS}+{COMPLETION_TOKENS} tokens")
print("=" * 75)
print(f"{'模型':<30} {'单次成本':>12} {'每日成本':>12} {'每月成本':>12}")
print("-" * 75)

costs_data = []
for model_name in MODEL_PRICES:
    single = cost_estimate(model_name, PROMPT_TOKENS, COMPLETION_TOKENS)
    if "error" in single:
        continue
    
    per_call = single["total_usd"]
    
    # 自托管:按服务器成本计算
    if "server" in MODEL_PRICES[model_name]:
        server_hourly = MODEL_PRICES[model_name]["server"]
        daily = server_hourly * 24  # $48/天
    else:
        daily = per_call * DAILY_REQUESTS
    
    monthly = daily * 30
    costs_data.append({"model": model_name, "per_call": per_call, "daily": daily, "monthly": monthly})
    
    print(f"{model_name:<30} ${per_call:>10.6f} ${daily:>10,.2f} ${monthly:>10,.0f}")

print("-" * 75)
cheapest = min(costs_data, key=lambda x: x["daily"])
most_expensive = max(costs_data, key=lambda x: x["daily"])
ratio = most_expensive["daily"] / cheapest["daily"]
print(f"\n最贵: {most_expensive['model']} (${most_expensive['daily']:.2f}/天)")
print(f"最便宜: {cheapest['model']} (${cheapest['daily']:.2f}/天)")
print(f"价差: {ratio:.0f}x")
场景:每天 1,000,000 次请求,每次 300+200 tokens
===========================================================================
模型                                     单次成本         每日成本         每月成本
---------------------------------------------------------------------------
gpt-4o                         $  0.002750 $  2,750.00 $    82,500
gpt-4o-mini                    $  0.000165 $    165.00 $     4,950
claude-3-5-sonnet              $  0.003900 $  3,900.00 $   117,000
claude-3-haiku                 $  0.000325 $    325.00 $     9,750
gemini-1.5-flash               $  0.000082 $     82.00 $     2,460
gemini-1.5-pro                 $  0.001375 $  1,375.00 $    41,250
llama-3-8b-self-hosted         $  0.000000 $     48.00 $     1,440
---------------------------------------------------------------------------

最贵: claude-3-5-sonnet ($3900.00/天)
最便宜: llama-3-8b-self-hosted ($48.00/天)
价差: 81x
# 可视化:成本对比柱状图
try:
    import matplotlib.pyplot as plt
    import matplotlib
    matplotlib.rcParams['font.family'] = ['Arial Unicode MS', 'DejaVu Sans']
    
    models = [d["model"].replace("-self-hosted", "\n(自托管") for d in costs_data]
    daily_costs = [d["daily"] for d in costs_data]
    
    colors = ["#e74c3c" if c > 1000 else ("#f39c12" if c > 100 else "#2ecc71")
              for c in daily_costs]
    
    fig, ax = plt.subplots(figsize=(12, 5))
    bars = ax.bar(range(len(models)), daily_costs, color=colors, edgecolor="white", linewidth=0.5)
    
    ax.set_xticks(range(len(models)))
    ax.set_xticklabels(models, rotation=30, ha="right", fontsize=9)
    ax.set_ylabel("每日成本 (USD)")
    ax.set_title(f"LLM 每日成本对比({DAILY_REQUESTS:,} 请求/天,{PROMPT_TOKENS}+{COMPLETION_TOKENS} tokens/次)")
    ax.set_yscale("log")
    
    for bar, cost in zip(bars, daily_costs):
        ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() * 1.1,
                f"${cost:.0f}", ha="center", va="bottom", fontsize=8)
    
    ax.axhline(y=100, color="orange", linestyle="--", alpha=0.7, label="$100/天 预算线")
    ax.legend()
    plt.tight_layout()
    plt.savefig("/tmp/llm_cost_comparison.png", dpi=150, bbox_inches="tight")
    plt.show()
    print("图表已保存到 /tmp/llm_cost_comparison.png")

except ImportError:
    print("matplotlib 未安装,跳过可视化")
    print("安装:pip install matplotlib")
    print("\n成本数据(每日,USD):")
    for d in costs_data:
        bar = "█" * min(int(d["daily"] / 100), 50)
        print(f"  {d['model']:<30} ${d['daily']:>8,.0f} {bar}")
../../_images/cd4d0820fa09957d14f5e1f919efda35ee2c413fbf65ccc22600d6eef27bd500.png
图表已保存到 /tmp/llm_cost_comparison.png

Section 2:提示词压缩#

减少输入 token 是降低成本最直接的方式。提示词压缩不改变语义,只减少冗余。

import re

def count_tokens(text: str, model: str = "gpt-4o-mini") -> int:
    """估算 token 数量"""
    if TIKTOKEN_AVAILABLE:
        try:
            enc = tiktoken.encoding_for_model(model)
            return len(enc.encode(text))
        except:
            pass
    # fallback:中文约1.5字符/token,英文约4字符/token
    chinese_chars = len(re.findall(r'[\u4e00-\u9fff]', text))
    other_chars = len(text) - chinese_chars
    return int(chinese_chars / 1.5 + other_chars / 4)


def compress_prompt(text: str) -> dict:
    """
    提示词压缩:去除冗余内容
    
    策略:
    1. 去除多余空白和空行
    2. 合并重复指令
    3. 去除礼貌性冗余词
    4. 压缩常见短语
    """
    original = text
    compressed = text
    
    steps = []
    
    # 1. 去除多余空白
    before = len(compressed)
    compressed = re.sub(r' {2,}', ' ', compressed)      # 多个空格→单个
    compressed = re.sub(r'\n{3,}', '\n\n', compressed)   # 多个空行→两个
    compressed = re.sub(r'\t', ' ', compressed)           # Tab→空格
    compressed = compressed.strip()
    saved = before - len(compressed)
    if saved > 0:
        steps.append(f"去除多余空白:节省 {saved} 字符")
    
    # 2. 去除礼貌性冗余
    redundant_phrases = [
        (r'请你?帮(?:我|我们)?', ''),
        (r'麻烦你?', ''),
        (r'谢谢[你您]?[!!。]?', ''),
        (r'好的,我明白了[。!]?\s*', ''),
        (r'非常感谢[!!。]?', ''),
        (r'您好[!!,,]?\s*', ''),
    ]
    before = len(compressed)
    for pattern, replacement in redundant_phrases:
        compressed = re.sub(pattern, replacement, compressed)
    saved = before - len(compressed)
    if saved > 0:
        steps.append(f"去除礼貌词:节省 {saved} 字符")
    
    # 3. 合并重复的指令
    # 找到重复的句子
    sentences = re.split(r'[。!?\n]', compressed)
    seen = set()
    unique_sentences = []
    for s in sentences:
        s_stripped = s.strip()
        if s_stripped and s_stripped not in seen:
            seen.add(s_stripped)
            unique_sentences.append(s_stripped)
    deduped = '。'.join(unique_sentences)
    if len(deduped) < len(compressed):
        steps.append(f"去重重复句子:节省 {len(compressed) - len(deduped)} 字符")
        compressed = deduped
    
    # 4. 最终清理
    compressed = compressed.strip()
    
    original_tokens = count_tokens(original)
    compressed_tokens = count_tokens(compressed)
    reduction = (original_tokens - compressed_tokens) / original_tokens if original_tokens > 0 else 0
    
    return {
        "original": original,
        "compressed": compressed,
        "original_chars": len(original),
        "compressed_chars": len(compressed),
        "original_tokens": original_tokens,
        "compressed_tokens": compressed_tokens,
        "token_reduction": f"{reduction:.1%}",
        "steps": steps
    }


# 演示
verbose_prompt = """您好!麻烦请你帮我做一件事情,非常感谢。

我想让你帮我分析一下这段文字的情感倾向。
请你帮我判断这段文字的情感倾向。
需要你分析情感倾向,包括正面、负面或者中性。

文字内容如下所示:

"今天天气很好,心情愉快!"

请输出情感分析结果。谢谢!"""

result = compress_prompt(verbose_prompt)

print("提示词压缩演示")
print("=" * 60)
print("原始提示词:")
print(result["original"])
print("\n压缩后提示词:")
print(result["compressed"])
print("\n压缩统计:")
print(f"  字符数: {result['original_chars']}{result['compressed_chars']}")
print(f"  Token数: {result['original_tokens']}{result['compressed_tokens']}")
print(f"  Token节省: {result['token_reduction']}")
print("  压缩步骤:")
for step in result["steps"]:
    print(f"    - {step}")
提示词压缩演示
============================================================
原始提示词:
您好!麻烦请你帮我做一件事情,非常感谢。

我想让你帮我分析一下这段文字的情感倾向。
请你帮我判断这段文字的情感倾向。
需要你分析情感倾向,包括正面、负面或者中性。

文字内容如下所示:

"今天天气很好,心情愉快!"

请输出情感分析结果。谢谢!

压缩后提示词:
做一件事情,。我想让你帮我分析一下这段文字的情感倾向。判断这段文字的情感倾向。需要你分析情感倾向,包括正面、负面或者中性。文字内容如下所示:。"今天天气很好,心情愉快。"。请输出情感分析结果

压缩统计:
  字符数: 124 → 95
  Token数: 91 → 75
  Token节省: 17.6%
  压缩步骤:
    - 去除礼貌词:节省 21 字符
    - 去重重复句子:节省 8 字符
# System Prompt 压缩:用 LLM 压缩长 System Prompt

def compress_system_prompt_with_llm(system_prompt: str, model: str = MODEL) -> dict:
    """
    用 LLM 压缩系统提示词,保留核心语义
    适用:将 500+ token 的 system prompt 压缩到 100-200 token
    """
    compression_prompt = f"""将以下系统提示词压缩到原来的 1/3 长度,保留所有关键指令和约束,去除冗余解释:

原始提示词:
{system_prompt}

压缩版本(只输出压缩结果,不要解释):"""
    
    response = _c(
        model=model,
        messages=[{"role": "user", "content": compression_prompt}],
        temperature=0,
        max_tokens=500
    )
    
    compressed = response.choices[0].message.content
    original_tokens = count_tokens(system_prompt)
    compressed_tokens = count_tokens(compressed)
    
    return {
        "original": system_prompt,
        "compressed": compressed,
        "original_tokens": original_tokens,
        "compressed_tokens": compressed_tokens,
        "reduction": f"{(1 - compressed_tokens/original_tokens):.1%}"
    }


long_system_prompt = """你是一位专业的客服代表,代表我们公司为客户提供优质的服务。
在回复客户时,你应该保持友好、专业的态度,始终用礼貌的语气与客户交流。
当客户提出问题时,你需要认真倾听他们的需求,理解他们的困惑,然后提供清晰、准确的解答。
你的回复应该简洁明了,不要使用过于复杂的术语,确保客户能够理解你的回答。
如果客户遇到投诉或不满,你应该先表示理解和同情,然后尽力帮助解决问题。
当遇到你无法处理的问题时,应该及时告知客户会转接给专业人员处理。
在每次回复结束时,询问客户是否还有其他需要帮助的地方,展现我们对客户的关心。"""

result = compress_system_prompt_with_llm(long_system_prompt)
print("System Prompt 智能压缩")
print("=" * 60)
print(f"原始({result['original_tokens']} tokens):")
print(result["original"][:200] + "...")
print(f"\n压缩后({result['compressed_tokens']} tokens,节省 {result['reduction']}):")
print(result["compressed"])
System Prompt 智能压缩
============================================================
原始(171 tokens):
你是一位专业的客服代表,代表我们公司为客户提供优质的服务。
在回复客户时,你应该保持友好、专业的态度,始终用礼貌的语气与客户交流。
当客户提出问题时,你需要认真倾听他们的需求,理解他们的困惑,然后提供清晰、准确的解答。
你的回复应该简洁明了,不要使用过于复杂的术语,确保客户能够理解你的回答。
如果客户遇到投诉或不满,你应该先表示理解和同情,然后尽力帮助解决问题。
当遇到你无法处理的问题时,应该及时...

压缩后(0 tokens,节省 100.0%):

Section 3:语义缓存#

对于相似问题,不重复调用 LLM,直接返回缓存结果。这是降低成本最有效的策略之一。

相似度阈值 = 0.95 意味着:只有问题在语义上几乎完全相同时才使用缓存。

import hashlib

class SemanticCache:
    """
    语义缓存:基于向量相似度的 LLM 结果缓存
    
    工作原理:
    1. 对每个查询生成 embedding
    2. 与缓存中的查询计算余弦相似度
    3. 相似度 > threshold 时返回缓存结果
    """
    
    def __init__(self, similarity_threshold: float = 0.95, model: str = MODEL):
        self.threshold = similarity_threshold
        self.model = model
        self.cache = []  # list of {query, embedding, response, hits}
        self.stats = {"hits": 0, "misses": 0, "total_saved_tokens": 0}
    
    def _get_embedding(self, text: str) -> list:
        """获取文本 embedding"""
        response = litellm.embedding(
            model="text-embedding-3-small",
            input=[text]
        )
        return response.data[0]["embedding"]
    
    def _cosine_similarity(self, vec_a: list, vec_b: list) -> float:
        """计算余弦相似度"""
        if NUMPY_AVAILABLE:
            a = np.array(vec_a)
            b = np.array(vec_b)
            return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))
        else:
            # 纯 Python 实现
            dot = sum(x*y for x, y in zip(vec_a, vec_b))
            norm_a = sum(x**2 for x in vec_a) ** 0.5
            norm_b = sum(x**2 for x in vec_b) ** 0.5
            return dot / (norm_a * norm_b) if norm_a and norm_b else 0.0
    
    def get(self, query: str) -> tuple:
        """
        查询缓存
        Returns: (cached_response, similarity) or (None, 0)
        """
        if not self.cache:
            return None, 0.0
        
        query_emb = self._get_embedding(query)
        
        best_sim = 0.0
        best_entry = None
        
        for entry in self.cache:
            sim = self._cosine_similarity(query_emb, entry["embedding"])
            if sim > best_sim:
                best_sim = sim
                best_entry = entry
        
        if best_sim >= self.threshold:
            best_entry["hits"] += 1
            self.stats["hits"] += 1
            return best_entry["response"], best_sim
        
        return None, best_sim
    
    def set(self, query: str, response: str, tokens_used: int = 0):
        """存入缓存"""
        embedding = self._get_embedding(query)
        self.cache.append({
            "query": query,
            "embedding": embedding,
            "response": response,
            "tokens_used": tokens_used,
            "hits": 0
        })
        self.stats["misses"] += 1
    
    def completion(self, query: str, messages: list = None) -> dict:
        """带缓存的 completion"""
        # 尝试缓存
        cached, similarity = self.get(query)
        if cached:
            return {
                "response": cached,
                "from_cache": True,
                "similarity": round(similarity, 4),
                "tokens_saved": self.cache[0].get("tokens_used", 0)
            }
        
        # 调用 LLM
        if messages is None:
            messages = [{"role": "user", "content": query}]
        
        response = _c(
            model=self.model,
            messages=messages,
            temperature=0
        )
        
        answer = response.choices[0].message.content
        tokens = response.usage.total_tokens
        self.set(query, answer, tokens)
        
        return {
            "response": answer,
            "from_cache": False,
            "similarity": round(similarity, 4),
            "tokens_used": tokens
        }
    
    @property
    def hit_rate(self) -> float:
        total = self.stats["hits"] + self.stats["misses"]
        return self.stats["hits"] / total if total > 0 else 0.0


# 演示语义缓存效果
print("语义缓存演示")
print("=" * 60)

cache = SemanticCache(similarity_threshold=0.92, model=MODEL)

# 模拟一批相似的用户查询
queries = [
    "退款要几天才能到账?",          # 原始
    "退款多久能到?",                 # 相似
    "我申请退款了,几天到账?",        # 相似
    "如何查看订单状态?",             # 不同主题
    "退款大概需要多少工作日?",        # 相似(与第1个)
]

for q in queries:
    result = cache.completion(q)
    source = "缓存" if result["from_cache"] else "API"
    sim = result.get("similarity", 0)
    print(f"\n查询: {q}")
    print(f"  来源: [{source}] 相似度={sim:.3f}")
    print(f"  回答: {result['response'][:80]}...")

print(f"\n\n缓存统计:")
print(f"  命中率: {cache.hit_rate:.1%}")
print(f"  缓存命中次数: {cache.stats['hits']}")
print(f"  实际 API 调用: {cache.stats['misses']}")
print(f"  节省的 API 调用: {cache.stats['hits']} ({cache.hit_rate:.0%})")
语义缓存演示
============================================================
查询: 退款要几天才能到账?
  来源: [API] 相似度=0.000
  回答: 要看退款走哪条通道、哪个商家/哪个银行,时间差别比较大。常见情况和大致时长(仅供参考):

- 支付宝/微信钱包/平台余额:通常几分钟到24小时内到账,少数情况...
查询: 退款多久能到?
  来源: [API] 相似度=0.838
  回答: 要看你是通过什么渠道付款和商家的退款流程。常见情况大致时间(仅供参考):

- 商家内部处理:通常 1–7 个工作日,商家确认并发起退款前会有这段时间。  
-...
查询: 我申请退款了,几天到账?
  来源: [API] 相似度=0.828
  回答: 一般取决于你用的支付方式和商家/平台的处理速度,常见情况大致是:

- 平台/商家处理时间:商家先审核并发起退款,通常需要1–5个工作日(有的平台会更快、也有可...
查询: 如何查看订单状态?
  来源: [API] 相似度=0.367
  回答: 你是在哪个平台或哪个商家下的单?不同平台查看方法有细微差别。下面先给出通用步骤和常见状态说明,你可以按需跟我说具体平台我再给出针对性操作。

通用查看流程
1....
查询: 退款大概需要多少工作日?
  来源: [API] 相似度=0.764
  回答: 一般来说退款到账时间取决于商家处理速度和你的支付方式,常见情况大致如下:

- 信用卡/借记卡:商家发起退款后通常需要 5–10 个工作日,有时银行处理慢或跨国...


缓存统计:
  命中率: 0.0%
  缓存命中次数: 0
  实际 API 调用: 5
  节省的 API 调用: 0 (0%)

Section 4:模型路由#

根据任务复杂度自动选择合适的模型。简单任务用小模型,复杂任务用大模型。

def classify_complexity(prompt: str, classifier_model: str = "gpt-4o-mini") -> str:
    """
    用小模型判断任务复杂度
    Returns: "simple" | "medium" | "complex"
    """
    classification_prompt = f"""判断以下任务的复杂度,只输出一个词:simple/medium/complex

判断标准:
- simple: 事实查询、简单翻译、格式转换、基础问答(无需推理)
- medium: 摘要、改写、简单分析、基础代码(需要一定推理)
- complex: 复杂推理、多步骤分析、高质量创作、专业领域问题

任务:{prompt[:200]}

复杂度(只输出 simple/medium/complex):"""
    
    response = _c(
        model=classifier_model,
        messages=[{"role": "user", "content": classification_prompt}],
        temperature=0,
        max_tokens=10
    )
    
    label = response.choices[0].message.content.strip().lower()
    if label not in ["simple", "medium", "complex"]:
        label = "medium"  # 默认
    return label


# 路由规则
ROUTING_CONFIG = {
    "simple": {
        "model": "gpt-4o-mini",
        "cost_per_1k_tokens": 0.000375,  # $0.15/M input + $0.60/M output 平均
        "reason": "简单任务,小模型足够"
    },
    "medium": {
        "model": "gpt-4o-mini",
        "cost_per_1k_tokens": 0.000375,
        "reason": "中等任务,mini 模型性价比高"
    },
    "complex": {
        "model": "gpt-4o",
        "cost_per_1k_tokens": 0.00625,  # $2.5/M input + $10/M output 平均
        "reason": "复杂任务,需要最强模型"
    }
}


def smart_route(prompt: str) -> dict:
    """智能路由:根据复杂度选择模型"""
    complexity = classify_complexity(prompt)
    config = ROUTING_CONFIG[complexity]
    return {
        "complexity": complexity,
        "routed_model": config["model"],
        "reason": config["reason"],
        "cost_per_1k": config["cost_per_1k_tokens"]
    }


# 测试 10 个不同复杂度的任务
test_prompts = [
    ("把 'Hello' 翻译成中文", "simple"),
    ("今天是星期几?", "simple"),
    ("将以下 JSON 格式化:{a:1,b:2}", "simple"),
    ("总结以下文章的主要观点(500字文章)", "medium"),
    ("写一个 Python 函数实现二分查找", "medium"),
    ("分析这家公司的竞争优势和潜在风险", "complex"),
    ("设计一个分布式缓存系统的架构方案,考虑一致性、可用性和分区容错性的权衡", "complex"),
]

print("智能模型路由演示")
print("=" * 70)
print(f"{'任务':<40} {'分类':>8} {'路由到':<15} {'成本/1K'}")
print("-" * 70)

total_smart_cost = 0
total_naive_cost = 0  # 如果全用 gpt-4o

for prompt, expected in test_prompts:
    route = smart_route(prompt)
    
    smart_cost = route["cost_per_1k"]
    naive_cost = ROUTING_CONFIG["complex"]["cost_per_1k_tokens"]  # gpt-4o
    
    total_smart_cost += smart_cost
    total_naive_cost += naive_cost
    
    match = "✅" if route["complexity"] == expected else "⚠️"
    print(f"{match} {prompt[:38]:<38} {route['complexity']:>8} {route['routed_model']:<15} ${smart_cost:.6f}")

savings = (total_naive_cost - total_smart_cost) / total_naive_cost
print("-" * 70)
print(f"\n成本对比:")
print(f"  全用 GPT-4o:    ${total_naive_cost:.6f} per 1K tokens")
print(f"  智能路由后:     ${total_smart_cost:.6f} per 1K tokens")
print(f"  成本节省:        {savings:.1%}")
智能模型路由演示
======================================================================
任务                                             分类 路由到             成本/1K
----------------------------------------------------------------------
✅ 把 'Hello' 翻译成中文                          simple gpt-4o-mini     $0.000375
✅ 今天是星期几?                                  simple gpt-4o-mini     $0.000375
✅ 将以下 JSON 格式化:{a:1,b:2}                   simple gpt-4o-mini     $0.000375
✅ 总结以下文章的主要观点(500字文章)                      medium gpt-4o-mini     $0.000375
✅ 写一个 Python 函数实现二分查找                      medium gpt-4o-mini     $0.000375
✅ 分析这家公司的竞争优势和潜在风险                        complex gpt-4o          $0.006250
✅ 设计一个分布式缓存系统的架构方案,考虑一致性、可用性和分区容错性的权衡     complex gpt-4o          $0.006250
----------------------------------------------------------------------

成本对比:
  全用 GPT-4o:    $0.043750 per 1K tokens
  智能路由后:     $0.014375 per 1K tokens
  成本节省:        67.1%

Section 5:批量处理#

并行处理多个请求,不影响总 token 成本,但大幅减少总耗时(间接提升吞吐量,降低服务器成本)。

import time
from concurrent.futures import ThreadPoolExecutor, as_completed

# 生成 10 个测试任务
batch_tasks = [
    f"用一句话解释:{topic}"
    for topic in [
        "什么是API", "什么是云计算", "什么是机器学习",
        "什么是区块链", "什么是微服务", "什么是DevOps",
        "什么是Kubernetes", "什么是GraphQL", "什么是WebAssembly",
        "什么是边缘计算"
    ]
]


def single_request(prompt: str, model: str = MODEL) -> str:
    """单次同步请求"""
    response = _c(
        model=model,
        messages=[{"role": "user", "content": prompt}],
        max_tokens=50,
    )
    return response.choices[0].message.content


def sequential_requests(prompts: list, model: str = MODEL) -> dict:
    """顺序处理请求"""
    start = time.time()
    responses = [single_request(p, model) for p in prompts]
    elapsed = time.time() - start
    return {"responses": responses, "elapsed": elapsed, "count": len(prompts)}


def parallel_requests(prompts: list, model: str = MODEL, max_workers: int = 5) -> dict:
    """并行处理请求(ThreadPoolExecutor,Jupyter 兼容)"""
    start = time.time()
    responses = [None] * len(prompts)
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        future_to_idx = {executor.submit(single_request, p, model): i
                         for i, p in enumerate(prompts)}
        for future in as_completed(future_to_idx):
            idx = future_to_idx[future]
            responses[idx] = future.result()
    elapsed = time.time() - start
    return {"responses": responses, "elapsed": elapsed, "count": len(prompts)}


# 对比测试(各 5 个,避免耗时过长)
sample = batch_tasks[:5]
print(f"批量处理对比:{len(sample)} 个请求")
print("=" * 60)

print("[1/2] 顺序处理中...")
seq_result = sequential_requests(sample)
print(f"  顺序耗时: {seq_result['elapsed']:.2f}s  ({seq_result['elapsed']/len(sample):.2f}s/请求)")

print("\n[2/2] 并行处理中(5 线程)...")
par_result = parallel_requests(sample, max_workers=5)
print(f"  并行耗时: {par_result['elapsed']:.2f}s  ({par_result['elapsed']/len(sample):.2f}s/请求)")

speedup = seq_result["elapsed"] / par_result["elapsed"] if par_result["elapsed"] > 0 else 1
print(f"\n加速比: {speedup:.1f}x")
print(f"时间节省: {seq_result['elapsed'] - par_result['elapsed']:.1f}s")
print("\n注:token 成本相同,但总延迟大幅降低,服务器资源利用率更高")

print("\n示例回答(前3个):")
for prompt, resp in zip(sample[:3], par_result["responses"][:3]):
    print(f"  Q: {prompt}")
    print(f"  A: {resp}")
    print()
批量处理对比:5 个请求
============================================================
[1/2] 顺序处理中...
  顺序耗时: 6.23s  (1.25s/请求)

[2/2] 并行处理中(5 线程)...
  并行耗时: 1.40s  (0.28s/请求)

加速比: 4.4x
时间节省: 4.8s

注:token 成本相同,但总延迟大幅降低,服务器资源利用率更高

示例回答(前3个):
  Q: 用一句话解释:什么是API
  A: 

  Q: 用一句话解释:什么是云计算
  A: 

  Q: 用一句话解释:什么是机器学习
  A: 

Section 6:输出长度控制#

输出 token 的成本通常是输入的 2-5 倍。控制输出长度是降低成本的有效手段。

# 三种输出长度控制策略对比

question = "解释什么是机器学习"

strategies = [
    {
        "name": "无约束(基准)",
        "messages": [{"role": "user", "content": question}],
        "kwargs": {}
    },
    {
        "name": "明确长度指令",
        "messages": [{"role": "user", "content": f"{question},用50字以内回答"}],
        "kwargs": {}
    },
    {
        "name": "max_tokens 硬限制",
        "messages": [{"role": "user", "content": question}],
        "kwargs": {"max_tokens": 80}
    },
    {
        "name": "stop 序列",
        "messages": [{"role": "user", "content": f"{question},用一句话。"}],
        "kwargs": {"stop": ["。", "!", "\n"]}
    },
    {
        "name": "长度+max_tokens 组合",
        "messages": [{"role": "user", "content": f"{question},30字以内,直接回答"}],
        "kwargs": {"max_tokens": 60}
    },
]

print("输出长度控制策略对比")
print("=" * 70)
print(f"{'策略':<22} {'输出token':>10} {'输出内容'}")
print("-" * 70)

baseline_tokens = None
results = []

for strategy in strategies:
    response = _c(
        model=MODEL,
        messages=strategy["messages"],
        temperature=0,
        **strategy["kwargs"]
    )
    
    output = response.choices[0].message.content
    completion_tokens = response.usage.completion_tokens
    
    if baseline_tokens is None:
        baseline_tokens = completion_tokens
    
    reduction = (baseline_tokens - completion_tokens) / baseline_tokens if baseline_tokens else 0
    reduction_str = f"(-{reduction:.0%})" if reduction > 0 else "(基准)"
    
    results.append({"strategy": strategy["name"], "tokens": completion_tokens, "reduction": reduction})
    print(f"{strategy['name']:<22} {completion_tokens:>6} tokens {reduction_str:>8}  {output[:50]}...")

print("-" * 70)
best = min(results, key=lambda x: x["tokens"])
print(f"\n最高压缩: {best['strategy']} (节省 {best['reduction']:.0%} 输出 tokens)")
print(f"\n建议: 在 system prompt 中始终设置长度期望,配合 max_tokens 作为安全网")
输出长度控制策略对比
======================================================================
策略                        输出token 输出内容
----------------------------------------------------------------------
无约束(基准)                  1127 tokens     (基准)  简短定义
- 机器学习(Machine Learning,简称 ML)是人工智能的一个分支,研究如何...
明确长度指令                    349 tokens   (-69%)  机器学习是让计算机从数据中自动学习并改进决策或预测能力。...
max_tokens 硬限制             80 tokens   (-93%)  ...
stop 序列                   164 tokens   (-85%)  机器学习是让计算机通过从数据中自动学习规律,从而在新数据上进行预测或决策的技术。...
长度+max_tokens 组合           60 tokens   (-95%)  ...
----------------------------------------------------------------------

最高压缩: 长度+max_tokens 组合 (节省 95% 输出 tokens)

建议: 在 system prompt 中始终设置长度期望,配合 max_tokens 作为安全网

Section 7:极端成本场景优化#

# 成本优化检查清单与预期节省

print("=" * 75)
print("LLM 成本优化完整检查清单")
print("=" * 75)

optimizations = [
    {
        "technique": "模型降级(GPT-4o → GPT-4o-mini)",
        "savings": "85-95%",
        "effort": "低",
        "risk": "质量可能下降",
        "best_for": "结构化输出、简单问答"
    },
    {
        "technique": "语义缓存",
        "savings": "30-70%",
        "effort": "中",
        "risk": "缓存失效风险",
        "best_for": "重复类查询(FAQ、搜索)"
    },
    {
        "technique": "提示词压缩",
        "savings": "10-30%",
        "effort": "低",
        "risk": "可能丢失细节",
        "best_for": "所有场景"
    },
    {
        "technique": "输出长度控制",
        "savings": "20-50%",
        "effort": "低",
        "risk": "可能截断重要内容",
        "best_for": "对话、问答类场景"
    },
    {
        "technique": "智能模型路由",
        "savings": "40-70%",
        "effort": "中",
        "risk": "路由误判",
        "best_for": "混合复杂度的任务"
    },
    {
        "technique": "批量处理(并行)",
        "savings": "0%(token相同)",
        "effort": "中",
        "risk": "并发限制",
        "best_for": "批量数据处理"
    },
    {
        "technique": "微调小模型替代大模型",
        "savings": "90%+",
        "effort": "高",
        "risk": "需要数据、维护成本",
        "best_for": "高量重复任务(>100万/天)"
    },
    {
        "technique": "自托管开源模型",
        "savings": "70-95%(大规模时)",
        "effort": "高",
        "risk": "运维成本、性能",
        "best_for": "隐私要求高或超大规模"
    },
]

print(f"\n{'技术':<30} {'节省':>10} {'实施难度':>8} {'风险':<20} {'最适合'}")
print("-" * 95)
for opt in optimizations:
    print(f"{opt['technique']:<30} {opt['savings']:>10} {opt['effort']:>8} {opt['risk']:<20} {opt['best_for']}")

print("\n" + "=" * 75)
print("组合优化效果估算(1M 请求/天,使用 GPT-4o 为基准):")
print("=" * 75)

baseline_daily = 2500  # $2500/天(GPT-4o,500 tokens/请求)

print(f"基准成本(GPT-4o):                    ${baseline_daily:,.0f}/天")
after_model = baseline_daily * 0.10   # 模型降级节省 90%
print(f"+ 模型降级到 GPT-4o-mini:              ${after_model:,.0f}/天 (节省 90%)")
after_cache = after_model * 0.50      # 缓存命中率 50%
print(f"+ 语义缓存(50% 命中率):              ${after_cache:,.0f}/天 (再节省 50%)")
after_compress = after_cache * 0.80   # 压缩提示词节省 20%
print(f"+ 提示词压缩(20%):                   ${after_compress:,.0f}/天 (再节省 20%)")
after_length = after_compress * 0.70  # 控制输出长度节省 30%
print(f"+ 输出长度控制(30%):                 ${after_length:,.0f}/天 (再节省 30%)")
total_saving = (baseline_daily - after_length) / baseline_daily
print(f"\n组合优化后总成本:                      ${after_length:,.0f}/天")
print(f"总节省:                                {total_saving:.0%} ({baseline_daily-after_length:,.0f} USD/天)")
print(f"年度节省:                              ${(baseline_daily-after_length)*365:,.0f} USD")
===========================================================================
LLM 成本优化完整检查清单
===========================================================================

技术                                     节省     实施难度 风险                   最适合
-----------------------------------------------------------------------------------------------
模型降级(GPT-4o → GPT-4o-mini)         85-95%        低 质量可能下降               结构化输出、简单问答
语义缓存                               30-70%        中 缓存失效风险               重复类查询(FAQ、搜索)
提示词压缩                              10-30%        低 可能丢失细节               所有场景
输出长度控制                             20-50%        低 可能截断重要内容             对话、问答类场景
智能模型路由                             40-70%        中 路由误判                 混合复杂度的任务
批量处理(并行)                       0%(token相同)        中 并发限制                 批量数据处理
微调小模型替代大模型                           90%+        高 需要数据、维护成本            高量重复任务(>100万/天)
自托管开源模型                        70-95%(大规模时)        高 运维成本、性能              隐私要求高或超大规模

===========================================================================
组合优化效果估算(1M 请求/天,使用 GPT-4o 为基准):
===========================================================================
基准成本(GPT-4o):                    $2,500/天
+ 模型降级到 GPT-4o-mini:              $250/天 (节省 90%)
+ 语义缓存(50% 命中率):              $125/天 (再节省 50%)
+ 提示词压缩(20%):                   $100/天 (再节省 20%)
+ 输出长度控制(30%):                 $70/天 (再节省 30%)

组合优化后总成本:                      $70/天
总节省:                                97% (2,430 USD/天)
年度节省:                              $886,950 USD