第四章:Claude Code CLI中的CLAUDE.md 系统、团队记忆安全、配置开关与最佳实践

第四章:Claude Code CLI中的CLAUDE.md 系统、团队记忆安全、配置开关与最佳实践

  • [第四章:Claude Code CLI中的CLAUDE.md 系统、团队记忆安全、配置开关与最佳实践](#第四章:Claude Code CLI中的CLAUDE.md 系统、团队记忆安全、配置开关与最佳实践)
    • [一、CLAUDE.md 系统:静态指令层](#一、CLAUDE.md 系统:静态指令层)
      • [1.1 两类记忆的互补关系](#1.1 两类记忆的互补关系)
      • [1.2 四层文件结构](#1.2 四层文件结构)
      • [1.3 `@include` 指令](#1.3 @include 指令)
      • [1.4 `getMemoryFiles()` 聚合接口](#1.4 getMemoryFiles() 聚合接口)
    • 二、团队记忆:协作场景下的记忆共享
      • [2.1 目录结构](#2.1 目录结构)
      • [2.2 路径安全防护体系](#2.2 路径安全防护体系)
        • [第一层:`sanitizePathKey()` --- 路径键净化](#第一层:sanitizePathKey() — 路径键净化)
        • [第二层:`realpathDeepestExisting()` --- 符号链接逃逸检测](#第二层:realpathDeepestExisting() — 符号链接逃逸检测)
        • 第三层:前缀攻击防护
    • [三、启用控制:`isAutoMemoryEnabled()` 完整优先级链](#三、启用控制:isAutoMemoryEnabled() 完整优先级链)
      • [3.1 特性门控汇总](#3.1 特性门控汇总)
      • [3.2 settings.json 配置](#3.2 settings.json 配置)
      • [3.3 环境变量汇总](#3.3 环境变量汇总)
    • [四、/memory 命令与用户界面](#四、/memory 命令与用户界面)
    • 五、最佳实践:来自源码设计理念的指导
      • [5.1 记忆文件质量指南](#5.1 记忆文件质量指南)
      • [5.2 MEMORY.md 索引维护原则](#5.2 MEMORY.md 索引维护原则)
      • [5.3 四种类型的典型保存时机](#5.3 四种类型的典型保存时机)
      • [5.4 description 字段的重要性](#5.4 description 字段的重要性)
      • [5.5 `project` 类型的时效性管理](#5.5 project 类型的时效性管理)
    • 六、系列总结

第四章:Claude Code CLI中的CLAUDE.md 系统、团队记忆安全、配置开关与最佳实践

系列说明:本章涵盖记忆系统的剩余核心模块------静态指令文件 CLAUDE.md 的四层架构、团队记忆的路径安全防护体系、记忆功能的配置与开关机制,以及基于源码设计理念总结的最佳实践。


一、CLAUDE.md 系统:静态指令层

源文件:utils/claudemd.ts

1.1 两类记忆的互补关系

Claude Code 有两种本质不同的记忆机制:

类型 存储内容 更新方式 生命周期
自动记忆(memdir/) 动态积累的对话上下文 AI 自动写入 永久,跨会话
CLAUDE.md 系统 静态的项目/用户指令 人工手动维护 版本控制管理

两者互补------自动记忆无法替代 CLAUDE.md,因为 CLAUDE.md 的内容是需要人工审核和刻意维护的规范文档,而不是对话中自然流露的上下文。

1.2 四层文件结构

系统按优先级处理四种 CLAUDE.md 文件:
Managed

/etc/claude-code/CLAUDE.md

全局策略,不可覆盖
User

~/.claude/CLAUDE.md

跨项目的用户全局偏好
Project

CLAUDE.md / .claude/CLAUDE.md

.claude/rules/*.md

提交到代码库的项目规范
Local

CLAUDE.local.md

不提交的个人项目偏好

层级 位置 典型用途 是否提交 Git
Managed /etc/claude-code/CLAUDE.md 企业/组织强制策略 不适用(系统目录)
User ~/.claude/CLAUDE.md 个人全局偏好(如语言偏好)
Project CLAUDE.md / .claude/CLAUDE.md 项目编码规范、架构决策
Local CLAUDE.local.md 个人对该项目的私有偏好 否(加入 .gitignore)

1.3 @include 指令

CLAUDE.md 支持引入外部文件,避免在多处维护相同规范:

markdown 复制代码
@include ./docs/coding-standards.md
@include ./deployment-rules.md
@include ./api-conventions.json

支持的文件类型.md.txt.json.yaml.ts.js 等文本格式

限制 :单个被包含文件最大 40,000 字符MAX_MEMORY_CHARACTER_COUNT),防止误包含大型生成文件。

1.4 getMemoryFiles() 聚合接口

typescript 复制代码
// utils/claudemd.ts
export const getMemoryFiles = memoize(
  async (forceIncludeExternal?: boolean): Promise<MemoryFileInfo[]>
)

interface MemoryFileInfo {
  path: string
  type: MemoryType           // managed | user | project | local
  content: string
  description?: string
  parent?: string
  isNested?: boolean         // 是否通过 @include 引入
  exists: boolean
}

该函数被 memoize,因为它在渲染循环中频繁调用(每次工具调用后的消息重渲染),缓存避免了重复文件 I/O。


二、团队记忆:协作场景下的记忆共享

源文件:memdir/teamMemPaths.ts

2.1 目录结构

TEAMMEM 特性门控启用时,记忆系统扩展为双目录:

复制代码
~/.claude/projects/<hash>/memory/
├── MEMORY.md                    ← 个人记忆索引
├── user_role.md
├── feedback_response_style.md
└── team/                        ← 团队记忆子目录
    ├── MEMORY.md                ← 团队记忆索引
    ├── feedback_testing_policy.md
    ├── project_auth_rewrite.md
    └── ref_linear_ingest.md
typescript 复制代码
// 团队记忆路径 = 自动记忆路径 + "team/"
export function getTeamMemPath(): string {
  return (join(getAutoMemPath(), 'team') + sep).normalize('NFC')
}

// 团队记忆依赖自动记忆:两者同时启用才有效
export function isTeamMemoryEnabled(): boolean {
  if (!isAutoMemoryEnabled()) {
    return false
  }
  return getFeatureValue_CACHED_MAY_BE_STALE('tengu_herring_clock', false)
}

2.2 路径安全防护体系

团队记忆面临特殊的安全威胁:团队成员(或恶意的 Git 仓库内容)可能构造特殊路径,试图写入 ~/.ssh/ 等敏感目录。

源码实现了多层防护teamMemPaths.ts):

第一层:sanitizePathKey() --- 路径键净化
typescript 复制代码
function sanitizePathKey(key: string): string {
  // 防护 1:空字节(可在 C 系统调用中截断路径)
  if (key.includes('\0')) {
    throw new PathTraversalError(`Null byte in path key: "${key}"`)
  }

  // 防护 2:URL 编码遍历(%2e%2e%2f = ../)
  let decoded = decodeURIComponent(key)
  if (decoded !== key && (decoded.includes('..') || decoded.includes('/'))) {
    throw new PathTraversalError(`URL-encoded traversal in path key: "${key}"`)
  }

  // 防护 3:Unicode 规范化攻击
  // 全角字符 ../ (U+FF0E U+FF0F) 在 NFKC 下规范化为 ASCII ../
  const normalized = key.normalize('NFKC')
  if (normalized !== key && (normalized.includes('..') || normalized.includes('/'))) {
    throw new PathTraversalError(`Unicode-normalized traversal in path key: "${key}"`)
  }

  // 防护 4:Windows 反斜杠(常被用作路径分隔符遍历向量)
  if (key.includes('\\')) {
    throw new PathTraversalError(`Backslash in path key: "${key}"`)
  }

  // 防护 5:绝对路径直接拒绝
  if (key.startsWith('/')) {
    throw new PathTraversalError(`Absolute path key: "${key}"`)
  }

  return key
}
第二层:realpathDeepestExisting() --- 符号链接逃逸检测

这是最精细的防护,来自 PSR M22186 安全审查:

typescript 复制代码
async function realpathDeepestExisting(absolutePath: string): Promise<string> {
  // path.resolve() 不解析符号链接!
  // 攻击者可在 teamDir 内放一个指向 ~/.ssh 的符号链接,
  // 通过 resolve() 的包含性检查,但实际写入目标在 teamDir 外。
  //
  // 解决方案:对最深已存在的祖先目录调用 realpath(),
  // 比较真实的文件系统位置,而非符号路径。

  const tail: string[] = []
  let current = absolutePath

  for (let parent = dirname(current); current !== parent; parent = dirname(current)) {
    try {
      const realCurrent = await realpath(current)
      return tail.length === 0 ? realCurrent : join(realCurrent, ...tail.reverse())
    } catch (e) {
      if (code === 'ENOENT') {
        // 检查是否是悬空符号链接(目标不存在但链接本身存在)
        const st = await lstat(current)
        if (st.isSymbolicLink()) {
          throw new PathTraversalError(`Dangling symlink detected: "${current}"`)
        }
      } else if (code === 'ELOOP') {
        throw new PathTraversalError(`Symlink loop detected: "${current}"`)
      }
      tail.push(...)
      current = parent
    }
  }
}

为什么要处理悬空符号链接? 攻击者可以创建一个目标不存在的符号链接(例如指向 ~/.ssh/injected_key),然后通过写入触发文件创建------writeFile 会跟随链接在目标位置创建文件。lstat() 能区分"真正不存在"和"悬空链接",后者必须拒绝。

第三层:前缀攻击防护
typescript 复制代码
// 不能只用 startsWith,因为 "/foo/team-evil" 也以 "/foo/team" 开头
return realCandidate.startsWith(realTeamDir + sep)  // 必须加路径分隔符

三、启用控制:isAutoMemoryEnabled() 完整优先级链

源文件:memdir/paths.ts

typescript 复制代码
export function isAutoMemoryEnabled(): boolean {
  // ① 环境变量显式控制(最高优先级)
  const envVal = process.env.CLAUDE_CODE_DISABLE_AUTO_MEMORY
  if (isEnvTruthy(envVal)) return false     // =1/true → 禁用
  if (isEnvDefinedFalsy(envVal)) return true // =0/false → 强制启用

  // ② --bare / SIMPLE 模式(精简模式)
  if (isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)) return false

  // ③ CCR(Cloud Code Runner)模式但无持久存储
  if (isEnvTruthy(process.env.CLAUDE_CODE_REMOTE) &&
      !process.env.CLAUDE_CODE_REMOTE_MEMORY_DIR) {
    return false
  }

  // ④ settings.json 配置(项目/用户/本地设置)
  const settings = getInitialSettings()
  if (settings.autoMemoryEnabled !== undefined) {
    return settings.autoMemoryEnabled
  }

  // ⑤ 默认:启用
  return true
}

可视化为流程图:
=1/true
=0/false
未设置




false
true
未设置
isAutoMemoryEnabled 调用
CLAUDE_CODE_DISABLE_AUTO_MEMORY 已设置?
❌ 禁用
✅ 强制启用
CLAUDE_CODE_SIMPLE=1?
❌ 禁用
CLAUDE_CODE_REMOTE=1 且无 MEMORY_DIR?
❌ 禁用
settings.json 有 autoMemoryEnabled?
❌ 禁用
✅ 启用
✅ 默认启用

3.1 特性门控汇总

系统通过 GrowthBook 管理多个实验性功能:

门控名称 功能 默认值
tengu_passport_quail 后台抽取代理(EXTRACT_MEMORIES) false
tengu_herring_clock 团队记忆(TEAMMEM) false
tengu_session_memory 会话记忆 false
KAIROS 日志追加模式(长期会话) false
tengu_coral_fern 搜索历史上下文功能 false
tengu_moth_copse skipIndex 模式 false
tengu_bramble_lintel 抽取节流间隔(N轮一次) null(=1)
MEMORY_SHAPE_TELEMETRY 记忆召回形状遥测 false

3.2 settings.json 配置

json 复制代码
{
  "autoMemoryEnabled": false,
  "autoMemoryDirectory": "~/my-custom-memory"
}

安全约束projectSettings.claude/settings.json,提交到代码库)不允许 设置 autoMemoryDirectory

typescript 复制代码
function getAutoMemPathSetting(): string | undefined {
  // 刻意排除了 projectSettings!
  const dir =
    getSettingsForSource('policySettings')?.autoMemoryDirectory ??
    getSettingsForSource('flagSettings')?.autoMemoryDirectory ??
    getSettingsForSource('localSettings')?.autoMemoryDirectory ??
    getSettingsForSource('userSettings')?.autoMemoryDirectory
  return validateMemoryPath(dir, true)
}

原因:恶意仓库可设置 "autoMemoryDirectory": "~/.ssh",让 AI 将记忆文件写入 SSH 密钥目录,绕过危险目录写保护。

3.3 环境变量汇总

bash 复制代码
# 禁用/启用自动记忆
CLAUDE_CODE_DISABLE_AUTO_MEMORY=1

# 精简模式(同时禁用记忆)
CLAUDE_CODE_SIMPLE=1

# Cowork 专用:覆盖记忆目录(绝对路径,不支持 ~/)
CLAUDE_COWORK_MEMORY_PATH_OVERRIDE=/cowork/shared/memory

# 远程模式的持久化挂载点
CLAUDE_CODE_REMOTE_MEMORY_DIR=/mount/persistent

# 向记忆提示注入额外策略文本
CLAUDE_COWORK_MEMORY_EXTRA_GUIDELINES="Always save in English."

四、/memory 命令与用户界面

源文件:commands/memory/memory.tsx

typescript 复制代码
export const call: LocalJSXCommandCall = async onDone => {
  clearMemoryFileCaches()         // 清除 memoize 缓存,确保读到最新文件
  await getMemoryFiles()          // 预加载记忆文件列表
  return <MemoryCommand onDone={onDone} />
}

用户交互流程
EDITOR MemoryFileSelector /memory 命令 用户 EDITOR MemoryFileSelector /memory 命令 用户 显示:User / Project / Local Auto memory / Agent memory Team memory 如文件不存在则自动创建 输入 /memory clearMemoryFileCaches() 展示文件选择器 选择要编辑的文件 在 EDITOR/VISUAL 中打开 编辑保存 onDone()

/remember 技能(skills/bundled/remember.ts)提供更高层的整理功能:

  1. 收集所有层的记忆内容(CLAUDE.md + auto-memory + team)
  2. 分析每条记忆的最佳目标位置
  3. 识别重复、过时、互相冲突的条目
  4. 生成结构化整理报告,供用户审批后执行

五、最佳实践:来自源码设计理念的指导

5.1 记忆文件质量指南

高质量的 feedback 记忆

markdown 复制代码
---
name: 集成测试策略:禁止 mock 数据库
description: 所有涉及数据库的测试必须连接真实 DB,禁止使用 mock
type: feedback
---

集成测试必须连接真实数据库,禁止使用 mock。

**Why:** 上季度 mock 测试全部通过,但 prod 数据库迁移失败------mock 与真实 DB 
的行为差异被完全掩盖,导致严重线上事故。

**How to apply:** 所有涉及数据库操作的测试,无论是单元测试还是集成测试粒度,
都必须使用真实 DB 连接。测试数据库连接字符串在 `test.env` 中配置。

错误示例(不该保存的内容)

markdown 复制代码
# ❌ 这些不应该保存为记忆文件:

- "项目使用 React 18.3 和 TypeScript 5.4" → 读 package.json
- "getUserById 函数在 src/db/users.ts 第 42 行" → 会过期,用 grep
- "上周修复了 auth bug,commit abc123" → 读 git log
- "目前正在实现 feature/payment-refactor 分支" → 临时状态

5.2 MEMORY.md 索引维护原则

复制代码
原则                    说明
──────────────────────────────────────────────────────────────
每行 ≤ 150 字符         超长行会触发字节截断而非行数截断
语义分组,非时间顺序     相关记忆放在一起,便于 Sonnet 批量召回
定期删除过时条目         过时记忆比没有记忆更危险(会被信任)
description 要具体       模糊描述会降低语义召回准确率

5.3 四种类型的典型保存时机

不值得保存
读 package.json
读 git log
临时状态
'项目用 Node 20'

'修了登录 bug'

'正在写测试'

值得保存
user
feedback
project
reference
'我是 ML 工程师,

第一次做前端'
user_role.md
'不要总结 diff,

我能看懂'
feedback_style.md
'周四前必须发版,

法务截止日期'
project_deadline.md
'Linear 项目 BUGS

追踪后端 bug'
ref_linear.md

5.4 description 字段的重要性

对语义召回的影响 :Sonnet 在选择召回哪些记忆时,只能看到 frontmatter(前 30 行),不会读取记忆正文。description 是召回决策的核心依据。

description 质量 示例 召回效果
过于模糊 "关于测试的记忆"
一般 "测试策略"
具体清晰 "禁止在数据库测试中使用 mock,有线上事故背景"

5.5 project 类型的时效性管理

project 类型记忆衰减最快------项目状态、截止日期、人员分工随时变化。最佳实践:

  1. 保存时使用绝对日期"2026-06-01 发版截止" 而非 "下个月发版"
  2. 定期用 /remember 清理过期的 project 记忆
  3. 在正文中添加 **How to apply:**:帮助判断该记忆是否仍然 load-bearing

六、系列总结

四章完整剖析了 Claude Code 记忆系统的核心设计:
记忆系统
类型系统
4种闭合类型
只保存不可推导的上下文
feedback记录正负向
存储层
索引+内容
Git根目录作key
双重截断防护
语义召回
Sonnet做相关性判断
只读frontmatter前30行
新鲜度警告机制
后台抽取
forked agent共享cache
主/后台代理互斥
trailing run防丢失
CLAUDE.md
四层静态指令
@include指令
人工维护规范
团队记忆
多层路径安全防护
符号链接逃逸检测
Unicode规范化攻击防护
配置
5级优先级链
projectSettings禁止路径配置
特性门控管理实验功能

核心设计决策 原因
文件即记忆(Markdown) 人类可读、Git 友好、工具无关
两层存储(索引+内容) 平衡 token 效率和信息完整性
AI 辅助语义召回 比关键词匹配智能,比向量数据库简单
闭合四类型系统 防止退化为低信息密度日志
安全优先(团队记忆) 设计阶段考虑攻击面,不是事后补丁
提前预创建目录 消除模型浪费轮次检查目录存在性

相关推荐
qq_232045573 小时前
ai agent学习大纲
agent
程序消消乐6 小时前
第三章:Claude Code CLI 语义召回机制与后台自动抽取代理
agent·claude code
景同学7 小时前
MCP与CLI之争:AI Agent的协议之辩
人工智能·agent
Henrybit933687 小时前
如何构建高质量Skills?
人工智能·agent
景同学7 小时前
CLI化浪潮:三大企业办公平台的72小时开源赛
agent·mcp
前端双越老师8 小时前
为什么说 OpenClaw 应该装在自己的电脑上
人工智能·agent·全栈
EdisonZhou8 小时前
MAF快速入门(22)声明式Agent实战
llm·aigc·agent·.net core
竹之却9 小时前
OpenClaw 2026.4.5版本更新详解
网络·人工智能·agent·openclaw
专职17 小时前
Cline与大模型的交互协议(内涵Agent实现原理)
agent