知识库成本约 8 分钟

降低 Claude 用量的工程方法:先测量再优化 ​

一句话答案:token 成本通常不是模型定价问题,而是信息路由问题。先按「指令 / 上下文 / 任务 / 输出」四段记账,看清大头在哪;然后按顺序做:给每条路由设预算、检索前裁剪、结构化摘要替代整段历史、用 schema 约束输出、把简单子任务路由到便宜档位。衡量口径是「每个成功任务的 token 数」,不是单次调用。

token 用量是架构问题 ​

结论:大多数团队把它当成定价问题,实际上通常是信息路由问题。一次生产调用有四个 token 桶:指令(系统提示、格式规则、安全约束)、上下文(历史对话、检索文档、工具结果)、任务(用户请求与中间细节)、输出(生成的文本、JSON、工具调用)。

失控时,上下文和输出通常是大头。不是用户问了难题所以贵,而是应用把整个世界塞进了 prompt,又允许模型长篇大论。

第一步:按分段记账 ​

json
{
  "request_id": "ticket_81291",
  "model": "sonnet",
  "tokens": {
    "system": 742,
    "history": 3890,
    "retrieval": 12640,
    "tool_results": 2110,
    "user": 184,
    "output": 931
  },
  "latency_ms": 4820
}

看到明细之后,优化路径自己会浮出来:上面这份里真正的大头是检索(12,640),不是用户问题(184)。这时不需要讨论哪个模型「最好」,只需要停止发送无关的检索结果。

在 Claude Code 里,对应的动作是会话里输入 /context 看构成。

第二步:给每条路由设预算 ​

结论:按产品路由而不是全局设预算。合同审查、代码 agent、FAQ 机器人不该共用同一套 token 假设。

路由输入预算输出预算模型档位
工单分类60050便宜档
检索证据1,5000不生成
起草答案4,000600中档
汇总会话1,000250便宜档
升级分析6,000900强档

这些数字是刻意保守的起点,不是通用答案。有用的行为是「每一段都要为自己的 token 辩护」。

常见误区:只设了输出上限。输出上限能防长答案,阻止不了输入被塞满——输入才是通常的大头。

第三步:检索前裁剪,而不是检索后堆 ​

结论:检索要窄,不要多。一个常见失败模式是 top-k 设成 10 或 20、每块 1,000 token,最终 prompt 里塞进一堆只是沾边的文档。

  • 检索的候选可以多,但重排后再放进去;
  • 只抽取相关片段,不传整块文档;
  • 给检索本身设 token 预算,超预算就停;
  • 保留文档 ID,便于模型引用证据。

块大小也有讲究:太小丢上下文,太大每次匹配都拖进无关文本。产品文档和政策页可以先从每块 300–600 token 开始,保留标题,再按失败案例调。

值得注意:塞太多上下文往往让模型更不准——它会试图调和相邻但不相关的规则,捡起过时的例外。减 token 有时反而提升质量,因为它移除了干扰项。

第四步:用结构化摘要替代整段历史 ​

结论:对话历史是逐字稿,不是记忆。摘要应该是蒸馏出来的状态,保留事实、ID、约束和失败尝试。

json
{
  "task": "排查 staging 上失败的 OAuth 回调",
  "environment": { "service": "auth-api", "branch": "release-2025-02-14" },
  "confirmed": ["回调 302 到 /login", "provider 发来了 code 和 state", "state 校验通过"],
  "suspected": ["回调后 session cookie 没有持久化"],
  "rejected": ["provider 凭据无效", "redirect URI 不匹配"],
  "next_steps": ["检查 staging 响应的 Set-Cookie 属性"]
}

一份好的摘要要保留:用户目标、硬事实、已做的决定、约束与偏好、失败过的尝试、还没解决的问题、以及 ID / 文件名 / 时间戳这类精确句柄。

不要把精确值摘掉:「用户有个发票问题」便宜但没用;「2025-01-08 重复扣款两笔,单号 inv_1042 和 inv_1077」才是你要的压缩。

第五步:用 schema 约束输出 ​

结论:模型写一段前言、免责声明、总结和「还有什么可以帮你」,是最被低估的浪费。给机器消费的输出要定形状,而不是请它「简洁一点」。

json
{
  "type": "object",
  "additionalProperties": false,
  "required": ["category", "priority"],
  "properties": {
    "category": { "type": "string", "enum": ["billing", "bug", "account", "how_to", "other"] },
    "priority": { "type": "string", "enum": ["low", "normal", "high", "urgent"] }
  }
}

给用户看的回答则给一个硬格式:

text
按这个结构回答:
- 第一句:直接回答,不超过 25 个字。
- 然后 2–4 条具体步骤。
- 不要寒暄,不要「还需要什么吗」。
- 最后一条之后停止。

注意:过低的 max_tokens 会在句子中间截断;schema 或固定格式在减少输出的同时保留完整性,截断是最后手段,不是格式化策略。

第六步:把简单子任务路由到便宜档位 ​

结论:不是每一步都需要最强的模型。语言检测、工单分类、查询改写、格式修正用便宜档;最终答案、需要权衡的政策例外才用强档。

但要算清代价:多一次模型调用意味着更多延迟、更多失败点、更复杂的日志。便宜分类省 300 token 却多花 600ms 和一条重试路径,就不划算。

哪一档该承担哪类任务,见按任务选模型;把大块文件读取整块移出主会话是另一种做法,见子代理怎么用。

第七步:诚实地衡量 ​

结论:看 p50、p90、p99,而不是平均。token 的问题常常藏在长尾里。

至少跟踪:按段的输入输出 token、检索文档数与 token 数、模型与版本、延迟分位数、缓存命中率、重试次数、用户可见的质量信号、人工修正率。

说「token 降了 40%」之前先问:重试是不是变多了?升级人工的比例是不是上升了?用户是不是因为回答太短而追问更多?便宜档是不是产出更多无效 JSON,触发了修复调用?

诚实的指标不是每次调用的 token,而是每个成功任务的 token。

先算清楚一次请求到底由哪些部分构成,才知道该砍哪里,见Claude API 怎么计费。

长上下文不是偷懒的许可 ​

结论:1M 窗口这类能力适合离线分析、整库审查、几小时的转录;不适合在线服务里每次请求都重读一遍全量语料。

好的模式是:用长上下文离线分析一次,产出结构化的策略图、摘要或测试用例;运行时的助手只读需要的那部分。

常见问题 ​

为什么我的账单比「用户问的那句话」贵得多? ​

用户的问题通常不到 300 token,其余是系统提示、完整历史、检索到的文档和工具结果的 JSON。默认设置里检索常常就是最大的那桶。

降低 max_tokens 能省钱吗? ​

能限制长输出,但阻止不了输入被塞满。输入才是通常的大头,先做输入侧的优化。

用便宜模型分类会不会得不偿失? ​

如果便宜分类省 300 token 却多花 600ms 和一条重试路径,就不划算。路由的前提是它可靠地完成那一步。

怎样才算真的省了? ​

看「每个成功任务的 token 数」,同时对照重试次数、升级人工的比例和用户追问次数;只比较单次调用会被省 token 但省坏质量的做法骗到。

相关阅读 ​