AI Agent上下文工程:从提示词优化到上下文压缩的全链路实践

引言:Agent真正的瓶颈,正在从“模型能力”转向“上下文能力”

过去两年,AI Agent工程实践中有一个非常明显的变化:开发者最初把主要精力放在模型选择、Prompt编写和Tool Calling上,而进入生产环境之后,真正让系统变得复杂的往往不是这些环节,而是上下文管理


一个简单的聊天机器人,只需要把用户问题发送给大模型即可:

用户问题
   ↓
Prompt
   ↓
LLM
   ↓
回答

但一个真正的企业级Agent通常需要同时处理:

  • 当前用户请求;

  • 多轮对话历史;

  • 用户长期偏好;

  • 当前任务状态;

  • 历史任务结果;

  • 工具调用记录;

  • 数据库查询结果;

  • RAG检索结果;

  • 外部网页或API数据;

  • 系统规则;

  • 安全策略;

  • Agent自身产生的中间状态;

  • 其他Agent返回的信息。

最终发送给模型的已经不再是一个简单Prompt,而是一套动态构建出来的Context Package(上下文包)


可以把Agent的一次模型调用抽象为:


[

C_t = S + I_t + H_t + M_t + R_t + T_t + O_t

]


其中:

  • (S):System Instruction,系统指令;

  • (I_t):当前用户输入;

  • (H_t):短期对话历史;

  • (M_t):长期记忆;

  • (R_t):检索结果;

  • (T_t):工具及其执行状态;

  • (O_t):Agent运行过程中的其他状态。

因此,Agent的核心问题已经从:

“怎么写一个更好的Prompt?”

逐渐变成:

“在有限Token预算下,如何让模型在当前任务中获得最有价值、最可靠、最及时的上下文?”

这就是上下文工程(Context Engineering)真正解决的问题。


长上下文模型并不意味着可以无限制地把所有历史数据塞进去。研究已经发现,即使模型具备很长的上下文窗口,关键信息位于长上下文中间位置时,模型的利用能力仍可能明显下降,这就是著名的“Lost in the Middle”现象。([arXiv][1])


所以,上下文工程不是简单地“扩大Context Window”,而是围绕上下文生命周期进行系统设计。


一、什么是AI Agent上下文工程

1.1 Prompt Engineering与Context Engineering的区别

Prompt Engineering主要关注:

如何设计一段更好的提示词,让模型完成特定任务。

例如:

你是一名专业的软件架构师。

请分析下面的系统需求,并输出:

1. 系统架构

2. 技术选型

3. 数据库设计

4. 风险分析

这属于典型的Prompt Engineering。


而Context Engineering关注的是:

模型在某一次推理时,到底应该看到什么信息、以什么顺序看到、哪些信息应该被压缩、哪些信息应该实时检索、哪些信息应该永久保存。

例如用户连续进行了50轮对话:

System Prompt

+
用户画像

+
最近10轮对话

+
任务摘要

+
历史决策

+
当前数据库状态

+
RAG结果

+
工具调用结果

+
当前问题

这里真正困难的已经不是Prompt本身,而是:


哪些信息应该进入Context?


这也是为什么一个Agent即使使用同一个模型,不同上下文架构可能产生完全不同的效果。


二、Agent上下文的完整生命周期

一个生产级Agent可以采用下面的上下文生命周期:

                 ┌─────────────────┐
                 │   User Request  │
                 └────────┬────────┘
                          ↓
                 ┌─────────────────┐
                 │ Context Builder │
                 └────────┬────────┘
                          ↓
        ┌─────────────────┼──────────────────┐
        ↓                 ↓                  ↓
   Short Memory      Long Memory          Retrieval
        ↓                 ↓                  ↓
        └─────────────────┼──────────────────┘
                          ↓
                  Context Ranking
                          ↓
                  Context Compression
                          ↓
                    Token Budget
                          ↓
                   Prompt Assembly
                          ↓
                         LLM
                          ↓
              ┌───────────┴───────────┐
              ↓                       ↓
         Tool Calling             Final Answer
              ↓
        State Update
              ↓
       Memory Extraction
              ↓
        Context Persistence

