Agentpath
推演25 / 31 节 · 预计 35 分钟

成本与延迟:把账压下来

同一个任务,做对缓存和模型分层能便宜一个数量级。

学完这节你能做到

  • 算出单次任务成本的构成,并指出最值得优化的那一项
  • 设计缓存友好的上下文布局
  • 用模型分层把简单步骤交给便宜模型

同一个任务,做对缓存和模型分层能便宜一个数量级。这不是夸张 —— 缓存本身就是一折价,再叠上模型分层,十倍是常规操作。

这一节把账拆开,然后按「性价比」排个序。

成本由什么构成

一次 agent 任务的账单:

Σ(每轮)  未命中缓存的输入 × 输入价
       + 命中缓存的输入   × 缓存价(≈ 输入价 × 0.1)
       + 输出            × 输出价(通常是输入价的 3–5 倍)
       + 重试的部分       × 全价
       + 压缩的那几轮     × 全价(前缀变了)
       + 子 agent 的全部

四个直觉误区:

  1. 输出比输入贵得多,但 agent 的输出通常很短 —— 大头几乎总是输入。
  2. 重试是全价,而且重试往往发生在上下文最长的时候。
  3. 压缩省窗口但花钱:那一轮缓存全失效。
  4. 子 agent 的钱最容易漏算 —— 记得用工具返回的 usage 上报。

先用 L1 的计算器把自己的形态跑一遍,再往下看。

优化的性价比排序

按「省下的钱 ÷ 花的工夫」排:

手段典型收益工夫
① 稳定前缀,让缓存命中输入成本降到约 1/10
② 裁剪工具输出直接减少输入量,还顺带推迟撞墙
③ 收窄工具集每轮省几千 token
④ 模型分层简单步骤便宜 5–20 倍
⑤ 减少无谓轮次(提示词/工具改进)轮次少三成,钱就少三成
⑥ 换更小的模型跑全部省钱但成功率下降,要评测背书
⑦ 自建/微调视规模

前三条几乎是免费的,先把它们做完再谈别的。

① 缓存友好的布局

命中条件是前缀逐字节相同。所以顺序固定成:

[ 系统提示 ][ 项目指令 ][ 工具声明 ][ 早期历史 ][ 新增的这一轮 ]
   稳定 ────────────────────────────────→   ← 只有这里是新的

会打碎缓存的常见操作,逐条检查:

操作后果
系统提示里注入时间戳 / 随机 id每轮全废
工具声明顺序不稳定(从 Map / Set 遍历)每轮全废
中途插入工具、改工具描述从改动处往后全废
compaction那一轮全价(不可避免,但可以少触发)
动态注入项目状态到系统提示每轮全废 —— 放到最后一条用户消息里
一个 5 分钟的检查

把连续两轮的请求体 dump 出来,diff 一下前缀。 第一个不同的字节出现在哪,缓存就从哪断。 理想情况:只有最后的新消息不同。

② 裁剪工具输出

这条既省钱又推迟撞墙,是最划算的一项。三个具体动作:

// 1. 上限 + 说清怎么拿剩下的
if (lines.length > 50) {
  text = lines.slice(0, 50).join("\n")
    + `\n\n[共 ${lines.length} 处,只显示前 50。缩小范围或指定目录。]`
}

// 2. 摘要优先:先给分布,再按需给明细
text = `命中 47 处,分布:src/api(31) src/web(12) tests(4)。需要明细请指定目录。`

// 3. 结构化压缩:去掉模型不需要的字段
const slim = raw.items.map(({ id, name, status }) => ({ id, name, status }))

第 2 条最容易被忽略:模型要的往往是「在哪、有多少」,不是 47 行原文。

③ 收窄工具集

L1 算过:40 个工具 × 320 token = 12.8k,每轮都付。 按场景分组、运行中按需注册(L3 的 MCP 那节给了代码), 常驻的就只剩基础几件。

④ 模型分层

不是所有步骤都需要最强模型。常见的分法:

步骤模型
主循环:决策、写代码、判断强模型
压缩摘要中小模型(Pi 允许扩展换摘要模型)
结构化提取、分类、格式转换小模型
子 agent 的检索类任务中模型

Pi 里有两个现成落点:session_before_compact 事件可以接管压缩、用别的模型生成摘要; scopedModelspi.setModel() 可以按阶段切主模型。

!分层必须有评测背书

「简单步骤用小模型」听起来安全,但摘要质量下降会在十轮之后才显形 —— 表现是 agent 忘了早期约束。上线前用长任务用例测一遍(L3 的评测那节)。

⑤ 减少轮次

轮次是成本的乘数:每多一轮,就多付一次完整上下文。 减轮次的三个最有效手段其实都在前面的课里:

  • 工具返回可行动的错误(L1)—— 一次纠正 vs 三次试错。
  • description 写清触发条件与限制(L1)—— 少一轮「用错工具再改」。
  • 允许并行只读工具 —— 三个 read 一轮完成,而不是三轮。

延迟:优化的地方常常和直觉相反

先量,再优化。一次典型任务的耗时构成往往是:

模型思考与输出   ████████░░░░░░░░  30%
工具执行         ██████████████░░  55%   ← 跑测试、装依赖、网络请求
上下文组装等     ██░░░░░░░░░░░░░░  15%

对应的手段:

症状手段
首字慢流式(必须)、缩短系统提示、缓存命中
工具慢并行只读工具、给命令加超时、缓存重复查询的结果
总时长长减轮次;把「跑全量测试」换成「只跑相关测试」
感觉卡住onUpdate 报进度 —— 不减少时间,但显著改善体感

给自己设预算上限

再省也架不住一次失控。三道闸门都要有:

const MAX_TURNS  = 40
const MAX_TOKENS = 800_000
const MAX_USD    = 5
const MAX_WALL   = 30 * 60 * 1000

无人值守场景再加上一条无进展检测(连续 N 轮无写操作就停)—— L2 的闯关算过账:它能把 47 美元变成 3 美元。

检查点单选

一个编码 agent 的账单是同行的 8 倍。查了一下:缓存读取 token 几乎为零。最该先做什么?

这一节的结论

  1. 大头几乎总是输入;重试和压缩按全价算;子 agent 的钱别漏。
  2. 优化顺序:稳定前缀 → 裁剪输出 → 收窄工具 → 模型分层 → 减轮次。
  3. 缓存读取占比接近零 = 前缀不稳定,是最值得先修的一项。
  4. 延迟的大头常常是工具执行而不是模型,先量再优化。
  5. 四道闸门 + 无进展检测,是无人值守的最低配置。

延伸资料