在上一篇文章中,我们讨论了 Claude Code 的 Agentic 循环------它如何自主规划、执行、观察并修正自己的行为。但这个循环有一个隐藏的前提:它必须真正理解你的项目。如果一个 AI 搭档每次与你协作时都需要你重新解释文件结构、技术栈、命名约定和业务背景,那么所谓"自主规划"就失去了根基。
在传统的 AI 编码工具中,"理解项目"是一个脆弱的幻象。它们通常只能看到你当前打开的寥寥几个文件,或者截取与当前问题最相关的局部片段。当你跨越多个模块进行变更时,工具要么完全不知道其他文件的存在,要么给出与项目整体架构相悖的建议。开发者不得不承担起所有"上下文搬运"的工作。
Claude Code 从根本上改变了这一点。凭借 200,000 token 的超长上下文窗口,以及一套精巧的项目记忆机制,它能够在单次会话中建立一个覆盖整个代码库的"脑图"。更关键的是,这种状态可以跨会话持久化,使得 AI 搭档在下一次启动时仿佛从未离开过。本文将系统性地拆解 Claude Code 的上下文管理机制,并给出开发者如何主动设计这一机制、避免其被"污染"的实践策略。
1. 为什么上下文窗口的大小,决定了 AI 编程搭档的"智力天花板"
如果我们将一个 LLM 视为一个函数,它的输入不仅仅是单次提问,而是整个上下文窗口内的所有文本------包括系统提示、项目文件、对话历史、工具调用结果等。在这个窗口内,模型使用自注意力机制建立所有 token 之间的关联。因此,窗口的大小直接决定了模型"同时思考"的信息量。
一个 4,096 token 的窗口(许多早期编码工具的默认配置)大约只能容纳 3000 个英文单词,或相当于一个中等长度的源文件。在这种约束下,AI 看到的永远是管中窥豹。你让它"统一修改所有控制器的错误处理格式",但它根本看不到其他控制器的存在。它只能基于当前文件,或者你手动粘贴进去的几个片段,做出盲人摸象般的推测。
Claude 的 200K token 窗口彻底消除了这种"片段式认知"。200K token 可以容纳:
- 一个超过 5 万行的中型代码库的全部核心源文件;
- 或者一整本《重构:改善既有代码的设计》;
- 或者一次包含数小时对话历史、数百次工具调用结果的完整工作日志。
在 Claude Code 的日常使用中,我多次经历了这样的场景:一个请求涉及改动分散在十几个文件中的逻辑,它自行阅读了所有相关文件,从中提取出共享的常量、类型定义和调用链,然后提交了一份准确无误的修改计划。没有遗漏任何一个调用点,没有错误理解任何一个接口的语义------因为它真的读了所有这些代码。
这种能力带来的不仅是准确性的提升,更是协作方式的质变。你不再需要像对待一个健忘的实习生那样,每次都重新叙述背景。你可以像与一个从项目第一天就在团队里的资深工程师那样沟通:"把用户权限模块的接口改成我们上个月在支付模块用的那种签名格式。"而它能自己去找到那个签名格式的定义,理解它的约定,并应用到目标模块。
2. CLAUDE.md:将上下文从"会话级"提升到"项目级"
长上下文窗口解决了一次会话中能装下多少信息的问题。但它没有解决另一个难题:如何让 AI 在多次会话之间保持对项目的理解?
默认情况下,LLM 是无状态的。每次启动新会话,它的记忆被重置为一片空白。你可以再次手动上传文件、重述项目背景,但这低效且容易遗漏。Claude Code 的解决方案是 CLAUDE.md 文件------一个存放在项目根目录下,由你自行维护的项目知识文件。每次 Claude Code 启动时,它会将这个文件的内容注入到系统提示之中,成为贯穿所有会话的永久上下文。
一个设计良好的 CLAUDE.md 至少应包含以下信息:项目概述,目录结构,编码规范,命令,边界约束等。以下是一个具体项目的claude.md 文档实例
## 项目概述
基于 RuoYi v4.8.3 框架的XXXX管理后端系统,使用 Spring Boot 3.x + Java 21。
## 技术栈
- **框架**: Spring Boot 3.5.13 (RuoYi v4.8.3)
- **JDK**: 21
- **数据库**: PostgreSQL (Druid 连接池)
- **ORM**: MyBatis 3.x (XML Mapper)
- **缓存**: Redis (Spring Data Redis)
- **安全**: Spring Security + JWT (jjwt 0.12.6)
- **任务调度**: Quartz
- **API 文档**: Springdoc OpenAPI 2.8.0 (Swagger UI)
- **工具**: Lombok, Fastjson2, Apache POI 5.3.0 (Excel), PageHelper, OSHI
- **大数据**: HBase 2.4.18 (hbase-client)
## 项目结构
src/main/java/com/smc/
├── RuoYiApplication.java # 启动类
├── common/ # 通用模块
│ ├── annotation/ # 自定义注解 (@Log, @Excel, @DataScope 等)
│ ├── config/ # 通用配置
│ ├── constant/ # 常量定义
│ ├── core/ # 核心基类 (BaseController, AjaxResult, BaseEntity)
│ ├── enums/ # 枚举 (BusinessType, UserStatus 等)
│ ├── exception/ # 异常定义
│ ├── filter/ # 过滤器 (XSS, Repeatable)
│ ├── utils/ # 工具类
│ └── xss/ # XSS 相关
├── framework/ # 框架配置
│ ├── aspectj/ # 切面 (操作日志, 数据权限)
│ ├── config/ # 框架配置 (Security, Redis, 线程池等)
│ ├── datasource/ # 多数据源
│ ├── interceptor/ # 拦截器 (重复提交)
│ ├── manager/ # 异步管理器 (ShutdownManager, AsyncFactory)
│ ├── security/ # Spring Security 配置上下文
│ └── web/ # Web 层 (异常处理, 服务监控)
├── system/ # 系统管理
│ ├── controller/ # (common, monitor, system, tool)
│ ├── domain/ # (SysUser, SysRole, SysMenu 等)
│ ├── mapper/ # MyBatis 接口
│ └── service/ # 业务逻辑
├── project/ # 业务模块
│ ├── parameter/ # 参数管理
│ ├── task/ # 业务定时任务
│ └── util/ # 业务工具类
└── quartz/ # Quartz 定时任务
├── config/ # Quartz 配置
├── controller/ # 任务调度控制器
├── domain/ # SysJob, SysJobLog
├── mapper/ # 任务 Mapper
├── service/ # 任务服务
├── task/ # 任务执行
└── util/ # 任务工具
src/main/resources/
├── application.yml # 主配置
├── application-dev.yml # 开发环境配置
├── banner.txt # 启动 Banner
├── logback.xml # 日志配置
├── cloud-data.properties # HBase/云存储配置
├── i18n/messages.properties # 国际化
├── mybatis/mybatis-config.xml # MyBatis 全局配置
├── mapper/ # XML Mapper 文件
│ ├── system/ # 系统模块 Mapper
│ ├── quartz/ # 定时任务 Mapper
│ └── project/ # 业务模块 Mapper
│ ├── alarm/
│ ├── deviceinfo/
│ ├── fence/
│ ├── firmware/
│ └── parameter/
└── template/ # Excel 模板
## 编码规范
### 分层架构
Controller → Service(Interface + Impl) → Mapper(Interface) → XML Mapper
- **Controller**: 处理 HTTP 请求,参数校验,调用 Service
- **Service**: 业务逻辑,事务控制,调用 Mapper
- **Mapper**: MyBatis 接口,定义数据库操作
- **Domain**: 实体类,继承 `BaseEntity`,使用 Lombok `@Data`
- **DTO**: 数据传输对象(用于参数接收,与 Domain 分离)
- **XML**: SQL 映射文件,位于 `src/main/resources/mapper/`
### Controller 层约定
1. 所有 Controller 继承 `BaseController`
2. 类级别 `@RequestMapping("/module")` 使用小写/连字符
3. 分页查询: `startPage()` + `service.selectXxxList(param)` + `return getDataTable(list)`
4. 单条查询: `return success(service.selectXxxById(id))`
5. 新增: `return toAjax(service.insertXxx(obj))`
6. 修改: `return toAjax(service.updateXxx(obj))`
7. 删除: `return toAjax(service.deleteXxxByIds(ids))` (批量删除)
8. 响应: 成功用 `AjaxResult.success(data)` / `success(data)`, 失败用 `AjaxResult.error(msg)` / `error(msg)`
### Service 层约定
1. 接口名以 `I` 开头 (如 `IAlarmInfoService`)
2. 实现类在 `impl` 子包中,命名如 `AlarmInfoServiceImpl`
3. 使用 `@Service` 注解,`@Autowired` 注入 Mapper
4. 写操作使用 `@Transactional`
5. 方法命名前缀:
- `selectXxx` - 查询
- `insertXxx` - 新增
- `updateXxx` - 修改
- `deleteXxx` - 删除
- `checkXxx` - 校验
- `importXxx` - 导入
- `exportXxx` - 导出
### Domain 层约定
1. 继承 `BaseEntity` (含 `createBy`, `createTime`, `updateBy`, `updateTime`, `remark`, `delFlag`)
2. 使用 Lombok `@Data` + `@EqualsAndHashCode(callSuper = true)`
3. Excel 导出字段使用 `@Excel` 注解
4. 日期字段使用 `@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")`
5. 有 DTO 的业务模块将请求参数封装在 DTO 中,Domain 用于数据库映射
### 数据库约定
- PostgreSQL,表名以 `t_` 前缀(系统表除外)
- 列名风格: `c_` 前缀 = 字符串/文本, `n_` 前缀 = 数值, `d_` 前缀 = 日期
- 逻辑删除使用 `del_flag` 字段 (`'0'`=正常, `'1'`=删除)
- 时间戳使用 Epoch 秒数存储
### 安全约定
1. 接口使用 `@PreAuthorize("@ss.hasPermi('module:action')")` 控制权限
2. 写操作使用 `@Log(title = "模块名", businessType = BusinessType.XXX)` 记录日志
3. 敏感操作需校验用户数据范围 (`checkXxxDataScope`)
4. 密码使用 `SecurityUtils.encryptPassword()` 加密
### API 文档约定
使用 Springdoc (OpenAPI 3.0):
- 类级: `@Tag(name = "模块名")`
- 方法级: `@Operation(summary = "接口说明", description = "详细描述")`
## 构建与运行
# Maven 打包
mvn clean package
# 运行 (默认 dev 环境)
java -jar target/smart-XX-server.jar
# 配置
活跃环境: dev (application-dev.yml)
启动端口: 8080
Swagger UI: http://localhost:8080/swagger-ui.html
OpenAPI: http://localhost:8080/v3/api-docs
## 注意事项
- 业务代码在 `com.smc.project` 包中,不要修改 `common`/`framework`/`system` 基础框架代码
- XML Mapper 中的列名使用 PostgreSQL 大小写不敏感风格
- 分页通过 `startPage()` (PageHelper) 自动拦截 SQL,无需手写 LIMIT
- 导入功能通过 `ExcelUtil` + `@Excel` 注解实现
这个文件充当了 AI 搭档的"项目入职文档"。它不仅包含技术事实,更重要的是记录了项目内部的隐含约定:那些在代码里看不出来,但团队每个人都心知肚明的规范。
CLAUDE.md 的另一个关键作用是设定安全边界。通过明确定义哪些目录和命令是被禁止的,你在赋予 AI 自主权的同时,也划定了它不得逾越的红线。这是一种与 Agentic 编程范式相适应的控制模式:你不需要命令它每一步做什么,但你必须提前声明它的行动边界。
3. 项目记忆的延伸:让上下文像代码一样被版本控制
CLAUDE.md 是静态项目知识的载体,但项目在演进,知识也在持续更新。技术决策的原因、特定模块的历史成因、某些看起来奇怪但有意为之的实现方式------这些"隐性知识"往往只存在于开发者的头脑中,AI 无法从代码本身推导出来。
.claude/ 文件夹是项目的"持久记忆层",主要有以下文件类型:
目录结构
.claude/
├── CLAUDE.md # 项目核心文档(已创建) ├── MEMORY.md # 记忆索引文件(自动管理)
├── settings.json # 项目级 Claude Code 配置/权限/钩子
├── settings.local.json # 本地覆盖配置
├── scheduled_tasks.json # 持久化定时任务(自动管理)
├── worktrees/ # 工作区隔离目录(自动管理)
└── memory/ # 记忆文件存放目录
├── user_*.md # 用户画像记忆
├── feedback_*.md # 行为偏好/纠正记录
├── project_*.md # 项目进展/目标/决策记录
└── reference_*.md # 外部系统指针
各文件用途
| 文件 | 维护者 | 用途 |
|---|---|---|
| CLAUDE.md | 你/CLaude | 项目全局约定:技术栈、架构、编码规范、构建命令 |
| MEMORY.md | 自动 | 记忆索引,指向 memory/ 下的文件 |
| memory/ | 你/(CLaude) | 会话间持久化:用户偏好、项目决策、外部参考 |
| settings.json | 你 | 配置权限白名单、环境变量、钩子脚本、模型设置 |
适合放什么
CLAUDE.md: 相对静态的、从代码中不易推导的项目知识
- 技术栈与框架版本
- 项目结构导览
- 编码/命名约定
- 构建/测试/部署命令
- 数据库约定(字段前缀、命名风格等)
MEMORY.md 是一个纯索引文件,不写具体内容,只做跳转和摘要
一句话摘要的作用是帮 Claude 判断"这条记忆是否相关",相关才去读具体文件,避免每次把所有记忆文件全读一遍。
memory/ 目录: 动态的、跨会话的上下文
- 用户角色和技术背景
- 过往的架构决策和原因
- 项目当前阶段目标/截止日期
- 曾纠正过的行为模式
settings.json: 权限和钩子(免打扰的自动化)
- 常用命令权限白名单(减少确认弹窗)
- 钩子脚本(git commit 前自动格式化等)
- 环境变量注入
不适合放什么
- 代码本身能推导的信息(函数签名、文件路径)
- 临时会话状态(当前正在改什么 bug)
- Git 历史已经记录的内容(谁改了什么、提交信息)
Claude Code 在启动时会自动读取 .claude/ 目录下的相关文件。这种设计的精妙之处在于:
纳入版本控制:项目记忆与代码共同演进。当架构发生变化,相关的记忆文件也会在同一个 PR 中被更新。新人加入时,git clone 的同时也获得了项目完整的知识体系。
AI 知识的可审计性:如果 Claude Code 给出了一个奇怪的实现建议,你可以检查项目记忆,看是 AI 忽略了它,还是记忆文件本身包含过时信息。这消除了"AI 为什么这样做"的黑箱。
团队级别的共享上下文:当一个团队共同使用 Claude Code 时,每个人都能访问同一套项目知识。不会出现"我的 Claude 知道这个约定,你的不知道"的情况。
在这一体系下,Claude Code 不仅是一个个人工具,而开始呈现团队 AI 成员的雏形:它共享了团队的集体记忆,遵守同样的规范,在所有会话中保持行为的一致性。
4. 上下文污染的威胁:过多的信息比过少更危险
长上下文窗口是强大的武器,但锋利的刀也可以割伤自己。上下文越大,噪音混入的风险就越高。所谓"上下文污染",是指无关、冗余甚至错误的信息占据了宝贵的 token 空间,导致模型注意力分散,输出质量显著下降。
具体而言,上下文污染有几种常见形态:
4.1 历史膨胀
一场持续数小时的工作会话,对话历史可能膨胀到数万 token。早期的讨论、已经解决的问题、走入死胡同的尝试,如果一直残留,会挤占模型的"工作记忆"。你可能会发现 Claude Code 越来越频繁地重提已解决的问题,或者将旧上下文中的特定细节错误地应用到新任务上。
对策:使用 /clear 命令在新任务开始前重置会话历史,但保留项目记忆文件。同时,将长时间、多任务的工作拆分为多个独立会话,每个会话专注于一个主题。
4.2 无关文件干扰
当你在一次任务中将整个 node_modules 目录或编译产物上传到上下文,或者让 Claude Code 不加区分地阅读大量与当前任务无关的文件时,会引入大量无效信号。模型的注意力机制会被无意义的导入语句、第三方库代码和构建配置分散。
对策:为 Claude Code 建立清晰的"搜索-聚焦"行为。在 CLAUDE.md 中明确哪些目录需要排除(如 node_modules/, dist/, .git/)。当需要跨文件分析时,先让它用 grep 和 ls 定位相关文件,再精确读取,而非无差别地吞噬整个目录。
4.3 错误假设的固化
如果 Claude Code 在会话早期产生了一个错误推断(比如认错了某个函数的参数类型),而你没有及时纠正,这个错误会在后续的对话中被反复引用,不断强化成一个"事实"。最后生成的一大段代码可能都基于这个错误前提。
对策:在每次关键执行步骤后的"观察"阶段,不仅关注结果是否成功,也要抽查 AI 对关键事实的理解是否正确。一旦发现偏差,立刻在对话中明确指出并纠正,要求它更新自己的认知后再继续。
4.4 安全边界被上下文稀释
一个极其隐蔽的风险:当会话历史变得极长时,你在系统提示和 CLAUDE.md 中设定的安全约束可能因为处在上下文的"远距离"位置,而变得权重降低。模型更倾向于关注近期的对话指令,可能"忘记"早期设定的禁止规则。
对策:关键的安全约束在 CLAUDE.md 中以简洁、醒目的形式标注(如使用 [安全约束] 标记)。对于高危操作,开启 Claude Code 的"需要批准"模式,以硬性阻断替代依赖模型自律。
5. 设计你的 AI 搭档的记忆系统:实践清单
长上下文和项目记忆不是一劳永逸的"开启"选项,而是一个需要持续设计的工程系统。以下是一份可供启动的实践清单:
即刻行动
-
在项目根目录创建 CLAUDE.md,写入:技术栈、常用命令、目录结构、顶层约束。
-
检查 .gitignore,确保 CLAUDE.md 被纳入版本控制(通常应纳入,除非包含敏感信息)。
-
启动一次新会话,验证 Claude Code 是否正确读取了 CLAUDE.md(可以问它"你知道这个项目的测试命令是什么吗?")。
短期优化(第一个迭代内)
-
记录团队中每个人都心知肚明但从未写下来的"隐含约定",添加到 CLAUDE.md。
-
创建 .claude/memory/gotchas.md,将最近踩过的坑(奇怪的依赖、特殊的环境变量等)记录下来。
-
在团队中形成习惯:任何架构决策变更时,同时更新对应的 .claude/memory/ 文件。
长期维护
- 定期审查会话历史,识别哪些信息是 AI 频繁误解的,将其澄清后加入项目记忆。
- 为安全边界建立团队共识:哪些操作必须保持"需批准"模式,哪些可以放开。
- 在代码审查中引入"AI 记忆"的视角:如果一个 PR 涉及约定变更,是否已同步更新项目记忆文件。
结语
长上下文不是一项抽象的技术规格,它是 AI 编程搭档从"任务执行器"升级为"长期工程协作者"的基石。当一个 AI 能够同时持有整个项目的细节、理解它的历史约定、并在会话间保持稳定的认知状态时,编程的性质就发生了微妙而深刻的变化:你不再是和工具通信,而是开始和一个理解你项目灵魂的工程同伴对话。