因此可以把上下文工程拆成七个核心环节:

  1. 上下文构建;

  2. 上下文分层;

  3. 上下文检索;

  4. 上下文排序;

  5. 上下文压缩;

  6. 上下文装配;

  7. 上下文更新。

这七个环节构成Agent Context Pipeline。


三、第一层:上下文分层设计

最常见的错误,是把所有信息都放进一个messages数组。


例如:

{
  "messages": [
    {
      "role": "system",
      "content": "..."
    },
    {
      "role": "user",
      "content": "..."
    },
    {
      "role": "assistant",
      "content": "..."
    }
  ]
}

在Agent运行时间较短时没有问题,但随着任务复杂度增加,这种设计会逐渐失控。


更合理的方式是把Context拆成不同层级。

3.1 Immutable Context

这部分内容在一个Agent生命周期内基本不变化。


例如:

Agent身份
系统规则
输出格式
安全策略
工具定义
业务规则

可以称为:

Static Context

这部分应该尽可能稳定。


原因很简单:很多模型平台支持Prompt Cache。如果每次请求前面的内容发生变化,会降低缓存命中率。


例如OpenAI API已经提供Prompt Cache相关能力,并通过prompt_cache_key等机制帮助优化相似请求的缓存命中。([OpenAI 平台][2])


因此生产系统中应该尽量做到:

稳定System Prompt
        ↓
稳定Tool Definition
        ↓
稳定业务规则
        ↓
动态Context
        ↓
用户问题

而不是:

用户问题
随机信息
Tool结果
System Prompt
用户画像
历史消息

频繁改变Prompt前缀会破坏缓存收益。


四、第二层:短期记忆与长期记忆

Agent记忆不能简单理解为“聊天记录”。


更合理的方式是分成:

Short-Term Memory
        +
Working Memory
        +
Long-Term Memory

4.1 Short-Term Memory

保存当前任务最近发生的事件。


例如:

用户:帮我设计一个订单系统

Agent:需要支持哪些支付方式?

用户:微信和支付宝

Agent:是否需要分销?

用户:需要三级分销

当前任务中这些信息具有较高价值。


因此最近若干轮应该保留。


4.2 Working Memory

Working Memory比聊天历史更重要。


它不是“用户说了什么”,而是:

Agent现在知道什么,以及任务进行到了哪一步。

例如:

{
  "goal": "设计订单系统",
  "payment": [
    "wechat",
    "alipay"
  ],
  "distribution": {
    "enabled": true,
    "level": 3
  },
  "database": "MySQL",
  "backend": "Go",
  "status": "architecture_design"
}

这类结构化状态比几十轮自然语言对话更加稳定。


因此一个成熟Agent应该逐渐从:

Conversation as State

升级到:

State + Conversation

也就是说:

不要让聊天记录承担数据库的职责。


五、第三层:长期记忆不是“把所有历史保存下来”

长期记忆最容易出现两个极端。


第一种:

什么都保存。

第二种:

什么都不保存。

正确的方法是建立Memory Policy。


例如:

用户明确偏好
     ↓
长期记忆

临时问题
     ↓
不保存

任务结果
     ↓
根据价值决定

重要业务决策
     ↓
长期保存

一次性的闲聊
     ↓
丢弃

可以定义一个Memory Score:


[

Score = Importance \times Recency \times Relevance

]


例如:

Importance = 0.9
Recency    = 0.8
Relevance  = 0.95

Score = 0.9 × 0.8 × 0.95
      = 0.684

当Score低于阈值时,不需要进入模型Context。


六、上下文检索:不是“搜索越多越好”

RAG系统经常存在一个误区:

找到越多文档,模型获得的信息越充分。

实际上恰恰相反。


如果一次检索返回:

20个文档
每个文档1500 Token

那么:

20 × 1500 = 30000 Token

即使模型能够接受30K上下文,也不代表30K信息都应该输入。


因为真正需要解决的问题是:

Information Retrieval → Context Selection

而不是:

Information Retrieval → Context Dump


七、Context Ranking:给上下文排序

可以为每个候选Context计算综合评分:


[

Score_i =

\alpha R_i +

\beta S_i +

\gamma T_i +

\delta Q_i

]


其中:

  • (R_i):与当前Query相关性;

  • (S_i):信息可信度;

  • (T_i):时效性;

  • (Q_i):信息质量。

