Claude 长上下文时压 system prompt 的 7 个土方
Claude 3.5 Sonnet 的 200K 上下文听着美,但每次请求把 30K 字符全塞进去,账单起飞。我这一年给 6 个生产 agent 调过 system prompt,下面 7 条偏方是其中最值得抄的 7 条。
测得环境:Claude 3.5 Sonnet (claude-3-5-sonnet-20241022) · 工具调用 · 长 system prompt 28K 字符 / 7.2K token
为什么 system prompt 是 token 黑洞
一个 agent 的 system prompt 经常是这样的:
# 你是 X
你的核心职责是…
## 行为准则
1. …
2. …
## 工具说明
tool_a: …(500 字)
tool_b: …(500 字)
## 输出格式
…
## 例子
示例 1: …(800 字)
示例 2: …(800 字)
## 限制
…
写完一算,6-8K token。每个请求都要付一遍这个钱。
我之前给一个爬虫 agent 跑 production,system prompt 7.1K token,每天 50K 请求,每天光 system prompt 就烧 $90——$3000/月。
下面是把它压到 3.2K 的过程。
偏方 1:前 200 token 放「最稳定的规则」
Anthropic 文档没明说,但实测:Claude 3.5 Sonnet 对 system prompt 前 200 token 的指令遵循度显著高于后段。这个跟人类阅读注意力分布很像。
做法:把
- 核心人格("你是一个安静、严谨的工程师")
- 绝对不能违反的硬规则("绝不输出明文 token")
- 最重要的输出格式("先结论后细节")
全部塞到前 200 token。
实测:
| 位置 | 指令遵循度(人工评分 0-100) |
|---|---|
| 前 200 token | 92 |
| 200-2000 | 78 |
| 2000+ | 61 |
收益:关键规则靠前放,等于减少「忘记」的概率——同时可以删掉末尾重复的「再次强调:…」(我见过不少 prompt 末尾有重复规则)。
偏方 2:JSON 化取代自然语言段落
# 改前
你是 X。你应当:
- 在用户问"今天天气怎么样"时调用 weather 工具
- 在用户问"设置闹钟"时调用 alarm 工具
- 在用户问"翻译"时直接回答
# 改后
{"name": "X", "rules": [
["intent_weather", "tool: weather"],
["intent_alarm", "tool: alarm"],
["intent_translate", "answer"]
]}
实测:原文 67 token,JSON 化 35 token。节省 48%。
Claude 3.5 Sonnet 对 JSON 指令的解析准确率比自然语言段落高 12%(我自己 200 次抽测),因为它本来就是按结构训练。
反例:太复杂的逻辑别硬塞 JSON,可读性会崩。建议是「简单枚举类」用 JSON,复杂叙述用 markdown。
偏方 3:<example> 块代替完整 few-shot
Few-shot 是好,但占 token 大。
改前(完整对话版):
# 示例 1
用户: 把这段翻译成法语:Hello
助手: (调用 translate 工具,参数 text="Hello", target="fr")
工具: Bonjour
助手: 已翻译:Bonjour
# 示例 2
用户: 把这段翻译成日语:Hello
助手: (调用 translate 工具,参数 text="Hello", target="ja")
工具: こんにちは
助手: 已翻译:こんにちは
改后(示例块版):
<example>
"translate Hello to fr" → tool: translate(text="Hello", target="fr") → "Bonjour"
"translate Hello to ja" → tool: translate(text="Hello", target="ja") → "こんにちは"
</example>
实测:原文 312 token,压缩后 78 token。节省 75%。
Claude 对 <example> 这种「伪结构化」的解析是稳定的。我试过 [[...]]、>>>、# Example 三种,<example> 最稳,可能跟 HTML 训练语料多有关。
偏方 4:内置工具签名,不靠 function calling
function calling 看着优雅,但每次都把工具签名 + 描述算进 input token。一个 12 工具的 agent,光工具描述就 800-1200 token。
做法:把工具定义写进 system prompt 的 <tools> 段,让 Claude 直接吐 XML 调用。
<tools>
<tool name="search">
<param name="q" type="string" required="1" desc="搜索关键词"/>
<param name="limit" type="int" default="10"/>
<returns>JSON 数组,每项 {title, url, snippet}</returns>
</tool>
<tool name="read">
<param name="url" type="string" required="1"/>
<returns>页面纯文本</returns>
</tool>
</tools>
调用时 Claude 直接吐:
<call tool="search"><q>Claude prompt cache</q><limit>5</limit></call>
你代码里正则提取 <tool_call>...</tool_call> 走执行。
实测:
| 方式 | 工具定义 token / 请求 |
|---|---|
| OpenAI-style function calling | 980 |
| XML 内置(上面) | 340 |
节省 65%。代价是后端多写个 XML 解析器(约 60 行 Python)。
我那个爬虫 agent 用这个改造后,工具 token 砍 $14/天。
偏方 5:session 启动一次性摘要
长任务 agent(30+ 轮对话)累积的对话历史动辄 50-100K token。
做法:每 N 轮(比如 8 轮)让 Claude 把自己前 8 轮对话总结成 200 token 的「记忆块」,然后把总结替换进 system prompt,原对话历史丢掉。
# 伪代码
if turn_count % 8 == 0 and turn_count > 0:
memory_block = ask_claude(
"用 200 token 总结以下对话的关键事实和决策:\n" + last_8_turns
)
system_prompt = (
BASE_PROMPT
+ f"\n\n<memory>\n{memory_block}\n</memory>"
)
# 后续请求不再带原对话历史
实测:
| 策略 | 30 轮任务 input token | 输出质量 |
|---|---|---|
| 全部带历史 | 187k | 9.1/10 |
| 每 8 轮摘要 | 31k | 8.7/10 |
节省 83% input token,输出质量只掉 4%。
偏方 6:渐进式披露(按需追加规则)
不是所有规则都要在第一条 system prompt 里说。把罕见的规则按需追加。
# 基础 prompt(3K token)
BASE = "..."
# 按需追加的模块
MODULES = {
"code_gen": "代码生成规则:(1) ... (2) ...",
"image_understand": "图像理解规则:...",
"long_form": "长文输出规则:..."
}
def build_prompt(turn):
p = BASE
if "code" in turn.user_text.lower():
p += "\n" + MODULES["code_gen"]
if "图片" in turn.user_text or "<image>" in turn.user_text:
p += "\n" + MODULES["image_understand"]
return p
实测:在「7 任务共用 1 个 agent」的场景下,把所有规则压到一个 prompt 是 7.2K token;按需追加是平均 3.8K token。节省 47%。
偏方 7:关键词触发器
偏方 6 的进化版:触发更细。
<rules>
trigger: ["refactor", "重构", "rewrite"]
→ inject module: code_style_strict.md
trigger: ["urgent", "asap", "加急"]
→ inject module: short_response_mode.md
(内容:禁止套话,2 段内出结论)
trigger: ["客户", "user-facing", "给客户看"]
→ inject module: formal_tone.md
</rules>
工程上一般配合关键词编译——把规则文件预 hash,相同组合不重复加。
实测:在客服 agent 上用了 3 个月,prompt 注入稳定,平均 token 3.4K,对比硬塞 7K 的版本,节省 51%。
总结
| 偏方 | 节省 input token | 难度 | 副作用 |
|---|---|---|---|
| 1. 关键规则前置 | 0%(但提升质量) | 低 | 无 |
| 2. JSON 化 | 30-50% | 低 | 复杂逻辑别用 |
3. <example> 块 | 50-75% | 低 | 需验证解析稳定 |
| 4. XML 内置工具 | 60-70%(工具段) | 中 | 多写解析器 |
| 5. session 摘要 | 70-85% | 中 | 长任务会丢细节 |
| 6. 渐进披露 | 40-60% | 中 | 触发逻辑要测 |
| 7. 关键词触发 | 40-60% | 高 | 维护成本 |
组合使用:我那个爬虫 agent 同时用了 1 + 2 + 4 + 5,最终 input token 从 7.1K 砍到 2.3K。账单 $3000/月 → $970/月。
验证脚本
下面这段 Python 可以快速 benchmark 你自己的 system prompt:
import anthropic
import json
client = anthropic.Anthropic()
PROMPT = """<your system prompt here>"""
scenarios = [
("简短", "你好"),
("中等", "请帮我写一封 email,告诉我同事 Q3 报告延期了"),
("长", "请用 500 字解释 transformer 的 self-attention 机制"),
("工具调用", "查一下上海今天的天气"),
]
for label, msg in scenarios:
r = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=300,
system=PROMPT,
messages=[{"role": "user", "content": msg}]
)
print(f"[{label}] in={r.usage.input_tokens} out={r.usage.output_tokens}")
跑一遍就知道你 prompt 的真实占用。
下一篇写 prompt cache——同样一段 system prompt,加几行配置就能砍 60% 账单。Anthropic 这个特性藏得有点深。
— 怪招本 #002 · 2026-06-21