跳转至

2.6 Attachment System

本节摘要

Attachment System 是 Claude Code 中 system prompt 和对话消息之外的第三条状态注入通道。它在每个用户 turn 动态计算 30+ 类 Context 信息(Skill 列表、文件变更、Tool 变更、任务提醒、token 用量等),以 <system-reminder> 标签包裹的 user message 形式注入对话。这是 Claude Code 中变化最频繁的状态注入机制——与会话级缓存的 system prompt 形成互补。


一、为什么需要 Attachment System

Claude Code 的状态注入有三个通道,各自服务于不同的变化频率:

通道 变化频率 缓存行为 典型内容
System Prompt 会话级(几乎不变) 静态区域跨组织缓存 身份、规则、安全指令
Tool Definitions 会话级(偶尔变化) 随 system prompt 一起发送 Tool Schema
Attachment(<system-reminder> 每 turn 变化 不缓存,每次重算 Skill 列表、文件变更、任务提醒

System prompt 追求稳定以命中 Prompt Cache;Attachment System 则承担所有需要每 turn 更新的动态状态,且不破坏 system prompt 的缓存。


二、触发时机与调用点

Attachment System 在两个时机被调用:

时机 1:用户输入时(Turn 开始)

用户发送消息后、LLM 被调用前,getAttachmentMessages(input, ...) 计算初始 attachment 集合。此时 input 非空,可以触发用户输入相关的 attachment(如 @-mention 文件解析、Skill 发现)。

时机 2:Tool 执行后(Turn 中间)

每次 LLM 返回 tool call 并执行完毕后,在下一轮 API 调用前再次运行 getAttachmentMessages(null, ...)query.ts:1580)。此时文件变更、任务状态、动态 Skill 等可能因 Tool 执行而发生变化。

一个 turn 内可能触发多次

一个 agentic turn 可能包含多轮 tool call。每轮执行后都会重新计算 attachment——因此 Skill 列表、文件变更通知等会在 turn 内增量更新


三、完整 Attachment 类型清单

getAttachments()attachments.ts:743)并行计算三组 attachment,涵盖 30+ 类型:

第一组:用户输入相关(仅 Turn 开始时)

类型 触发条件 注入内容 影响的组件
at_mentioned_files 用户 @文件名 文件内容 Context
mcp_resources 用户 @MCP 资源 MCP 资源内容 Context
agent_mentions 用户 @agent 名 Agent 信息 Sub-agent
skill_discovery Turn 0 用户输入 发现的相关 Skill Skill

第二组:全线程通用(每次 Tool 执行后都计算)

类型 触发条件 注入内容 影响的组件
skill_listing SkillTool 可用且有新 Skill Skill 列表(增量) Skill
dynamic_skill 文件操作发现新 Skill 目录 新 Skill 名称(仅 UI) Skill
changed_files 自上次 turn 以来文件被修改 文件变更摘要 Context
nested_memory CWD 下有嵌套 CLAUDE.md 嵌套目录的 Memory 文件 Memory
deferred_tools_delta Tool 集发生延迟变更 新增或移除的 Tool 名 Tool
agent_listing_delta 可用 Agent 列表变化 新增或移除的 Agent Sub-agent
mcp_instructions_delta MCP 服务器指令变化 新增或移除的 MCP 指令 Tool
queued_commands 有排队的命令或通知 命令内容 Context
date_change 日期跨天 新日期 Context
ultrathink_effort 用户触发深度思考 effort 级别 Context
plan_mode 进入或退出 Plan 模式 模式指令 Context
auto_mode 进入或退出 Auto 模式 模式指令 Context
todo_reminders N 轮未更新 todo 当前待办列表 Task
teammate_mailbox 有未读团队消息 消息内容 Sub-agent
team_context Swarm 模式下 团队信息 Sub-agent
agent_pending_messages Sub-agent 有待处理消息 消息内容 Sub-agent
critical_system_reminder 有紧急系统提醒 提醒内容 Context
compaction_reminder Context 接近满 压缩状态 Context
companion_intro Buddy 模式首次出现 伴侣介绍 Context

第三组:主线程独占(Sub-agent 中不计算)

