SilverIce Toolbox
Back to course

Stage 5 / Chapter 36

第 36 章:成本优化与缓存

学习目标

  • 识别 Agent 成本的主要驱动因素。
  • 掌握模型路由、缓存、批量、提示压缩四种降本手段。
  • 能设计优雅降级策略,避免单点模型故障拖垮服务。
  • 建立“按任务选模型,而不是一把梭 GPT-4”的意识。

36.1 Agent 成本的来源

成本来源说明优化方向
模型选择大模型单价高简单任务用小模型
输入 token 长度长历史、大上下文压缩、截断、摘要
输出 token 长度模型啰嗦限制 max_tokens、明确格式
调用次数多步循环、重试减少步数、缓存
失败重试幻觉导致无效调用输出校验、重试预算

36.2 模型选择:按难度路由

不是所有任务都需要最强模型。引入模型路由器,根据请求特征选择模型。

python
import json

def route_request(user_input: str, history_len: int):
    # 简单分类:先让便宜模型判断任务复杂度
    classifier_prompt = [
        {"role": "system", "content": "Classify the user request as 'simple' or 'complex'. Return JSON."},
        {"role": "user", "content": user_input},
    ]
    resp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=classifier_prompt,
        response_format={"type": "json_object"},
        max_tokens=60,
        temperature=0.0,
    )
    level = json.loads(resp.choices[0].message.content).get("level", "simple")
    return "gpt-4o" if level == "complex" else "gpt-4o-mini"
任务类型推荐模型原因
分类/路由gpt-4o-mini / claude-3-haiku便宜、快速
结构化抽取gpt-4o-mini + JSON mode格式稳定即可
复杂推理/代码gpt-4o / claude-3.5-sonnet准确性优先
创意/长文本claude-3-opus / gpt-4o输出质量优先

36.3 缓存:不要重复问同样的问题

精确缓存

对确定性任务(如分类、翻译、摘要),按 prompt 哈希缓存结果。

python
import hashlib
import json
from functools import lru_cache

@lru_cache(maxsize=10000)
def cached_classify(prompt_hash: str) -> str:
    # 实际实现会按 prompt 调用模型
    return model_classify(json.loads(prompt_hash))

def classify(text: str) -> str:
    h = hashlib.sha256(text.encode()).hexdigest()
    return cached_classify(h)

语义缓存

用 embedding 缓存相似问题的答案。

python
from redis import Redis
import numpy as np

redis = Redis()

def semantic_cache_lookup(query: str, threshold: float = 0.92):
    emb = embed(query)
    # 简化示意:遍历最近 100 条缓存的 embedding
    for key in redis.lrange("cache_keys", 0, 100):
        cached_emb = np.frombuffer(redis.get(f"emb:{key}"))
        sim = cosine_sim(emb, cached_emb)
        if sim > threshold:
            return redis.get(f"ans:{key}")
    return None

Node.js/TypeScript 示例:

typescript
import { createHash } from "crypto";

function exactCacheKey(prompt: string): string {
  return createHash("sha256").update(prompt).digest("hex");
}

// 使用 Redis 缓存
const cached = await redis.get(`llm:${exactCacheKey(prompt)}`);
if (cached) return JSON.parse(cached);

36.4 批量处理

嵌入模型和某些 LLM 支持批量,摊薄每次请求开销。

python
def batch_embed(texts: list[str], batch_size: int = 32) -> list[list[float]]:
    results = []
    for i in range(0, len(texts), batch_size):
        batch = texts[i:i+batch_size]
        resp = client.embeddings.create(input=batch, model="text-embedding-3-small")
        results.extend([d.embedding for d in resp.data])
    return results

36.5 提示压缩

技术做法效果
历史摘要每 N 轮用模型摘要一次减少长对话 token
选择性记忆只保留相关记忆降低噪声
去除重复系统提示不要把全量文档每次塞入显著降本
截断保留最近 k 轮简单但可能丢失关键上下文
python
def compress_history(messages: list, max_messages: int = 10) -> list:
    if len(messages) <= max_messages:
        return messages
    # 保留 system 和最近 N 条
    system = [m for m in messages if m["role"] == "system"]
    recent = messages[-max_messages:]
    return system + recent

36.6 优雅降级

当主模型超时、报错或成本过高时,切换到更便宜/更快的模型或简化流程。

python
def robust_chat(messages, primary="gpt-4o", fallback="gpt-4o-mini"):
    try:
        return client.chat.completions.create(model=primary, messages=messages, timeout=15)
    except Timeout:
        logger.warning("primary model timeout, falling back")
        return client.chat.completions.create(model=fallback, messages=messages)

36.7 本章练习

  1. 实现一个简单路由器:用 gpt-4o-mini 判断用户问题是“简单”还是“复杂”,再选择模型。
  2. 为一个问答接口加入精确缓存,比较命中缓存前后的延迟和成本。
  3. 实现历史压缩函数,保留 system 提示和最近 6 轮对话。
  4. 统计过去 100 次请求中,各模型 token 占比,并给出降本建议。

检查点

  • 你能列出 Agent 的 5 个主要成本来源。
  • 你知道精确缓存和语义缓存的区别与适用场景。
  • 你能实现模型路由和优雅降级。
  • 你能用 prompt 压缩控制上下文长度。

配套代码

  • 本地:D:\Project\github\agent-labs\labs\28-cost-optimization
  • 在线:https://github.com/ZYZ-Labs/agent-labs/tree/master/labs/28-cost-optimization