例如:

type ContextItem struct {
    ID         string
    Content    string
    Relevance  float64
    Trust      float64
    Freshness  float64
    Quality    float64
    TokenCount int
}

排序:

func Score(c ContextItem) float64 {
    return 0.50*c.Relevance +
           0.20*c.Trust +
           0.15*c.Freshness +
           0.15*c.Quality
}

然后只选择Top-N。


这一步非常重要,因为:

Context Window应该被看作有限预算,而不是无限仓库。


八、上下文压缩:Agent进入生产环境的关键能力

上下文压缩通常分为四类:


1. 截断

2. 摘要

3. 结构化压缩

4. 语义压缩


九、第一种压缩:简单截断

最简单的策略:

保留System Prompt

+
保留最近N轮

+
删除最早消息

例如:

messages = messages[-20:]

优点:

  • 简单;

  • 快;

  • 不增加额外LLM调用。

缺点也非常明显:

早期重要决策
        ↓
直接丢失

例如用户在第2轮说:

数据库必须使用PostgreSQL。

第40轮继续讨论系统设计时,如果第2轮已经被截掉,Agent可能重新推荐MySQL。


这就是典型的上下文丢失。


十、第二种压缩:摘要

更成熟的方法是Summary。


例如原始对话:

用户:我要开发一个电商系统。
用户:后端使用Go。
用户:数据库使用MySQL。
用户:Redis用于缓存。
用户:支付使用微信和支付宝。
用户:需要三级分销。
...

压缩为:

【项目摘要】

项目类型:电商系统

技术栈:

- Go

- MySQL

- Redis

支付:

- 微信

- 支付宝

业务:

- 三级分销

当前阶段:
订单系统架构设计

这样可以从几千Token压缩到几百Token。


但摘要也存在一个严重问题:

摘要模型可能把“看起来不重要”的信息错误删除。

因此不能简单让模型:

请总结以上对话。

而应该指定Summary Schema。


例如:

{
  "goal": "",
  "constraints": [],
  "decisions": [],
  "open_questions": [],
  "facts": [],
  "tool_results": [],
  "next_action": ""
}

这种结构化摘要比自由文本摘要更适合Agent。


十一、第三种压缩:状态化压缩

这是企业Agent中非常值得采用的方法。


与其:

保留100轮对话

不如:

对话
 ↓
提取事实
 ↓
提取决策
 ↓
提取任务状态
 ↓
形成State

例如:

{
  "task": {
    "name": "订单系统",
    "status": "payment_design"
  },
  "decisions": [
    "Backend=Go",
    "Database=MySQL",
    "Cache=Redis"
  ],
  "constraints": [
    "支持微信支付",
    "支持支付宝",
    "三级分销"
  ],
  "pending": [
    "支付回调设计",
    "订单状态机设计"
  ]
}

这类State可以直接存储到Redis或数据库。


最终Agent上下文只需要:

当前State

+
最近对话

+
相关历史

而不是整个Conversation。


十二、第四种压缩:工具结果压缩

Agent最容易爆Token的地方之一,其实不是用户聊天,而是Tool Calling。


例如Agent调用数据库:

SELECT * FROM orders;

返回:

10000 rows

然后直接塞给LLM。


这基本等于主动制造Context灾难。


正确方式应该是:

Tool
 ↓
Raw Result
 ↓
Result Processor
 ↓
Structured Result
 ↓
Context

例如:

{
  "total": 10000,
  "status_distribution": {
    "paid": 7820,
    "pending": 1210,
    "cancelled": 970
  },
  "recent_orders": [
    {
      "id": 10001,
      "amount": 299
    }
  ]
}

模型通常只需要知道:

数据意味着什么。

而不是:

数据库返回了什么。


十三、Tool Result应该具有Token Budget

可以给每一个工具设置最大Context预算:

tools:
  order_query:
    max_context_tokens: 1500

  search:
    max_context_tokens: 3000

  database:
    max_context_tokens: 2000

  web_search:
    max_context_tokens: 4000

超过预算:

Raw Result
    ↓
Filter
    ↓
Rank
    ↓
Summarize
    ↓
Context

这样可以防止一个异常Tool调用直接吞掉整个Context Window。


