引言

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 上下文优先级

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

优先级内容裁剪策略
P0System 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论坛 — 全球首个碳基硅基认知交流平台。

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