降低成本与优化缓存

Agent、编程助手这类应用每一轮都会重新发送系统提示词、工具定义和全部历史,输入 Token 往往占费用的大头,所以提示词缓存命中率基本决定了 Token 费用。降低成本的主要做法是让请求前缀保持稳定、让同一会话留在同一个通道上,再减少不必要的输出和上下文。每项调整后,用日志和用量分析中的缓存命中率与费用验证效果。

先找到钱花在哪

在 用量分析 中把「统计视角」切到「费用」,按模型、API Key 查看费用占比;「总览」的「缓存命中率」和「按模型」表格的「缓存命中率」列显示命中情况。费用集中、命中率又低的模型,是最值得优化的对象。具体请求的四项 Token 在请求日志详情中查看,费用计算方法见调用费用与缓存计费。

在用量分析中切换统计视角,对比各模型的用量、费用和缓存命中率。

图 1:在用量分析中切换统计视角,对比各模型的用量、费用和缓存命中率。 截图为演示数据,点击图片可查看原图。

设缓存命中率为 h,模型的缓存读取单价与输入单价之比为 r,输入费用约为全部未命中时的 (1 − h) + h × r 倍。r 越小,提高命中率省得越多。

让请求前缀保持稳定

上游按字节前缀匹配缓存,从第一个不同的字节开始,之后的内容全部按未命中计费。把越稳定的内容放得越靠前,从前到后依次是:

  1. 固定的角色与规则:最稳定,放在最前。
  2. 工具定义:会话内不变,顺序固定。
  3. 示例与项目级上下文:同一项目内不变。
  4. 环境信息、日期等:容易变化,尽量靠后。
  5. 对话历史:只追加,不改写。
  6. 本轮新消息。
  • 前缀里不放变化的值:请求 ID、trace ID、精确到秒的时间戳、随机数不要写进系统提示词开头;必须带时放到最后一条消息里,日期精确到天即可。
  • 会话中途不改工具:增删或调整工具会改变工具定义之后的全部内容。需要临时限制工具时,在提示词或调用逻辑中约束;按需加入的工具放在工具列表最后。
  • 历史只追加、原样回放:不要每轮重新拼装或格式化历史消息;工具调用参数按模型上一轮返回的原样回传。
  • 不必担心 JSON 键顺序:网关发往上游前会把请求体中 JSON 对象的键按固定顺序排列,包括工具调用参数里的 JSON(参数中含 15 位以上连续数字时保持原样,以免改写数字)。消息、工具和内容块的先后顺序不会调整,需要由客户端保持稳定。

让同一会话留在同一个通道

缓存保存在具体的服务商通道上,换通道就要重新建立。工作空间的「多渠道选路策略」为「最低价」(新建工作空间的默认值)或「负载均衡」时,网关会把同一个 API Key 对同一模型的调用保持在同一个通道上:

  • 保持期为 30 分钟,每次调用续期;通道返回 429、408、5xx 或连接失败时改绑到其他通道,因请求本身格式错误而换通道时不改绑。
  • 「最低价」只为新会话或保持过期后的调用选择当前预估最便宜的通道,价格排序变化不会把进行中的会话挪走。
  • 「最低延迟」「最高吞吐」不做会话保持;虚拟模型的「Channel 排序策略」为按价格优先或负载均衡时同样会保持。
  • lyrouter/auto 按每次请求的内容选择模型,同一会话可能换到不同模型,长会话请固定一个模型 ID。

调用方只需让同一个会话或 Agent 始终使用同一个 API Key 和同一个模型,不需要额外参数。如果客户端会发送 X-Session-Id 请求头,网关会原样转发;请为每个会话固定一个值,不要每个请求更换。设置方法见通道选择与路由策略。

使用显式缓存(cache_control)

隐式缓存由上游自动完成,不需要任何参数。显式缓存在请求中用 cache_control 标出断点,只在实际服务这次调用的上游使用 Anthropic 协议时生效:

调用接口 实际服务的上游协议 cache_control 的处理
/v1/messages Anthropic 保留 system、消息文本块和工具定义上的标记
/v1/chat/completions Anthropic 保留消息文本部分和工具定义上的标记
任意接口 OpenAI 兼容 全部删除,只有隐式缓存
  • 通过 /v1/messages 调用时,打在图片块或 tool_result 块本身上的标记不会转发,请把断点打在文本块上。
  • system 有多个文本块时会合并成一个,只保留最后一个标记。
  • 标记中写 "ttl": "1h" 时,网关会把 anthropic-beta 请求头设为 1 小时缓存所需的值;客户端自带的 anthropic-beta 会被覆盖。
  • Anthropic 每个请求最多 4 个断点,前缀短于模型的最小可缓存长度时不会缓存。

下例把一段固定的长系统提示词标记为缓存断点(MODEL_ID 换成由 Anthropic 协议上游服务的模型,例如 Claude 系列;环境变量的设置见完成第一次模型调用):

curl "$LYROUTER_BASE_URL/v1/messages" \
  -H "x-api-key: $LYROUTER_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "MODEL_ID",
    "max_tokens": 1024,
    "system": [{"type": "text", "text": "你是合同审阅助手。以下是审阅规则……(每次相同的长文本)", "cache_control": {"type": "ephemeral"}}],
    "messages": [{"role": "user", "content": "请检查第 3 条的违约责任是否对等。"}]
  }'

第一次调用在日志中出现「显式缓存创建」,说明标记已被上游接受;之后几分钟内发送相同前缀的请求,这部分记为「输入(缓存命中)」。

客户端注意事项

  • Claude Code:命中率精确为 0 时先升级 Claude Code。部分版本会在系统提示词开头写入一行每个请求都变化的 cch= 标记,使前缀从第一个字节起就不同。会话中途调整可用工具也会让工具定义之后的内容全部未命中。接入方法见Claude Code。
  • 自研应用:确认 SDK 或框架没有在每次请求中自动插入时间戳、请求 ID,也没有每轮重新排序工具或消息。

减少不必要的用量

  • 控制输出:用 max_tokens 限制输出长度;不需要深度思考的任务降低思考强度或关闭思考,思考过程计入「输出」。
  • 控制上下文:清理与任务无关的历史;超长会话可能落入更贵的长上下文档,定期总结后开启新会话。
  • 选择模型与来源:在相同任务和时间范围内比较不同模型与来源的费用和效果,见如何选择模型与平台模型与 BYOK 如何选择。按 Credits 设置预算与配额见预算、配额与限流。

验证效果

调整后,在日志详情的「Token 消耗」中观察连续调用的「输入(缓存命中)」是否逐轮增加,再在 用量分析 中按相同时间长度比较调整前后的缓存命中率和费用。自己计算命中率时按 Token 加权:Σ 输入(缓存命中)÷ Σ(输入(缓存未命中)+ 输入(缓存命中)),不要对每次调用的命中率取平均。命中率仍然偏低时,按费用异常与缓存未命中逐项排查。

在日志详情中观察缓存命中输入随调用轮次的变化,验证优化效果。

图 2:在日志详情中观察缓存命中输入随调用轮次的变化,验证优化效果。 截图为演示数据,点击图片可查看原图。