十四、上下文装配:Context Builder才是Agent的核心组件

生产级Agent应该拥有一个独立的Context Builder。


架构可以设计成:

                 ┌───────────────┐
                 │ User Request  │
                 └───────┬───────┘
                         ↓
                ┌─────────────────┐
                │ Context Builder │
                └────────┬────────┘
                         │
      ┌──────────────────┼─────────────────┐
      ↓                  ↓                 ↓
  User Memory       Task State          Retrieval
      ↓                  ↓                 ↓
      └──────────────────┼─────────────────┘
                         ↓
                 Context Ranking
                         ↓
                Context Compression
                         ↓
                   Token Budget
                         ↓
                  Prompt Assembly
                         ↓
                       LLM

Go接口可以设计成:

type ContextBuilder interface {
    Build(ctx context.Context, req BuildRequest) (*Context, error)
}

type BuildRequest struct {
    UserID       string
    Conversation string
    Query        string
    TokenBudget  int
}

type Context struct {
    System       string
    Memory       []ContextItem
    History      []ContextItem
    Retrieval    []ContextItem
    ToolResults  []ContextItem
    TaskState    map[string]any
}

这样可以让Context成为独立基础设施,而不是散落在Agent代码中的大量字符串拼接。


十五、Token Budget:上下文工程的核心约束

假设模型Context Window为128K Token。


并不意味着:

128K全部给输入

还需要考虑:

System Prompt

+
Tools

+
Memory

+
History

+
Retrieval

+
Output

+
Reasoning

因此应该提前进行预算。


例如:

Context Window       128K

System                6K
Tools                12K
Memory                8K
History              20K
RAG                  30K
Output Reserve       16K
-------------------------
Safety Margin        10K

最终:

实际Context Budget ≈ 26K

这比简单依赖模型的最大上下文限制安全得多。


OpenAI API文档也明确说明,当输入超过模型上下文限制时,可以采用截断策略;如果禁用截断,超过限制则会直接报错。([OpenAI 平台][2])


因此生产系统不应该把:

Context Limit

当成:

Application Budget

两者不是一回事。


十六、上下文压缩触发机制

不要每一轮都压缩。


可以采用阈值策略:

Token Usage < 60%
        ↓
正常运行

60% ~ 75%
        ↓
优先减少低价值Context

75% ~ 85%
        ↓
摘要历史 + 压缩Tool Result

> 85%
        ↓
强制Compaction

例如:

func NeedCompaction(used, limit int) bool {
    return float64(used)/float64(limit) >= 0.80
}

更好的策略不是“达到100%再压缩”,而是提前留出安全区。


十七、Compaction:从截断升级为上下文重构

Compaction可以理解为:

把大量历史状态重新编码成更小、更高价值的上下文。

例如:

100轮Conversation
        ↓
事实提取
        ↓
任务状态
        ↓
关键决策
        ↓
未完成事项
        ↓
工具结果摘要
        ↓
20轮高价值Context

这比单纯删除旧消息可靠得多。


现代Agent平台也已经开始提供显式的Compaction能力。例如OpenAI Responses API中已经出现compaction相关对象,用于表示由上下文压缩产生的压缩项。([OpenAI 平台][2])


这意味着上下文压缩正在从“开发者自行拼Prompt的小技巧”,逐渐变成Agent Runtime的一等能力。


十八、Compaction必须保护关键事实

压缩过程中最重要的问题不是:

能压缩多少?

而是:

哪些信息绝对不能丢?

建议建立四级信息分类:

P0:不可丢失
P1:重要
P2:可压缩
P3:可删除

例如:

类型

等级

策略

用户明确业务约束

P0

永久保存

已确认技术选型

P0

结构化保存

当前任务状态

P0

实时更新

重要工具结果

P1

摘要

普通历史对话

P2

压缩

寒暄

P3

删除

重复信息

P3

去重

这样Context Compaction就不再是“让模型总结”,而是一个有明确策略的状态转换过程。


十九、Context Update:上下文必须持续更新

很多Agent系统只关注:

Context → LLM

却忽略:

LLM → Context

实际上Agent每次运行之后都应该更新状态。


完整流程:

Request
   ↓
Build Context
   ↓
LLM
   ↓
Tool Calls
   ↓
