diff --git a/docs/context/system-prompt.mdx b/docs/context/system-prompt.mdx index 98f60fff3..fcfa4005a 100644 --- a/docs/context/system-prompt.mdx +++ b/docs/context/system-prompt.mdx @@ -1,290 +1,116 @@ --- -title: "System Prompt 动态组装 - AI 工作记忆构建" -description: "深入解析 Claude Code 的 System Prompt 动态组装过程:缓存策略、分界标记、Section 注册表、CLAUDE.md 多级合并,以及如何将零散上下文拼装为 API 可消费的缓存友好结构。" -keywords: ["System Prompt", "系统提示词", "动态组装", "CLAUDE.md", "Prompt Cache", "缓存策略"] +title: "系统提示词" +description: "系统提示词不是一段写死的文本,而是一个动态组装、分块缓存的数组。理解三阶段管道、缓存分界标记和多级优先级选择的设计。" +keywords: ["系统提示词", "System Prompt", "动态组装", "Prompt Cache", "缓存策略"] --- -## 从数组到 API 调用:System Prompt 的完整链路 +## 核心问题 -System Prompt 在 Claude Code 中不是一段写死的文本,而是一个 **`string[]` 数组**(品牌类型 `SystemPrompt`,定义于 `src/utils/systemPromptType.ts:8`),经过组装、分块、缓存标记后发送给 API。 +AI 的行为由系统提示词(System Prompt)决定。但一个编程助手的系统提示词远不止一句"你是一个有用的助手"——它需要包含工具使用规则、安全策略、项目上下文、用户偏好等大量动态内容。 -### 三阶段管道 +挑战在于:**这些内容每次请求都要发送,而且大部分是不变的。** 如何在保持动态性的同时最小化 token 成本? + +## 从文本到数组:缓存驱动的架构选择 + +系统提示词不是单个字符串,而是一个 **字符串数组**。 + +### 为什么是数组? + +Anthropic 的 Prompt Cache 以**内容块**为单位缓存。将提示词拆为多个块,不变的部分可以获得独立的缓存命中。如果是一个单字符串,任何一个字符变化(如日期更新)都会导致整个提示词的缓存失效。 + +一个典型的系统提示词约 20K+ tokens,通过缓存分块可以节省 30-50% 的输入 token 费用。 + +### 品牌类型保证 + +系统使用品牌类型(branded type)防止普通字符串数组被意外传入 API 调用——只有通过显式转换才能获得系统提示词类型。这是零开销的编译时安全保证。 + +## 三阶段组装管道 ``` -getSystemPrompt() → string[] (组装内容) - ↓ -buildEffectiveSystemPrompt() → SystemPrompt (选择优先级路径) - ↓ -buildSystemPromptBlocks() → TextBlockParam[] (分块 + cache_control 标记) +收集内容 → 选择优先级 → 分块 + 缓存标记 ``` -1. **`getSystemPrompt()`**(`src/constants/prompts.ts:444`)—— 收集静态段 + 动态段,插入 `SYSTEM_PROMPT_DYNAMIC_BOUNDARY` 分界标记 -2. **`buildEffectiveSystemPrompt()`**(`src/utils/systemPrompt.ts:41`)—— 按 Override > Coordinator > Agent > Custom > Default 优先级选择 -3. **`buildSystemPromptBlocks()`**(`src/services/api/claude.ts:3279`)—— 调用 `splitSysPromptPrefix()` 分块,为每个块附加 `cache_control` +### 第一阶段:内容收集 -## SystemPrompt 品牌类型 +系统提示词的内容分为两个区域: -```typescript -// packages/@ant/model-provider/src/types/systemPrompt.ts:4 -export type SystemPrompt = readonly string[] & { - readonly __brand: 'SystemPrompt' -} -export function asSystemPrompt(value: readonly string[]): SystemPrompt { - return value as SystemPrompt // 零开销类型断言 -} -``` +| 区域 | 内容 | 特点 | +|------|------|------| +| **静态区** | 工具使用规则、安全策略、输出格式规范 | 所有用户相同 | +| **动态区** | 记忆、MCP 指令、模型覆盖、语言偏好 | 因用户/会话而异 | -品牌类型(branded type)防止普通 `string[]` 被意外传入 API 调用——只有通过 `asSystemPrompt()` 显式转换才能获得 `SystemPrompt` 类型。 +两个区域之间用一个特殊的**分界标记**分隔。这个标记永远不会发送给 AI——它只是告诉缓存系统:"到这里为止是静态的,从这里开始是动态的"。 -## getSystemPrompt():内容组装的全景 +### 第二阶段:优先级选择 -`src/constants/prompts.ts:444` 是 System Prompt 的核心工厂函数,返回一个有序数组: +最终使用哪个提示词取决于五级优先级: -| 阶段 | 内容 | 缓存策略 | -|------|------|----------| -| **静态区** | Intro Section、System Rules、Doing Tasks、Actions、Using Tools、Tone & Style、Output Efficiency | 可跨组织缓存(`scope: 'global'`) | -| **BOUNDARY** | `SYSTEM_PROMPT_DYNAMIC_BOUNDARY = '__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__'` | 分界标记(不发送给 API,仅用于分割静态区与动态区以实现全局缓存) | -| **动态区** | Session Guidance、Memory、Model Override、Env Info、Language、Output Style、MCP Instructions、Scratchpad、FRC、Summarize Tool Results、Token Budget、Brief | 每次会话不同(`scope: 'org'` 或无缓存) | - -> **Boundary 是什么**:它把 System Prompt 分成"不变的静态区"和"因用户/会话而异的动态区"。静态区对所有用户相同,可获得 `scope: 'global'` 跨组织缓存;动态区每次不同,只能 `scope: 'org'` 或不缓存。它本身是一个特殊字符串,在发送给 API 前被移除,AI 永远看不到。 - -### 动态区的 Section 注册表 - -动态区通过 `systemPromptSection()` / `DANGEROUS_uncachedSystemPromptSection()` 注册,这两个工厂函数定义于 `src/constants/systemPromptSections.ts`: - -```typescript -// 缓存式 Section:计算一次,/clear 或 /compact 后才重新计算 -systemPromptSection('memory', () => loadMemoryPrompt()) - -// 危险:每轮重新计算,会破坏 Prompt Cache -DANGEROUS_uncachedSystemPromptSection( - 'mcp_instructions', - () => isMcpInstructionsDeltaEnabled() ? null : getMcpInstructionsSection(mcpClients), - 'MCP servers connect/disconnect between turns' // 必须给出破坏缓存的理由 -) -``` - -`resolveSystemPromptSections()` 在每轮查询时解析所有 Section,对于 `cacheBreak: false` 的 Section,优先使用 `getSystemPromptSectionCache()` 中的缓存值。只有 MCP 指令等真正动态的内容使用 `DANGEROUS_uncachedSystemPromptSection`。 - -### `CLAUDE_CODE_SIMPLE` 快速路径 - -当环境变量 `CLAUDE_CODE_SIMPLE` 为真时,整个 System Prompt 缩减为一行: - -```typescript -`You are Claude Code, Anthropic's official CLI for Claude.\n\nCWD: ${getCwd()}\nDate: ${getSessionStartDate()}` -``` - -跳过所有 Section 注册、缓存分块、动态组装——用于最小化 token 消耗的测试场景。 - -## buildEffectiveSystemPrompt():五级优先级 - -`src/utils/systemPrompt.ts:41` 决定最终使用哪个 System Prompt: - -| 优先级 | 条件 | 行为 | +| 优先级 | 来源 | 场景 | |--------|------|------| -| **0. Override** | `overrideSystemPrompt` 非空 | 完全替换,返回 `[override]` | -| **1. Coordinator** | `COORDINATOR_MODE` feature + 环境变量 | 使用协调者专用提示词 | -| **2. Agent** | `mainThreadAgentDefinition` 存在 | Proactive 模式:追加到默认提示词尾部;否则:替换默认提示词 | -| **3. Custom** | `--system-prompt` 参数指定 | 替换默认提示词 | -| **4. Default** | 无特殊条件 | 使用 `getSystemPrompt()` 完整输出 | +| **Override** | 外部覆盖 | SDK 集成、自动化测试 | +| **Coordinator** | 协调者模式 | 多 Agent 编排 | +| **Agent** | Agent 定义 | 自定义 Agent | +| **Custom** | 命令行参数 | 用户的自定义提示词 | +| **Default** | 完整组装 | 正常使用 | -`appendSystemPrompt` 始终追加到末尾(Override 除外)。 +**设计考量**:Override 级别完全替换默认提示词(包括安全规则),这是危险的但必要的——自动化测试和 SDK 集成需要完全控制。其他级别则在默认提示词基础上追加或替换。 -## Provider 系统概述 +### 第三阶段:分块与缓存标记 -Claude Code 支持多种 API 提供商,分为两大类: +分块策略根据条件选择三种模式: -| 类别 | Provider | 环境变量 | 说明 | -|------|----------|---------|------| -| **1P (First Party)** | `firstParty` | 默认 | Anthropic 官方 API 直连 | -| **3P (Third Party)** | `bedrock` | `CLAUDE_CODE_USE_BEDROCK=1` | AWS Bedrock 托管服务 | -| **3P** | `vertex` | `CLAUDE_CODE_USE_VERTEX=1` | Google Vertex AI | -| **3P** | `openai` | `CLAUDE_CODE_USE_OPENAI=1` | OpenAI 兼容层(Ollama/DeepSeek/vLLM) | -| **3P** | `gemini` | `CLAUDE_CODE_USE_GEMINI=1` | Google Gemini API | -| **3P** | `grok` | `CLAUDE_CODE_USE_GROK=1` | xAI Grok | - -Provider 决定了: -- **可用的 beta headers**:部分 beta 功能仅限 1P 用户 -- **缓存策略**:全局缓存 `scope: 'global'` 仅 1P 可用 -- **Token 计数方式**:Bedrock 有独立的 countTokens 端点,OpenAI/Gemini 依赖估算 - -```typescript -// src/utils/model/providers.ts:5-13 -export type APIProvider = - | 'firstParty' // 1P - Anthropic 直连 - | 'bedrock' // 3P - AWS Bedrock - | 'vertex' // 3P - Google Vertex - | 'foundry' // 3P - Anthropic Foundry - | 'openai' // 3P - OpenAI 兼容层 - | 'gemini' // 3P - Google Gemini - | 'grok' // 3P - xAI Grok +**模式 1:全局缓存(1P 用户默认)** +``` +[归属头] → 不缓存 +[提示词前缀] → 不缓存 +[静态内容] → 全局缓存(跨组织共享) +[动态内容] → 不缓存 ``` -## 缓存策略:分块、标记、命中 - -这是 System Prompt 设计中最精密的部分。 - -### Anthropic Prompt Cache 基础 - -Anthropic API 的 Prompt Cache 允许跨请求复用相同的 System Prompt 前缀,按缓存命中量计费(远低于完整输入价格)。缓存键由内容的 Blake2b 哈希决定——任何字符变化都会导致缓存失效。 - -### `splitSysPromptPrefix()`:三种分块模式 - -`src/utils/api.ts:321` 是缓存策略的核心,根据条件选择三种分块模式: - -#### 模式 1:MCP 工具存在时(`skipGlobalCacheForSystemPrompt=true`) - +**模式 2:组织缓存(MCP 工具存在时)** ``` -[attribution header] → cacheScope: null (不缓存) -[system prompt prefix] → cacheScope: 'org' (组织级缓存) -[everything else] → cacheScope: 'org' (组织级缓存) +[归属头] → 不缓存 +[提示词前缀] → 组织缓存 +[其余内容] → 组织缓存 ``` -MCP 工具列表在会话中可能变化(连接/断开),破坏了跨组织缓存的基础,因此降级为组织级。 - -#### 模式 2:Global Cache + Boundary 存在(1P 专用) - +**模式 3:组织缓存(3P 用户)** ``` -[attribution header] → cacheScope: null (不缓存) -[system prompt prefix] → cacheScope: null (不缓存) -[static content] → cacheScope: 'global' (全局缓存!跨组织共享) -[dynamic content] → cacheScope: null (不缓存) +[归属头] → 不缓存 +[提示词前缀] → 组织缓存 +[其余内容] → 组织缓存 ``` -这是缓存效率最高的模式。`SYSTEM_PROMPT_DYNAMIC_BOUNDARY` 之前的静态内容(Intro、Rules、Tone & Style 等)对所有用户相同,可跨组织缓存。 +**为什么 MCP 工具会降级缓存**?MCP 工具列表在会话中可能变化(连接/断开),这会改变提示词内容,破坏跨组织缓存的基础。因此当 MCP 工具存在时,只能使用组织级缓存。 -> **Boundary 插入条件**:`SYSTEM_PROMPT_DYNAMIC_BOUNDARY` 标记**仅在特定条件**下插入: +### 缓存破坏的防御 -```typescript -// src/utils/betas.ts:226-229 -export function shouldUseGlobalCacheScope(): boolean { - return ( - getAPIProvider() === 'firstParty' && - !isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS) - ) -} -``` +动态区的某些内容(如会话特定的工具集)必须严格放在分界标记之后。如果错误地放在静态区,每个运行时条件的组合会产生 2^N 种不同的哈希值(N = 条件数量),完全破坏缓存命中率。 -```typescript -// src/constants/prompts.ts:574 -...(shouldUseGlobalCacheScope() ? [SYSTEM_PROMPT_DYNAMIC_BOUNDARY] : []), -``` +系统对"每轮都重新计算"的 Section 使用专门的危险标记——开发者必须给出破坏缓存的理由才能使用它。 -这意味着: -- **3P 用户(Bedrock/Vertex/OpenAI/Gemini)**:Boundary 永远不存在,始终使用模式 3 -- **1P 用户禁用实验性功能**:设置 `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1`,Boundary 不插入 -- **1P 用户默认**:Boundary 存在,使用模式 2(最高缓存效率) +## 上下文注入:两条独立管道 -#### 模式 3:默认(3P 提供商 或 Boundary 缺失) +系统提示词本身不包含运行时上下文。上下文通过两条独立管道注入: -``` -[attribution header] → cacheScope: null (不缓存) -[system prompt prefix] → cacheScope: 'org' (组织级缓存) -[everything else] → cacheScope: 'org' (组织级缓存) -``` +### System Context(会话级不变) -### `getCacheControl()`:TTL 决策 +- Git 分支和状态 +- 最近的提交记录 +- 计算一次,整个会话期间缓存 -`src/services/api/claude.ts:348` 生成的 `cache_control` 对象: +### User Context(动态变化) -```typescript -{ - type: 'ephemeral', - ttl?: '1h', // 仅特定 querySource 符合条件时 - scope?: 'global', // 仅静态区 -} -``` +- 合并后的 CLAUDE.md 内容 +- 当前日期 -1 小时 TTL 的判定逻辑(`should1hCacheTTL()`,第 383 行): -- **Bedrock 用户**:通过环境变量 `ENABLE_PROMPT_CACHING_1H_BEDROCK` 启用 -- **1P 用户**:通过 GrowthBook 配置的 `allowlist` 数组匹配 `querySource`,支持前缀通配符(如 `"repl_main_thread*"`) -- **会话级锁定**:资格判定结果在 bootstrap state 中缓存,防止 GrowthBook 配置中途变化导致同一会话内 TTL 不一致 - -### 缓存破坏:Session-Specific Guidance 的放置 - -`getSessionSpecificGuidanceSection()`(`src/constants/prompts.ts:354`)的内容必须放在 `SYSTEM_PROMPT_DYNAMIC_BOUNDARY` **之后**。因为它包含: -- 当前会话的 enabledTools 集合 -- `isForkSubagentEnabled()` 的运行时判定 -- `getIsNonInteractiveSession()` 的结果 - -这些运行时 bit 如果放在静态区,会产生 2^N 种 Blake2b 哈希变体(N = 运行时条件数),完全破坏缓存命中率。源码注释明确警告: - -> Each conditional here is a runtime bit that would otherwise multiply the Blake2b prefix hash variants (2^N). See PR #24490, #24171 for the same bug class. - -### `CLAUDE_CODE_SIMPLE` 模式 - -当设置了 `CLAUDE_CODE_SIMPLE` 环境变量时,整个系统提示词会大幅缩减: - -```typescript -return [`You are Claude Code, Anthropic's official CLI for Claude.\n\nCWD: ${getCwd()}\nDate: ${getSessionStartDate()}`] -``` - -## 上下文注入:System Context 与 User Context - -System Prompt 数组本身不包含运行时上下文(git 状态、CLAUDE.md 内容)。上下文通过两个独立的管道注入: - -### System Context(`src/context.ts:116`) - -```typescript -export const getSystemContext = memoize(async () => { - return { - gitStatus, // git 分支、状态、最近提交(截断至 MAX_STATUS_CHARS=2000) - cacheBreaker, // 仅 ant 用户的缓存破坏器 - } -}) -``` - -- 使用 `lodash.memoize` 缓存——**整个会话期间只计算一次** -- Git 状态快照包含 5 个并行 `git` 命令(branch、defaultBranch、status、log、userName) -- `status` 超过 2000 字符时截断并附加提示使用 BashTool 获取更多信息 -- `systemPromptInjection` 变更时,通过 `getUserContext.cache.clear?.()` 清除所有上下文缓存 - -### User Context(`src/context.ts:155`) - -```typescript -export const getUserContext = memoize(async () => { - return { - claudeMd, // 合并后的 CLAUDE.md 内容 - currentDate, // "Today's date is YYYY-MM-DD." - } -}) -``` - -- **CLAUDE.md 禁用条件**:`CLAUDE_CODE_DISABLE_CLAUDE_MDS` 环境变量,或 `--bare` 模式(除非通过 `--add-dir` 显式指定目录) -- `--bare` 模式的语义是"跳过我没要求的东西"而非"忽略所有" - -### 注入位置 - -在 `src/query.ts:449`: - -```typescript -// System Context 追加到 System Prompt 尾部 -const fullSystemPrompt = asSystemPrompt( - appendSystemContext(systemPrompt, systemContext) // 简单拼接 -) -``` - -User Context 通过 `prependUserContext()`(`src/utils/api.ts:449`)注入为 `` 标签包裹的首条用户消息,放在所有对话消息之前。 - -## Attribution Header:计费与安全 - -每个 API 请求的 System Prompt 首块是 Attribution Header(`src/constants/system.ts:30`),包含: -- **`cc_version`**:Claude Code 版本 + 指纹 -- **`cc_entrypoint`**:入口点标识(REPL / SDK / pipe 等) -- **`cch=00000`**(NATIVE_CLIENT_ATTESTATION 启用时):Bun 原生 HTTP 层在发送前将零替换为计算出的哈希值,服务器验证此 token 确认请求来自真实 Claude Code 客户端 - -Header 始终 `cacheScope: null`——它因版本和指纹不同而变化,不适合缓存。 +**为什么 User Context 不放在 System Prompt 中**?因为 User Context 作为用户消息发送,可以利用 Prompt Cache 的前缀共享——系统提示词是所有用户共享的前缀,User Context 是每个用户的变化部分。这种分层让缓存命中率最大化。 ## CLAUDE.md:项目级知识注入 -这是 Claude Code 最巧妙的设计之一。在项目根目录放一个 `CLAUDE.md` 文件,就能让 AI "理解" 你的项目: +这是 Claude Code 最巧妙的设计之一。在项目目录中放一个 `CLAUDE.md` 文件,就能让 AI "理解"项目。 -- **项目概述**:这个项目做什么、用了什么技术栈 -- **开发约定**:代码风格、命名规范、分支策略 -- **常用命令**:怎么构建、怎么测试、怎么部署 -- **注意事项**:已知的坑、特殊的配置 - -系统会自动发现并合并多级 CLAUDE.md: +### 多级合并 ``` ~/.claude/CLAUDE.md ← 用户全局(个人偏好) @@ -292,77 +118,34 @@ Header 始终 `cacheScope: null`——它因版本和指纹不同而变化,不 └── /project/src/CLAUDE.md ← 子目录(模块特定) ``` -加载逻辑在 `src/utils/claudemd.ts` 中的 `getClaudeMds()` 和 `getMemoryFiles()` 实现——从 CWD 向上遍历目录树,合并所有匹配的 CLAUDE.md 文件内容。 +系统从当前工作目录向上遍历,合并所有匹配的 CLAUDE.md 文件。子目录的 CLAUDE.md 可以覆盖或补充父目录的规则。 -## 设计洞察:为什么是 `string[]` 而非单个 `string` +**设计哲学**:项目知识应该由团队成员维护、随代码演进、可通过 git 追踪。CLAUDE.md 本质上是"给人读的项目文档,恰好 AI 也能读"。 -将 System Prompt 设计为数组而非单段文本,是为了 **缓存分块**: +### 安全考量 -1. Anthropic Prompt Cache 以 **内容块**(TextBlock)为缓存单位 -2. 将 System Prompt 拆为多个块,可以让不变的部分(Intro、Rules)获得独立的缓存命中 -3. 如果是单个 `string`,任何一个字符变化(如日期更新)都会导致整个 System Prompt 的缓存失效 -4. `SYSTEM_PROMPT_DYNAMIC_BOUNDARY` 标记允许 `splitSysPromptPrefix()` 精确地将静态区标记为 `scope: 'global'`,动态区不标记或标记为 `scope: 'org'` +项目级 CLAUDE.md 可以被仓库中的任何人修改(包括恶意贡献者)。系统因此限制了项目级 CLAUDE.md 的影响范围——它们不能覆盖安全关键设置,也不能修改权限模型。 -这是 Claude Code 在 token 成本优化上的核心设计——一次典型的 System Prompt 约 20K+ tokens,通过缓存分块可以节省 30-50% 的输入 token 费用。 +## Provider 差异与缓存 -## 兼容层:OpenAI 与 Gemini +不同的 API Provider 有不同的缓存能力: -Claude Code 提供了 OpenAI 和 Gemini 协议的兼容层,允许使用非 Anthropic 端点。 +| Provider | 全局缓存 | 精确 Token 计数 | 特殊 Beta 功能 | +|----------|:--------:|:---------------:|:--------------:| +| Anthropic 直连 | ✓ | ✓ | ✓ | +| AWS Bedrock | ✗ | ✓(独立端点) | 部分 | +| Google Vertex | ✗ | ✗ | 部分 | +| OpenAI 兼容 | ✗ | ✗ | ✗ | +| Gemini | ✗ | ✗ | ✗ | -### OpenAI 兼容层 +3P 用户的系统提示词始终使用组织级缓存,因为没有全局缓存的 API 支持。这也意味着 token 计数依赖估算,影响自动压缩的触发时机。 -通过 `CLAUDE_CODE_USE_OPENAI=1` 启用,支持任意 OpenAI Chat Completions 协议端点(Ollama、DeepSeek、vLLM 等)。 +## 最小化模式 -实现采用**流适配器模式**: -1. 将 Anthropic 格式请求转换为 OpenAI 格式 -2. 调用 OpenAI 兼容端点 -3. 将 SSE 流转换回 `BetaRawMessageStreamEvent` -4. 下游代码完全无感知 +环境变量 `CLAUDE_CODE_SIMPLE` 可以将整个系统提示词缩减为一行——跳过所有 Section 注册、缓存分块和动态组装。用于最小化 token 消耗的测试场景。 -``` -src/services/api/openai/ -├── client.ts # OpenAI 客户端配置 -├── convertMessages.ts # 消息格式转换(Anthropic → OpenAI) -├── convertTools.ts # 工具定义转换 -├── streamAdapter.ts # SSE 流适配(OpenAI → Anthropic) -├── modelMapping.ts # 模型名称映射 -└── index.ts # 入口函数 queryModelOpenAI() -``` - -关键环境变量: -- `CLAUDE_CODE_USE_OPENAI=1` — 启用 OpenAI provider -- `OPENAI_API_KEY` — API 密钥 -- `OPENAI_BASE_URL` — API 端点(默认 `https://api.openai.com/v1`) -- `OPENAI_MODEL` — 直接指定模型名 - -### Gemini 兼容层 - -通过 `CLAUDE_CODE_USE_GEMINI=1` 启用,支持 Google Gemini API。 - -``` -src/services/api/gemini/ -├── client.ts # Gemini 客户端配置 -├── convertMessages.ts # 消息格式转换 -├── convertTools.ts # 工具定义转换 -├── streamAdapter.ts # 流适配 -├── modelMapping.ts # 模型名称映射 -├── types.ts # 类型定义 -└── index.ts # 入口函数 -``` - -关键环境变量: -- `CLAUDE_CODE_USE_GEMINI=1` — 启用 Gemini provider -- `GEMINI_API_KEY` — API 密钥 -- `GEMINI_BASE_URL` — API 端点(默认 `https://generativelanguage.googleapis.com/v1beta`) -- `GEMINI_MODEL` — 直接指定模型名 -- `GEMINI_DEFAULT_SONNET_MODEL` / `GEMINI_DEFAULT_OPUS_MODEL` — 按能力级别映射 - -### 兼容层的限制 - -使用 3P 兼容层时,部分功能受限: -- **无精确 token 计数**:系统退回到近似估算,影响自动压缩触发时机 -- **无全局缓存**:只能使用组织级缓存 `scope: 'org'` -- **部分 beta 功能不可用**:依赖 Anthropic 特有 beta headers 的功能受限 - -详见 `docs/plans/openai-compatibility.md` 和 `CLAUDE.md` 中的相关章节。 +## 接下来 +- **上下文压缩** — 理解当上下文增长超出限制时的压缩策略 +- **令牌预算** — 了解 token 窗口的动态计算 +- **Provider 系统** — 了解多 Provider 支持的架构设计