想象一下,你聘请了一位才华横溢的咨询师。他拥有两个博士学位,会说七种语言,并能解决你甚至不知道存在的问题。你把他安排到一个会议室,然后告诉他:“我需要你重新设计项目的认证系统。”
咨询师看着你,点了点头,问道:“哪个项目?”
你没有提供代码访问权限,也没有解释其架构。他不知道你在使用JWT令牌还是会话cookie,也不了解你使用的编程语言或微服务数量,更不知道为什么你上一次迁移会失败。
这个咨询师,就像你的LLM(大型语言模型)。而你刚刚犯了90%的AI代理使用者都犯的错误:关注大脑本身,而不是关注大脑所看到的信息。
Prompt工程已经过时——上下文工程长盛不衰
过去几个月,我在每个论坛、每条推特讨论线程、每场团队会议中都看到同样的对话:“GPT-5还是Claude Opus?”“哪个模型对代码更好?”“哪个模型推理能力更强?”
我的回答,每次推算下来,都是一样的:这不重要。当然,不是完全不重要。但一个顶尖模型与另一个顶尖模型之间的差异,与提供优秀上下文和垃圾上下文之间的差异相比,简直微不足道。
一个表现平庸的模型但配备完美上下文,永远优于一个顶尖模型却给它垃圾上下文。总是如此,没有例外。
这就有了一个名词:上下文工程。而且,它与Prompt工程完全不同。
Prompt工程是编写一个好的提示。它是选择正确的词语、结构请求、加入示例。这很重要,但只是一个部分。
上下文工程则是设计模型看到的所有信息:哪些输入信息、顺序如何、空间有限时需要舍弃什么、压缩什么、哪些是必须保留的。这是为LLM做信息架构。
简单来说:Prompt工程是提出一个好的问题。而上下文工程则是在考试开始前决定学生桌上有什么书籍。
四阶段记忆:你看不到的生命周期
OpenAI最近发布了两个Cookbook文章,详细解构了具有长期记忆的代理如何进行上下文管理。这不是RAG,也不是矢量数据库,而是一种基于状态的系统,像一本具有严格规则的田野笔记本。
其模式是“本地优先”和“基于状态”的:一个结构化的状态对象与代理一起更新,贯穿每个阶段。
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拒绝PII和推测"] -.-> B
C1["异步任务\n合并会话→全局\nLLM辅助去重\n过滤临时记忆"] -.-> C
D1["修剪会话: 最近N次\n注入修剪记忆入\nsystem prompt"] -.-> 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:注入——考试桌上的资料
当会话启动时,代理会创建其初始上下文。这不是随机的,而是一个具体结构:
- YAML前置数据,包含用户状态(如偏好、配置)。
- 全局记忆列表:最多6个,按最近使用排序。为什么是6个?因为超过6个会相互竞争,导致信息模糊。少即是多。
<memory_policy>块,带有明确的优先规则。
优先规则至关重要:当前输入 > 会话记忆 > 全局记忆 > 同范围内的最新信息。如果用户说“我现在用Vim”,而你的全局记忆却显示“使用VS Code”,则以用户最新输入为准。虽然看起来显而易见,但没有明确规则的情况下,模型有时会选择其“记住”的内容而非用户给出的新信息。
阶段2:提纯——捕获而不污染
对话过程中,代理可以使用类似save_memory_note()的工具实时捕捉记忆。但并非所有信息都可接受。此工具有严格的保护措施:
- 验证持久性:例如“用户今晚想要吃披萨”不是持久记忆,应当拒绝。
- 要求可操作性:记忆必须在未来会话中具有实质意义。
- 拒绝PII(个人可识别信息):完整名字、地址、银行卡信息等会被剔除。
- 拒绝推测:例如“我猜用户更喜欢Python”属未经证实的意见,不是事实。
- 要求用户确认:在保存前需要用户确认。
这种过滤非常严格,原因显而易见。一条被污染的记忆会影响所有未来会话。就像在你的田野笔记本中包含错误信息,每次查询时结果都会受到不正确信息的干扰。
阶段3:合并——夜晚的清理工作
每次会话后,会运行一个异步进程,收集会话中的笔记并与全局记忆合并。合并不是简单附加,而是智能处理:
- LLM辅助去重:如果两条笔记内容相似,则进行整合。
- 过滤临时记忆:所有带有“这次”、“今天”、“现在”的内容将被丢弃。
- 依据最新性解决冲突:如果一条新笔记与旧笔记冲突,将选择新的。
这类似晚上清洁桌面的人。他不会把所有东西都丢掉,而是保存重要内容,合并重复信息,丢弃不再有用的备忘。
阶段4:修剪——减少但不丢失
当历史记录膨胀过大时,就需要修剪。TrimmingSession只保留最近的N次会话交互。但要注意——当部分会话被修剪时,其中的记忆不会丢失。它们会被重新注入到下一次系统提示中。
这类似撕掉笔记本里的旧页面,但在丢弃前将重要笔记复制到第一页。
修剪与摘要:两种处理哲学,一个难题
在短期内管理会话记忆时,有两种基本技术。每种都有其优势和风险。
flowchart LR
subgraph Trimming["修剪(最近N次交互)"]
direction TB
T1["完整历史记录\n(40次交互)"]
T2["修剪交互记录1-30"]
T3["保存31-40的交互记录\n(不变,无更改)"]
T1 --> T2 --> T3
end
subgraph Summarization["摘要(压缩方式)"]
direction TB
S1["完整历史记录\n(40次交互)"]
S2["LLM压缩交互记录1-30\n至约400Tokens"]
S3["注入摘要\n+交互记录31-40"]
S1 --> S2 --> S3
end
style Trimming fill:#1a2332,stroke:#4a9eed,color:#fff
style Summarization fill:#2a1a32,stroke:#9a4eed,color:#fff
修剪:确定性的切断方式
扫描交互记录,向后保存最近的N次完整交互,较早的记录完全删除。
优势:保留最近上下文的完整性。剩余部分未被更改或摘要,所有信息是原始消息。
劣势:突然失忆。第N-1次交互完整保留,第N-2次交互却完全丢失。没有逐渐衰退——只存在明确的记忆与完全遗忘之间的二元状态。
这类似于金鱼的记忆,能记住最后的10秒,但早些时候的信息根本不存在。
摘要:压缩方式的风险
当历史记录超过某个阈值时,将旧记录压缩,生成一个合成的user/assistant交互对,注入到会话开头。摘要提示有以下严格原则:
- 保留关键点(做出的决策、达成的共识)。
- 保持时间顺序。
- 检测矛盾并标注。
- 不确定的信息标记为“待验证”。
- 每个摘要最大400个Tokens。
优势:整体保留了对话的关键内容。没有突如其来的记忆丢失。即使交互已经过去30次,模型仍能“记得”之前决定使用PostgreSQL而非MongoDB。
劣势:错误积累。如果错误信息进入摘要,就会污染后续的行为。因为摘要是通过LLM生成的,理论上可能出现“幻觉”。一个错漏的摘要会让模型在其后的会话中错误地使用记录。
为区分真实与合成数据,所有摘要记录都包含可追溯的元数据:{"synthetic": bool, "kind": "...", "summary_for_turns": "..."}。这样当问题出现时,你至少可以检查模型处理的数据来源是原始记录还是压缩生成的摘要。
你已经在做这些(而不自知)
如果你在使用Claude Code,你已经有了一个运行中的上下文工程系统。只是这个系统不是你设计的,而是Anthropic团队的成果。但如果仔细观察,各种部件的确协同运行:
你的全局CLAUDE.md + 每个项目的CLAUDE.md + SKILL.md文件 = 手动完成的上下文注入。你决定模型在每次会话开始时看哪些信息。你决定哪些“书籍可以放到桌子上”。
~/.claude/projects/*/memory/目录,Claude Code存储跨会话的笔记=直接实现了上下文注入与提纯模式。模型在会话期间捕捉信息,并在下一次会话中恢复。
自动上下文压缩,当对话过长时,由Claude Code实现=修剪和摘要的结合。虽然过程是透明的,但每次会话超出一定长度时,部分对话会被压缩保存。
技能模块(如/blog、/commit等)=根据需求注入专门的上下文,而不是在会话开始时加载所有可能用到的信息。
最有趣的一点是:你的CLAUDE.md文件质量决定了你的代理质量。比你选择的模型更重要。一个结构清晰、有明确规则、路径正确的CLAUDE.md文件能将任何表现尚可的模型转化成优秀的助手。而一个空白或混乱的CLAUDE.md文件会让世界上最先进的模型,也变得像个被关在黑暗房间里的聪明咨询师。
Prompt债务:你看不到的技术债务
你是否听说过技术债务?是指能运行但未来会导致问题的代码。捷径成了之后的阻碍。
上下文工程也有类似的债务:Prompt债务。它包括所有配置文件、指令、记忆和笔记的积累,但没有人维护。
一个充满矛盾指令的CLAUDE.md。已经不适用的全局记忆。不更新路径已改变的技能模块。那些从未记录的隐含优先级规则。
每一片过时的上下文,都会成为噪声。而噪声与有效信息争夺模型的注意力。噪声越多,结果越糟。这并不是模型本身出了问题,而是它接收的信息被混合了无用数据,无法正确区分。
维护你的上下文工程层的清洁性,与维护代码的清洁性一样重要。也许更重要,因为代码中的错误会响亮地出错。而上下文中的错误则是悄无声息的——模型只是基于错误的信息做出错误决策,没人能察觉。
可行动建议:从今天开始做什么
所有这些理论都很好,但星期二早晨你该如何实践呢?
1. 审计你的CLAUDE.md(或等效文件)。 是否存在矛盾指令?已经不存在的路径?不再适用的规则?清理这些冗余。每一行多余信息都是干扰。
2. 按稳定性排序你的上下文信息。 将永远不变的信息放在最前面(规范、技术栈)。变化较频繁的信息放在后面(当前任务)。这会最大化缓存命中数并减少成本。这不仅是为了形式清晰,更是经济需求。
3. 明确优先规则。 如果用户表达与记忆存在冲突,谁优先?如果不定义,模型会替你做决定,而结果可能不可控。
4. 积极过滤。 并非所有信息都值得记住。架构决策值得记录;用户更喜欢使用Tab还是Space也许值得;而今天开始时下雨的事实,则没有必要。
5. 区分真实和合成上下文。 如果使用摘要技术,请标记摘要部分。当出现问题时,你需要知道模型处理的内容是否基于真实数据或一个可能错误的摘录。
6. 将上下文维护视为技术债务。 添加到待办列表中,定期检查。虽然这工作可能不够闪耀,但它是将效率普通的代理与智能助手区分开来的关键。
没人会把它写进简历的技能
上下文工程是一个隐形的技能。它不会出现在招聘广告中,没有证书认证,也没有40小时的在线课程可以拿到文凭。
然而,这就是那些“使用ChatGPT”的人,与那些打造强大功能AI代理的人之间的分水岭。这就是向LLM问问题,与设计一个能正确地给出答案的系统之间的区别。
下次当你的代理做出愚蠢的举动时,在指责模型之前,看看它处理的上下文内容。问题可能并不出在大脑,而在于大脑所看到的信息。
这部分,与你选择的模型不同,可以完全由你掌控。
参考资料: OpenAI Cookbook 两篇文章:用于长期个性化的上下文工程 和 通过会话管理短期记忆。如果你对AI编程代理的内部运行模式感兴趣,请查看你的AI编程代理是一个具有超大理想的while循环。对Prompt顺序如何影响成本有兴趣,请阅读为什么你发送给Claude的99%内容已经存在于缓存中。
本文原文为西班牙语,借助AI翻译。