Result
   ↓
State Update
   ↓
Memory Extraction
   ↓
Context Store

例如Agent完成一次任务:

{
  "event": "payment_design_completed",
  "decision": {
    "provider": "wechat",
    "callback": "async",
    "idempotency": true
  }
}

这个事件应该进入Task State。


而不是仅仅留在聊天记录里。


二十、Event Sourcing思路可以用于Agent状态管理

对于复杂Agent,可以把每一次重要变化记录为Event:

{
  "event_id": "evt_10001",
  "type": "decision_made",
  "timestamp": "2026-08-13T09:20:00Z",
  "payload": {
    "database": "mysql"
  }
}

连续事件:

TaskCreated
    ↓
DatabaseSelected
    ↓
PaymentSelected
    ↓
ArchitectureCompleted
    ↓
CodeGenerated
    ↓
TestCompleted

然后可以从Event重建Agent State。


这对于:

  • 长任务;

  • 多Agent协作;

  • Agent恢复;

  • 故障重试;

  • 审计;

  • Debug;

都非常有价值。


二十一、多Agent系统中的上下文工程

多Agent架构还有一个特殊问题:

Agent之间应该共享多少上下文?

最危险的设计是:

Agent A全部Context
       ↓
Agent B全部Context
       ↓
Agent C全部Context

这样不仅Token成本高,而且会产生严重的信息污染。


更好的方式是:

Agent A
  ↓
Task Result
  ↓
Structured Handoff
  ↓
Agent B

例如:

{
  "task": "architecture_analysis",
  "result": {
    "backend": "Go",
    "database": "MySQL",
    "cache": "Redis"
  },
  "confidence": 0.92,
  "open_questions": [
    "是否需要消息队列"
  ]
}

Agent B不需要知道Agent A的全部思考过程,只需要获得:

结论
依据
约束
未解决问题

这就是Context Isolation


二十二、不要把Chain-of-Thought当成Context数据库

一个非常重要的工程原则是:

模型推理过程和业务状态不是一回事。

Agent真正需要长期保存的应该是:

事实
决策
约束
任务状态
工具结果
最终结论

而不是:

模型所有中间推理文本

这样可以显著降低Context体积,同时提高状态稳定性。


对于复杂任务,更推荐保存:

{
  "decision": "使用Redis作为缓存",
  "reason": "降低高频读取数据库压力",
  "source": "architecture_analysis",
  "confidence": 0.91
}

而不是保存大量自然语言推理过程。


二十三、Prompt Cache与Context Engineering

上下文工程还有一个经常被忽略的经济因素:

相同Context是否可以被缓存?

如果一个Agent每次请求都发送:

10K System Prompt

+
20K Tools

+
30K Business Knowledge

+
2K User Query

其中前60K几乎完全不变,那么最合理的设计是让稳定前缀保持稳定。


可以组织为:

[Stable Prefix]
System
Tools
Business Rules
Static Knowledge

[Dynamic Context]
Memory
Task State
RAG
Tool Results

[User Query]

这样可以同时获得:

  • 更好的Prompt Cache命中;

  • 更低输入Token成本;

  • 更低延迟;

  • 更稳定的上下文结构。

OpenAI文档明确提供了缓存Token使用量等指标,API响应中可以看到cached_tokens等Usage信息,因此生产系统可以直接监控缓存命中效果。([OpenAI 平台][2])


二十四、上下文成本模型

可以把一次Agent调用成本抽象为:


[

Cost =

C_{input}


C_{cached}


C_{output}


C_{tool}


C_{retrieval}

]


如果每次请求都把100K历史上下文重新发送,那么即使模型性能没有问题,成本也可能迅速增长。


更值得关注的是:


[

TotalCost =

\sum_{t=1}^{N}

ContextTokens_t

\times

Price

]


所以优化Agent成本最直接的方法之一,不是简单更换便宜模型,而是:

减少无效Context Token。


二十五、上下文工程的缓存架构

推荐设计三级缓存:

                ┌──────────────┐
                │ L1 Memory    │
                │ Local/Process│
                └──────┬───────┘
                       ↓
                ┌──────────────┐
                │ L2 Redis     │
                │ Hot Context  │
                └──────┬───────┘
                       ↓
                ┌──────────────┐
                │ L3 Database  │
                │ Long Memory  │
                └──────────────┘

