想象一下,你聘请了一位才华横溢的顾问。他拥有两个博士学位,会说七种语言,并且能够解决你甚至都不知道存在的问题。你让他坐到会议室里,然后对他说:“我需要你重构项目的认证流程。”

顾问看着你,点点头,问:“哪个项目?”

你没有给他代码访问权限,也没有给他解释系统架构。他不知道你用的是 JWT 令牌还是会话 cookie,不知道你使用什么编程语言,也不知道你有多少微服务,更不知道为什么上次的迁移尝试以失败告终。

这个顾问,就好比你的 LLM(大型语言模型)。而你刚刚犯了一个 90% 使用 AI 代理的人都会犯的错误:你专注于“大脑”,却忽略了“大脑所看到的内容”。

Prompt engineering 已死。Context engineering 长存。

最近几个月,我在每个论坛、每个 Twitter 话题、每次团队会议中都看到同一个讨论:“用 GPT-5 还是 Claude Opus?哪个模型更适合编程?哪个模型的推理能力更强?”

每次我计算这些问题时,答案基本都是一样的:无所谓。好吧,也不能说完全无所谓。但是,对比选择最好的模型和提供一个完美的上下文,后者的重要性高得多。

一个中等水平的模型配上完美的上下文,能够轻松打败一个顶级模型却只有糟糕上下文的组合。没有例外。这永远成立。

这就是**上下文工程(Context Engineering)**的意义所在。而且,请注意,这与 prompt engineering 并不是同样的概念。

Prompt engineering 是编写一个好的提示:选择正确的词语、组织请求的结构、添加示例等。这很重要,但只是其中的一部分。

context engineering 则是在设计模型所看到的一切内容:包括哪些信息要输入、顺序如何、有什么被舍弃、如何压缩,以及哪些必须被优先保留。这是为 LLM 设计的信息架构。

简单来说:prompt engineering 是提出一个好的问题,而 context engineering 是决定学生在考试前可以参考哪些书。

记忆的四个阶段:隐藏的生命周期

OpenAI 最近发布了两篇 Cookbook 文章,深入分析了拥有长期记忆的 AI 代理如何管理上下文。这不是 RAG(检索增强生成),也不是矢量数据库管理。它是一个基于状态的系统,就像一本有严格规则的现场笔记本。

这个模式使用的是 local-firststate-based 的方法:一个结构化的状态对象随着代理的运行更新,分为几个主要阶段。

flowchart TD
    A["1. 注入\n(会话创建时)"] --> B["2. 精炼\n(会话中)"]
    B --> C["3. 整理\n(会话后)"]
    C --> D["4. 修剪\n(保存时)"]
    D -->|"新会话开始"| A

    A1["将状态渲染为 YAML\n+ 全局记忆(最多 6 条)\n+ 优先级规则"] -.-> A
    B1["save_memory_note()\n校验记忆持久性\n要求有可操作性\n拒绝保存个人信息和假设"] -.-> B
    C1["异步任务\n合并会话数据 → 全局记忆\n使用 LLM 进行去重\n过滤临时信息"] -.-> C
    D1["修剪会话历史至 N 条\n重新注入修剪笔记\n到系统提示中"] -.-> D

    style A fill:#2d3748,stroke:#4a9eed,color:#fff
    style B fill:#2d3748,stroke:#ed9a4a,color:#fff
    style C fill:#2d3748,stroke:#9a4eed,color:#fff
    style D fill:#2d3748,stroke:#4aed5c,color:#fff

阶段 1: 注入(Injection)—— 考试桌上的教科书

在会话开始时,AI 代理会准备好其初始上下文。这不是随意拼凑的,而是一个明确的结构:

  • YAML frontmatter:记录用户的状态(偏好、配置)。
  • 全局记忆列表:最多 6 条,按最近性排序。为什么是 6 条?因为超过 6 条会相互竞争,导致信息稀释。更少即是更多。
  • <memory_policy>:包含明确的优先级规则。

优先级规则至关重要:当前输入 > 会话记忆 > 全局记忆 > 同一范围内的最近原则。例如,如果用户告诉你“我现在用 Vim”,而全局记忆中显示的是“使用 VS Code”,那么当前输入会覆盖全局记忆。这看似显而易见,但如果没有明确规则,模型有时会更相信它“记得”的内容,而非你刚刚输入的信息。

阶段 2: 精炼(Distillation)—— 捕捉,不被污染

在会话过程中,代理可以使用类似 save_memory_note() 这样的工具实时捕捉记忆。但不是所有信息都可以保存,工具有严格的“护栏”:

  • 验证持久性:诸如“用户今晚想吃披萨”这种不持久的信息会被拒绝。
  • 要求有可操作性:记忆必须在未来的会话中具备可操作的用途。
  • 拒绝个人信息(PII):完整姓名、地址、信用卡信息,统统排除。
  • 拒绝猜测性内容:例如“我猜用户喜欢 Python”这种内容是无法被接受的。
  • 需要用户确认:在保存前,模型会向用户确认。

这种过滤机制异常严格,但也必要。一条受污染的记忆可能会破坏所有后续的会话,就像在你的笔记本上写下错误的信息,每次查看都会基于错误数据做决策。

阶段 3: 整理(Consolidation)—— 深夜清洁

在每次会话结束后,异步任务会将当前会话的笔记与全局记忆进行整合。这不是简单的“追加”,而是智能的合并:

  • 使用 LLM 进行去重:如果两条笔记内容相似但表述不同,会被合并。
  • 过滤临时性笔记:任何带有“这次”、“今天”、“刚才”的笔记会被舍弃。
  • 基于最新原则解决冲突:若新旧笔记相矛盾,则以最新为准。

可以把这看作是每天结束时,有人整理你的书桌。他们不会丢弃所有的东西,而是会保存重要内容,合并意义重复的便签,并丢弃过时的信息。

阶段 4: 修剪(Trimming)—— 剪裁而不遗失

当对话历史过长时,必须进行修剪。TrimmingSession 只保留最近的 N 条对话。然而,重要笔记不会因此丢失。那些位于被修剪掉的部分中的记忆会被重新注入到下个回合的系统提示中。

这就像是撕掉笔记本的旧页,但先把重要笔记抄在第一页上以作保留。

修剪与总结:两种哲学,一场博弈

管理短期对话历史(即会话中的交互记录)有两种主要技术,各有优劣。

flowchart LR
    subgraph 修剪["修剪(按最近 N 条)"]
        direction TB
        T1["完整历史\n(40条消息)"]
        T2["剪掉第1-30条消息"]
        T3["保留第31-40条消息\n(完整未删减)"]
        T1 --> T2 --> T3
    end

    subgraph 总结["总结(压缩历史)"]
        direction TB
        S1["完整历史\n(40条消息)"]
        S2["LLM 压缩第1-30条消息\n约400令牌内"]
        S3["注入压缩后的摘要\n+ 第31-40条消息"]
        S1 --> S2 --> S3
    end

    style 修剪 fill:#1a2332,stroke:#4a9eed,color:#fff
    style 总结 fill:#2a1a32,stroke:#9a4eed,color:#fff