Files
test-repo/docs/zh-cn/appendix/8-artificial-intelligence/context-engineering.md
T
sanbuphy 07d82d046b feat(docs): restructure appendix content into organized directories
- Move standalone AI-related files into 8-artificial-intelligence directory
- Move development tools content into 2-development-tools directory
- Move server/backend content into 4-server-and-backend directory
- Create new index files for each section
- Update .gitignore to exclude old backup directories
- Update theme imports for new component locations
2026-02-15 01:57:52 +08:00

481 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 上下文工程
> 💡 **学习指南**:提示词工程解决的是“怎么把话说清楚”,上下文工程解决的是“让模型在合适的时刻看到合适的信息”。本章节会围绕一个问题展开:**在有限的上下文窗口里,如何既让模型懂你,又不把钱烧光?**
在开始之前,建议你先补两块“基础砖”:
- **Token 是什么**:可以先阅读 [大语言模型入门](./llm-intro.md) 的「分词 & Token」部分。
- **Prompt 是什么**:如果你还不熟悉 System / User / Assistant 的基本结构,可以先看 [提示词工程](./prompt-engineering/)。
---
## 0. 引言:为什么聊着聊着,它就忘事,还越来越贵?
<AgentContextFlow />
很多人在实际使用大模型时都会遇到类似的情况:
- 聊到一半,模型突然“忘记”之前说过的关键条件;
- 长对话里,前后回答自相矛盾,很难保持同一套设定;
- 对话轮次一多,账单像打车计价一样不断往上走。
直觉上,我们会以为是:**“这个模型记性不好”**。
但大多数时候,问题并不在于模型“不会记”,而在于我们**没有设计好它能看到的上下文**。
<IntroProblemReasonSolution />
面对这些挑战,单纯依靠“写好提示词”已经捉襟见肘。我们需要一套更系统的工程方法,来在有限的窗口和预算内,让模型始终获得最关键的信息。这正是**上下文工程**试图解决的问题。
---
## 1. 什么是“上下文工程”?(定义 + 场景)
先给一个简短的工作定义,再看几个典型场景。
> 上下文工程,是一门为 LLM 构建和管理“信息环境”的工程方法,决定模型“看到什么、忽略什么、什么时候看到”,从而在有限的上下文窗口内稳定完成任务。
你可以简单地把它理解成三件事:整理信息、控制窗口、管理成本。
常见会用到它的场景包括:
- 对话型 Agent 和客服机器人
- 代码 / 文档助手
- 多轮工具调用和长流程编排
接下来,我们就从一个真实团队的“血泪教训”出发,看看他们是怎么一点点从“只会写 Prompt”进化到“会做上下文工程”的。
---
## 2. 从"血泪教训"说起:Manus 团队踩过的坑
本章案例来自 **Manus**(一款通用 AI Agent)。
与普通对话不同,Manus 需要自主规划并调用工具完成长任务(涉及几十甚至上百轮交互)。
这带来了核心矛盾:
- **如果不记**:关键信息丢失,任务中断。
- **全记**:成本和延迟爆炸,甚至超出窗口限制。
Manus 团队经历过多次架构重构,才明白一个道理:**上下文不能只靠“写”,而要靠“设计”。**
### 2.1 四次重构教会我们什么?
Manus 的联合创始人季逸超分享过他们的"踩坑史":
| 阶段 | 遇到的问题 | 当时的想法 | 结果 |
| :--- | :--- | :--- | :--- |
| **第一次** | AI 聊着聊着就忘事 | "多写点提示词就好了" | 越写越长,越写越贵 |
| **第二次** | 重要信息总被挤掉 | "把重要的多复制几遍" | 文本更长,成本更高 |
| **第三次** | 账单高得吓人 | "能不能复用之前的计算?" | 找到降低重复计算成本的方式 |
| **第四次** | 长文档处理不了 | "能不能需要时再查?" | 建立“图书馆+按需检索”的方案 |
**核心领悟**:**不是记得越多越好,而是记得越巧越好**。
### 2.2 AI 的"记性"到底像什么?
**传统电脑内存** = **硬盘**
- 容量大:可以长期保存大量数据;
- 价格低:存放一年成本较低;
- 读写速度相对较慢,查找信息需要一定时间。
**AI 的上下文** = **小黑板**
- 读写快:模型可以在一次调用中直接看到全部上下文;
- 容量有限:写满后不得不擦除旧内容;
- 每写入一个 token 都会带来额外计算与费用。
**Manus 的经验**:**小黑板要用得省,用得巧,别用来存百科全书**。
---
## 3. 第一步:认识成本 - 你的每一分钱花在哪?
### 3.1 为什么要先看成本?
让我们看看一次典型的 AI 对话,你的钱是怎么花的:
```
💰 成本构成(一次对话):
├─ 70% 重复看旧内容("刚才聊了什么?")
├─ 20% 处理新内容("现在说什么?")
└─ 10% 生成回复("怎么回答?")
```
**惊人发现**:**70% 的钱花在让 AI 重新看你之前说过的话!**
### 3.2 什么是 KV Cache?(前缀复用)
在讨论价格之前,我们得先搞懂一个核心技术概念:**KV Cache(键值缓存)**。
别被这个技术名词吓到,它其实就是 AI 的“短期记忆速查表”。
- **没有 KV Cache 时**:AI 每次都要像第一次看到这篇文章一样,从第一个字开始重新阅读、理解、计算。
- **有了 KV Cache 时**AI 会把看过的部分(Pre-fill)计算结果存下来。下次如果开头的内容没变,它就直接调取记忆,不用重新算了。
这就好比:
> 你去考场考试。
> **情况 A**:每次都要把整本教材从头读一遍,再开始答题。(慢、累、贵)
> **情况 B**:教材内容你已经背滚瓜烂熟了(Cache),坐下直接答题。(快、轻松、便宜)
在云厂商的计费表里,**“背过的书”(Cache Hit)**通常比**“新看的书”(Cache Miss**便宜 90% 以上。
### 3.3 "背课文" vs "现查现用"的价格差
以 Claude 为例:
- **现查现用**(没缓存):$3.00 / 百万字
- **背过再用**(有缓存):$0.30 / 百万字
- **相差 10 倍**
**Manus 的实践**:通过让 AI "背课文",他们把成本从 **$0.15 降到 $0.02****省了 87%**
<ContextWindowVisualizer />
### 3.4 避坑指南:别让时间戳毁了你的“缓存”
很多开发者习惯把“当前时间”写在 System Prompt 的第一句,觉得这样很严谨。
**但这其实是上下文工程中最大的反模式之一。**
想象一下:你背了一整本历史书(System Prompt),结果书的第一行写的是“现在的秒数”。
如果这行字每秒都在变,那你上一秒背的所有内容,下一秒就全废了——你得从头再背一遍。
这就是**前缀复用(KV Cache)**的死穴:**只要开头变了,后面全都要重算。**
#### 错误示范:把动态信息放前面
```text
System: 现在是 2024-01-01 12:00:01。你是助手...
(一分钟后)
System: 现在是 2024-01-01 12:01:01。你是助手...
```
**后果**:虽然只变了几个字,但因为在开头,导致后续 99% 的固定内容无法复用缓存,每次请求都像第一次一样慢且贵。
#### 正确姿势:动静分离
```text
System: 你是助手... (这里放几千字的固定规则、知识库)
User: (在这里通过工具调用或用户消息传入当前时间)
```
**好处**:前面的几千字规则永远不变,AI 只需要“背”一次。后续请求直接调用记忆,速度极快。
👇 **动手点点看**
点击下方的开关,开启**“背课文加速”**,然后多次点击“发送新请求”。
观察一下:当第一块内容变成“已背过”时,**开口速度(TTFT)**会发生什么变化?
<KVCacheDemo />
---
## 4. 第二步:滑动窗口 - 当"记性"变成"成本"
随着对话越来越长,最先遇到的问题就是:**窗口满了怎么办?**
### 4.1 为什么“先进先出”会出问题?
最简单的记忆管理是**滑动窗口(Sliding Window****新的进来,旧的出去**。
这听起来很公平,但在实际任务中却是个灾难。
**场景重现**
```text
对话记录:
[1] 用户:我是张三,负责支付系统
[2] 用户:项目用 Go 语言开发
[3] 用户:数据库是 PostgreSQL
...
[20] 用户:帮我写个接口
```
**结果**:当聊到第 20 句时,第 1 句“我是张三”已经被挤出了窗口。AI 彻底忘了你是谁,也不知道你在负责什么系统。
**问题本质**:这种策略把**重要信息**(身份、技术栈)和**废话**(“好的”、“收到”)同等对待,一起被踢了出去。
### 4.2 "中间失忆症" - 为什么 AI 总看不到关键信息?
除了“忘得快”,AI 还有一个怪癖:**它也会“看漏”**。
研究发现:**AI 对开头和结尾最敏感,中间最容易被忽略**。这就是著名的 **Lost in the Middle(中间迷失)**现象。
**U 型记忆曲线**
```text
位置:开头 → 中间 → 结尾
记忆: 高 → 低 → 高
```
👇 **动手点点看**
1. 先试试**“滑动窗口”**:在下面的聊天框里多发几条消息,看看旧的对话是怎么被无情“挤出去”的。
2. 再看看**“中间迷失”**:观察一下,当关键信息藏在整段话的中间位置时,检索成功率是不是最低的?
<SlidingWindowDemo />
<LostInMiddleDemo />
**解决方案**:把关键信息放在**开头**(系统提示)或**结尾**(用户问题)。
---
## 5. 第三步:选择性保留 - 如何"钉"住关键信息?
既然“先进先出”不靠谱,那我们该怎么办?
Manus 的答案是:**建立“信息等级制度”**。
### 5.1 为什么要给信息分等级?
不再平等对待每条信息,而是根据重要程度决定它们的去留:
| 等级 | 信息类型 | 待遇 | 成本影响 |
| :--- | :--- | :--- | :--- |
| **VIP** | 系统设定、用户身份 | **永远保留** | +15% 成本 |
| **重要** | 当前任务目标 | **任务期内保留** | +10% 成本 |
| **一般** | 普通对话历史 | **最近 5 轮保留** | 基准成本 |
| **可弃** | 可检索的知识 | **用时再查** | -60% 成本 |
**核心思想**:**用 25% 的成本增加,换取 90% 的关键信息保留**。
### 5.2 "钉钉子"策略
你可以把上下文窗口想象成一面黑板:
- **VIP 信息**:用钉子死死**钉在**黑板最上面(System Prompt)。
- **重要信息**:用磁铁**吸在**黑板中间(Context Injection)。
- **普通对话**:写在黑板下半部分,满了就擦掉旧的(Sliding Window)。
👇 **动手点点看**
试着在下面的演示里,把某条重要的对话“钉”住。
观察一下:当你继续聊天时,被钉住的信息是不是一直都在,而没钉住的就被挤走了?
<SelectiveContextDemo />
---
## 6. 第四步:RAG - 当"记性"需要"图书馆"
有时候,我们要处理的信息太多了(比如几百页的技术文档),黑板根本写不下。这时候就需要外挂大脑——**RAG(检索增强生成)**。
### 6.1 为什么“小黑板”不够用?
Manus 面对百万字级的技术文档时,对比了两种做法:
1. **全量写入**:所有内容一次性塞进上下文。
* **后果**:黑板瞬间被占满,处理极慢,而且根据“中间迷失”理论,AI 根本记不住中间的内容。
* **成本**:约 $50/次,等待 15 秒。
2. **按需检索(RAG**:先去图书馆(数据库)查,只把相关的几段话抄到黑板上。
* **后果**:黑板很清爽,AI 聚焦于关键信息。
* **成本**:约 $0.5/次,等待 2 秒。
**省了 99% 的钱,87% 的时间!**
### 6.2 "查资料"的最佳实践
Manus 的经验总结:
* **每本书撕成多大片?** 500-1000 字效果最好。
* **一次查几本书?** 3-5 本,多了反而干扰。
* **多相关的书才查?** 相似度 > 0.7,避免“硬凑”不相关的内容。
👇 **动手点点看**
在搜索框里输入问题(比如“如何重置密码”),看看系统是如何从一大堆文档里只找出最相关的那几条的。
<RAGSimulationDemo />
---
## 7. 第五步:压缩 - 如何让"小黑板"写得更密?
如果信息都很重要,实在删不掉,又不想查资料怎么办?
那就只能**把字写小点**——这就是**上下文压缩**。
### 7.1 什么时候需要"缩写"
* 检索回来的资料太厚(>2000 字)。
* 对话历史太啰嗦(占了 >80% 黑板空间)。
* 需要快速回答,不想让 AI 读长篇大论。
### 7.2 "缩写"的三种境界
| 压缩方式 | 压缩率 | 保留什么 | 适用场景 | 省钱效果 |
| :--- | :--- | :--- | :--- | :--- |
| **总结式** | 70% | 主要意思 | 快速了解 | 省 30% |
| **要点式** | 50% | 关键要点 | 结构化输出 | 省 50% |
| **表格式** | 30% | 核心数据 | 程序处理 | 省 70% |
👇 **动手点点看**
选择不同的压缩策略,看看长篇大论是如何变短、变精炼的。
<ContextCompressionDemo />
---
## 8. 系统整合:打造 AI 的“记忆宫殿”
前面我们像搭积木一样,学习了各种独立的策略:
* **KV Cache**:帮我们省钱(第 3 章)
* **滑动窗口**:帮我们腾位置(第 4 章)
* **分级保留**:帮我们留重点(第 5 章)
* **RAG**:帮我们开外挂(第 6 章)
现在,是时候把这些积木搭成一座完整的城堡了——我们称之为 Manus 的**“记忆宫殿”**。
### 8.1 像盖房子一样组装上下文
不要把上下文看作一堆乱糟糟的文字,而要把它看作一座分层的建筑。每一层都有它独特的功能和“居住规则”。
👇 **动手点点看**
点击“开始建造”,看看我们是如何一层层把这座宫殿盖起来的。
<MemoryPalaceDemo />
### 8.2 为什么这样设计最强?
这座宫殿的设计哲学,其实就为了解决三个矛盾:
1. **地基(System Prompt)—— 解决“贵”的问题**
* **矛盾**:系统设定(你是谁、规则是什么)最长,每次都要发。
* **解法**:把它放在最底层,利用 **KV Cache** 技术,只要不改动,AI 就能“背诵全文”。后续几百轮对话,这部分的计算成本几乎为 **0**
2. **支柱(Task Context)—— 解决“忘”的问题**
* **矛盾**:对话一长,AI 容易忘了最初的任务目标(比如“写一个贪吃蛇游戏”)。
* **解法**:利用**分级保留**策略,把任务目标“钉”在第二层。不管聊了多少轮,这层永远不删,确保 AI 不忘初心。
3. **顶层(Chat & RAG)—— 解决“乱”的问题**
* **矛盾**:又有新对话,又有查到的资料,混在一起容易晕。
* **解法**
* **客厅(对话)**:用**滑动窗口**管理,只留最近 5-10 句热乎的。
* **图书馆(RAG**:资料用完即走,不占地方。
### 8.3 实战效果
Manus 团队把这套架构搬上线后,效果立竿见影:
* **省钱了**:因为地基被“背”下来了,每轮对话的成本暴跌 **84%**
* **变快了**:AI 不用每次都从头读几千字,平均响应时间从 8 秒缩短到 **2 秒**
* **更准了**:关键信息被“钉”死,再也不会聊着聊着就忘了自己是干嘛的。
---
## 9. 实战模板:直接抄作业
为了让你更直观地理解这套机制是如何运作的,我们为你准备了**全链路模拟**。
请选择一个场景,点击“下一步”,看看从用户发问到 AI 回答的几秒钟内,**记忆宫殿**是如何动态调取、组装和清理上下文的。
<MemoryPalaceActionDemo />
### 📝 拿来即用的实战设计
如果你要设计一个类似 Manus 的系统,不要只盯着 Prompt 怎么写,更要关注**系统架构如何调度上下文**。
以下是两个经典场景的**系统设计蓝图**,包含了**提示词设计**和**代码逻辑(伪代码)**。
#### 场景 1:全栈工程师 Agent(长程记忆型)
> **核心挑战**:任务周期长,容易忘了最初的需求和项目背景。
> **解决策略**System 层(身份)+ Task 层(钉死目标)+ Chat 层(滑动窗口)。
**1. 系统提示词 (Layer 1 & 2)**
```markdown
# Layer 1: 身份设定 (System Prompt) - 永远不变,利用 KV Cache
你是一名资深的全栈工程师,精通 Python 和 Vue3。
代码风格:
- 变量命名严格遵守 PEP8
- 关键逻辑必须包含注释
- 优先使用项目已有的工具函数
# Layer 2: 任务锁定 (Task Context) - 任务期间不许删
当前任务:重构支付模块 (payment_module)
核心约束:
1. 必须兼容旧版 API 接口 v1.0
2. 数据库迁移脚本必须是幂等的
3. 截止时间:本周五
```
**2. 上下文组装逻辑 (Pseudo-Code)**
```python
def build_engineer_context(user_input, chat_history, task_info):
context = []
# 1. 地基层:身份设定 (利用 KV Cache 缓存)
# 这部分内容几百轮对话都不变,计算成本几乎为 0
context.append(SYSTEM_PROMPT)
# 2. 支柱层:任务锁定 (Pinned)
# 无论对话多长,这部分永远插入在 System 之后
context.append(f"当前任务:{task_info}")
# 3. 检索层:代码片段 (RAG)
# 根据用户的问题,去代码库里找相关的代码
relevant_code = search_codebase(user_input)
if relevant_code:
context.append(f"参考代码:\n{relevant_code}")
# 4. 交互层:对话历史 (Sliding Window)
# 只取最近 10 轮,避免撑爆上下文
recent_chat = chat_history[-10:]
context.extend(recent_chat)
# 5. 最新输入
context.append(user_input)
return context
```
#### 场景 2:智能客服 Agent(精准问答型)
> **核心挑战**:成本敏感,且绝对不能胡说八道。
> **解决策略**System 层(强约束)+ RAG 层(动态注入)。
**1. 系统提示词 (Layer 1)**
```markdown
# Layer 1: 身份设定 (System Prompt)
你是一名专业的电商客服专员。
回复原则:
1. 语气温柔、专业、简洁
2. **绝对禁止**编造事实,只根据[参考资料]回答
3. 如果资料里没有答案,请直接回答“非常抱歉,这个问题我需要转接人工客服”
```
**2. 上下文组装逻辑 (Pseudo-Code)**
```python
def build_support_context(user_input):
context = []
# 1. 地基层:身份设定
context.append(SYSTEM_PROMPT)
# 2. 图书馆层:动态检索 (RAG)
# 只有客服场景,RAG 才是主角,放在中间位置
docs = vector_db.search(user_input, top_k=3)
context.append("【参考资料开始】")
for doc in docs:
context.append(doc.content)
context.append("【参考资料结束】")
# 3. 交互层:极短的历史
# 客服通常不需要太久远的记忆,保留最近 3 轮即可
context.extend(get_recent_chat(limit=3))
context.append(user_input)
return context
```
---
## 10. 名词对照表
| 英文术语 | 中文对照 | 解释 |
| :--- | :--- | :--- |
| **Context Window** | 上下文窗口 | 模型一次性能够处理的文本最大长度(包括输入和输出)。超出限制的内容会被截断或遗忘。 |
| **Token** | 词元 | LLM 处理文本的最小单位。通常 1 个 Token 约等于 0.75 个英文单词或 0.5 个汉字。计费和窗口限制都以此为单位。 |
| **KV Cache** | KV 缓存 | 一种推理加速技术,通过缓存已经计算过的注意力键值对,避免对重复前缀进行重复计算,显著降低延迟和成本。 |
| **RAG** | 检索增强生成 | 在回答问题前,先从外部知识库检索相关信息,作为上下文提供给模型,以减少幻觉并扩展知识边界。 |
| **Sliding Window** | 滑动窗口 | 最基础的上下文管理策略。保持窗口内 Token 数量恒定,当新内容进入时,自动移除最早的旧内容。 |
| **Lost in Middle** | 中间迷失 | 大模型的一种局限性。研究表明,模型对长上下文开头和结尾的信息记忆最深,而容易忽略中间部分的信息。 |
| **System Prompt** | 系统提示 | 位于对话最开始的指令,用于设定模型的身份、行为规范、回复风格和核心任务。 |
| **Few-shot** | 少样本学习 | 在提示词中提供几个“问题-答案”的示例,帮助模型快速理解任务模式和输出格式。 |
| **Chain of Thought** | 思维链 | 引导模型在给出最终答案前,先输出推理步骤。这种方法能显著提升模型解决复杂逻辑和数学问题的能力。 |
| **Hallucination** | 幻觉 | 模型自信地生成看似合理但实际上错误或不存在的信息的现象。 |
| **Embedding** | 向量化 | 将文本转换为高维数值向量的技术。语义相似的文本在向量空间中的距离更近,是语义搜索的基础。 |
| **Vector DB** | 向量数据库 | 专门用于存储和检索向量数据的数据库。支持通过相似度搜索快速找到与查询最匹配的文档片段。 |
| **Temperature** | 温度 | 控制模型输出随机性的超参数。数值越高(如 0.8)输出越多样、有创意;数值越低(如 0.2)输出越确定、严谨。 |
| **TTFT** | 首字延迟 | Time to First Token,即从用户发送请求到模型输出第一个 Token 所花费的时间,是衡量交互体验的关键指标。 |
---
## 总结:上下文工程的本质
Manus 的四次重构告诉我们:
**从实践来看**:不是记得越多越好,而是记得越有结构、越有选择性越好。
**从成本视角看**
- 大部分浪费来自对固定前缀的重复计算,需要通过前缀稳定和缓存机制解决;
- 重要信息被误删,往往源于“一视同仁”的滑动窗口,需要通过信息分级与钉住策略解决;
- 面对超长文档和知识库时,仅依赖增大上下文窗口并不现实,必须结合检索与压缩机制。
目标是:在给定的模型与上下文上限下,让每一个 token 的投入都具备明确的用途。