例如:

L1:
当前请求Context

L2:
最近任务State、短期Memory

L3:
用户长期记忆、历史任务、知识数据

检索顺序:

L1 → L2 → L3 → Vector Search

这样能够减少大量无意义查询。


二十六、Context Store数据库设计

可以建立:

CREATE TABLE agent_context (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    session_id VARCHAR(64) NOT NULL,
    context_type VARCHAR(32) NOT NULL,
    content TEXT NOT NULL,
    importance FLOAT DEFAULT 0,
    relevance FLOAT DEFAULT 0,
    token_count INT DEFAULT 0,
    created_at DATETIME NOT NULL,
    updated_at DATETIME NOT NULL
);

其中:

context_type

可以是:

system
memory
fact
decision
task_state
tool_result
summary
retrieval

这样后续可以针对不同类型采用不同策略。


二十七、推荐的Agent Context架构

综合前面的设计,一个比较完整的企业级架构可以是:

                    ┌─────────────────────┐
                    │      User Input     │
                    └──────────┬──────────┘
                               ↓
                    ┌─────────────────────┐
                    │ Query Understanding │
                    └──────────┬──────────┘
                               ↓
                    ┌─────────────────────┐
                    │   Context Builder   │
                    └──────────┬──────────┘
                               │
       ┌───────────────────────┼───────────────────────┐
       ↓                       ↓                       ↓
┌──────────────┐       ┌──────────────┐       ┌──────────────┐
│ Short Memory │       │ Long Memory  │       │ Retrieval    │
└──────┬───────┘       └──────┬───────┘       └──────┬───────┘
       │                       │                       │
       └───────────────────────┼───────────────────────┘
                               ↓
                    ┌─────────────────────┐
                    │ Context Ranking     │
                    └──────────┬──────────┘
                               ↓
                    ┌─────────────────────┐
                    │ Token Budget Manager│
                    └──────────┬──────────┘
                               ↓
                    ┌─────────────────────┐
                    │ Context Compaction  │
                    └──────────┬──────────┘
                               ↓
                    ┌─────────────────────┐
                    │ Prompt Assembly     │
                    └──────────┬──────────┘
                               ↓
                    ┌─────────────────────┐
                    │        LLM          │
                    └──────────┬──────────┘
                               ↓
                 ┌─────────────┴─────────────┐
                 ↓                           ↓
           Tool Calling                 Final Answer
                 ↓
          Result Processing
                 ↓
           State Update
                 ↓
          Memory Extraction
                 ↓
           Context Store


二十八、一个可落地的Context Builder实现

下面给出一个简化的Go实现:

type ContextManager struct {
    MemoryStore    MemoryStore
    Retrieval      Retriever
    Compressor     Compressor
    Tokenizer      Tokenizer
}

func (m *ContextManager) Build(
    ctx context.Context,
    req BuildRequest,
) (*Context, error) {

    memory, err := m.MemoryStore.Search(
        ctx,
        req.UserID,
        req.Query,
    )
    if err != nil {
        return nil, err
    }

    docs, err := m.Retrieval.Search(
        ctx,
        req.Query,
        10,
    )
    if err != nil {
        return nil, err
    }

    context := &Context{
        Memory:    memory,
        Retrieval: docs,
        History:   req.History,
        TaskState: req.TaskState,
    }

    tokens := m.Tokenizer.Count(context)

    if tokens > req.TokenBudget {
        context, err = m.Compressor.Compress(
            ctx,
            context,
            req.TokenBudget,
        )
        if err != nil {
            return nil, err
        }
    }

    return context, nil
}

关键点不是代码本身,而是:

Build
 ↓
Count
 ↓
Budget
 ↓
Compress

Context Manager应该拥有明确的Token预算控制能力。


二十九、Context Compression不要只看Token压缩率

一个压缩器如果:

原始:10000 Token
压缩:1000 Token

看起来非常优秀。


但如果压缩后丢失:

数据库选型
支付方式
关键业务约束

那么这个压缩器实际上是失败的。


因此应该定义:


[

Compression\ Efficiency =

\frac{Useful\ Information\ Retained}

{Context\ Tokens}

]


