1599 lines
43 KiB
Markdown
1599 lines
43 KiB
Markdown
# 上下文工程入门 (Context Engineering)
|
||
|
||
> 💡 **学习指南**:上下文是 AI 理解当前对话的"记忆"。本章节将通过详细的可视化演示和交互式实验,带你掌握上下文窗口管理、记忆系统设计、上下文压缩等核心技能。了解上下文工程,让你的 AI 不仅能"看见"更多信息,还能"理解"得更好。
|
||
|
||
## 0. 引言:什么是上下文工程?
|
||
|
||
**上下文工程**是指在与大语言模型交互时,如何有效地组织、管理和优化输入信息,以在有限的上下文窗口内实现最佳效果的技术。
|
||
|
||
### 0.1 为什么需要上下文工程?
|
||
|
||
**问题场景**:
|
||
|
||
当你问 AI 一个问题时,它需要"记住"很多信息:
|
||
- 📋 **你的问题**(当前在问什么)
|
||
- 📜 **对话历史**(之前说了什么)
|
||
- 📚 **背景知识**(需要知道的资料)
|
||
- 🎯 **任务要求**(期望的输出格式)
|
||
|
||
**上下文窗口的限制**:📦
|
||
|
||
大语言模型的"上下文窗口"(Context Window)就像一个**短期记忆容量**。它决定了模型一次性能"看到"多少文本。
|
||
|
||
**实际影响**:
|
||
|
||
```
|
||
❌ 上下文太小 → AI "忘记"了重要信息
|
||
❌ 上下文太乱 → AI "迷失"在海量文字中
|
||
✅ 上下文工程 → 在有限空间内,呈现最相关的信息
|
||
```
|
||
|
||
### 0.2 上下文工程 vs 提示词工程
|
||
|
||
| 维度 | 提示词工程 | 上下文工程 |
|
||
|------|-----------|-----------|
|
||
| **关注点** | 如何表达需求 | 如何组织信息 |
|
||
| **优化目标** | 让 AI 理解指令 | 让 AI 找到答案 |
|
||
| **主要技术** | 角色设定、任务描述 | 记忆管理、压缩、优先级排序 |
|
||
| **典型场景** | 单次问答 | 长对话、知识检索 |
|
||
| **核心挑战** | 指令清晰度 | 信息密度和相关性 |
|
||
|
||
**简单理解**:
|
||
- 📝 **提示词工程**:教 AI "怎么做"(How to do)
|
||
- 📦 **上下文工程**:给 AI "什么材料"(What to use)
|
||
|
||
---
|
||
|
||
## 1. 理解上下文窗口
|
||
|
||
### 1.1 什么是上下文窗口?
|
||
|
||
**上下文窗口**是大语言模型一次性能处理的最大文本长度。
|
||
|
||
<ContextWindowDemo />
|
||
|
||
**为什么有这个限制**?
|
||
|
||
- 💾 **计算成本**:处理更多文本需要更多计算资源
|
||
- ⏱️ **推理速度**:上下文越长,生成速度越慢
|
||
- 🎯 **性能权衡**:长上下文模型更贵、更慢
|
||
- 🧠 **注意力机制**:Transformer 的注意力复杂度是 O(n²)
|
||
|
||
**历史演进**:
|
||
|
||
```
|
||
2020年: GPT-3 → 2K tokens (约 3 页 A4 纸)
|
||
2022年: GPT-3.5 → 4K tokens (约 6 页 A4 纸)
|
||
2023年: GPT-4 → 8K tokens (约 12 页 A4 纸)
|
||
2023年: Claude 2 → 100K tokens (约 150 页 A4 纸)
|
||
2024年: Gemini → 1M tokens (约 1500 页 A4 纸)
|
||
```
|
||
|
||
### 1.2 Token 换算:如何计算?
|
||
|
||
**什么是 Token?**
|
||
|
||
Token 是文本的最小单位,可以是:
|
||
- 一个完整的单词(如 `hello`)
|
||
- 一个单词的一部分(如 `ing`)
|
||
- 一个汉字或标点符号
|
||
|
||
**实用换算表**:
|
||
|
||
| 文本类型 | Token 数量 | 说明 |
|
||
|---------|-----------|------|
|
||
| 1 个英文单词 | ~1.3 tokens | 平均值 |
|
||
| 1 个汉字 | ~1-2 tokens | 取决于编码 |
|
||
| 1 页 A4 纸(英文) | ~500 tokens | 单倍行距 |
|
||
| 1 页 A4 纸(中文) | ~800 tokens | 单倍行距 |
|
||
| 1 本书(300 页) | ~150K tokens | 技术类书籍 |
|
||
|
||
**代码示例**:
|
||
|
||
```python
|
||
import tiktoken
|
||
|
||
# 计算 Token 数量
|
||
encoder = tiktoken.encoding_for_model("gpt-4")
|
||
|
||
text = "上下文工程是 AI 系统的核心技术"
|
||
tokens = encoder.encode(text)
|
||
print(f"Token 数量: {len(tokens)}") # 输出: Token 数量: 18
|
||
print(f"Tokens: {tokens}") # 输出: Tokens: [32456, 124, 892, ...]
|
||
|
||
# 解码回文本
|
||
decoded = encoder.decode(tokens)
|
||
print(f"解码文本: {decoded}") # 输出: 解码文本: 上下文工程是 AI 系统的核心技术
|
||
```
|
||
|
||
**实用技巧**:
|
||
|
||
1. **预留余量**:上下文窗口的 70-80% 用于输入,20-30% 用于输出
|
||
2. **精确计算**:使用模型对应的 Tokenizer(不同模型不同)
|
||
3. **估算规则**:中文按 1.5 tokens/字,英文按 1.3 tokens/词
|
||
|
||
### 1.3 上下文窗口的实际影响
|
||
|
||
**场景 1:对话系统**
|
||
|
||
```
|
||
用户:你好,我是小明,喜欢吃苹果
|
||
AI:你好小明!很高兴认识你。
|
||
|
||
用户:我姓什么?
|
||
AI:你姓小明。
|
||
|
||
用户:(50 轮对话后)
|
||
用户:我姓什么?
|
||
AI:对不起,我不确定你姓什么(上下文窗口溢出)
|
||
```
|
||
|
||
**场景 2:长文档分析**
|
||
|
||
```
|
||
任务:分析一份 100 页的合同,找出所有风险条款
|
||
|
||
问题:合同超过上下文窗口
|
||
|
||
方案 1:分成小块分析 → 看不到整体逻辑 ❌
|
||
方案 2:只用长上下文模型 → 成本很高 ✅
|
||
方案 3:用压缩 + 记忆系统 → 智能处理 ✅✅
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 记忆管理系统
|
||
|
||
### 2.1 为什么需要记忆系统?
|
||
|
||
**人类记忆的类比**:
|
||
|
||
人类有三个记忆系统:
|
||
1. **感觉记忆**:瞬间记忆(0.5-3 秒)
|
||
2. **短期记忆**:工作记忆(15-30 秒,7±2 个项目)
|
||
3. **长期记忆**:永久存储
|
||
|
||
**AI 的记忆系统**:
|
||
|
||
```
|
||
人类记忆 AI 记忆
|
||
─────────────────────────────────────────
|
||
感觉记忆 → 当前输入 User Query
|
||
短期记忆 → 上下文窗口 Context Window
|
||
长期记忆 → 向量数据库/文件 Vector DB / Files
|
||
```
|
||
|
||
**核心问题**:上下文窗口 = 短期记忆
|
||
|
||
- ⚠️ 容量有限(4K-200K tokens)
|
||
- ⚠️ 会丢失旧信息
|
||
- ⚠️ 每次对话都要重新发送
|
||
|
||
**解决方案**:设计智能的记忆管理系统
|
||
|
||
### 2.2 三层记忆架构
|
||
|
||
**完整架构**:
|
||
|
||
```
|
||
┌─────────────────────────────────────┐
|
||
│ 第 0 层:实时输入 │ 当前问题
|
||
│ - 用户的新问题 │
|
||
│ - 最新的反馈 │ 临时存储
|
||
└─────────────────────────────────────┘
|
||
↓
|
||
┌─────────────────────────────────────┐
|
||
│ 第 1 层:即时上下文 │ 当前会话(最近 N 轮)
|
||
│ - 最近的对话 │
|
||
│ - 当前任务信息 │ 高细节
|
||
│ - 临时数据 │ 快速访问
|
||
│ │ 每次对话都发送
|
||
└─────────────────────────────────────┘
|
||
↓ 归档
|
||
┌─────────────────────────────────────┐
|
||
│ 第 2 层:短期记忆 │ 会话摘要(最近 1-7 天)
|
||
│ - 任务目标 │
|
||
│ - 工作进度 │ 中等细节
|
||
│ - 关键事实 │ 压缩存储
|
||
│ - 待办事项 │ 需要时检索
|
||
└─────────────────────────────────────┘
|
||
↓ 归档
|
||
┌─────────────────────────────────────┐
|
||
│ 第 3 层:长期记忆 │ 用户档案(长期保存)
|
||
│ - 用户偏好 │
|
||
│ - 历史记录 │ 低细节
|
||
│ - 知识库 │ 按需访问
|
||
│ - 个性化设置 │ 向量索引
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
**各层特点对比**:
|
||
|
||
| 层级 | 容量 | 访问速度 | 信息密度 | 持久化 | 更新频率 | 成本 |
|
||
|------|------|---------|---------|--------|---------|------|
|
||
| **实时输入** | 极小 | ⚡⚡⚡⚡⚡ | 原始 | 否 | 每次交互 | 极低 |
|
||
| **即时上下文** | 小(4K-32K) | ⚡⚡⚡⚡ | 高 | 否 | 每轮对话 | 高(每次发送) |
|
||
| **短期记忆** | 中(100K-1M) | ⚡⚡⚡ | 中 | 是 | 每天归档 | 中 |
|
||
| **长期记忆** | 大(1G+) | ⚡⚡ | 低 | 是 | 按需更新 | 低 |
|
||
|
||
### 2.3 即时上下文管理
|
||
|
||
**策略 1:滑动窗口(Sliding Window)**
|
||
|
||
只保留最近 N 轮对话
|
||
|
||
**工作原理**:
|
||
|
||
```python
|
||
# 示例:保留最近 10 轮对话
|
||
conversation_history = [
|
||
{"role": "user", "content": "第一轮对话"},
|
||
{"role": "assistant", "content": "回复"},
|
||
# ... 更多对话
|
||
{"role": "user", "content": "第20轮对话"},
|
||
{"role": "assistant", "content": "回复"}
|
||
]
|
||
|
||
# 滑动窗口:只保留最后 10 轮
|
||
MAX_HISTORY = 10
|
||
recent_context = conversation_history[-MAX_HISTORY:]
|
||
```
|
||
|
||
**可视化演示**:
|
||
|
||
```
|
||
原始对话历史(20 轮):
|
||
[1][2][3][4][5][6][7][8][9][10][11][12][13][14][15][16][17][18][19][20]
|
||
↑
|
||
滑动窗口开始
|
||
↓
|
||
保留的上下文(10 轮):
|
||
[11][12][13][14][15][16][17][18][19][20]
|
||
```
|
||
|
||
**优点**:
|
||
- ✅ 简单易实现
|
||
- ✅ 保证上下文不超限
|
||
- ✅ 始终有最新信息
|
||
|
||
**缺点**:
|
||
- ❌ 可能丢失早期重要信息(如用户姓名、任务目标)
|
||
- ❌ 无法追溯完整对话历史
|
||
- ❌ 长对话中 AI 会"忘记"初始设定
|
||
|
||
**策略 2:智能窗口(Smart Window)**
|
||
|
||
结合关键信息提取的滑动窗口
|
||
|
||
```python
|
||
def smart_window_management(history, window_size=10):
|
||
"""
|
||
智能窗口管理:保留最近的对话 + 关键事实
|
||
"""
|
||
# 1. 提取关键事实(从全部历史)
|
||
key_facts = extract_key_facts(history)
|
||
# 例如:
|
||
# {
|
||
# "user_name": "小明",
|
||
# "goal": "开发一个博客系统",
|
||
# "tech_stack": "Python + FastAPI",
|
||
# "preferences": {"style": "简洁"}
|
||
# }
|
||
|
||
# 2. 获取最近的对话
|
||
recent_conversation = history[-window_size:]
|
||
|
||
# 3. 组合上下文
|
||
context = [
|
||
{
|
||
"role": "system",
|
||
"content": f"""关键信息请记住:
|
||
- 用户姓名:{key_facts['user_name']}
|
||
- 目标:{key_facts['goal']}
|
||
- 技术栈:{key_facts['tech_stack']}
|
||
- 偏好:{key_facts['preferences']}
|
||
"""
|
||
},
|
||
*recent_conversation
|
||
]
|
||
|
||
return context
|
||
```
|
||
|
||
**策略 3:摘要式窗口(Summary Window)**
|
||
|
||
定期生成摘要,释放空间
|
||
|
||
```python
|
||
class SummaryWindow:
|
||
def __init__(self, max_messages=20, summary_interval=10):
|
||
self.history = []
|
||
self.max_messages = max_messages
|
||
self.summary_interval = summary_interval
|
||
self.summaries = []
|
||
|
||
def add_message(self, role, content):
|
||
self.history.append({"role": role, "content": content})
|
||
|
||
# 检查是否需要摘要
|
||
if len(self.history) >= self.max_messages:
|
||
self._compress_history()
|
||
|
||
def _compress_history(self):
|
||
"""
|
||
压缩历史:将旧消息转为摘要
|
||
"""
|
||
# 1. 保留最近的消息
|
||
keep_recent = self.history[-self.summary_interval:]
|
||
|
||
# 2. 将旧消息摘要化
|
||
old_messages = self.history[:-self.summary_interval]
|
||
summary = generate_summary(old_messages)
|
||
|
||
# 3. 保存摘要
|
||
self.summaries.append(summary)
|
||
|
||
# 4. 更新历史
|
||
self.history = keep_recent
|
||
|
||
def get_context(self):
|
||
"""
|
||
获取完整上下文:摘要 + 最近对话
|
||
"""
|
||
context = []
|
||
|
||
# 添加所有摘要
|
||
for summary in self.summaries:
|
||
context.append({
|
||
"role": "system",
|
||
"content": f"[对话摘要] {summary}"
|
||
})
|
||
|
||
# 添加最近的对话
|
||
context.extend(self.history)
|
||
|
||
return context
|
||
|
||
# 使用示例
|
||
window = SummaryWindow(max_messages=20, summary_interval=10)
|
||
|
||
window.add_message("user", "我是小明")
|
||
window.add_message("assistant", "你好小明!")
|
||
# ... 更多对话
|
||
|
||
# 当消息达到 20 条时,自动压缩前 10 条为摘要
|
||
context = window.get_context()
|
||
```
|
||
|
||
### 2.4 短期记忆管理
|
||
|
||
**目标**:管理当前会话的重要信息(1-7 天)
|
||
|
||
**存储什么?**
|
||
|
||
```python
|
||
short_term_memory = {
|
||
# 会话信息
|
||
"session_id": "sess_20250115_001",
|
||
|
||
# 用户目标
|
||
"user_goal": {
|
||
"primary": "开发一个博客系统",
|
||
"sub_goals": [
|
||
"设计数据库架构",
|
||
"实现后端 API",
|
||
"开发前端界面"
|
||
],
|
||
"constraints": ["时间: 2周", "预算: $0"]
|
||
},
|
||
|
||
# 工作进度
|
||
"progress": {
|
||
"completed": ["需求分析", "技术选型"],
|
||
"in_progress": ["数据库设计"],
|
||
"pending": ["API 开发", "前端开发"]
|
||
},
|
||
|
||
# 关键事实
|
||
"key_facts": {
|
||
"user_name": "小明",
|
||
"tech_stack": ["Python", "FastAPI", "Vue.js"],
|
||
"database": "PostgreSQL",
|
||
"deployment": "Docker"
|
||
},
|
||
|
||
# 临时数据
|
||
"temp_data": {
|
||
"last_code_snippet": "...",
|
||
"recent_errors": ["Error 1", "Error 2"]
|
||
},
|
||
|
||
# 元数据
|
||
"created_at": "2025-01-15T10:00:00",
|
||
"updated_at": "2025-01-15T15:30:00",
|
||
"message_count": 45
|
||
}
|
||
```
|
||
|
||
**自动更新机制**:
|
||
|
||
```python
|
||
class ShortTermMemoryManager:
|
||
def __init__(self, ttl=7*24*3600): # 7 天过期
|
||
self.memory = {} # {session_id: memory_data}
|
||
self.ttl = ttl
|
||
|
||
def update(self, session_id, updates):
|
||
"""
|
||
更新短期记忆
|
||
"""
|
||
if session_id not in self.memory:
|
||
self.memory[session_id] = {
|
||
"session_id": session_id,
|
||
"created_at": datetime.now(),
|
||
"updated_at": datetime.now()
|
||
}
|
||
|
||
# 更新字段
|
||
for key, value in updates.items():
|
||
if key == "progress":
|
||
# 智能合并进度
|
||
self._merge_progress(session_id, value)
|
||
else:
|
||
self.memory[session_id][key] = value
|
||
|
||
# 更新时间戳
|
||
self.memory[session_id]["updated_at"] = datetime.now()
|
||
|
||
def _merge_progress(self, session_id, new_progress):
|
||
"""
|
||
智能合并进度信息
|
||
"""
|
||
current = self.memory[session_id].get("progress", {})
|
||
|
||
for status in ["completed", "in_progress", "pending"]:
|
||
if status in new_progress:
|
||
current[status] = current.get(status, [])
|
||
current[status].extend(new_progress[status])
|
||
# 去重
|
||
current[status] = list(set(current[status]))
|
||
|
||
self.memory[session_id]["progress"] = current
|
||
|
||
def get(self, session_id):
|
||
"""
|
||
获取短期记忆
|
||
"""
|
||
if session_id not in self.memory:
|
||
return None
|
||
|
||
# 检查是否过期
|
||
memory = self.memory[session_id]
|
||
age = (datetime.now() - memory["updated_at"]).total_seconds()
|
||
|
||
if age > self.ttl:
|
||
# 过期,删除
|
||
del self.memory[session_id]
|
||
return None
|
||
|
||
return memory
|
||
|
||
def cleanup_expired(self):
|
||
"""
|
||
清理过期的记忆
|
||
"""
|
||
now = datetime.now()
|
||
expired = []
|
||
|
||
for session_id, memory in self.memory.items():
|
||
age = (now - memory["updated_at"]).total_seconds()
|
||
if age > self.ttl:
|
||
expired.append(session_id)
|
||
|
||
for session_id in expired:
|
||
del self.memory[session_id]
|
||
|
||
return len(expired)
|
||
```
|
||
|
||
### 2.5 长期记忆管理
|
||
|
||
**目标**:永久保存用户画像、历史记录、知识库
|
||
|
||
**存储结构**:
|
||
|
||
```python
|
||
long_term_memory = {
|
||
# 用户画像
|
||
"user_profile": {
|
||
"user_id": "user_12345",
|
||
"name": "小明",
|
||
"preferences": {
|
||
"communication_style": "简洁",
|
||
"coding_style": "Pythonic",
|
||
"learning_style": "实战导向"
|
||
},
|
||
"expertise": ["Python", "Web 开发", "AI"],
|
||
"goals_history": [
|
||
{
|
||
"goal": "学习 FastAPI",
|
||
"achieved": True,
|
||
"date": "2024-12-01"
|
||
},
|
||
{
|
||
"goal": "开发博客系统",
|
||
"achieved": False,
|
||
"date": "2025-01-15"
|
||
}
|
||
]
|
||
},
|
||
|
||
# 知识库(向量存储)
|
||
"knowledge_base": {
|
||
"documents": [
|
||
{
|
||
"id": "doc_001",
|
||
"content": "FastAPI 是一个现代、快速的 Web 框架...",
|
||
"embedding": [0.1, 0.2, ...], # 向量
|
||
"metadata": {
|
||
"topic": "FastAPI",
|
||
"importance": "high"
|
||
}
|
||
}
|
||
]
|
||
},
|
||
|
||
# 技能映射
|
||
"skill_map": {
|
||
"Python": {
|
||
"level": "advanced",
|
||
"last_practiced": "2025-01-10",
|
||
"related_projects": ["博客系统", "API 服务"]
|
||
}
|
||
},
|
||
|
||
# 交互历史摘要
|
||
"interaction_history": {
|
||
"total_sessions": 50,
|
||
"total_messages": 1234,
|
||
"favorite_topics": ["Web 开发", "AI 应用"],
|
||
"success_rate": 0.85
|
||
}
|
||
}
|
||
```
|
||
|
||
**检索机制**:
|
||
|
||
```python
|
||
class LongTermMemoryManager:
|
||
def __init__(self, vector_db):
|
||
self.vector_db = vector_db # 向量数据库
|
||
self.profiles = {} # 用户画像
|
||
|
||
def store(self, user_id, content, metadata):
|
||
"""
|
||
存储到长期记忆
|
||
"""
|
||
# 1. 生成向量
|
||
embedding = generate_embedding(content)
|
||
|
||
# 2. 存储到向量数据库
|
||
doc_id = self.vector_db.add(
|
||
content=content,
|
||
embedding=embedding,
|
||
metadata={
|
||
"user_id": user_id,
|
||
**metadata
|
||
}
|
||
)
|
||
|
||
return doc_id
|
||
|
||
def retrieve(self, user_id, query, top_k=5):
|
||
"""
|
||
从长期记忆检索相关信息
|
||
"""
|
||
# 1. 生成查询向量
|
||
query_embedding = generate_embedding(query)
|
||
|
||
# 2. 向量检索
|
||
results = self.vector_db.search(
|
||
query_embedding,
|
||
filter={"user_id": user_id},
|
||
top_k=top_k
|
||
)
|
||
|
||
return results
|
||
|
||
def update_profile(self, user_id, profile_data):
|
||
"""
|
||
更新用户画像
|
||
"""
|
||
if user_id not in self.profiles:
|
||
self.profiles[user_id] = {}
|
||
|
||
self.profiles[user_id].update(profile_data)
|
||
|
||
def get_profile(self, user_id):
|
||
"""
|
||
获取用户画像
|
||
"""
|
||
return self.profiles.get(user_id, {})
|
||
```
|
||
|
||
### 2.6 记忆整合系统
|
||
|
||
**将三层记忆整合到一个系统**:
|
||
|
||
```python
|
||
class MemorySystem:
|
||
def __init__(self):
|
||
# 第 1 层:即时上下文
|
||
self.immediate_context = ImmediateContext(max_messages=10)
|
||
|
||
# 第 2 层:短期记忆
|
||
self.short_term = ShortTermMemoryManager(ttl=7*24*3600)
|
||
|
||
# 第 3 层:长期记忆
|
||
self.long_term = LongTermMemoryManager(vector_db)
|
||
|
||
def process_message(self, user_id, message):
|
||
"""
|
||
处理新消息:更新三层记忆
|
||
"""
|
||
# 1. 添加到即时上下文
|
||
self.immediate_context.add_message("user", message)
|
||
|
||
# 2. 提取并更新短期记忆
|
||
key_info = extract_key_info(message)
|
||
self.short_term.update(user_id, key_info)
|
||
|
||
# 3. 重要信息存入长期记忆
|
||
if is_important(message):
|
||
self.long_term.store(
|
||
user_id=user_id,
|
||
content=message,
|
||
metadata={"type": "important_fact", "timestamp": now()}
|
||
)
|
||
|
||
def build_context(self, user_id, query):
|
||
"""
|
||
构建完整上下文
|
||
"""
|
||
context_parts = []
|
||
|
||
# 1. 从长期记忆检索相关信息
|
||
long_term_info = self.long_term.retrieve(user_id, query, top_k=3)
|
||
if long_term_info:
|
||
context_parts.append({
|
||
"role": "system",
|
||
"content": f"[相关历史] {format_retrieved_info(long_term_info)}"
|
||
})
|
||
|
||
# 2. 从短期记忆获取会话信息
|
||
short_term_info = self.short_term.get(user_id)
|
||
if short_term_info:
|
||
context_parts.append({
|
||
"role": "system",
|
||
"content": f"[当前会话] 目标:{short_term_info['user_goal']}\n进度:{short_term_info['progress']}"
|
||
})
|
||
|
||
# 3. 获取即时上下文
|
||
immediate = self.immediate_context.get_context()
|
||
context_parts.extend(immediate)
|
||
|
||
return context_parts
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 上下文压缩技术
|
||
|
||
### 3.1 为什么需要压缩?
|
||
|
||
**问题场景**:
|
||
|
||
```
|
||
任务:总结一份 100 页的报告
|
||
|
||
问题:
|
||
- 报告有 50K tokens
|
||
- 上下文窗口只有 8K tokens
|
||
- 无法一次性放入所有内容
|
||
|
||
解决方案:
|
||
压缩到 8K tokens 以内,同时保留关键信息
|
||
```
|
||
|
||
**压缩的权衡**:
|
||
|
||
| 压缩率 | 信息保留 | 适用场景 |
|
||
|--------|---------|---------|
|
||
| 0-20% | 90-100% | 不需要压缩 |
|
||
| 20-50% | 70-90% | 保留重要细节 |
|
||
| 50-80% | 40-70% | 快速浏览 |
|
||
| 80-95% | 10-40% | 极简摘要 |
|
||
|
||
### 3.2 压缩方法 1:摘要压缩(Summarization)
|
||
|
||
**原理**:用 LLM 生成文本摘要
|
||
|
||
**基础版本**:
|
||
|
||
```python
|
||
def compress_by_summarization(text, target_ratio=0.3):
|
||
"""
|
||
将文本压缩到原长度的 30%
|
||
"""
|
||
current_length = count_tokens(text)
|
||
target_length = int(current_length * target_ratio)
|
||
|
||
prompt = f"""
|
||
将以下文本压缩到 {target_length} tokens 以内。
|
||
保留所有关键信息,删除冗余内容。
|
||
|
||
原文:
|
||
{text}
|
||
|
||
压缩后的文本:
|
||
"""
|
||
|
||
compressed = llm_call(prompt, max_tokens=target_length)
|
||
return compressed
|
||
```
|
||
|
||
**进阶版本:迭代式摘要**:
|
||
|
||
```python
|
||
def iterative_summarization(text, target_tokens):
|
||
"""
|
||
迭代式摘要:逐步压缩到目标大小
|
||
"""
|
||
current_text = text
|
||
current_length = count_tokens(text)
|
||
|
||
while current_length > target_tokens:
|
||
# 每次压缩 50%
|
||
compression_ratio = target_tokens / current_length
|
||
|
||
prompt = f"""
|
||
将以下文本压缩到原长度的 {compression_ratio*100:.0f}%。
|
||
保留关键信息,删除冗余。
|
||
|
||
原文:
|
||
{current_text}
|
||
|
||
压缩后:
|
||
"""
|
||
|
||
current_text = llm_call(prompt)
|
||
current_length = count_tokens(current_text)
|
||
|
||
# 防止无限循环
|
||
if current_length == count_tokens(text):
|
||
break
|
||
|
||
return current_text
|
||
|
||
# 示例
|
||
long_text = "..." # 50K tokens
|
||
compressed = iterative_summarization(long_text, target_tokens=5000)
|
||
```
|
||
|
||
**分层摘要(Hierarchical Summarization)**:
|
||
|
||
```python
|
||
def hierarchical_summarization(text, levels=3):
|
||
"""
|
||
分层摘要:生成不同详细程度的摘要
|
||
"""
|
||
summaries = {}
|
||
|
||
# 第 1 层:详细摘要(50%)
|
||
summaries["detailed"] = summarize(text, target_ratio=0.5)
|
||
|
||
# 第 2 层:简明摘要(20%)
|
||
summaries["brief"] = summarize(
|
||
summaries["detailed"],
|
||
target_ratio=0.4
|
||
)
|
||
|
||
# 第 3 层:核心要点(5%)
|
||
summaries["key_points"] = extract_key_points(
|
||
summaries["brief"]
|
||
)
|
||
|
||
return summaries
|
||
|
||
# 使用示例
|
||
summaries = hierarchical_summarization(long_report)
|
||
|
||
# 根据任务选择合适详细程度
|
||
if task_type == "quick_overview":
|
||
context = summaries["key_points"] # 最简洁
|
||
elif task_type == "detailed_analysis":
|
||
context = summaries["detailed"] # 最详细
|
||
else:
|
||
context = summaries["brief"] # 中等
|
||
```
|
||
|
||
### 3.3 压缩方法 2:选择性保留(Selective Retention)
|
||
|
||
**原理**:根据重要性选择保留内容
|
||
|
||
**优先级规则**:
|
||
|
||
```python
|
||
IMPORTANCE_RULES = {
|
||
# 最高优先级(必须保留)
|
||
"user_question": 1, # 用户当前问题
|
||
"system_prompt": 2, # 系统提示词
|
||
"key_constraints": 3, # 关键约束条件
|
||
|
||
# 高优先级(尽量保留)
|
||
"recent_conversation": 4, # 最近对话
|
||
"task_goal": 5, # 任务目标
|
||
"critical_facts": 6, # 关键事实
|
||
|
||
# 中等优先级(空间允许时保留)
|
||
"background_info": 7, # 背景信息
|
||
"examples": 8, # 示例代码
|
||
"historical_context": 9, # 历史上下文
|
||
|
||
# 低优先级(可省略)
|
||
"detailed_explanation": 10, # 详细解释
|
||
"redundant_info": 11, # 冗余信息
|
||
}
|
||
```
|
||
|
||
**实现**:
|
||
|
||
```python
|
||
def selective_compression(context_items, budget):
|
||
"""
|
||
根据优先级选择性保留内容
|
||
"""
|
||
selected = []
|
||
current_tokens = 0
|
||
|
||
# 按优先级排序
|
||
sorted_items = sorted(
|
||
context_items,
|
||
key=lambda x: IMPORTANCE_RULES.get(x["type"], 99)
|
||
)
|
||
|
||
for item in sorted_items:
|
||
item_tokens = count_tokens(item["content"])
|
||
|
||
if current_tokens + item_tokens <= budget:
|
||
# 完全保留
|
||
selected.append(item)
|
||
current_tokens += item_tokens
|
||
else:
|
||
# 尝试压缩
|
||
remaining_budget = budget - current_tokens
|
||
compressed = compress_item(item, remaining_budget)
|
||
|
||
if compressed:
|
||
selected.append(compressed)
|
||
current_tokens += count_tokens(compressed["content"])
|
||
|
||
# 预算用完,停止
|
||
break
|
||
|
||
return selected
|
||
|
||
# 使用示例
|
||
context_items = [
|
||
{"type": "user_question", "content": "如何优化这个函数?"},
|
||
{"type": "background_info", "content": "这是一个..."},
|
||
{"type": "examples", "content": "示例代码..."},
|
||
# ... 更多项目
|
||
]
|
||
|
||
budget = 4000 # tokens
|
||
selected = selective_compression(context_items, budget)
|
||
```
|
||
|
||
### 3.4 压缩方法 3:结构化压缩(Structured Compression)
|
||
|
||
**原理**:保留结构,压缩细节
|
||
|
||
**示例:保留代码结构**
|
||
|
||
```python
|
||
def compress_code_structure(code, detail_level="medium"):
|
||
"""
|
||
压缩代码,但保留结构
|
||
"""
|
||
if detail_level == "high":
|
||
# 保留完整代码
|
||
return code
|
||
|
||
elif detail_level == "medium":
|
||
# 保留函数签名和注释,删除实现细节
|
||
compressed = []
|
||
for line in code.split('\n'):
|
||
stripped = line.strip()
|
||
|
||
# 保留空行、注释、函数/类定义
|
||
if (not stripped or
|
||
stripped.startswith('#') or
|
||
stripped.startswith('def ') or
|
||
stripped.startswith('class ')):
|
||
compressed.append(line)
|
||
# 跳过实现细节
|
||
elif not line.startswith(' '):
|
||
compressed.append(line)
|
||
|
||
return '\n'.join(compressed)
|
||
|
||
elif detail_level == "low":
|
||
# 只保留结构(类名、函数名)
|
||
parser = parse_code(code)
|
||
structure = []
|
||
|
||
for cls in parser.get_classes():
|
||
structure.append(f"class {cls.name}")
|
||
for method in cls.methods:
|
||
structure.append(f" def {method.name}({method.params})")
|
||
|
||
return '\n'.join(structure)
|
||
|
||
# 示例
|
||
code = """
|
||
def calculate_sum(numbers):
|
||
'''计算数字列表的总和'''
|
||
total = 0
|
||
for num in numbers:
|
||
total += num
|
||
return total
|
||
|
||
def calculate_average(numbers):
|
||
'''计算数字列表的平均值'''
|
||
if not numbers:
|
||
return 0
|
||
return calculate_sum(numbers) / len(numbers)
|
||
"""
|
||
|
||
print(compress_code_structure(code, detail_level="medium"))
|
||
# 输出:
|
||
# def calculate_sum(numbers):
|
||
# '''计算数字列表的总和'''
|
||
# def calculate_average(numbers):
|
||
# '''计算数字列表的平均值'''
|
||
```
|
||
|
||
### 3.5 压缩方法 4:语义压缩(Semantic Compression)
|
||
|
||
**原理**:提取语义信息,丢弃表面形式
|
||
|
||
**提取关键信息**:
|
||
|
||
```python
|
||
def semantic_compression(text):
|
||
"""
|
||
语义压缩:提取关键信息
|
||
"""
|
||
# 1. 识别关键实体
|
||
entities = extract_entities(text)
|
||
# 例如:人名、地名、技术名词、数字
|
||
|
||
# 2. 识别关键关系
|
||
relations = extract_relations(text)
|
||
# 例如:A 包含 B、C 导致 D
|
||
|
||
# 3. 生成结构化表示
|
||
compressed = {
|
||
"entities": entities,
|
||
"relations": relations,
|
||
"main_idea": extract_main_idea(text)
|
||
}
|
||
|
||
return compressed
|
||
|
||
# 示例
|
||
text = """
|
||
FastAPI 是一个现代、快速的 Web 框架,用于基于 Python
|
||
构建 API。它由 Sebastián Ramírez 创建,于 2018 年首次发布。
|
||
FastAPI 使用 Starlette 进行路由,使用 Pydantic 进行数据验证。
|
||
"""
|
||
|
||
compressed = semantic_compression(text)
|
||
# 输出:
|
||
# {
|
||
# "entities": ["FastAPI", "Web 框架", "Python", "Sebastián Ramírez",
|
||
# "2018", "Starlette", "Pydantic"],
|
||
# "relations": [
|
||
# "FastAPI 是 Web 框架",
|
||
# "FastAPI 基于 Python",
|
||
# "FastAPI 由 Sebastián Ramírez 创建",
|
||
# "FastAPI 使用 Starlette 进行路由",
|
||
# "FastAPI 使用 Pydantic 进行数据验证"
|
||
# ],
|
||
# "main_idea": "FastAPI 是一个现代的 Python Web 框架"
|
||
# }
|
||
```
|
||
|
||
### 3.6 压缩方法 5:增量压缩(Incremental Compression)
|
||
|
||
**原理**:逐步压缩,每步保留关键信息
|
||
|
||
```python
|
||
def incremental_compression(text, budget):
|
||
"""
|
||
增量压缩:逐步压缩到目标大小
|
||
"""
|
||
compression_steps = [
|
||
("删除冗余", remove_redundancy),
|
||
("合并重复", merge_duplicates),
|
||
("压缩长段落", compress_long_paragraphs),
|
||
("提取要点", extract_key_points),
|
||
("极致压缩", extreme_compression)
|
||
]
|
||
|
||
current = text
|
||
current_size = count_tokens(text)
|
||
|
||
for step_name, compress_func in compression_steps:
|
||
if current_size <= budget:
|
||
print(f"在 '{step_name}' 前已满足要求")
|
||
break
|
||
|
||
print(f"执行: {step_name}")
|
||
current = compress_func(current)
|
||
current_size = count_tokens(current)
|
||
|
||
print(f" 当前大小: {current_size} tokens")
|
||
|
||
if current_size <= budget:
|
||
print(f"✓ 在 '{step_name}' 完成压缩")
|
||
break
|
||
|
||
return current
|
||
|
||
def remove_redundancy(text):
|
||
"""删除冗余内容"""
|
||
# 1. 删除重复的句子
|
||
sentences = text.split('. ')
|
||
seen = set()
|
||
unique_sentences = []
|
||
|
||
for sent in sentences:
|
||
# 使用简单的相似度判断
|
||
sent_hash = hash(sent.lower().strip())
|
||
if sent_hash not in seen:
|
||
seen.add(sent_hash)
|
||
unique_sentences.append(sent)
|
||
|
||
return '. '.join(unique_sentences)
|
||
|
||
def merge_duplicates(text):
|
||
"""合并重复的信息"""
|
||
# 实现略...
|
||
pass
|
||
|
||
def compress_long_paragraphs(text, max_length=100):
|
||
"""压缩过长的段落"""
|
||
paragraphs = text.split('\n\n')
|
||
compressed = []
|
||
|
||
for para in paragraphs:
|
||
if count_tokens(para) > max_length:
|
||
# 生成摘要
|
||
compressed.append(summarize(para, target_ratio=0.5))
|
||
else:
|
||
compressed.append(para)
|
||
|
||
return '\n\n'.join(compressed)
|
||
|
||
# 使用示例
|
||
long_text = "..." # 假设有 20K tokens
|
||
compressed = incremental_compression(long_text, budget=5000)
|
||
```
|
||
|
||
### 3.7 压缩质量评估
|
||
|
||
**如何判断压缩是否丢失重要信息?**
|
||
|
||
```python
|
||
def evaluate_compression(original, compressed):
|
||
"""
|
||
评估压缩质量
|
||
"""
|
||
metrics = {}
|
||
|
||
# 1. 压缩率
|
||
original_size = count_tokens(original)
|
||
compressed_size = count_tokens(compressed)
|
||
metrics["compression_ratio"] = compressed_size / original_size
|
||
|
||
# 2. 信息保留率(使用 LLM 评估)
|
||
metrics["info_retention"] = llm_evaluate_retention(original, compressed)
|
||
|
||
# 3. 关键事实保留检查
|
||
original_facts = extract_facts(original)
|
||
compressed_facts = extract_facts(compressed)
|
||
|
||
retained_facts = set(original_facts) & set(compressed_facts)
|
||
metrics["fact_retention_rate"] = len(retained_facts) / len(original_facts)
|
||
|
||
# 4. 一致性检查(LLM 判断)
|
||
metrics["consistency"] = llm_check_consistency(original, compressed)
|
||
|
||
# 综合评分
|
||
weights = {
|
||
"compression_ratio": 0.2,
|
||
"info_retention": 0.4,
|
||
"fact_retention_rate": 0.3,
|
||
"consistency": 0.1
|
||
}
|
||
|
||
overall_score = sum(
|
||
metrics[key] * weights[key]
|
||
for key in metrics
|
||
)
|
||
|
||
return {
|
||
"overall": overall_score,
|
||
"metrics": metrics
|
||
}
|
||
|
||
# 示例
|
||
evaluation = evaluate_compression(original_text, compressed_text)
|
||
print(f"压缩质量评分: {evaluation['overall']:.2f}/1.0")
|
||
print(f"信息保留率: {evaluation['metrics']['info_retention']:.2%}")
|
||
```
|
||
|
||
---
|
||
|
||
## 4. RAG:检索增强生成(简介)
|
||
|
||
### 4.1 什么是 RAG?
|
||
|
||
**RAG (Retrieval-Augmented Generation)** 检索增强生成,是一种结合信息检索和文本生成的技术。
|
||
|
||
< RAGPipelineDemo />
|
||
|
||
**核心思想**:
|
||
|
||
```
|
||
传统方法:
|
||
用户问题 → 直接问 LLM → 回答(可能过时或错误)
|
||
|
||
RAG 方法:
|
||
用户问题 → 检索相关文档 → 将文档加入上下文 → 问 LLM → 回答(基于真实数据)
|
||
```
|
||
|
||
### 4.2 为什么需要 RAG?
|
||
|
||
| 问题 | 传统方法 | RAG 方法 |
|
||
|------|---------|---------|
|
||
| 知识截止日期 | ❌ 模型知识有截止日期 | ✅ 实时更新知识库 |
|
||
| 私有数据 | ❌ 无法访问私有文档 | ✅ 支持企业内部文档 |
|
||
| 幻觉问题 | ❌ 容易胡编乱造 | ✅ 基于真实数据回答 |
|
||
|
||
### 4.3 RAG 的基本流程
|
||
|
||
```
|
||
用户问题
|
||
↓
|
||
文档检索(向量数据库)
|
||
↓
|
||
上下文构建
|
||
↓
|
||
LLM 生成答案
|
||
↓
|
||
返回结果
|
||
```
|
||
|
||
**简单示例**:
|
||
|
||
```python
|
||
# 伪代码
|
||
def rag_query(question):
|
||
# 1. 检索相关文档
|
||
docs = vector_db.search(question, top_k=3)
|
||
|
||
# 2. 构建上下文
|
||
context = "\n\n".join([doc["content"] for doc in docs])
|
||
|
||
# 3. 生成答案
|
||
prompt = f"""
|
||
基于以下文档回答问题:
|
||
|
||
文档:
|
||
{context}
|
||
|
||
问题:{question}
|
||
|
||
答案:
|
||
"""
|
||
|
||
answer = llm_call(prompt)
|
||
return answer
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 实战技巧
|
||
|
||
### 5.1 上下文模板化
|
||
|
||
使用模板组织上下文,提高一致性和效率。
|
||
|
||
**标准上下文模板**:
|
||
|
||
```markdown
|
||
# 系统角色
|
||
{role_definition}
|
||
|
||
# 任务目标
|
||
{objective}
|
||
|
||
# 背景信息
|
||
{background}
|
||
|
||
# 约束条件
|
||
{constraints}
|
||
|
||
# 参考文档
|
||
{documents}
|
||
|
||
# 历史对话
|
||
{conversation_history}
|
||
|
||
# 当前问题
|
||
{current_question}
|
||
|
||
# 输出要求
|
||
{output_requirements}
|
||
```
|
||
|
||
### 5.2 动态上下文调整
|
||
|
||
根据任务类型动态调整上下文策略。
|
||
|
||
```python
|
||
CONTEXT_STRATEGIES = {
|
||
"code_review": {
|
||
"priority": ["code", "requirements", "standards"],
|
||
"format": "structured",
|
||
"compression": "low"
|
||
},
|
||
|
||
"qa": {
|
||
"priority": ["knowledge_base", "user_query", "conversation_history"],
|
||
"format": "concise",
|
||
"compression": "medium"
|
||
},
|
||
|
||
"creative_writing": {
|
||
"priority": ["prompt", "style_examples", "genre_rules"],
|
||
"format": "narrative",
|
||
"compression": "minimal"
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.3 上下文质量检查
|
||
|
||
```python
|
||
def check_context_quality(context):
|
||
"""
|
||
检查上下文质量
|
||
"""
|
||
checks = {
|
||
"length": count_tokens(context) < MAX_CONTEXT,
|
||
"relevance": calculate_relevance(context) > 0.7,
|
||
"clarity": check_structure(context),
|
||
"completeness": has_required_elements(context)
|
||
}
|
||
|
||
return all(checks.values()), checks
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 总结:上下文工程的核心原则
|
||
|
||
### 6.1 设计原则
|
||
|
||
1. **质量 > 数量**
|
||
- 不是上下文越长越好
|
||
- 相关性比全面性更重要
|
||
- 精心组织比堆砌信息有效
|
||
|
||
2. **分层记忆**
|
||
- 即时上下文:当前会话
|
||
- 短期记忆:会话摘要
|
||
- 长期记忆:用户档案
|
||
|
||
3. **智能压缩**
|
||
- 根据重要性选择保留内容
|
||
- 使用多种压缩方法
|
||
- 评估压缩质量
|
||
|
||
4. **动态调整**
|
||
- 根据任务类型选择策略
|
||
- 根据预算压缩内容
|
||
- 持续监控和优化
|
||
|
||
### 6.2 实践建议
|
||
|
||
**上下文工程检查清单**:
|
||
|
||
```
|
||
□ 明确任务类型(问答、分析、创作等)
|
||
□ 确定预算限制(上下文窗口大小)
|
||
□ 设计记忆系统(三层架构)
|
||
□ 选择合适的压缩策略
|
||
□ 组织上下文结构(清晰的格式)
|
||
□ 评估上下文质量(相关性、完整性)
|
||
□ 监控效果(准确率、成本、延迟)
|
||
□ 持续优化(迭代改进)
|
||
```
|
||
|
||
### 6.3 常见陷阱
|
||
|
||
| 陷阱 | 表现 | 解决方案 |
|
||
|------|------|---------|
|
||
| **上下文过载** | 塞入太多信息 | 精准选择,动态压缩 |
|
||
| **信息混乱** | 没有清晰结构 | 使用模板,结构化组织 |
|
||
| **过度压缩** | 丢失关键信息 | 分层压缩,保留要点 |
|
||
| **忽视记忆** | 不使用长期记忆 | 设计三层记忆系统 |
|
||
| **静态不变** | 一套模板用所有场景 | 动态调整,因任务而异 |
|
||
|
||
---
|
||
|
||
## 7. Agent 上下文工程
|
||
|
||
> 💡 **特别说明**:Agent 系统对上下文工程有特殊要求。与普通聊天机器人不同,Agent 需要通过 50+ 次工具调用完成任务,每次迭代都会增加上下文长度。本章节基于 Manus 等实战经验,介绍 Agent 上下文工程的关键技术。
|
||
|
||
### 7.1 Agent 与普通应用的区别
|
||
|
||
**Agent 的特殊性**:
|
||
|
||
- ❌ **聊天机器人**:单轮或几轮对话,上下文短,不需要优化
|
||
- ✅ **AI Agent**:多轮迭代完成任务,上下文长,必须精心设计
|
||
|
||
**为什么 Agent 需要特殊的上下文工程?**
|
||
|
||
```
|
||
Agent 的一个任务可能需要 50+ 次工具调用:
|
||
|
||
迭代 1:输入(系统提示 + 工具定义)→ 输出(search 调用)
|
||
迭代 2:输入(+ 第一次动作 + 观察)→ 输出(read_page 调用)
|
||
迭代 3:输入(+ 第二次动作 + 观察)→ 输出(think 调用)
|
||
...
|
||
迭代 50:输入(+ 第49次动作 + 观察)→ 输出(最终答案)
|
||
|
||
如果上下文管理不当:
|
||
💰 成本爆炸:每次都重新计算整个上下文
|
||
🐌 速度变慢:上下文越长,推理越慢
|
||
📉 性能下降:模型在超长上下文中"迷失"
|
||
```
|
||
|
||
<AgentContextFlow />
|
||
|
||
### 7.2 核心指标:KV 缓存命中率
|
||
|
||
**什么是 KV 缓存?**
|
||
|
||
KV (Key-Value) 缓存是 LLM 推理的优化技术:
|
||
- 相同的前缀可以被缓存
|
||
- 缓存的部分不需要重新计算
|
||
- 大幅降低成本和延迟
|
||
|
||
**成本对比**(以 Claude 为例):
|
||
- 未缓存输入:`$3.00 / 百万 tokens`
|
||
- 缓存输入:`$0.30 / 百万 tokens`
|
||
- **节省 90%** 🎉
|
||
|
||
**Agent 的输入输出比**:
|
||
|
||
```
|
||
输入/输出比 ≈ 100:1
|
||
|
||
每次迭代:
|
||
- 输入:系统提示 + 工具定义 + 历史动作 + 历史观察(长)
|
||
- 输出:一个工具调用(短)
|
||
```
|
||
|
||
**这意味着**:输入部分的缓存优化至关重要!
|
||
|
||
**提高缓存命中率的策略**:
|
||
|
||
1. **保持前缀稳定**:系统提示和工具定义不要频繁变化
|
||
2. **只追加不修改**:上下文应该只追加新的动作和观察
|
||
3. **确定性序列化**:保证键顺序、避免随机性
|
||
|
||
### 7.3 工具管理:遮蔽而非移除
|
||
|
||
**问题**:工具太多会让 Agent 变笨!
|
||
|
||
**为什么不能动态添加/删除工具?**
|
||
|
||
```python
|
||
# ❌ 动态移除工具的缺点
|
||
tools.remove("browser_search") # 历史上下文还引用这个工具!
|
||
|
||
# 导致问题:
|
||
# 1. KV 缓存失效(工具定义在上下文前部)
|
||
# 2. 模型困惑(看到历史中的未定义工具)
|
||
# 3. 产生幻觉(编造工具调用)
|
||
```
|
||
|
||
**正确做法:Logits 遮蔽**
|
||
|
||
在模型生成 token 时,直接禁止某些 token 的生成概率:
|
||
|
||
```python
|
||
# Manus 的做法:工具命名分组
|
||
|
||
# 浏览器相关工具都以 browser_ 开头
|
||
"browser_search"
|
||
"browser_navigate"
|
||
"browser_click"
|
||
|
||
# 状态 1:刚接收用户输入
|
||
# → 强制使用 reply 模式
|
||
force_prefix = "assistant\n"
|
||
|
||
# 状态 2:执行工具中
|
||
# → 只允许浏览器工具
|
||
force_prefix = "assistant\n{\"name\": \"browser_"
|
||
```
|
||
|
||
**优势**:
|
||
- ✅ 工具定义保持稳定(缓存友好)
|
||
- ✅ 动态控制可用工具
|
||
- ✅ 避免模型困惑
|
||
- ✅ 不需要状态管理
|
||
|
||
### 7.4 外部记忆:文件系统作为上下文
|
||
|
||
**问题**:观察结果太大
|
||
|
||
Agent 与环境交互时,观察结果可能很大:
|
||
- 网页内容:10-100 KB
|
||
- PDF 文档:100-1000 KB
|
||
- 代码文件:10-100 KB
|
||
|
||
**解决方案**:文件系统作为终极上下文
|
||
|
||
```python
|
||
# ❌ 错误:把整个网页放入上下文
|
||
context.append(f"网页内容:{full_page_html}") # 可能 100KB
|
||
|
||
# ✅ 正确:保存到文件,上下文只保留路径
|
||
file_path = agent.write_file("page.html", full_page_html)
|
||
context.append(f"网页已保存到:{file_path}")
|
||
```
|
||
|
||
**关键原则**:
|
||
|
||
1. **可恢复性**:保留恢复信息,而非内容本身
|
||
2. **按需读取**:Agent 学会按需读取文件
|
||
3. **结构化存储**:使用 todo.md 作为外部记忆
|
||
|
||
### 7.5 注意力管理:复述重要信息
|
||
|
||
**问题**:"迷失在中间"
|
||
|
||
长任务中,Agent 容易:
|
||
- 🎯 忘记初始目标
|
||
- 🔄 陷入循环
|
||
- 📉 偏离主任务
|
||
|
||
**Manus 的创新:todo.md**
|
||
|
||
```markdown
|
||
## 任务:搜索 AI 文章并生成总结
|
||
|
||
### 步骤
|
||
- [x] 1. 搜索最新的 AI 技术文章
|
||
- [x] 2. 提取前 5 篇文章的标题和链接
|
||
- [ ] 3. 阅读每篇文章的摘要
|
||
- [ ] 4. 生成综合总结
|
||
|
||
### 进行中
|
||
- 正在阅读第 1 篇文章...
|
||
```
|
||
|
||
**为什么有效?**
|
||
|
||
1. 将目标推入近期注意力
|
||
2. 避免目标不一致
|
||
3. 简单但有效的自然语言注意力机制
|
||
|
||
### 7.6 错误处理:保留失败尝试
|
||
|
||
**直觉**:隐藏错误 ❌
|
||
|
||
```python
|
||
try:
|
||
result = agent.call_tool("some_tool")
|
||
except Exception as e:
|
||
pass # 忽略错误
|
||
```
|
||
|
||
**正确做法**:保留错误信息 ✅
|
||
|
||
```python
|
||
try:
|
||
result = agent.call_tool("some_tool")
|
||
except ToolError as e:
|
||
# 保留错误信息
|
||
context.append(f"""
|
||
工具调用失败:
|
||
工具:{tool_name}
|
||
错误:{error_message}
|
||
""")
|
||
```
|
||
|
||
**为什么这样有效?**
|
||
|
||
- 隐式学习:模型看到失败 → 自动更新"信念"
|
||
- 改进策略:避免重复错误
|
||
- 真正的智能:能从错误中学习
|
||
|
||
**关键洞察**(来自 Manus):
|
||
|
||
> "根据我们的经验,改善代理行为最有效的方法之一出奇地简单:**将错误的尝试保留在上下文中**。"
|
||
|
||
### 7.7 Agent 上下文模板
|
||
|
||
**标准结构**:
|
||
|
||
```python
|
||
agent_context = f"""
|
||
# 系统提示(稳定,可缓存)
|
||
{system_prompt}
|
||
|
||
# 工具定义(稳定,可缓存)
|
||
{tool_definitions}
|
||
|
||
# 任务说明(相对稳定)
|
||
{task_description}
|
||
|
||
# 当前状态(频繁更新)
|
||
{current_state}
|
||
|
||
# 历史动作和观察(追加模式)
|
||
{history}
|
||
"""
|
||
```
|
||
|
||
**缓存策略**:
|
||
|
||
| 部分 | 频率 | 缓存策略 |
|
||
|------|------|----------|
|
||
| 系统提示 | 不变 | 完全缓存 |
|
||
| 工具定义 | 不变 | 完全缓存 |
|
||
| 任务说明 | 罕变 | 缓存直到修改 |
|
||
| 当前状态 | 实时 | 不缓存 |
|
||
| 历史动作 | 迭代 | 追加部分缓存 |
|
||
|
||
---
|
||
|
||
## 8. 记住
|
||
|
||
> **上下文质量 > 上下文数量**
|
||
|
||
更好的组织方式,比单纯的增加信息量更重要。
|
||
|
||
> **记忆系统是关键**
|
||
|
||
设计好即时、短期、长期三层记忆,让 AI 真正"记住"重要信息。
|
||
|
||
> **压缩是必要的艺术**
|
||
|
||
在有限的窗口内,用最精炼的方式呈现最相关的信息。
|
||
|
||
> **持续优化是关键**
|
||
|
||
没有万能的模板,只有不断迭代和优化的过程。
|
||
|
||
通过有效的上下文工程和记忆管理,你可以让 AI 在有限的能力下,发挥出最大的潜力!🚀
|