InstructionsLoaded:角色与约束的动态注入
在 Claude Code 的启动或重置过程中,InstructionsLoaded 钩子扮演着“灵魂注入”的角色。它允许开发者根据当前项目、环境或任务,动态地为模型生成系统指令(System Instructions)。
先搞清楚这是什么
InstructionsLoaded 本质上是 系统 Prompt 的动态生成器(System Prompt Generator)。
它解决了静态指令文件的局限性。虽然 CLAUDE.md 可以提供项目规范,但 InstructionsLoaded 可以提供更具时效性的约束。它不是对对话内容的拦截(那是 UserPromptSubmit 的职责),它专注于“你现在应该扮演什么角色”以及“你应该遵循哪些特殊的临时规则”。
例如:根据当前是星期几、当前所在的 Git 分支名、或者是当前服务器的负载状态,动态地向模型解释当前的执行上下文。
代码里的真实逻辑
该钩子的运行逻辑紧密结合了系统的初始化流程:
- 触发点:系统在每次会话开始、上下文压缩(Compact)完成或手动触发重载时,会重新加载所有指令源。在
CLAUDE.md等静态文件读取完毕后,系统派发InstructionsLoaded事件。 - 输入上下文:钩子接收到
InstructionsLoadedHookInput,其中包含了目前已加载的所有静态指令内容。 - 动态增强(Augmentation): 钩子可以利用其 Shell 执行能力,实时获取外部信息(如调用 API、读取环境变量),然后通过
HookJSONOutput回传:- 追加指令:向模型的 System Prompt 中添加一段自定义的文字。
- 覆盖约束:通过更精确的提示词,微调模型在当前会话中的行为边界。
- 注入时机(Pre-inference): 注入的指令在首轮用户 Prompt 发送前就已经合并到了 System Prompt 视图中。这意味着模型在“睁眼”的第一时间,就已经被这些动态规则所约束。
踩坑指南
- Token 成本:动态注入的每一行指令都会持续占据上下文窗口。应避免在钩子中注入大量冗余的文档内容。
- 非实时性:一旦指令加载完成并开启了对话,后续的
InstructionsLoaded只有在触发/compact或重启会话时才会再次运行。它不是针对每一轮对话都重新生成的。 - 优先级:通常作为静态指令的补充。
CLAUDE.md存放长期不变的规范,InstructionsLoaded存放随环境变化的上下文。 - 与 SessionStart 的关系:
SessionStart通常用于注入消息流,而InstructionsLoaded专门用于注入 System Instructions。
接下来看什么
- 如果你想了解静态指令文件的加载规则,请看 项目指令加载协议:CLAUDE.md 与路径规则。
- 如果你关心启动时的其他注入点,请看 会话钩子:SessionStart 与 SessionStop。
- 如果你想了解这些指令如何被模型看到,请看 上下文窗口管理:装配、补载与压缩。
源码锚点
claude-code-opensource/src/utils/claudemd.ts— 指令加载的主逻辑及其对InstructionsLoaded的集成。
📄 src/utils/claudemd.ts — 指令加载的主逻辑及其对 `InstructionsLoaded` 的集成。
typescript
executeInstructionsLoadedHooks,
hasInstructionsLoadedHook,
type InstructionsLoadReason,
type InstructionsMemoryType,
} from './hooks.js'
import type { MemoryType } from './memory/types.js'claude-code-opensource/src/types/hooks.ts—InstructionsLoadedHookInput结构定义。
📄 src/types/hooks.ts — `InstructionsLoadedHookInput` 结构定义。
typescript
export function isHookEvent(value: string): value is HookEvent {
return HOOK_EVENTS.includes(value as HookEvent)
}