课程首页 › Module 6 · 协议与工程:MCP / Agent 协议 / 可信赖 / Context / Memory › L14

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,越陷越深)

解法:

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 Poisoninghallucination 被反复引用验证 + 隔离 + 重新开始
Context Distraction模型过度依赖历史定期压缩
Context Confusion工具太多选错RAG 选工具,< 30 个
Context Clash矛盾信息互相打架删除旧信息 + 显式 override

四大策略:写(scratchpad)、选(RAG)、压(摘要)、隔(多 Agent)。


十、参考资源#


十一、下一课预告#

L15 代理记忆:Agent 怎么拥有"长期记忆"——让用户三天后回来,Agent 还能记得他上次说过"我不吃辣"。

为什么重要:本课只管一次请求内怎么把砧板摆好(Context Engineering);L15 管的是跨会话的"冰箱 + 采购记录"——即"用户上次在我店里吃过什么"。两者边界与协作详见 L15 第零节。

没有记忆的 Agent 永远是"金鱼脑"(金鱼只有 7 秒记忆),永远把用户当陌生人。

自测 · 检验是否真的掌握了

1. Context Engineering 与 Prompt Engineering 最本质的区别是什么?

课程的核心论断是「Prompt Engineering 是静态指令,Context Engineering 是动态信息系统」。Prompt 告诉 Agent 做什么,Context Engineering 则动态管理指令、知识、工具、对话历史、用户偏好这五类信息的进出与压缩,因此决定了 Agent 每一步该喂什么。单轮多轮、输入输出、是否用检索都只是表象差异。

2. Agent 暴露了 200 个工具,导致它经常选错工具。这属于哪种失败模式,正确解法是?

工具太多导致选错工具是典型的 Context Confusion,因为 Agent 面对大量并列工具时无法辨别相关性,解法是用 RAG 检索工具描述,只暴露相关的几个(经验上不超过 30 个)。干扰针对上下文过大导致的过度依赖历史,中毒针对幻觉被反复引用,冲突针对新旧信息互相矛盾,都不是这里的症结。

3. 关于 Context 管理的四大策略,下列对应正确的是?

写策略是把思考过程记到上下文窗口之外的 scratchpad,选策略是用检索挑出相关的工具与文档,压策略是把旧消息摘要化,隔策略是让多个 Agent 各守自己的上下文窗口。四者一一对应,错配了就无法达到压缩上下文、减少干扰的目的。