而不是:


[

Compression\ Ratio =

\frac{CompressedTokens}

{OriginalTokens}

]


真正优秀的Context压缩应该做到:

Token ↓
信息密度 ↑
任务成功率不下降


三十、建立Context Evaluation体系

生产环境中不能只测试:

回答是否正确?

还应该测试:

1. Context Recall

关键事实是否被保留?

2. Context Precision

加入的上下文是否真正有用?

3. Context Utilization

模型是否正确使用上下文?

4. Compression Loss

压缩前后任务成功率下降多少?

5. Token Efficiency

完成相同任务需要多少Token?

6. Cache Hit Rate

稳定Context的缓存命中率是多少?


三十一、Lost in the Middle应该成为测试项

长Context测试不能只把正确答案放在:

开头

或者:

结尾

而应该测试:

10%
30%
50%
70%
90%

不同位置。


因为研究显示,长上下文模型在关键证据位于中间位置时可能出现明显性能下降。([arXiv][1])


因此Agent评测数据集应该专门设计:

Long Context

+
Distractors

+
Position Shift

+
Multiple Relevant Facts

测试模型是否真的使用了Context,而不是只依赖最近几条消息。


三十二、一个生产级Context策略

对于绝大多数企业Agent,可以从下面这套策略开始:

System Prompt
    ↓
稳定不变

Tool Definition
    ↓
稳定不变

User Profile
    ↓
只保留长期有效信息

Task State
    ↓
结构化存储

Recent History
    ↓
保留最近5~20轮

Long History
    ↓
Summary

RAG
    ↓
Top-K + Rerank

Tool Result
    ↓
结构化 + 摘要

Context
    ↓
Token Budget

超过阈值
    ↓
Compaction

这比单纯使用:

messages = 全部历史消息

可靠得多。


三十三、常见错误与反模式

33.1 把所有聊天记录全部发送

这是最常见的问题。


结果:

Token成本 ↑
延迟 ↑
噪声 ↑
相关性 ↓
模型注意力分散


33.2 把RAG结果全部塞给模型

RAG应该解决:

不知道

而不是:

什么都给模型

检索系统必须承担过滤责任。


33.3 只使用Summary,不保存原始事实

Summary有损压缩。


因此重要事实应该独立保存:

Fact Store

+
Summary

而不是:

只有Summary


33.4 让Tool直接返回巨大文本

应该:

Tool
 ↓
Result Processor
 ↓
Structured Data
 ↓
LLM


33.5 Context没有Token Budget

如果Context没有Budget,它迟早会失控。


所有生产Agent都应该能够回答:

当前Context多少Token?

各模块占多少?

哪部分最占Token?

哪些Context被压缩?

哪些Context被丢弃?


三十四、Context Observability:必须知道模型到底看到了什么

Agent日志不能只记录:

request
response

至少应该记录:

{
  "session_id": "sess_001",
  "model": "xxx",
  "input_tokens": 18240,
  "cached_tokens": 12100,
  "output_tokens": 1300,
  "context": {
    "system": 3200,
    "memory": 900,
    "history": 4200,
    "retrieval": 6800,
    "tools": 2400,
    "task_state": 740
  },
  "compaction": {
    "triggered": true,
    "before": 31000,
    "after": 14200
  }
}

这样出现:

“Agent为什么忘记用户刚才说的话?”

时,工程师才能真正定位:

是没有检索?
还是排序错误?
还是Context被压缩?
还是Token Budget不足?
还是Tool结果覆盖?
还是模型没有正确利用Context?


三十五、推荐的Context工程目录结构

对于Go Agent项目,可以采用:

agent/
├── context/
│   ├── builder.go
│   ├── budget.go
│   ├── ranking.go
│   ├── compression.go
│   ├── summarizer.go
│   └── policy.go
│
├── memory/
│   ├── short_term.go
│   ├── long_term.go
│   ├── vector.go
│   └── store.go
│
├── retrieval/
│   ├── search.go
│   ├── reranker.go
│   └── filter.go
│
├── state/
│   ├── state.go
│   ├── event.go
│   └── reducer.go
│
├── tools/
│   ├── registry.go
│   ├── executor.go
│   └── result_processor.go
│
├── agent/
│   ├── runner.go
│   └── planner.go
│
└── observability/
    ├── tracing.go
    ├── metrics.go
    └── context_log.go

