上下文工程
Context Engineering(Offload / Retrieve / Reduce / Isolate)
规则:上下文 = 模型在一次交互中”看得到”的全部信息。按来源分为 5 类,按生命周期分为 3 层。
[Requirement] 提交给 AI 的任何上下文,必须能明确归入下表中的某一类,禁止”凭感觉全贴”。
| 分类 | 说明 | 典型内容 | 优先级 |
|---|---|---|---|
| 系统上下文 (System) | 不可变的全局约束 | 角色设定、工程规范、输出格式 | P0 |
| 规范上下文 (Specification) | 当前任务的规格说明 | 需求、验收标准、技术方案 | P0 |
| 代码上下文 (Code) | 相关源码与结构 | 待修改文件、调用链、类型定义 | P1 |
| 数据上下文 (Data) | 运行时数据样例 | 请求/响应样例、测试数据、日志 | P1 |
| 环境上下文 (Environment) | 运行与工具环境 | 语言版本、框架、依赖清单 | P2 |
| 层级 | 生命周期 | 存放位置 | 示例 |
|---|---|---|---|
| 持久层 | 跨会话长期有效 | 团队规范仓库、Agent System Prompt | 编码规范、架构决策 |
| 项目层 | 单个项目内有效 | 项目 README、docs/、配置文件 | 项目目录结构、API 契约 |
| 会话层 | 单次任务有效 | 当前对话、临时挂载文件 | 本次需求、相关代码片段 |
示例:
任务:为 FastAPI 项目新增一个"用户注册"接口
持久层:团队 Python 编码规范(已作为 System Prompt 注入)项目层:README.md、openapi.yaml、现有的 user 模块代码会话层:本次需求描述 + 验收标准 + 待修改的 user.py 片段反模式:
- 把整个项目所有文件一次性塞给模型(超出有效窗口,稀释关键信息)
- 不区分层级,把临时调试日志和长期规范混在一起
- 上下文来源不明,模型无法判断哪些是”必须遵守”、哪些是”仅供参考”
规则:Offload = 把信息从”对话上下文”转移到”外部存储”,用引用替代内联。核心是”能引用就不粘贴”。
[Requirement] 超过 200 行的代码文件,不得直接粘贴到对话中;必须通过文件路径或符号引用让 AI 读取。
[Standard] 反复使用的规范、模板、代码片段,必须沉淀到持久层(规范仓库 / System Prompt),不得每次手动粘贴。
[Default] 工具支持的场景下,优先使用”文件挂载 + 路径引用”,而非复制粘贴内容。
| 手段 | 适用场景 | 实现方式 |
|---|---|---|
| 文件挂载 | 单次任务需查看多个文件 | IDE 插件挂载、@file 引用 |
| 符号引用 | 只需要某个函数/类/类型定义 | @user.py#register_user、行号范围 |
| 链接引用 | 外部文档、规范、Issue | 粘贴 URL(模型可联网时)或文档 ID |
示例:
# 正确:用路径引用,不贴全文请修改 src/services/user.py 中的 register_user 函数,参考 src/schemas/user.py 中 UserCreate 模型的定义。验收标准见 docs/requirements/user-registration.md 第 3 节。
# 错误:把三个文件全文粘贴到对话[全文 300 行的 user.py][全文 150 行的 user.py schema][全文 80 行的需求文档]请修改...反模式:
- 把
node_modules、dist/、生成产物贴进上下文 - 明明工具支持文件读取,仍然手动复制大段代码
- 规范内容散落在聊天记录里,每次重新粘贴一遍
规则:Retrieve = 在需要时精准拉取相关上下文,而非一次性塞入全部信息。核心是”按需检索、精准命中”。
[Requirement] 启动任务前,必须先明确”本次需要哪些上下文”,列清单后再逐一检索,禁止无目的全量加载。
[Standard] 检索遵循”先元数据、后内容”:先看文件树、函数签名、类型定义,再按需展开具体实现。
[Suggestion] 对大型仓库,先通过全局搜索(grep / 符号索引)定位相关代码,再读取命中文件。
1. 定位 → 通过文件名 / 符号名 / 调用关系找到候选文件2. 筛选 → 读取文件头部、import、函数签名,判断是否相关3. 展开 → 对相关文件读取具体实现段落(行号范围)4. 验证 → 确认上下文是否足够回答问题,不足则回到步骤 1示例:
任务:排查用户注册接口返回 500 的原因
1. 定位:搜索 "register" 命中 src/services/user.py、src/api/users.py2. 筛选:读 user.py 的函数签名,发现 register_user 调用了 send_welcome_email3. 展开:读取 register_user 第 40-80 行 + send_welcome_email 实现4. 验证:发现 send_welcome_email 未捕获 SMTP 异常 → 根因明确反模式:
- 一上来就把整个
src/目录所有文件读一遍 - 只靠文件名猜测相关性,不看内容就贴进来
- 检索到第一个匹配就停止,遗漏调用链上下游
规则:Reduce = 对已加载的上下文做裁剪、压缩、结构化,只保留与当前任务直接相关的信息。核心是”去噪、提炼、聚焦”。
[Requirement] 提交给 AI 的代码片段,必须裁剪掉与任务无关的部分(无关函数、注释、空行、导入)。
[Standard] 超过 50 行的上下文,应先做结构化摘要(函数签名列表、关键数据流、约束条件),再附带必要的原始片段。
[Suggestion] 上下文总 token 数控制在模型窗口的 30% 以内,留出足够空间给思考和输出。
| 手法 | 说明 | 示例 |
|---|---|---|
| 行号裁剪 | 只引用相关行范围 | user.py#L40-L80 |
| 签名提取 | 只保留函数/类签名,去掉实现 | def register_user(data: UserCreate) -> User |
| 依赖剥离 | 去掉与任务无关的 import 和辅助代码 | 只保留当前函数实际调用的依赖 |
| 摘要前置 | 先给一句话摘要,再给细节 | “下面是注册流程,核心逻辑在第 3 步发邮件” |
示例:
# 正确:裁剪后的上下文相关代码:src/services/user.py
def register_user(data: UserCreate) -> User: # L42-L48: 校验邮箱唯一性 if user_repo.exists(email=data.email): raise EmailAlreadyExists() # L50-L55: 创建用户 user = user_repo.create(data) # L57-L60: 发送欢迎邮件(此处抛异常未捕获) send_welcome_email(user.email) return user
# 错误:把整个文件 300 行全贴进来,包含 5 个无关函数反模式:
- 把
git diff的全部变更(含格式化、空白改动)塞进上下文 - 不做任何摘要,直接丢一堆文件让 AI 自己找重点
- 上下文里混入调试日志、临时变量、被注释掉的废弃代码
规则:Isolate = 把不同任务、不同模块、不同关注点的上下文隔离开,避免相互干扰。核心是”一次只做一件事,一个上下文只服务一个目标”。
[Requirement] 单个会话/任务只聚焦一个明确目标;多目标必须拆分为多个独立会话或子任务。
[Standard] 上下文按”职责”分块组织:需求归需求、代码归代码、测试归测试,不混在同一段文本里。
[Default] 不同模块的修改应通过不同的子任务/子 Agent 处理,每个子任务只加载本模块相关上下文。
| 维度 | 隔离方式 | 目的 |
|---|---|---|
| 任务隔离 | 一个会话只做一个功能点 | 避免多需求相互污染 |
| 模块隔离 | 按模块/服务划分上下文边界 | 避免无关代码干扰 |
| 角色隔离 | 不同角色(编码/测试/评审)用不同上下文集 | 避免角色越界 |
示例:
# 正确:分两个独立任务任务 A(会话 1):实现用户注册接口 上下文:注册需求、user 模块代码、User schema
任务 B(会话 2):为注册接口补充单元测试 上下文:注册接口签名、测试框架规范、Mock 规范
# 错误:一个会话里同时做"实现接口 + 写测试 + 改前端页面" 上下文混乱,AI 输出互相矛盾反模式:
- 在同一个对话里既讨论架构设计又讨论具体 bug 修复
- 后端接口开发和前端页面开发混在一个上下文里
- 评审上下文(只读评论)和编码上下文(可修改代码)不区分