L14 Context Engineering:给 LLM 喂"对的"信息
课程目标:理解 Context Engineering 与 Prompt Engineering 的区别,掌握"写、选、压、隔"四大策略,能识别并修复 Context 中毒、Context 干扰、Context 混乱、Context 冲突四大常见失败。
范围说明:本节关注一次请求内怎么摆好砧板——不包含跨会话的长期记忆。跨 session 的"冰箱 + 客户档案"由 L15 代理记忆 负责。两者边界与协作详见 L15 第零节。
一、Prompt 不够,还需要 Context Engineering#
老王写了一个看似万灵的产品 Agent:
agent = create_agent(
instructions="你是一个产品助手,帮用户解决问题",
tools=[search_docs, create_ticket]
)
agent.run("帮我看下我的退款进度")
# 输出:好的,请告诉我你的订单号
老王吐槽:"我都让它帮用户解决问题了,怎么还问订单号?"
问题根源:Agent 不知道这个用户是谁、之前买过什么、订单在哪里。
Prompt 告诉了 Agent "做什么",但没告诉它"有哪些信息可用"。
这就是 Context Engineering 想解决的核心问题:
Prompt Engineering 是"静态指令",Context Engineering 是"动态信息系统"。
二、用一个比喻:Context = 餐厅服务员的"记忆"#
把 Agent 比作餐厅服务员:
| 服务员 | Agent |
|---|---|
| 菜单(菜品列表) | Instructions(系统提示) |
| 你今天点过的菜 | Conversation History(对话历史) |
| 你的口味偏好(不吃辣) | User Preferences(用户偏好) |
| 食材库存表 | Knowledge(通过 RAG 检索的知识) |
| 厨房能做的菜(工具) | Tools(可用工具) |
| 别的服务员帮忙送来的甜点 | Multi-Agent Context(多 Agent 共享的上下文) |
好服务员 = 知道什么时候该看哪张表——不是所有信息一股脑塞给顾客。
好 Agent = 知道每一步该喂什么 context——不是把所有信息都塞进 LLM 的 context window。
三、Context 的五大类型#
一个 Agent 在每一步推理中可能需要的 context:
| 类型 | 内容 | 来源 |
|---|---|---|
| Instructions | 系统提示、Few-shot 示例、工具描述 | 开发者写死 |
| Knowledge | 事实、检索数据、长期记忆 | RAG / 向量数据库 |
| Tools | 工具的 schema 和调用反馈 | 函数注册表 |
| Conversation History | 当前会话的对话记录 | 用户/Agent 消息流 |
| User Preferences | 用户学到的偏好 | 长期存储 |
Context Engineering = 动态管理这五类信息的进出与压缩。
四、四大 Context 管理策略#
4.1 写策略(Write):把信息从 context window 拿出来#
Agent scratchpad(草稿本)——在 context window 之外记笔记:
# ❌ 错:把整个思考过程塞进 context
agent.run("""
Question: What is 17 × 23?
Let me think step by step:
Step 1: 17 × 20 = 340
Step 2: 17 × 3 = 51
Step 3: 340 + 51 = 391
Answer: 391
""")
# ✓ 对:让 Agent 用 scratchpad 工具记笔记
agent = create_agent(
instructions="Use the scratchpad tool to take notes. Don't put everything in your response.",
tools=[scratchpad_write, scratchpad_read]
)
# Agent 会调用 scratchpad_write("Step 1: 17 × 20 = 340")
# 然后继续推理,但 scratchpad 不在 context 里
4.2 选策略(Select):只选相关的信息#
不要把所有工具都暴露给 Agent——选最相关的几个:
# ❌ 错:暴露 200 个工具
agent = create_agent(tools=all_200_tools) # Agent 会乱选
# ✓ 对:用 RAG 动态选择相关工具
relevant_tools = vector_search(all_tool_descriptions, user_query, top_k=5)
agent = create_agent(tools=relevant_tools)
经验法则:工具数量不要超过 30 个(小模型尤其敏感)。
4.3 压策略(Compress):压缩历史 context#
对话长了之后压缩:
def compress_context(messages: list) -> list:
"""当 context 接近窗口限制时,把旧消息摘要化"""
if total_tokens(messages) < TOKEN_LIMIT * 0.7:
return messages
# 保留最近 5 轮 + 早期消息的摘要
recent = messages[-10:]
old_summary = llm.summarize(messages[:-10])
return [
{"role": "system", "content": f"Earlier summary: {old_summary}"},
*recent
]
4.4 隔策略(Isolate):用多 Agent 隔离 context#
每个 Agent 各自维护自己的 context window:
# 复杂任务拆给多个 Agent
planner = create_agent(instructions="Break down tasks", tools=[])
executor = create_agent(instructions="Execute steps", tools=[*])
# Planner 的 context 不会污染 Executor
plan = await planner.run("Plan a trip to Paris")
result = await executor.run(f"Execute: {plan}")
好处——每个 Agent 的 context window 都"清爽",互不干扰。
五、规划策略:在动手前先想清楚 context#
5.1 定义期望结果#
"Agent 完成后,世界应该是什么样子?"
success_criteria = """
After the agent finishes:
- User's refund is processed
- User receives confirmation email
- Database shows refund status = 'completed'
"""
5.2 映射所需 context#
"Agent 需要哪些信息?这些信息在哪?"
context_map = {
"user_info": "PostgreSQL users table",
"order_info": "PostgreSQL orders table",
"refund_policy": "Vector DB (policy_docs)",
"email_template": "Static file",
"refund_tool": "refund_api.py"
}
5.3 规划 context 管道#
"Agent 怎么拿到这些信息?"
def get_context(user_id: str):
return {
"user": db.query("SELECT * FROM users WHERE id = ?", user_id),
"orders": db.query("SELECT * FROM orders WHERE user_id = ?", user_id),
"policy_chunks": vector_search(policy_docs, query, top_k=3),
"email_template": load_file("templates/refund_email.txt"),
"tools": [refund_tool]
}
六、Context 的四大失败模式#
Context Engineering 列出了四大常见失败,每一种都有具体场景和解法。
6.1 Context Poisoning(中毒)#
场景:Agent 自己 hallucinate 的信息被反复引用。
用户:帮我订明天去巴黎的机票
Agent:好的,从上海直飞巴黎,戴高乐机场,明天 8:30 起飞
(其实是 hallucination——上海没有直飞巴黎的航班)
Agent:好的,已经预订 PVG → CDG 8:30 航班
(继续 hallucination,越陷越深)
解法:
写入长期 context 前先验证
发现中毒时开新 thread
把可疑信息隔离到 scratchpad,不进入主 context
6.2 Context Distraction(干扰)#
场景:context 太大,模型过度依赖历史,忽略当前问题。
用户:(第 50 轮对话)你叫什么名字?
Agent:基于您之前提到的 2019 年背包旅行、2021 年的瑞士之旅...
(答非所问)
解法:定期压缩 context,让模型"重置"注意力。
6.3 Context Confusion(混乱)#
场景:工具太多,Agent 选错工具。
# ❌ 暴露 200 个工具
agent = create_agent(tools=[book_flight, rent_car, book_hotel, ...200 个])
# 小模型的表现:随便挑一个工具调用
解法:用 RAG 检索工具描述,只暴露相关的几个(< 30 个)。
6.4 Context Clash(冲突)#
场景:context 里同时有矛盾的信息。
context = """
[2020 年的偏好] 用户喜欢经济舱
[2025 年的偏好] 用户现在只飞商务舱
"""
Agent:嗯...让我选个折中的,超级经济舱?
解法:删掉旧信息;显式 override;冲突放到 scratchpad 让 Agent 推理决定。
七、检查 Context 的可观测性#
Context 管理不像普通代码能直接 debug——你需要专门的可观测性:
# 在每次 LLM 调用前记录
@before_llm_call
def log_context(agent, messages, tools):
log.info({
"agent": agent.name,
"message_count": len(messages),
"token_count": count_tokens(messages),
"tool_count": len(tools),
"context_sources": [
{"type": "history", "tokens": ...},
{"type": "rag", "doc_ids": [...]},
{"type": "memory", "memory_ids": [...]}
]
})
每次 LLM 调用都留下"它收到了什么 context"的快照——事后能分析"为什么这一步错了"。
八、动手:一个完整的 Context Engineering 示例#
class SmartAgent:
def __init__(self, user_id):
self.user_id = user_id
self.scratchpad = {} # 隔离策略
def get_context(self, query):
# 选策略:只取相关工具
tools = vector_search(tool_descriptions, query, top_k=5)
# 选策略:只取相关文档
docs = vector_search(knowledge_base, query, top_k=3)
# 写策略:scratchpad 拿出去
scratchpad_summary = self.scratchpad.get_summary()
return {
"instructions": "你是一个差旅助手...",
"conversation": self.get_recent_history(compress=True),
"user_preferences": db.get_user_prefs(self.user_id),
"knowledge": docs,
"tools": tools,
"scratchpad": scratchpad_summary,
"available_tools": [scratchpad_write, scratchpad_read] # 隔离
}
def step(self, query):
context = self.get_context(query)
# 调用 LLM
response = llm.complete(context)
# 检查是否有 hallucination
if self.detect_hallucination(response):
self.scratchpad["warnings"] = self.scratchpad.get("warnings", []) + [
"Potential hallucination detected"
]
# 隔离到 scratchpad,不污染主 context
return self.retry_with_warning(query)
return response
九、关键要点回顾#
| 失败模式 | 表现 | 解法 |
|---|---|---|
| Context Poisoning | hallucination 被反复引用 | 验证 + 隔离 + 重新开始 |
| Context Distraction | 模型过度依赖历史 | 定期压缩 |
| Context Confusion | 工具太多选错 | RAG 选工具,< 30 个 |
| Context Clash | 矛盾信息互相打架 | 删除旧信息 + 显式 override |
四大策略:写(scratchpad)、选(RAG)、压(摘要)、隔(多 Agent)。
十、参考资源#
Anthropic 的 Effective Context Engineering 指南
LangChain 的 Context Engineering 实践
十一、下一课预告#
L15 代理记忆:Agent 怎么拥有"长期记忆"——让用户三天后回来,Agent 还能记得他上次说过"我不吃辣"。
为什么重要:本课只管一次请求内怎么把砧板摆好(Context Engineering);L15 管的是跨会话的"冰箱 + 采购记录"——即"用户上次在我店里吃过什么"。两者边界与协作详见 L15 第零节。
没有记忆的 Agent 永远是"金鱼脑"(金鱼只有 7 秒记忆),永远把用户当陌生人。
1. Context Engineering 与 Prompt Engineering 最本质的区别是什么?
2. Agent 暴露了 200 个工具,导致它经常选错工具。这属于哪种失败模式,正确解法是?
3. 关于 Context 管理的四大策略,下列对应正确的是?