【Claude Code-System Prompt实测】System Prompt 跨模型处理与缓存命中总论:Claude Code 请求前缀治理
非线智能API经验 [Claude Code-System Prompt实测] 第8篇
摘要
Claude Code 接入非 Anthropic 模型或第三方 LLM gateway 时,system prompt 不是一段可以随手搬运的普通提示词。它更像一段请求前缀资产:Claude Code 内置 agent 指令、工具定义、MCP 描述、项目上下文、CLAUDE.md、beta 能力开关、cache control,以及归属指纹块都会影响模型看到的开头内容、网关转换后的请求结构和 prompt cache 命中率。
这个方向的核心目标不是“提示词优化”,而是请求前缀治理:把 Claude Code 发出的稳定前缀转换成不同模型和网关都能接受的结构,同时剥离或归一化动态噪声,固定工具和 MCP 描述排序,统计 token 构成,观测每轮 prefix diff,并明确哪些内容可缓存、哪些内容必须留在动态窗口。
如果这一层不处理,跨模型兼容会同时遇到三类问题。第一是协议不兼容:Anthropic Messages API 的起始系统指令使用顶层 system 字段,而 OpenAI-compatible 生态长期使用 messages / instructions 表达系统级指令;把 system role 直接塞进 Anthropic messages 数组,可能触发严格角色校验。第二是缓存命中率崩塌:Claude Code attribution block 可能包含动态 prompt fingerprint,自定义 gateway 如果按完整 request body 或完整 prompt 前缀缓存,会把本该稳定的前缀打碎。第三是上下文不可控:工具定义、MCP 描述和项目规则持续膨胀,会提高 token 成本,也会稀释模型对当前任务的注意力。
本文是方向总论。后续六篇文章分别展开每个证据点和规划点:
1、Anthropic / OpenAI system 结构差异:解释为什么顶层 system 与 messages[0].role=system 不能机械互转。
2、Claude Code attribution block 与第三方缓存 miss:分析动态归属指纹如何破坏自定义 gateway 的 cache key。
3、归属指纹块与兼容端保留关键字错误:处理 x-anthropic-billing-header 进入 provider 校验链路后的 400 风险。
4、CLAUDE.md、工具定义与 MCP 描述的上下文膨胀:拆解初始上下文增长和 token budget。
5、归属指纹块剥离与前缀稳定化:给出 strip / normalize、canonicalization 和 cache segment 设计。
6、请求前缀可观测性:设计 token profile、prefix diff、HMAC 日志和 cache miss 归因。
数据来源:非线智能Nonlinear 官网
证据强度分层
这类问题必须区分“官方规格”“工程案例”和“社区逆向观察”。
| 证据类型 | 适合证明什么 | 使用方式 |
|---|---|---|
| 官方文档 | API 结构、环境变量、gateway 行为、prompt caching 语义 | 作为文章的规格依据 |
| GitHub issue / PR | 具体版本、具体网关、具体错误信息 | 作为工程案例,不等同于长期规格 |
| 第三方网关文档 | 企业接入方式、观测和治理需求 | 说明真实部署形态 |
| 逆向分析 / 社区讨论 | system prompt 构造、工具描述体积、缓存体验 | 作为观察线索,不当作官方事实来源 |
一个关键修正是:不要简单写成“CLAUDE.md 被注入 system prompt”。Claude Code 官方 memory 文档的说法更精确:CLAUDE.md 会进入初始上下文 / project context,并且其内容在 system prompt 之后作为 user message 提供。对缓存和 token 成本来说,它仍然属于请求前缀治理范围;但对协议转换来说,它不一定属于 Anthropic 顶层 system 字段。
另一个关键修正是:不要写成“attribution block 会破坏 Anthropic 官方 prompt cache”。Claude Code 官方 LLM gateway 和环境变量文档说明,Anthropic 自家 API 会在处理前剥离该 block,因此 Anthropic API caching 不受影响。问题主要发生在自定义 ANTHROPIC_BASE_URL、第三方 gateway、本地模型网关,或任何按完整 request body / 完整 prompt 前缀做缓存的实现中。
结构差异:顶层 system 不是 messages 里的普通角色
Anthropic Messages API 的起始系统指令应放在请求顶层 system 字段中。这个字段可以是字符串,也可以是 content block 数组;在 prompt caching 场景下,顶层 system 还可以携带 cache breakpoint。Anthropic 的对话 messages 序列主要承载 user 和 assistant 消息。
OpenAI-compatible 生态则长期把系统级指令表达为消息序列中的 role: "system" 或更新接口里的 instructions / developer 指令。这个差异不是字段名不同,而是请求 AST 不同:Anthropic 把系统前缀作为请求级字段,OpenAI-compatible 多数实现把它看成消息流的一部分。
错误转换通常长这样:
{
"messages": [
{
"role": "system",
"content": "You are Claude Code..."
},
{
"role": "user",
"content": "fix this bug"
}
]
}
如果这个请求被发往严格的 Anthropic-compatible endpoint,messages[].role 中的 system 可能被拒绝,因为该位置只接受 user / assistant。vLLM issue 和相关论坛讨论中就出现过 Claude Code 新版本把 system / ctx / msg 等 role 放入 Anthropic Messages API 的 messages 数组后,vLLM 侧报 Input should be 'user' or 'assistant' 的案例。类似问题也出现在 Claude Code 社区 issue 中,报错指向 messages[n].role must be either 'user' or 'assistant'。
兼容层因此不能只做 role 字符串映射,而应先把请求解析成内部结构:
type PromptPrefixIR = {
system: PromptBlock[];
projectContext: PromptBlock[];
toolDefinitions: ToolDefinitionIR[];
mcpDescriptors: McpDescriptorIR[];
dynamicHeaders: PromptBlock[];
conversation: ConversationMessageIR[];
cacheHints: CacheHint[];
};
渲染到 Anthropic 时,system 应回到顶层 system 字段,conversation 只渲染合法消息角色;渲染到 OpenAI-compatible 时,再按目标 profile 决定使用 messages[0].role = "system"、developer role、instructions 字段,还是把部分上下文折叠进普通 user message。这样才能避免把某一家 API 的表面格式误认为通用聊天格式。
动态归属指纹:缓存命中率的隐形破坏点
Claude Code 官方文档说明,Claude Code 可能在 system prompt 前加入 attribution block,包含 client version 和 prompt fingerprint。社区中观察到的形式大致类似:
x-anthropic-billing-header: cc_version=<version>; cc_entrypoint=cli; cch=<fingerprint>;
在直连 Anthropic API 时,这个 block 会被服务端处理前剥离,因此不会影响 Anthropic 自家的 prompt cache。但第三方 gateway 往往没有这层特殊处理。尤其在 ANTHROPIC_BASE_URL 指向自定义服务时,gateway 可能直接把完整 body、完整 prompt 或完整 system prefix 作为 cache key。此时 attribution block 如果位于请求前缀第一位,且 fingerprint 随会话或首条用户 prompt 变化,就会导致稳定前缀从第一个 token 开始不同。
这对 prompt cache 是致命的。prefix cache 的基本前提是“开头相同”。一旦开头第一段每次不同,后面的 Claude Code 内置指令、工具定义、MCP 描述、项目规则即使完全一致,也无法复用同一个前缀缓存。anthropics/claude-code #50085 把这个问题落到 ANTHROPIC_BASE_URL 下的 cache miss;claude-code-router 的相关 PR 则采用设置 CLAUDE_CODE_ATTRIBUTION_HEADER=0 的方式避免动态 header 破坏缓存。
这个 block 还可能触发兼容端错误。anthropics/claude-code #24168 的 issue reporter 在 Claude Code + Bedrock 场景中观察到:x-anthropic-billing-header 被作为 system prompt text block 发送后,触发 400 x-anthropic-billing-header is a reserved keyword。这说明 attribution block 不只是缓存问题,也可能成为 provider 关键字、保留字段和安全校验问题;但这个具体报错是工程案例,不是 AWS 官方规格。
前缀稳定化策略
兼容层应把前缀稳定化放在请求进入模型网关之前,而不是等 cache miss 后再分析日志。
第一步是识别动态块。默认规则应覆盖:
| 动态来源 | 识别方式 | 默认处理 |
|---|---|---|
x-anthropic-billing-header attribution block |
system prompt 开头的固定 header 名 | 剥离,或归一化为固定占位 |
| 时间戳 | ISO 时间、Unix time、日期字符串 | 移出 stable prefix |
| UUID / 随机 ID | UUID v4、短 hash、session id | 移出 stable prefix 或规范化 |
| 动态路径 | 临时目录、workspace session path | 改成稳定相对路径 |
| 工具顺序抖动 | 每轮工具数组顺序不同 | 按稳定 key 排序 |
| MCP 描述顺序抖动 | server / tool 枚举无序 | 按 server name、tool name、schema hash 排序 |
第二步是建立前缀分层。不要让所有上下文自然堆叠在同一个消息数组里:
| 分层 | 内容 | 缓存策略 |
|---|---|---|
| Stable System | Claude Code 内置指令、兼容层约束、稳定安全规则 | 长周期缓存 |
| Stable Capabilities | 工具定义、MCP 工具描述、模型 capability profile | schema 或能力变化才失效 |
| Project Context | CLAUDE.md、项目规则、仓库摘要 |
中周期缓存,带文件 hash |
| Task State | 当前目标、关键决策、近期文件摘要 | 短周期缓存 |
| Live Window | 最新用户消息、工具结果、错误反馈 | 高频变化,不追求长缓存 |
| Dynamic Metadata | attribution、trace id、session id、计费标签 | 不进入 cacheable prefix |
第三步是让 cache key 显式化:
type PrefixCacheKey = {
provider: "anthropic" | "openai-compatible" | "bedrock" | "vllm" | "local";
model: string;
systemHash: string;
toolSchemaHash: string;
mcpDescriptorHash: string;
projectContextHash: string;
adapterVersion: string;
};
trace_id、session_id、用户本轮 prompt、最新工具输出不应进入这个 key。它们可以进入审计日志,也可以作为 gateway header 参与观测,但不能污染 cacheable prefix。
对 Anthropic 这类支持显式 cache breakpoint 的后端,兼容层可以把 cache hint 渲染成 provider 原生 cache_control。对 vLLM、本地模型或普通 OpenAI-compatible gateway,则应降级为内部 prefix cache key 或 KV reuse hint,而不是把 cache_control 当成自然语言塞回 prompt。
type CacheSegment = {
id: string;
type: "system" | "tools" | "mcp" | "project_context" | "task_state" | "live_window";
stability: "static" | "semi_static" | "dynamic";
tokenEstimate: number;
hash: string;
providerPolicy: "anthropic_cache_control" | "gateway_prefix_cache" | "kv_reuse_hint" | "none";
};
请求前缀可观测性
没有观测,前缀治理会退化成“感觉 prompt 好像变大了”。这里的可观测对象是完整请求前缀;system prompt token 只是其中一个 segment。兼容层应给每轮请求输出一份 prefix profile:
{
"request_id": "req_...",
"provider": "vllm",
"model": "qwen3-coder",
"tokens": {
"total_input": 48231,
"system": 12980,
"claude_code_builtin": 7210,
"project_context": 2840,
"claude_md": 1630,
"tools": 15440,
"mcp": 6150,
"task_state": 2760,
"live_window": 11051,
"dynamic_uncacheable": 20
},
"cache": {
"cacheable_prefix_tokens": 30270,
"uncacheable_dynamic_tokens": 20,
"stable_prefix_hmac": "hmac-sha256:...",
"changed_segments": ["live_window"]
},
"stability_score": 0.94
}
指标至少应覆盖:
| 指标 | 目的 |
|---|---|
| system prompt token 数 | 判断系统级指令是否膨胀 |
| Claude Code 内置 prompt token 数 | 区分平台固定成本和项目成本 |
CLAUDE.md / project context token 数 |
发现项目规则是否失控 |
| 工具定义 token 数 | 发现工具 schema / description 是否过长 |
| MCP 描述 token 数 | 发现 MCP server 暴露过多能力 |
| cacheable prefix token 数 | 判断可复用前缀规模 |
| uncacheable dynamic token 数 | 判断动态噪声是否进入前缀 |
| prefix diff | 定位每轮变化来自哪里 |
| cache hit / miss / write token | 观察真实缓存效果 |
| 首 token 延迟 | 验证缓存是否带来延迟收益 |
| role rewrite count | 发现跨协议转换是否在频繁修正非法角色 |
| stripped attribution block count | 判断动态归属块是否仍在入口出现 |
| prefix churn ratio | 衡量可缓存前缀中有多少 token 发生变化 |
稳定性评分可以先用可解释的启发式,而不是黑盒模型:
stability_score = 100
- 40 * cacheable_churn_ratio
- 20 * dynamic_prefix_ratio
- 15 * ordering_instability
- 15 * role_rewrite_risk
- 10 * cache_boundary_risk
其中 cacheable_churn_ratio 是本轮可缓存前缀中发生变化的 token 占比,dynamic_prefix_ratio 是第一个缓存边界之前的动态 token 占比,ordering_instability 反映工具 / MCP canonicalization 前后的顺序抖动,role_rewrite_risk 反映非法 role 或跨协议降级风险,cache_boundary_risk 反映 cache breakpoint 是否被放在动态块之后。评分不是为了追求绝对准确,而是为了让工程团队能在版本升级、MCP 增加、工具描述改写后立刻看到“为什么缓存掉了”。
Prefix diff:只暴露结构,不泄露 prompt
system prompt 可观测有一个现实风险:日志本身可能泄露项目规则、工具参数、路径、密钥或用户上下文。因此 prefix diff 不应默认记录完整 prompt 原文。推荐记录结构化摘要和 hash:
{
"segment": "tools",
"before_hmac": "hmac:aaa",
"after_hmac": "hmac:bbb",
"token_delta": 840,
"change_type": "tool_added",
"examples_redacted": [
{
"tool_name": "deploy_prod",
"schema_hmac": "hmac:..."
}
]
}
对需要深度排障的环境,可以启用短期原文采样,但必须有保留周期、脱敏规则和访问控制。默认观测应遵循三个原则:hash 可比对,token 可计量,原文不可见。
生产环境更稳妥的做法是使用带环境 salt 的 HMAC,而不是裸 SHA。裸 hash 在固定 prompt、常见工具描述或公开 system prompt 场景中可能被字典反推;HMAC 可以保留“同一环境内可比较”的能力,同时降低跨环境泄露风险。prompt telemetry 也应设置 TTL:长期保留聚合指标,短期保留调试事件,不把原始 prompt 或脱敏片段送入长期分析仓库。
跨模型渲染器设计
最终落地应是一个 prefix compiler,而不是一组字符串替换规则:
Claude Code request
-> parse to PromptPrefixIR
-> classify stable / project / task / live / dynamic
-> normalize dynamic blocks
-> sort tools and MCP descriptors
-> compute token profile and prefix hashes
-> render by ProviderProfile
-> attach cache_control / headers / observability metadata
-> send to model gateway
ProviderProfile 至少应表达:
type ProviderProfile = {
systemMode: "top_level_system" | "messages_system" | "instructions" | "developer_message";
allowedMessageRoles: string[];
supportsSystemContentBlocks: boolean;
supportsCacheControl: boolean;
supportsToolChoice: boolean;
reservedKeywords: string[];
maxSystemTokens?: number;
cacheBoundaryPolicy: "anthropic_cache_control" | "gateway_prefix_hash" | "none";
};
Anthropic profile 会把起始系统指令渲染到顶层 system,并保留合法 cache control。OpenAI-compatible profile 会根据具体接口选择 system / developer / instructions。Bedrock profile 要避开保留关键字和供应商限制。vLLM Anthropic-compatible profile 要严格保证 messages 中只出现 endpoint 接受的角色。Local model profile 则要特别关注 KV cache:即使没有商业 prompt caching,也要避免动态前缀导致本地推理服务重复处理完整 prompt。
失败模式
| 失败模式 | 触发原因 | 修复策略 |
|---|---|---|
| Anthropic-compatible endpoint 返回 400 | system role 被塞进 messages |
使用顶层 system,按 profile 渲染合法角色 |
| prompt cache hit rate 接近 0 | attribution fingerprint 或 session id 位于前缀开头 | 剥离、归一化或设置 CLAUDE_CODE_ATTRIBUTION_HEADER=0 |
| Bedrock 报 reserved keyword | x-anthropic-billing-header 被当作 prompt text 发送 |
provider profile 过滤保留关键字 |
| 工具 schema 每轮 hash 不同 | 工具或 MCP 描述顺序不稳定 | 稳定排序,schema canonicalization |
| system token 持续上涨 | CLAUDE.md、MCP、工具描述无边界增长 |
token budget、分层加载、按需暴露工具 |
| 观测日志泄露 prompt | prefix diff 记录原文 | 默认记录 hash、token delta 和结构化变更 |
验证清单
• 构造 Anthropic Messages 请求,确认 messages 中不会出现非法 system role。
• 构造 OpenAI-compatible 请求,确认系统级指令按目标接口落到 system / developer / instructions 中。
• 连续发送两轮同任务请求,确认剥离 attribution 后 stable prefix hash 不变。
• 增减一个工具,确认变化只反映到 toolSchemaHash,而不是整段 system 随机变化。
• 打乱 MCP server 返回顺序,确认 canonicalized descriptor hash 不变。
• 在 Bedrock / vLLM / local gateway profile 下跑保留关键字和角色校验测试。
• 开启 cache 指标,比较归一化前后的 cache hit rate、cache read tokens、cache write tokens 和首 token 延迟。
• 对 prefix diff 日志做 secret scan,确认不会记录原始密钥、完整 prompt 或敏感路径。
工程落地顺序
1、先实现 PromptPrefixIR,把 system、project context、tools、MCP、dynamic metadata 和 conversation 分开。
2、实现 provider profile,把 Anthropic 顶层 system、OpenAI-compatible system message / instructions、Bedrock / vLLM 限制都收敛到渲染层。
3、默认剥离或归一化 attribution block,并提供等效 CLAUDE_CODE_ATTRIBUTION_HEADER=0 的网关策略。
4、对工具定义、MCP 描述和 project context 做 canonicalization:稳定排序、稳定 JSON 序列化、hash 化。
5、接入 token counting 和 prefix profile,输出 system、tools、MCP、project context、live window 的 token 构成。
6、实现 prefix diff 和稳定性评分,默认只记录 hash、token delta 和结构化变更。
7、最后接入真实 cache 指标,用 hit rate、write/read tokens、首 token 延迟和 400 错误率做灰度验收。
结论
Claude Code 的 system prompt 跨模型处理,本质上是 agent runtime 的请求前缀治理。Anthropic、OpenAI-compatible、Bedrock、vLLM、本地模型网关对 system、messages、tools、cache control 和保留关键字的处理并不等价;而 Claude Code 自身又会把内置 prompt、工具定义、MCP、项目上下文和 attribution metadata 组合成一个很长的初始上下文。
稳健的兼容层应该把这段前缀当作可编译、可审计、可缓存的数据结构:先拆成 IR,再按 provider profile 渲染;先剥离动态噪声,再计算稳定 hash;先统计 token 构成,再谈成本优化。只有这样,跨模型 Claude Code 才不会在角色校验、动态 header、上下文膨胀和 cache miss 之间反复踩坑。
参考链接
• Claude Code Environment Variables
• Claude Code LLM Gateway
• Claude Code Memory
• Claude Code Modifying system prompts
• Anthropic Using the Messages API
• Anthropic Prompt Caching
• Anthropic Mid-conversation system messages
• Anthropic Token Counting
• Anthropic Define Tools
• Anthropic Engineering: Effective context engineering for AI agents
• AWS Bedrock Anthropic Claude Messages API
• vLLM Claude Code Integration
• vllm-project/vllm #44000
• anthropics/claude-code #24168
• anthropics/claude-code #50085
• anthropics/claude-code #63469
• musistudio/claude-code-router #1220
• Prompt Cache: Modular Attention Reuse
• CacheProbe
• On the Use of Agentic Coding Manifests
• Decoding the Configuration of AI Coding Agents
• Dive into Claude Code
本文由非线智能API Claude Code 行业专家整理编写