CodeBuddy 学习(4):上下文管理
一、概述:上下文是 AI 理解你的唯一窗口
1.1 为什么上下文管理如此重要
对话式 AI 编程的质量不取决于 AI 模型有多强——取决于你给了它什么信息。AI 不会主动"了解"你的项目,它只在上下文中看到你主动投喂的内容。
1.2 金发姑娘原则(Goldilocks Principle)
| 状态 | 信息量 | 后果 |
|---|---|---|
| 信息不足 | 太少 | AI 瞎猜,生成代码与项目格格不入,不知道你用什么框架 |
| 信息过多 | 太多 | AI 混乱,抓不住重点,输出质量下降 |
| 信息精准 | 刚刚好 | AI 高效,代码直接可用 |
结论:精准投喂是关键——不太少、不太多、刚刚好。
二、上下文的四个层级
| 层级 | 来源 | 控制方式 | 优先级 | 说明 |
|---|---|---|---|---|
| 系统层 | Rules 文件 | 编辑 .codebuddy/rules/ | 最低 | 全局生效,对话启动时自动加载 |
| 用户层 | 用户偏好设置 | 用户级 Rules 配置 | 低 | 个人习惯,跨项目共享 |
| 项目层 | 项目规则 | 项目根目录配置 | 中 | 团队约定,项目内共享 |
| 对话层 | @ 引用 + 直接指令 | 你主动选择 | 最高 | 本章核心——你随时可控 |
重点:对话层优先级最高——你通过
@主动注入的上下文,可以覆盖任何层级设置。
三、@ 引用的五种用法
3.1 原理讲解
@ 引用是 CodeBuddy 的上下文注入指令。每次你使用 @ 引用某个实体,CodeBuddy 会:
- 读取该实体(文件、目录、终端输出等)的完整内容。
- 将内容序列化为文本。
- 追加到当前消息的上下文窗口中,随问题一起发送给 AI。
关键特性:
- 一次性:只在当前消息有效,下一条消息若不再次引用,AI 就不再知道文件内容。
- 实时性:@terminal 引用的是最近一次执行的真实输出,而非缓存。
- 手动触发:必须手动输入
@符号,粘贴文本中的@不会生效。
| 引用类型 | 写法 | 说明 | 典型用途 |
|---|---|---|---|
| @Files | @file | 注入文件的完整内容 | 让 AI 分析、重构或基于特定文件生成代码 |
| @Folders | @folder | 注入目录结构(不阅内容) | 了解项目组织,规划新文件位置 |
| @Terminal | @terminal | 注入最近一次终端输出 | 排错、分析运行日志、理解报错信息 |
| @Docs | @docs | 注入文档或外部 API 参考 | 让 AI 参照文档风格或 API 签名 |
| @Code | @code | 注入选中的代码片段 | 精准分析某段代码,避免注入整个文件 |
四、示例一:精确投喂——排错场景
4.1 场景描述
运行 node server.js 时报错 Cannot find module './config',需要排查原因。
4.2 完整操作流程
Step 1:在 CodeBuddy 内置终端中执行命令,获取完整报错输出。
node server.js
终端输出:
Error: Cannot find module './config'
Require stack:
- /Users/project/src/server.js:3:20
- /Users/project/index.js:1:14
at Module._resolveFilename (node:internal/modules/cjs/loader:xxx)
at Module._load (node:internal/modules/cjs/loader:xxx)
...
Step 2:在对话框中组合输入:
@file src/server.js
@terminal
请分析终端报错的原因,并给出修复方案。
4.3 预期输出
AI 会同时看到 server.js 的源代码和终端报错,给出根因分析:
原因分析:
server.js 第 3 行:
const config = require('./config');
Node.js 在 server.js 同级目录(src/)下找不到名为 config 的模块。
可能的原因:
1. config 文件不存在 —— 最常见的情况。
检查 src/ 下是否有 config.js / config.json / config/index.js。
2. config 文件存在但路径写错了 —— 例如文件叫 configs.js 但 require 的是 config。
3. 配置文件在上级目录 —— require 应该是 require('../config') 而非 require('./config')。
修复方案:
# 在终端中确认文件位置
ls src/config*
# 根据结果修改 server.js 中的 require 路径
4.4 调试过程
场景一:@terminal 引用的不是最新输出
现象:使用 @terminal 后,AI 分析的是旧的报错信息,和当前问题无关。
原因:在 CodeBuddy 外部终端(系统 Terminal)执行命令不会更新 @terminal 引用。
排查:
1. 确认是在 CodeBuddy 内置终端(IDE 底部面板)中执行的命令。
2. 重新在内置终端中执行命令,再引用 @terminal。
修复:在内置终端重新执行命令后,立即引用 @terminal 提问。
场景二:抱怨 AI 没看到我引用的文件内容
现象:你用了 @file,但 AI 的回复和文件内容不匹配。
原因排查:
1. @ 符号是手动输入的吗?
粘贴文本中的 @ 不生效——必须手动在输入框中输入。
2. 文件名路径正确吗?文件在当前项目中吗?
3. 文件内容是否太长,被上下文窗口截断?
排查步骤:
1. 重新手动输入 @file,从弹出的文件列表中选择目标文件(不要手动敲路径)。
2. 确认文件在 IDE 左侧文件树中可见。
3. 如果文件超过 2000 行,改用 @code 只引用关键片段。
修复:确保 @ 引用是通过交互菜单选择的,而非手动输入路径。
五、示例二:分层投喂——按流水线递进
5.1 原理讲解
分层投喂的核心理念:每一步只引用上一步的产出,而不是把所有东西一股脑扔给 AI。
这种方法的好处:
- 精简上下文:每个对话只包含当前步骤所需的最小信息。
- 减少幻觉:AI 不会因为看到太多无关代码而混淆。
- 可追溯:出问题时,很容易定位是哪一步的上下文偏差导致的。
5.2 四步流水线
Step 1: 产品文档 → 生成类型定义
↓
Step 2: 类型定义 → 生成 API 接口
↓
Step 3: API 接口 → 生成 Hook 封装
↓
Step 4: Hook 封装 → 生成页面组件
5.3 完整可运行示例
Step 1:产品文档 → 类型定义
操作:
根据产品需求文档,生成 TypeScript 类型定义:
- 商品:id, name, price, category, stock, imageUrl
- 订单:id, items(商品列表), totalAmount, status, createdAt
- 用户:id, name, email, avatar
请只输出类型文件内容。
预期输出(src/types/models.ts):
// 商品类型
export interface Product {
id: number;
name: string;
price: number;
category: string;
stock: number;
imageUrl: string;
}
// 订单项
export interface OrderItem {
productId: number;
productName: string;
price: number;
quantity: number;
}
// 订单状态
export type OrderStatus = 'pending' | 'paid' | 'shipped' | 'completed' | 'cancelled';
// 订单类型
export interface Order {
id: number;
items: OrderItem[];
totalAmount: number;
status: OrderStatus;
createdAt: string;
}
// 用户类型
export interface User {
id: number;
name: string;
email: string;
avatar: string;
}
Step 2:类型定义 → API 接口
操作(新建对话):
@file src/types/models.ts
基于 Product 类型,创建获取商品列表的 API 接口(mock 数据)。
返回分页结构,包含筛选参数(分类、搜索关键词)。
响应格式:{ code, data: { list, total, page, pageSize }, message }
Step 3:API 接口 → Hook 封装
操作(新建对话):
@file 上一步生成的 API 文件
创建 useProducts Hook:
- 封装 API 调用
- 返回 { data, loading, error, refresh }
- 支持自动请求和手动刷新
Step 4:Hook → 页面组件
操作(新建对话):
@file 上一步生成的 Hook 文件
创建 ProductList 页面组件:
- 使用 useProducts Hook
- 展示商品网格(卡片式布局)
- 加载态:骨架屏
- 空数据态:提示文案
- 错误态:重试按钮
5.4 调试过程
场景:Step 3 发现 Step 2 的 API 签名有问题
现象:写 Hook 时发现 API 返回的结构和预期不一样。
原因:Step 2 生成 API 时的上下文不足,签名理解偏差。
排查:Step 2 操作时没有 @file 引用类型文件,AI 凭描述猜测 API 签名。
修复(分层投喂的标准修复方式):
1. 回到 Step 2 的对话。
2. 用 @file 引用 Step 1 的类型文件,让 AI 修正 API 签名。
3. 确认修正后重新进入 Step 3。
关键:每层只修自己的问题,不跨层修改。
六、四种上下文污染与排查
6.1 污染类型
| 污染类型 | 表现 | 后果 | 排查信号 |
|---|---|---|---|
| 陈旧信息 | 引用了已废弃的旧代码 | AI 生成兼容旧方案的代码 | 生成代码用了旧 API 命名 |
| 噪声过多 | 500 行文件全扔给 AI | 抓不住重点,质量急剧下降 | 回复东拉西扯、不聚焦 |
| 矛盾信息 | 前面说 REST 后面说 GraphQL | AI 左右为难,前后不一致 | 输出中两种方案交替出现 |
| 跨任务污染 | 一个对话讨论 3 个功能 | AI 把功能搞混 | 回复中串入了其他任务的内容 |
6.2 排查与修复
场景一:陈旧信息污染
现象:AI 生成的代码用了一个你三周前就改掉的函数名。
排查:
1. 回忆你是否在早期对话中引用过那个旧函数。
2. 检查当前对话是否从旧对话延续而来(AI 记得早期上下文)。
修复:
1. 新建对话——切断历史上下文。
2. 用最新的 @file 重新引用当前版本的代码。
3. 必要时在 Rules 中维护一份"废弃列表",告诉 AI 哪些 API 已经不用了。
场景二:噪声过多污染
现象:你引用了整个 src/ 目录下的 5 个大文件,AI 的回复质量明显下降。
排查:统计当前消息中引用的文件总量(行数/字符数)。
修复(两种策略):
策略 A(精确投喂):用 @code 只引用与当前任务相关的 50-100 行代码。
策略 B(分层递进):只引用上一步产出(类型文件),而不是整个实现。
急救手段:立刻新建对话 + 只引用最关键的 1-2 个文件。
场景三:跨任务污染
现象:同一个对话里,你问了"创建订单页面",又问了"修复登录 Bug",
又问了"优化数据库查询",AI 开始把登录 Bug 的修复思路用到订单页面上。
排查:回顾对话历史,是否包含 3 个以上不相关的任务。
修复:牢记法则——**一个任务一个对话**。三个任务 = 三个独立对话。
七、最小上下文原则
7.1 核心公式
输出质量 = 相关性 × (1 / 噪声量)
减噪就是提质——只给 AI 完成当前任务所需的最少信息。
7.2 两条黄金法则
| 法则 | 内容 | 说明 |
|---|---|---|
| 法则一 | 一个任务一个对话 | 避免跨任务污染 |
| 法则二 | 类型文件优先于实现文件 | 接口定义优先于内部逻辑 |
7.3 急救手段
当 AI 输出混乱时,三步急救:
- 缩减上下文:只保留最关键的 1 个 @ 引用。
- 重开对话:新建对话,切断污染历史。
- 明确需求:用一句话描述当前任务目标。
八、进阶技巧:预热与锚点
8.1 预热(Warm-up)
正式提需求前,先让 AI 认识你的项目:
操作:
@folder src
这是一个 Next.js 14 + TypeScript + TailwindCSS 项目。
App Router 架构,shadcn/ui 组件库。
请确认你已经理解了项目结构。
效果:后续对话中,AI 对项目结构有基本了解,不需要每次都解释。
8.2 锚点(Anchor)
对话超过 10 轮后,AI 开始遗忘早期上下文。新任务开头设置锚点:
操作:
回顾一下:类型文件在 src/types/,API 路由在 src/app/api/,
Hook 在 src/hooks/,组件在 src/components/。
现在开始新任务:创建 [功能描述]。
效果:用一句话帮 AI 重新定位关键路径,避免遗忘导致的质量下降。
九、负面上下文:告诉 AI 不要做什么
9.1 原理讲解
有时候,告诉 AI 不要做什么比告诉它要做什么更有效。因为 AI 的"创作欲"可能会让它主动做你不需要的事情(如引入新依赖、修改接口签名、重构无关代码)。
9.2 示例对比
未给禁止项:
"帮我优化这个组件"
→ AI 可能:重构 Props、引入新库、修改 CSS、重组目录结构
→ 结果:合入风险高,需要大量审查
给出禁止项:
"帮我优化这个组件,约束:
- 不修改 Props 接口
- 不引入新的第三方库
- 不修改 CSS 类名
- 不改变组件对外行为"
→ AI 只做:拆分逻辑、提取子函数、优化变量名、添加 early return
→ 结果:变更聚焦,可直接合并
9.3 调试过程
场景:给了禁止项,AI 仍然违反
现象:你说"不要改 Props 接口",AI 还是改了。
原因:禁止项描述不够明确,或者与其他需求产生冲突。
排查:
1. 检查禁止项是用什么语气写的——"请不要..." 对 AI 的约束力弱于 "禁止..."。
2. 检查是否有其他正向需求与禁止项冲突。
(如既要"重构接口设计"又要"不要改接口"——这本身就是矛盾的)。
修复:
- 用明确的否定词:"禁止修改"、"不允许引入"、"不得改动"。
- 检查需求之间的一致性,消除逻辑冲突。
十、小结
| 知识模块 | 核心要点 |
|---|---|
| @ 引用 | 五种用法:Files / Folders / Terminal / Docs / Code |
| 四层上下文 | 系统 → 用户 → 项目 → 对话,对话层优先级最高 |
| 金发姑娘原则 | 不太少、不太多、刚刚好 |
| 四种污染 | 陈旧信息 / 噪声过多 / 矛盾信息 / 跨任务 |
| 最小上下文 | 核心公式:输出质量 = 相关性 × (1/噪声量) |
| 分层投喂 | 类型 → API → Hook → 页面,每步只引用上一步产出 |
| 预热 + 锚点 | 用最小代价换最大输出稳定性 |
| 负面上下文 | 告诉 AI 不要做什么,限制"创作欲" |