你的 AI 智能体 token 账单主要来自输入。以下是削减它的实战指南。
Your AI agent's token bill is mostly input. Here is the field guide to cutting it.
我们发现了什么
你的 AI 智能体 token 账单主要来自输入。# 你的 AI 智能体 token 账单主要来自输入。
- 来源:DEV Community(发现于 2026-11-09)
- 证据等级:C · 存在定价或订阅线索;有收费设计不等于已有收入。
- 商业模式:待核验
- 主题:AI Agent
- 初筛评分:14.9/100 · 收录 1 次
证据,比故事更重要。
规则清洗与初筛,未经人工商业核验。原文语境、实际客户和付费情况仍需自行验证。
引用与数字披露
来源类型(原作者自述/第三方测算/媒体转引)需采集端标注,本版尚未落字段。
- 作者
- 未标注
- 抓取日期
- 来源类型
- 未标注
- 币种
- 未标注
- 口径
- 未标注
- 披露主体
- 未标注
- 披露日期
- 未标注
中文辅助译文(全文)
你的 AI agent 的 token 账单主要花在输入上。这是一份削减它的实战指南。
TL;DR - 在一个示例的 10 轮 agent 会话中,会发出 16,559 个输入 token,并返回 3,090 个输出 token。按 Claude Sonnet 4.5 的价格计算,输入仍占成本的 52%,而下面三种削减手段会把它降到 3,290 个输入等效 token,减少了 80%。 - 仅截断工具输出并修剪旧历史记录,就能把该会话的输入减少 62%,降至 6,445 个 token。 - 缓存一个稳定的前缀会让它在首次调用后按 0.1x 计费,但一个只使用一次的前缀,其成本比不缓存高 25%。
你的 agent 账单比你记忆中要求完成的工作更大,而原因是机械性的。模型 API 是无状态的,因此每一轮都会重新发送系统提示词、到目前为止的所有工具输出以及整个对话。第 2 轮粘贴的文件列表,在之后的每一轮都会被再次计费。TechCrunch 报道称,Y Combinator CEO Garry Tan 每年在 token 上花费高达 $200K,而在任何规模下,重复的输入都是你可以压缩的部分。下面:一个用来度量它的脚本,以及三种削减手段。
10 轮会话的 token 都花在哪里
第 10 轮会携带第 1 轮到第 9 轮的结果,因此一个 10 轮会话的成本,是十个不断增长的提示词之和。在下面的示例运行中,该总和是 16,559 个输入 token。真实的 agent 回复会携带工具调用和代码,因此我们把每条回复建模为约 300 个 token,这使得该会话的输出 token 为 3,090 个。
输出每个 token 的成本更高。Anthropic 的定价页面列出 Claude Sonnet 4.5 为每百万输入 token $3、每百万输出 token $15,因此一个输出 token 相当于 5 个输入 token。按价格加权后,输入仍占该会话的 52%,只是微弱多数。该脚本会打印出这三个数字。
为了用相同单位计算缓存,我们使用输入等效 token:一次缓存读取计为 0.1 个 token,一次缓存写入计为 1.25 个 token。
先度量:统计一轮中每个部分的 token 数
从你的真实基线开始:记录每次 API 响应的 usage 块(每次调用的输入和输出 token),并按会话分别求和。这个总和就是你的真实数字。然后脚本会分别统计系统提示词、一个工具输出和历史记录,展示这些 token 都去了哪里。最大的那一项会告诉你应该先尝试哪种削减手段。
数据是假的,所以请在你自己的数据上重新运行脚本。除了缓存相关行和成本占比行之外,每个数字都是 tiktoken 计数;缓存行和成本占比行是根据厂商文档中的乘数和价格进行的算术计算,没有调用 API。
# pip install tiktoken
import tiktoken
enc = tiktoken.get_encoding("o200k_base")
def n(text):
return len(enc.encode(text))
system = """You are a coding agent. You read files, run commands, and edit code.
Always explain your plan before acting. Follow the repo style guide. When you
write tests, use the project's existing fixtures. Never touch the migrations
folder without asking. Summarize diffs at the end of each task. Be concise but
complete. Handle errors gracefully and retry once on network failures.
[... style guide: 40 more lines of formatting rules ...]"""
# One fake tool output: a 60-line file listing.
tool_output = "\n".join(
f"src/module_{i:02d}.py: def handler_{i}(request) -> Response: ..."
for i in range(60)
)
# One fake model reply per turn. A real agent reply carries its tool calls and
# code, so we model it at a few hundred tokens.
reply = (
"I read the listing. handler_07 builds the response before it validates "
"the request, which is why test_handler_07 fails. I'll move the check "
"above the response and add a regression test.\n"
'{"tool": "edit_file", "path": "src/module_07.py", "patch": "'
+ "\n".join(
f"- response = build(request, {i})\n+ validate(request)\n+ response = build(request, {i})"
for i in range(10)
)
+ '"}'
)
def history_of(turns):
return "\n".join(
f"Turn {i}: user asked something, agent read files, ran tests, "
f"edited two files, tool output followed. {tool_output[:400]}"
for i in range(turns)
)
def truncate(text, head=600, tail=200):
if len(text) <= head + tail:
return text
cut = text[head:-tail].count("\n")
return text[:head] + f"\n[... {cut} lines truncated ...]\n" + text[-tail:]
def turn(history_turns=10, tool=tool_output):
return "\n".join([system, history_of(history_turns), tool])
full = n(turn())
pruned = n(turn(history_turns=3))
truncated = n(turn(tool=truncate(tool_output)))
both = n(turn(history_turns=3, tool=truncate(tool_output)))
def smaller(x):
return 100 - x * 100 // full
print(f"system prompt: {n(system)} tokens")
print(f"one raw tool output: {n(tool_output)} tokens")
print(f"10 turns of history: {n(history_of(10))} tokens")
print(f"one full turn as sent: {full} tokens")
print(f"history pruned (keep 3): {pruned} tokens ({smaller(pruned)}% smaller)")
print(f"tool output truncated: {truncated} tokens ({smaller(truncated)}% smaller)")
print(f"both cuts: {both} tokens ({smaller(both)}% smaller)")
# A 10-turn session: turn i re-sends i earlier turns of history.
naive = sum(n(turn(history_turns=i)) for i in range(10))
session_cut = sum(n(turn(history_turns=min(i, 3), tool=truncate(tool_output))) for i in range(10))
out = 10 * n(reply)
print(f"10-turn input, no cuts: {naive} tokens")
print(f"10-turn input, both cuts: {session_cut} tokens ({100 - session_cut * 100 // naive}% smaller)")
# Cut 3 arithmetic (documented multipliers, no API call): a cache write costs
# 1.25x, a cache read 0.1x. Stable prefix = system prompt + the 3 kept turns.
prefix = n(system + "\n" + history_of(3))
cached = prefix * 1.25 + prefix * 0.1 * 9
# All three cuts stacked, same model. Each turn re-sends the previous turn's
# system prompt + history as a cache read (0.1x), writes whatever the prefix
# grew by (1.25x), and bills the rest of the turn (the tool output) at 1x.
# Assumes pruning happens at fixed points, so the prefix stays valid after
# turn 3, and every call lands inside the 5-minute cache lifetime.
stacked, prev = 0, 0
for i in range(10):
total = n(turn(history_turns=min(i, 3), tool=truncate(tool_output)))
pre = n(system + "\n" + history_of(min(i, 3)))
stacked += prev * 0.1 + (pre - prev) * 1.25 + (total - pre)
prev = pre
print(f"10-turn input, all 3 cuts: {stacked:.0f} input-equivalent tokens ({100 - stacked * 100 / naive:.0f}% smaller)")
print(f"10-turn output (replies): {out} tokens")
# Price-weighted: Sonnet 4.5 lists $3 per million input, $15 per million
# output, so one output token weighs 5 input tokens.
OUT_WEIGHT = 5
print(f"input cost share, no cuts: {100 * naive / (naive + OUT_WEIGHT * out):.0f}%")
print(f"input cost share, 3 cuts: {100 * stacked / (stacked + OUT_WEIGHT * out):.0f}%")
print(f"stable prefix: {prefix} tokens")
print(f"prefix x10, uncached: {prefix * 10} input-equivalent tokens")
print(f"prefix x10, cached: {cached:.0f} input-equivalent tokens")
print(f"prefix used once, cached: {prefix * 1.25:.0f} input-equivalent tokens")Output:
system prompt: 93 tokens
one raw tool output: 960 tokens
10 turns of history: 1339 tokens
one full turn as sent: 2393 tokens
history pruned (keep 3): 1455 tokens (40% smaller)
tool output truncated: 1663 tokens (31% smaller)
both cuts: 725 tokens (70% smaller)
10-turn input, no cuts: 16559 tokens
10-turn input, both cuts: 6445 tokens (62% smaller)
10-turn input, all 3 cuts: 3290 input-equivalent tokens (80% smaller)
10-turn output (replies): 3090 tokens
input cost share, no cuts: 52%
input cost share, 3 cuts: 18%
stable prefix: 494 tokens
prefix x10, uncached: 4940 input-equivalent tokens
prefix x10, cached: 1062 input-equivalent tokens
prefix used once, cached: 618 input-equivalent tokens试试这个: 把
system、tool_output、history_of和reply替换为你真实的系统提示词、一个真实的工具结果、你记录的历史记录以及一条典型回复。
削减 1:截断工具输出,保留头部和尾部
工具输出通常是最大的块,而其中大部分内容之后不会再被读取。truncate 保留前 600 个字符和后 200 个字符,并在中间放一个标记。这会让一轮从 2,393 个 token 降到 1,663 个 token,减少了 31%。
保留两端,是因为错误往往出现在日志顶部,而摘要行出现在底部。
将每个工具结果限制为头部、尾部和一个标记,说明丢掉了多少行,这样 agent 就知道它可以请求更多内容。
试试这个: 在一次真实运行中记录每个工具结果的长度。排在列表最前面的那个,最先被加上限制。
削减 2:结果一旦用过就修剪历史记录
在 agent 已经基于某个工具结果行动之后,原始结果很少需要继续随行。示例循环只保留最后 3 轮,这会把一轮从 2,393 个 token 降到 1,455 个 token(40%),而两种削减合在一起会把它降到 725 个 token(70%)。一个更温和的版本会为每个旧结果保留一行:
# pseudocode, language-neutral
for each message older than the last 3 turns:
if it is a tool result:
replace the body with one line, for example:
"read src/auth.py (212 lines): login() calls verify_token(); test_login_expired fails"
otherwise keep it as it is过度修剪会让 agent 变笨。如果早期某一轮发现了失败测试的名称,这个事实必须在修剪后保留下来,就像上面的摘要行那样。原始列表和庞大的结果可以删掉,但后续步骤需要引用的任何事实都必须保留。
试试这个: 修剪之后,重新运行一个你知道答案的任务。如果它现在失败了,或者重新读取了它已经读过的文件,那说明你削减得太多了。
削减 3:保持前缀完全一致,以便缓存可以复用
Anthropic 的提示缓存文档将缓存读取定为基准输入价格的 0.1x,而 OpenAI 的提示缓存指南表示缓存输入最高可享受 95% 的折扣。脚本对稳定前缀运行 Anthropic 的乘数,该前缀是系统提示词加上保留的 3 轮,共 494 个 token。发送 10 轮时,不缓存计费相当于 4,940 个 token,而使用缓存在第 1 轮按 1.25x 写入、之后按 0.1x 读取,计费相当于 1,062 个 token。只发送一次时,计费相当于 618 个 token,比完全不缓存高 25%。
这个 494 token 的前缀低于两家厂商 1,024 token 的最低值,所以示例无法在自身规模上展示这种节省。可以沿用的是真实前缀上的比例:一次 1.25x 写入,然后按 0.1x 读取。“全部 3 种削减”那一行把相同的乘数应用到整个会话,假设修剪在固定点发生,并且每次调用都在 5 分钟窗口内。
618 这个数字展示了缓存如何反噬。三个条件决定你落在哪一边:
- 前缀必须保持完全一致。 可缓存的顺序是工具、系统提示词、历史记录。过早更改任何内容,都会破坏其后所有内容的匹配。
- 它必须及时被复用。 在 Anthropic 上,一次缓存写入成本为 1.25x,而一个条目默认存活 5 分钟。一次从未被复用的调用,其成本高于不缓存。
- 它必须足够长。 OpenAI 会缓存至少 1,024 个 token 的提示词。Anthropic 对 Claude Sonnet 4.5 的最低值是 1,024,在其其他模型上范围从 512 到 4,096。
安静的杀手是系统提示词里的时间戳。请求 ID,或者以不同顺序返回的工具列表,也是如此。其中任何一个都会让每次调用的前缀不同,因此在 Anthropic 上你每次都要支付 1.25x 写入费用,永远看不到 0.1x 读取。不会出现错误;账单只是会稍微高一点。
修剪会重写历史记录,所以缓存匹配会在修剪开始的地方结束:在固定点修剪,而不是每一轮都修剪。
将提示词顺序安排为工具、系统提示词、历史记录,把时间戳和 ID 排除在外,并在 5 分钟内再次发送相同的前缀。
试试这个: 在 Anthropic 上,从每次响应的 usage 块中读取
cache_creation_input_tokens和cache_read_input_tokens。如果第二次调用后读取量仍为零,说明你前缀中的某些内容在轮次之间发生了变化。
这些削减解决不了什么
它们会缩小输入。你要求的输出 token 仍然存在,而在全部三种削减之后,同样的算术会让输入占会话成本的 18%,所以输出现在成了账单中更大的部分。一个游荡 20 轮而不是 10 轮的循环,会多发送十次上下文,任何修剪都无法挽回。
从哪里开始:
- 工具输出是最大的块:削减 1。
- 历史记录是最大的块:削减 2。
- 提示词很大且稳定:削减 3,并检查 usage 字段。
你明天 15 分钟版本:用你真实的系统提示词、一个真实的工具结果和你记录的历史记录运行脚本。三种计数中哪一种最大,就先应用那种削减,然后再次运行脚本查看下降。在示例会话中,完整组合把 16,559 个输入 token 降到 3,290 个输入等效 token;你的数据会落在别处,而重新运行会告诉你落在哪里。
译文由上游机器翻译生成,可能有误;判断请以英文原文为准。
英文原文(来源本站未改写)
Your AI agent's token bill is mostly input. Here is the field guide to cutting it.
TL;DR
- In a toy 10-turn agent session, 16,559 input tokens go out and 3,090 output tokens come back. At Claude Sonnet 4.5 prices input is still 52% of the cost, and all three cuts below shrink it to 3,290 input-equivalent tokens, 80% less.
- Truncating tool output and pruning old history alone cut the session's input by 62%, to 6,445 tokens.
- Caching a stable prefix bills it at 0.1x after the first call, but a prefix used only once costs 25% more than no caching.
Your agent bill is bigger than the work you remember asking for, and the cause is mechanical. A model API is stateless, so every turn re-sends the system prompt, all tool output so far and the whole conversation. A file listing pasted on turn 2 is billed again on every turn after it. TechCrunch reported that Y Combinator CEO Garry Tan spends up to $200K a year on tokens, and at any scale the repeated input is the part you can shrink. Below: one script to measure it, and three cuts.
Where a 10-turn session's tokens go
Turn 10 carries the results of turns 1 through 9, so a 10-turn session costs the sum of ten growing prompts. In the toy run below that sum is 16,559 input tokens. A real agent reply carries tool calls and code, so we model each reply at about 300 tokens, which makes 3,090 output tokens for the session.
Output costs more per token. Anthropic's pricing page lists Claude Sonnet 4.5 at $3 per million input tokens and $15 per million output tokens, so one output token weighs 5 input tokens. Weighted by price, input is still 52% of the session, a bare majority. The script prints all three numbers.
To count caching in the same units, we use input-equivalent tokens: a cache read counts as 0.1 of a token and a cache write as 1.25.
Measure first: count tokens for each part of a turn
Start with your real baseline: log the usage block of every API response (input and output tokens per call) and sum each per session. That total is your real number. The script then shows where it goes, by counting the system prompt, one tool output and the history separately. The biggest one tells you which cut to try first.
The data is fake, so rerun the script on your own. Every figure is a tiktoken count except the cache lines and the cost-share lines, which are arithmetic from the vendors' documented multipliers and prices, with no API call.
# pip install tiktoken
import tiktoken
enc = tiktoken.get_encoding("o200k_base")
def n(text):
return len(enc.encode(text))
system = """You are a coding agent. You read files, run commands, and edit code.
Always explain your plan before acting. Follow the repo style guide. When you
write tests, use the project's existing fixtures. Never touch the migrations
folder without asking. Summarize diffs at the end of each task. Be concise but
complete. Handle errors gracefully and retry once on network failures.
[... style guide: 40 more lines of formatting rules ...]"""
# One fake tool output: a 60-line file listing.
tool_output = "\n".join(
f"src/module_{i:02d}.py: def handler_{i}(request) -> Response: ..."
for i in range(60)
)
# One fake model reply per turn. A real agent reply carries its tool calls and
# code, so we model it at a few hundred tokens.
reply = (
"I read the listing. handler_07 builds the response before it validates "
"the request, which is why test_handler_07 fails. I'll move the check "
"above the response and add a regression test.\n"
'{"tool": "edit_file", "path": "src/module_07.py", "patch": "'
+ "\\n".join(
f"- response = build(request, {i})\\n+ validate(request)\\n+ response = build(request, {i})"
for i in range(10)
)
+ '"}'
)
def history_of(turns):
return "\n".join(
f"Turn {i}: user asked something, agent read files, ran tests, "
f"edited two files, tool output followed. {tool_output[:400]}"
for i in range(turns)
)
def truncate(text, head=600, tail=200):
if len(text) <= head + tail:
return text
cut = text[head:-tail].count("\n")
return text[:head] + f"\n[... {cut} lines truncated ...]\n" + text[-tail:]
def turn(history_turns=10, tool=tool_output):
return "\n".join([system, history_of(history_turns), tool])
full = n(turn())
pruned = n(turn(history_turns=3))
truncated = n(turn(tool=truncate(tool_output)))
both = n(turn(history_turns=3, tool=truncate(tool_output)))
def smaller(x):
return 100 - x * 100 // full
print(f"system prompt: {n(system)} tokens")
print(f"one raw tool output: {n(tool_output)} tokens")
print(f"10 turns of history: {n(history_of(10))} tokens")
print(f"one full turn as sent: {full} tokens")
print(f"history pruned (keep 3): {pruned} tokens ({smaller(pruned)}% smaller)")
print(f"tool output truncated: {truncated} tokens ({smaller(truncated)}% smaller)")
print(f"both cuts: {both} tokens ({smaller(both)}% smaller)")
# A 10-turn session: turn i re-sends i earlier turns of history.
naive = sum(n(turn(history_turns=i)) for i in range(10))
session_cut = sum(n(turn(history_turns=min(i, 3), tool=truncate(tool_output))) for i in range(10))
out = 10 * n(reply)
print(f"10-turn input, no cuts: {naive} tokens")
print(f"10-turn input, both cuts: {session_cut} tokens ({100 - session_cut * 100 // naive}% smaller)")
# Cut 3 arithmetic (documented multipliers, no API call): a cache write costs
# 1.25x, a cache read 0.1x. Stable prefix = system prompt + the 3 kept turns.
prefix = n(system + "\n" + history_of(3))
cached = prefix * 1.25 + prefix * 0.1 * 9
# All three cuts stacked, same model. Each turn re-sends the previous turn's
# system prompt + history as a cache read (0.1x), writes whatever the prefix
# grew by (1.25x), and bills the rest of the turn (the tool output) at 1x.
# Assumes pruning happens at fixed points, so the prefix stays valid after
# turn 3, and every call lands inside the 5-minute cache lifetime.
stacked, prev = 0, 0
for i in range(10):
total = n(turn(history_turns=min(i, 3), tool=truncate(tool_output)))
pre = n(system + "\n" + history_of(min(i, 3)))
stacked += prev * 0.1 + (pre - prev) * 1.25 + (total - pre)
prev = pre
print(f"10-turn input, all 3 cuts: {stacked:.0f} input-equivalent tokens ({100 - stacked * 100 / naive:.0f}% smaller)")
print(f"10-turn output (replies): {out} tokens")
# Price-weighted: Sonnet 4.5 lists $3 per million input, $15 per million
# output, so one output token weighs 5 input tokens.
OUT_WEIGHT = 5
print(f"input cost share, no cuts: {100 * naive / (naive + OUT_WEIGHT * out):.0f}%")
print(f"input cost share, 3 cuts: {100 * stacked / (stacked + OUT_WEIGHT * out):.0f}%")
print(f"stable prefix: {prefix} tokens")
print(f"prefix x10, uncached: {prefix * 10} input-equivalent tokens")
print(f"prefix x10, cached: {cached:.0f} input-equivalent tokens")
print(f"prefix used once, cached: {prefix * 1.25:.0f} input-equivalent tokens")Output:
system prompt: 93 tokens
one raw tool output: 960 tokens
10 turns of history: 1339 tokens
one full turn as sent: 2393 tokens
history pruned (keep 3): 1455 tokens (40% smaller)
tool output truncated: 1663 tokens (31% smaller)
both cuts: 725 tokens (70% smaller)
10-turn input, no cuts: 16559 tokens
10-turn input, both cuts: 6445 tokens (62% smaller)
10-turn input, all 3 cuts: 3290 input-equivalent tokens (80% smaller)
10-turn output (replies): 3090 tokens
input cost share, no cuts: 52%
input cost share, 3 cuts: 18%
stable prefix: 494 tokens
prefix x10, uncached: 4940 input-equivalent tokens
prefix x10, cached: 1062 input-equivalent tokens
prefix used once, cached: 618 input-equivalent tokensTry this: replace system, tool_output, history_of and reply with your real system prompt, one real tool result, your logged history and a typical reply.
Cut 1: truncate tool output, keep the head and tail
Tool output is often the biggest block, and most of it is never read again. truncate keeps the first 600 characters and the last 200 and puts a marker in the middle. That takes one turn from 2,393 to 1,663 tokens, 31% smaller.
Keep both ends because errors tend to show up at the top of a log and the summary line at the bottom.
Cap each tool result at a head, a tail and a marker that says how many lines were dropped, so the agent knows it can ask for more.
Try this: log the length of every tool result for one real run. Whatever tops the list gets the cap first.
Cut 2: prune history once a result has been used
After the agent has acted on a tool result, the raw result rarely needs to travel with it. The toy loop keeps only the last 3 turns, which cuts one turn from 2,393 to 1,455 tokens (40%), and both cuts together bring it to 725 (70%). A gentler version keeps a line per old result:
# pseudocode, language-neutral
for each message older than the last 3 turns:
if it is a tool result:
replace the body with one line, for example:
"read src/auth.py (212 lines): login() calls verify_token(); test_login_expired fails"
otherwise keep it as it isOver-pruning makes the agent dumber. If an early turn found the name of the failing test, that fact has to survive the prune, as it does in the summary line above. The raw listings and bulky results can go, but any fact a later step will need to quote has to stay.
Try this: after pruning, rerun one task you know the answer to. If it now fails or re-reads files it already read, you cut too much.
Cut 3: keep the prefix identical so the cache can reuse it
Anthropic's prompt caching docs put a cache read at 0.1x the base input price, and OpenAI's prompt caching guide says cached input is discounted up to 95%. The script runs the Anthropic multipliers on the stable prefix, which is the system prompt plus the 3 kept turns, 494 tokens. Sent for 10 turns it bills like 4,940 tokens uncached and like 1,062 with a 1.25x write on turn 1 and 0.1x reads after. Sent once, it bills like 618, 25% more than not caching at all.
That 494-token prefix sits below both vendors' 1,024-token minimums, so the toy cannot show this saving at its own scale. What carries over is the ratio on a real prefix: one write at 1.25x, then reads at 0.1x. The "all 3 cuts" line applies the same multipliers to the whole session, assuming pruning at fixed points and every call inside the 5-minute window.
The 618 is how caching backfires. Three conditions decide which side you land on:
- The prefix must stay identical. The cacheable order is tools, then system prompt, then history. Changing anything early breaks the match for everything after it.
- It must be reused in time. On Anthropic a cache write costs 1.25x, and an entry lives 5 minutes by default. One call that never gets reused costs more than no caching.
- It must be long enough. OpenAI caches prompts of at least 1,024 tokens. Anthropic's minimum is 1,024 for Claude Sonnet 4.5 and ranges from 512 to 4,096 across its other models.
The quiet killer is a timestamp in the system prompt. So is a request ID, or a tool list that comes back in a different order. Any of them makes the prefix different on every call, so on Anthropic you pay the 1.25x write each time and never see a 0.1x read. No error appears; the bill just goes up a little.
Pruning rewrites the history, so the cache match ends where the prune starts: prune at fixed points instead of every turn.
Order the prompt as tools, system prompt, then history, keep timestamps and IDs out of it, and send the same prefix again within 5 minutes.
Try this: on Anthropic, read cache_creation_input_tokens and cache_read_input_tokens from each response's usage block. If reads stay at zero after the second call, something in your prefix changes between turns.
What these cuts will not fix
They shrink input. The output tokens you asked for stay, and after all three cuts the same arithmetic puts input at 18% of the session's cost, so output is now the bigger part of the bill. A loop that wanders for 20 turns instead of 10 re-sends the context ten more times, which no trim recovers.
Where to start:
- Tool output is the biggest block: cut 1.
- History is the biggest block: cut 2.
- The prompt is large and stable: cut 3, and check the usage fields.
Your 15-minute version for tomorrow: run the script with your real system prompt, one real tool result and your logged history. Whichever of the three counts is largest, apply that cut first and run the script again to see the drop. On the toy session the full stack takes 16,559 input tokens to 3,290 input-equivalent ones; your data will land elsewhere, and the rerun tells you where.
这条还缺什么证据?
下面每条都由本条已有字段推出(等级、理由、商业模式、来源次数、是否演示), 本站不生成推测性结论;通用验证方法放在方法论页。
- 可核验的收入或付费证据查官网定价页与付费口径;第三方数据源(如 GetLatka)只作旁证,需标注来源与时点。
- 商业模式未定确认按席位/按用量/授权还是开源托管版收费;开源项目另查 LICENSE 与是否存在付费版。
- 只有单一来源找一手站点或其他渠道是否重复出现同一产品;社区热帖数量不等于商业进展。
通用验证清单(谁有这个问题/谁愿意付费/一个人能交付哪一小步)见我们的筛选方法。