这样的架构可以把Context从Agent业务逻辑中解耦出来。


三十六、从Prompt Engineering走向Context Engineering

Prompt Engineering解决的是:

怎么告诉模型做什么

Context Engineering解决的是:

模型现在应该知道什么

两者并不是替代关系,而是上下游关系:

Prompt Engineering
        ↓
Instruction

Context Engineering
        ↓
Information

Agent Runtime
        ↓
Execution

LLM
        ↓
Decision

未来Agent系统的竞争力,很大程度上不再取决于谁写了一段更漂亮的System Prompt,而取决于谁能构建更加可靠的Context Runtime。


三十七、企业级落地路线

如果从零开始建设Context Engineering体系,不建议一开始就做得非常复杂。


可以分四个阶段。

第一阶段:基础Context

实现:

System Prompt

+
Recent History

+
Token Counting

目标:

防止Context无限增长。


第二阶段:Memory + State

加入:

Short Memory
Long Memory
Task State

目标:

不再依赖完整聊天记录保存任务状态。


第三阶段:Retrieval + Compression

加入:

RAG
Rerank
Summary
Compaction
Tool Result Compression

目标:

在有限Token下提升信息密度。


第四阶段:Context Runtime

最终建设:

Context Builder
Context Policy
Context Budget
Context Ranking
Context Compression
Context Cache
Context Observability
Context Evaluation

这时候Context已经不再是Agent代码中的一个变量,而成为独立基础设施。


三十八、结语:Agent的核心不是记住一切,而是知道此刻需要什么

长上下文解决的是:

模型能够容纳多少信息。

上下文工程解决的是:

模型应该看到哪些信息。

这是两个完全不同的问题。


一个128K甚至更大的Context Window,并不能自动解决长对话中的“遗忘”、RAG噪声、Tool结果膨胀和Token成本问题。已有研究表明,长上下文模型依然可能受到信息位置和上下文长度的影响。([arXiv][1])


因此,一个真正成熟的Agent应该遵循这样的上下文原则:

不是保存所有信息
        ↓
而是保存重要信息

不是检索所有文档
        ↓
而是检索相关信息

不是发送所有工具结果
        ↓
而是发送有效结果

不是无限扩大Context
        ↓
而是管理Context Budget

不是简单删除历史
        ↓
而是进行结构化Compaction

不是让Conversation承担全部状态
        ↓
而是Conversation + State + Memory

不是只看模型输出
        ↓
而是观察整个Context Pipeline

最终可以把Agent上下文工程浓缩成一个公式:


[

Agent\ Intelligence

\approx

Model\ Capability

\times

Context\ Quality

\times

State\ Consistency

]


模型决定Agent的上限,而上下文决定模型在具体任务中能够发挥多少能力。


对于生产环境而言,真正值得投入的不是把Prompt再写长一点,而是建立一套完整的Context Runtime:能够检索、筛选、排序、压缩、缓存、更新和观测上下文,并且始终围绕当前任务的Token预算做信息取舍。


当Agent从“聊天机器人”升级到能够连续运行数小时、数天甚至数周的业务系统之后,上下文工程就不再是优化项,而会成为Agent架构中与模型、工具、记忆和工作流同等重要的基础设施。

参考资料

  1. Nelson F. Liu, Kevin Lin, John Hewitt, Ashwin Paranjape, Michele Bevilacqua, Fabio Petroni, Percy Liang. Lost in the Middle: How Language Models Use Long Contexts. arXiv:2307.03172, 2023. ([arXiv][1])

  2. OpenAI API Documentation. Responses API / Streaming Events / Context Truncation / Compaction / Prompt Caching. OpenAI API官方文档

  3. OpenAI. Data controls in the OpenAI platform. OpenAI Platform Documentation. OpenAI Platform官方文档

[1]: https://arxiv.org/abs/2307.03172?utm_source=chatgpt.com "Lost in the Middle: How Language Models Use Long Contexts"

[2]: https://platform.openai.com/docs/api-reference/responses-streaming/response/refusal/delta?lang=curl&utm_source=chatgpt.com "Streaming events | OpenAI API Reference"