类型 触发条件 注入内容 影响的组件
ide_selection IDE 有选中文本 选中行内容 Context
ide_opened_file IDE 有打开文件 文件路径 Context
output_style 有输出风格配置 风格指令 Context
diagnostics 有诊断信息 错误或警告 Context
lsp_diagnostics LSP 有诊断 LSP 错误 Context
unified_tasks 有后台任务 任务状态 Task
token_usage 每 turn 已用或剩余 token Context
budget_usd 有预算限制 已用或剩余预算 Context
output_token_usage 每 turn 输出 token 用量 Context
verify_plan_reminder 有待验证的计划 验证提醒 Task

四、序列化机制

步骤 1:计算

getAttachments() 并行计算所有 attachment,每个通过 maybe(name, fn) 包裹——如果 fn 抛异常或超时(1 秒),该 attachment 被安全跳过。

步骤 2:序列化

normalizeAttachmentForAPI(attachment) 将每个 attachment 转化为 isMeta: true 的 user message,用 wrapInSystemReminder() 包裹:

function wrapInSystemReminder(content: string): string {
  return `<system-reminder>\n${content}\n</system-reminder>`
}

步骤 3:合并

attachment message 不是独立消息——在 normalizeMessagesForAPI 中被合并进相邻的 user messagemergeUserMessages)。smooshSystemReminderSiblings 进一步将 <system-reminder> 文本折叠到 tool_result 旁边。最终 LLM 看到的是一条 user message 中混合了真实用户输入和多个 <system-reminder> 块。

步骤 4:模型感知

System prompt 静态区域中告知模型注意这些标签:

Tool results and user messages may include <system-reminder> tags. Tags contain information from the system. They bear no direct relation to the specific tool results or user messages in which they appear.


五、增量更新(Delta 语义)

部分 attachment 采用增量语义——只发送自上次以来的变化:

Attachment Delta 机制 追踪方式
skill_listing 只发新增的 Skill sentSkillNames: Map<agentKey, Set<name>>
deferred_tools_delta 只发新增或移除的 Tool getDeferredToolsDelta() 比较前后快照
agent_listing_delta 只发新增或移除的 Agent 比较前后快照
mcp_instructions_delta 只发新增或移除的 MCP 指令 getMcpInstructionsDelta()

频率控制

部分 attachment 有稀疏化策略(不是每 turn 都发完整内容):

配置 说明
TODO_REMINDER_CONFIG.TURNS_SINCE_WRITE 10 10 轮未写 todo 才提醒
TODO_REMINDER_CONFIG.TURNS_BETWEEN_REMINDERS 10 两次提醒间隔至少 10 轮
PLAN_MODE_ATTACHMENT_CONFIG.TURNS_BETWEEN_ATTACHMENTS 5 Plan 模式每 5 轮提醒
PLAN_MODE_ATTACHMENT_CONFIG.FULL_REMINDER_EVERY_N_ATTACHMENTS 5 每 5 次中有 1 次是完整版

六、对其他组件的影响

对 Context Management 的影响

  • 占用 Context Window:attachment 序列化后成为 messages 的一部分,占用 token
  • 触发压缩:大量 attachment 会加速 Context Window 消耗,间接触发 autocompact
  • 压缩后丢失与恢复:attachment 内容在压缩时被摘要替代;部分关键信息通过 postCompactCleanup 恢复(如 invoked_skills 重新注入已加载的 Skill 内容)

对 Memory System 的影响

  • nested_memory 将嵌套目录的 CLAUDE.md 内容注入对话
  • relevant_memories 将 Sonnet 召回的 Memory 文件注入对话
  • changed_files 通知模型文件已变化(可能使之前读取的 Memory 过时)

对 Tool 和 Skill 的影响

  • deferred_tools_delta 通知模型 Tool 集的变化
  • mcp_instructions_delta 通知模型 MCP 服务器指令的变化
  • skill_listing 是 Skill 列表到达 LLM 的唯一通道

对 Sub-agent 的影响

  • Sub-agent 共享第二组(全线程通用)attachment,但不获取第三组(主线程独占)
  • agent_listing_delta 通知 Agent 可用列表变化

对 Prompt Cache 的影响

  • 不破坏 Prompt Cache:attachment 位于 user message 中,不在 system prompt 静态区域内

关键文件索引

文件 职责
src/utils/attachments.ts 核心文件(3,998 行):30+ 类 attachment 的计算逻辑
src/utils/messages.ts normalizeAttachmentForAPI() 序列化、wrapInSystemReminder()
src/query.ts 两个调用点:用户输入后 + Tool 执行后
src/services/compact/compact.ts 压缩后 attachment 恢复
src/tools/SkillTool/prompt.ts formatCommandsWithinBudget() Skill listing 格式化