引言

Prompt工程关注"如何写好一条提示",而上下文工程关注"如何设计Agent的整个认知环境"。在2026年,随着Agent需要处理越来越复杂的任务,单条Prompt的优化已经触及天花板。上下文工程——系统性地设计Agent在每一步推理时看到什么信息、以什么顺序、什么格式——成为了新的性能提升杠杆。

一、什么是上下文工程

1.1 从Prompt到Context

Prompt工程是上下文工程的子集。一条Prompt包含在上下文中,但上下文远不止Prompt:

Agent上下文 = System Prompt + 用户消息 + 对话历史 + 工具描述 
            + 检索到的记忆 + 工具调用结果 + 环境状态 + 元指令

每个组成部分都需要精心设计,任何一个部分的质量问题都会影响Agent的整体表现。

1.2 上下文预算

上下文窗口是有限的资源。2026年主流模型的上下文窗口为128K-2M tokens,但"能用"和"用好"是两回事:

  • 过长上下文导致注意力分散:模型对中间部分的信息关注不足(“lost in the middle"问题)
  • 成本与长度正相关:每多1000 tokens的输入,就多一份成本
  • 延迟与长度正相关:更长的上下文意味着更长的处理时间

因此,上下文工程的核心是:在有限的预算内,让每一条信息都发挥最大价值。

二、上下文组装策略

2.1 上下文分区

将上下文分为不同的功能区,每个区有明确的职责:

┌─────────────────────────────────────┐
│ System Prompt (固定区)               │  ← 身份、能力、规则
├─────────────────────────────────────┤
│ Tool Descriptions (工具区)           │  ← 可用工具的描述
├─────────────────────────────────────┤
│ Retrieved Memory (记忆区)            │  ← 检索到的相关记忆
├─────────────────────────────────────┤
│ Conversation History (对话区)        │  ← 对话历史
├─────────────────────────────────────┤
│ Current Input (输入区)               │  ← 用户当前输入
├─────────────────────────────────────┤
│ Instructions (指令区)                │  ← 当前步骤的具体指令
└─────────────────────────────────────┘

2.2 动态上下文组装

不同任务、不同阶段需要不同的上下文结构:

class ContextAssembler:
    def __init__(self, max_tokens=128000):
        self.max_tokens = max_tokens
        self.budgets = {
            "system": 2000,      # 固定预算
            "tools": 5000,       # 工具描述预算
            "memory": 10000,     # 记忆预算
            "history": 80000,    # 对话历史预算
            "input": 20000,      # 用户输入预算
            "instructions": 3000, # 指令预算
            "reserve": 8000,     # 输出预留
        }
    
    def assemble(self, task_context):
        context_parts = []
        
        # 1. 系统提示(固定)
        context_parts.append(self.get_system_prompt())
        
        # 2. 工具描述(动态选择)
        relevant_tools = self.select_relevant_tools(
            task_context.user_input,
            max_tokens=self.budgets["tools"]
        )
        context_parts.append(self.format_tools(relevant_tools))
        
        # 3. 记忆检索
        memories = self.retrieve_memories(
            task_context.user_input,
            max_tokens=self.budgets["memory"]
        )
        context_parts.append(self.format_memories(memories))
        
        # 4. 对话历史(智能截断)
        history = self.truncate_history(
            task_context.conversation,
            max_tokens=self.budgets["history"]
        )
        context_parts.append(history)
        
        # 5. 用户输入
        context_parts.append(task_context.user_input)
        
        # 6. 指令
        context_parts.append(self.get_instructions(task_context))
        
        return "\n\n".join(context_parts)

2.3 上下文优先级

当总上下文超过预算时,需要按优先级裁剪:

优先级 内容 裁剪策略
P0 System Prompt 不裁剪
P0 用户当前输入 不裁剪
P1 当前指令 不裁剪
P1 必需工具描述 不裁剪
P2 最近3轮对话 不裁剪
P3 检索到的记忆 按相关性裁剪
P4 更早的对话历史 摘要后保留
P5 非必需工具描述 移除

三、对话历史管理

3.1 滑动窗口

只保留最近N轮对话:

def sliding_window(conversation, max_turns=10):
    return conversation[-max_turns:]

简单但会丢失早期的重要信息。

3.2 摘要压缩

将旧对话压缩为摘要:

async def summarize_old_messages(messages, keep_recent=5):
    """保留最近5轮,将更早的消息摘要"""
    recent = messages[-keep_recent*2:]  # 5轮 = 10条消息
    old = messages[:-keep_recent*2]
    
    if old:
        summary = await llm.summarize(old)
        return [{"role": "system", "content": f"对话摘要:{summary}"}] + recent
    return recent

3.3 选择性保留

根据重要性选择保留哪些历史消息:

def select_messages(messages, max_tokens):
    """选择性保留重要消息"""
    scored_messages = []
    for msg in messages:
        score = 0
        # 包含用户明确要求的保留
        if msg["role"] == "user" and any(kw in msg["content"] for kw in ["记住", "记住这个", "注意"]):
            score += 10
        # 包含关键决策的保留
        if msg["role"] == "assistant" and "决定" in msg["content"]:
            score += 5
        # 包含工具调用结果的保留
        if msg["role"] == "tool":
            score += 3
        # 越新的消息分数越高
        score += msg["index"] / len(messages) * 5
        
        scored_messages.append((msg, score))
    
    # 按分数选择,不超过token预算
    scored_messages.sort(key=lambda x: -x[1])
    selected = []
    total_tokens = 0
    for msg, score in scored_messages:
        msg_tokens = count_tokens(msg["content"])
        if total_tokens + msg_tokens <= max_tokens:
            selected.append(msg)
            total_tokens += msg_tokens
    
    # 按原始顺序排列
    selected.sort(key=lambda x: x["index"])
    return selected

四、工具描述优化

4.1 工具选择注入

不是所有工具都需要同时注入上下文。根据用户意图选择相关工具:

def select_tools_for_context(user_input, all_tools, max_tokens=5000):
    """根据用户输入选择最相关的工具"""
    # 语义检索相关工具
    query_embedding = embed(user_input)
    scored_tools = []
    for tool in all_tools:
        similarity = cosine_similarity(query_embedding, tool.embedding)
        scored_tools.append((tool, similarity))
    
    # 按相关性排序,选择Top-K
    scored_tools.sort(key=lambda x: -x[1])
    
    selected = []
    total_tokens = 0
    for tool, score in scored_tools:
        tool_tokens = count_tokens(tool.description)
        if total_tokens + tool_tokens <= max_tokens:
            selected.append(tool)
            total_tokens += tool_tokens
        else:
            break
    
    return selected

4.2 工具描述压缩

将冗长的工具描述压缩为精简版:

完整描述(200 tokens):
"发送电子邮件工具。使用SMTP协议发送邮件。需要提供收件人邮箱地址、邮件主题和邮件正文。
可选择提供CC和BCC。支持HTML格式正文。如果发送失败会重试3次..."

精简描述(50 tokens):
"send_email(to, subject, body, cc?, bcc?): 发送邮件。to=收件人, subject=主题, body=正文"

五、记忆注入

5.1 主动检索

在每轮交互前,主动检索与当前任务相关的记忆:

async def inject_relevant_memories(user_input, memory_store, max_tokens=5000):
    """检索并注入相关记忆"""
    # 多路检索
    semantic_results = await memory_store.semantic_search(user_input, top_k=5)
    keyword_results = await memory_store.keyword_search(user_input, top_k=5)
    recent_results = await memory_store.recent(top_k=3)
    
    # 去重合并
    all_memories = deduplicate(semantic_results + keyword_results + recent_results)
    
    # 重排序
    reranked = rerank(all_memories, query=user_input)
    
    # 截断到token预算
    selected = truncate_to_budget(reranked, max_tokens)
    
    if selected:
        memory_text = format_memories(selected)
        return f"[相关记忆]\n{memory_text}\n[/相关记忆]"
    return ""

5.2 记忆格式

记忆的注入格式影响LLM的利用效率:

✗ 低效格式:
"记忆1:用户喜欢简洁的回答。记忆2:用户是Python开发者。记忆3:上次讨论了数据库优化。"

✓ 高效格式:
## 用户画像
- 偏好:简洁回答、代码示例
- 背景:Python开发者,5年经验
- 技术栈:Django, PostgreSQL, Redis

## 近期对话
- 2024-07-01: 讨论了PostgreSQL查询优化,建议添加索引
- 2024-06-28: 帮助解决了Django ORM的N+1查询问题

六、上下文评估与优化

6.1 上下文质量指标

  • 信息密度:有效信息 / 总Token数。目标:>70%
  • 冗余度:重复信息的比例。目标:<5%
  • 时效性:上下文中信息的时效。对时效性敏感的任务尤为重要
  • 完整性:完成任务所需信息是否都在上下文中

6.2 A/B测试

对比不同上下文组装策略的效果:

策略A: 全量工具 + 摘要历史 + 语义记忆
策略B: 动态工具 + 滑窗历史 + 关键词记忆
策略C: 动态工具 + 混合历史 + 混合记忆

指标: 任务完成率、Token消耗、响应延迟、用户满意度

6.3 上下文可视化

将Agent每一步的上下文可视化,帮助开发者理解"Agent看到了什么”:

Step 5 Context (总计 45,000 tokens):
  System Prompt:      1,500 tokens (3.3%)
  Tool Descriptions:  3,200 tokens (7.1%) [8 tools selected from 50]
  Memory:             2,800 tokens (6.2%) [5 memories retrieved]
  Conversation:      35,000 tokens (77.8%) [15 turns, truncated]
  User Input:         1,200 tokens (2.7%)
  Instructions:       1,300 tokens (2.9%)

结语

上下文工程是Prompt工程的进化形态。它不再局限于"如何写一条好的提示",而是关注"如何为Agent构建一个最优的工作环境"。

在2026年,上下文工程已经成为Agent优化的主要手段。一个精心设计的上下文可以让中等模型表现出接近顶级模型的效果,而一个糟糕的上下文会让顶级模型表现平平。

未来,上下文工程将更加自动化——Agent能够根据任务特征自动选择最优的上下文组装策略,动态调整各部分的预算分配,甚至实时评估上下文质量并自动优化。这种"元认知"能力可能是Agent走向AGI的重要一步。

加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。