Pi :上下文工程——Context Assembly、Compaction 与 Memor

这是本系列最重要的"横向能力"篇。前面所有篇都在讲"Agent 怎么运行",这一篇讲Agent 每轮调用 LLM 时,最终进入模型窗口的那堆 token 到底是怎么来的。

每轮给模型构造的上下文,才是 Agent 的真正 CPU。 模型选错了可以换,上下文构造错了,Agent 会"越跑越傻"------这正是本系列一直强调"Context 是编译出来的"的完整展开。


〇、先建立全景:一次推理的上下文从哪里来

某一轮调用 LLM 时,进入窗口的内容可以画成一条流水线:

text 复制代码
用户输入
   ↓
System Prompt
   ↓
项目级 instructions(AGENTS.md / CLAUDE.md)
   ↓
Skill instructions(按需读取的 SKILL.md)
   ↓
历史 conversation(会话树 → 当前叶子路径)
   ↓
tool result(工具执行结果)
   ↓
文件内容(read 工具读进来的代码/文档)
   ↓
错误信息(bash 报错、测试失败输出)
   ↓
压缩 / 截断 / 摘要(compaction 检查点)
   ↓
最终 Context
   ↓
LLM

Pi 对这条链路的工程化,散在本系列各篇里;这篇把它们收拢成一个完整学科。记住:模型看到什么,决定模型做什么。上下文构造是 Agent 的隐性控制器。

一、Context Assembly:谁有资格进窗口

Pi 的上下文由这些来源组装(每个来源都有独立的生命周期和信任等级):

来源 Pi 的机制 生命周期
System Prompt 构建链:AGENTS.md → skills 目录 → 模板 → before_run 覆盖 每 run 构建,可覆盖
项目 instructions AGENTS.md/CLAUDE.md/.pi/SYSTEM.md(未信任也加载) 每会话加载
Skill instructions <available_skills> 目录 + 模型按需 read 全文 渐进式披露
历史对话 会话树 → 当前叶子到根的路径 append-only
工具结果 ToolResultMessage 随消息落账 每轮追加
运行时元数据 env 注入的 PI_SESSION_ID 等 每命令解析

关键原则(本系列反复出现):

  • 系统提示词保持极简:skills 只列描述不内联,因为"给模型多少判断原料"是预算。
  • 项目宪法优先 :AGENTS.md 在项目未信任时也加载------"这个仓库的规矩"优先级高于一切。
  • transform_context 是最后一道变换 :只改 provider 看到的、不改会话存的------"呈现层"和"存储层"分离,防止为临时目的污染永久记录。

二、Context Budget:窗口满了谁被扔

假设模型窗口 200k,Agent 跑久以后:

text 复制代码
历史消息      70k
代码          80k
tool output   50k
system         5k
----------------
             205k  ← 超了

"谁被扔"是决定 Agent 是否"越跑越傻"的关键决策。 候选策略(按信息价值从低到高):

策略 丢弃对象 代价
时间优先 最早的消息 可能丢"关键但早"的事实
重要度优先 低优先级消息(闲聊) 需要消息分级(总纲上下文篇)
工具结果优先 旧工具输出 模型可能"忘了"自己做过什么
system 永不删 系统提示词恒定 占固定预算
文件重读而非常驻 不保存文件内容,用时再 read 每次重新检索

Pi 的立场是"追加不变式 + 一次性失效":

Across the requests of a lane, provider context only grows at the tail. An insertion before the previous request's tail invalidates the provider's KV cache from that point on and multiplies token cost.

上下文只允许尾增长------因为中间插入会无效化 KV cache 并放大成本。所以:回合中途的写入推迟到 checkpoint 追加,compaction 是唯一一次刻意的缓存失效。

预算的硬护栏 :reserveTokens(默认 16k 留给响应)+ 溢出判定(显式超限错误 / 输入超窗口 / 可恢复 length)。预算要在发请求前就算好,超了就分类处理,事后才裁已经来不及。

三、Compaction:长周期运行的命脉

Compaction 是 Agent 长周期运行的核心技术。完整生命周期:

text 复制代码
Raw history
   ↓ 触发(阈值 / /compact 手动 / overflow 溢出)
Summary generation(LLM 摘要)
   ↓
Important state extraction(保留关键状态)
   ↓
Old messages replacement(CompactionEntry 落账)
   ↓
Continue execution(从 retainedTail 重建上下文)

Pi 的 CompactionEntry带三样东西:summary(摘要)、firstKeptEntryId(保留起点)、retainedTail(自包含检查点)。retainedTail 让重建可以从检查点直接往后走,不翻旧条目。

Compaction 的四个"能不能压"的判断,是工程精华:

  1. 摘要丢信息怎么办? 分级处理------普通对话可压,代码/SQL/合同这类"语义敏感"内容压了等于改语义。
  2. 工具执行事实能不能被 summary 覆盖? 危险。工具做了什么(改了哪个文件、结果是什么)是审计和继续推理的锚点,Pi 的 overflow compaction 明确把链接的那个响应从摘要准备中排除("exact overflow-response omission")------关键事实留原文。
  3. 文件修改记录该不该压缩? 不该。修改记录是"Agent 对世界做过的改变",压掉就无法审计/回滚。
  4. 用户约束是否允许压缩? 用户明确说的要求("不要改这个文件")是 CRITICAL 级,永不压(总纲上下文篇)。

compaction 的正确姿势:压缩的是"过程性的旧对话",保留的是"结论性的状态和约束"。

四、Working Memory vs Long-term Memory:五种不同的存储

很多 Agent 项目把"记忆"当成一个筐,但 Pi(和 CC 的对照)揭示了至少五种不同的存储,各有生命周期:

存储 生命周期 例子 谁来管
Conversation History 一次会话 会话树 引擎
Working Memory 一次 run 变量池/车道记录 运行时
Long-term Memory 跨会话 CC 的 memory 文件、项目事实 应用
Project State 随项目 .pi/settings.json、AGENTS.md 应用
Artifact State 随产物 git 历史、生成文件 外部

关键区分(总纲上下文篇):知识进上下文靠检索(RAG),经验进行为靠记忆(memory 文件),事实靠状态存储(项目/产物)。 三件事别混进一个筐:

  • Pi 的会话账本是 Conversation History + Working Memory 的合体(append-only + lane records);
  • CC 的 memory 文件是 Long-term Memory(跨会话的身份/偏好/被纠正过的工作方式);
  • 项目文件本身就是 Project/Artifact State(Agent 不需要"记住",需要时 read)。

设计你自己的 Agent 时,先回答:这五种存储各归谁、各活多久、谁能写。 最常见的错误是把 Long-term Memory 塞进 Conversation History------一旦会话压缩,记忆跟着丢。

五、对照你的工程:上下文系统的设计清单

能力 做法 优先级
ContextBuilder 单一管道 所有模块不许自己拼 prompt(总纲) P0
组装顺序即策略 身份→检索→去重→排序→过滤→预算(总纲) P0
预算前置 发请求前算 token,超了分类处理 P0
追加不变式 上下文只尾增长,不中间插入 P0
compaction 分级 普通对话可压,约束/事实/敏感内容不压 P0
五种存储分离 对话/工作/长期/项目/产物各归其位 P1
transform_context 呈现层 改 provider 看的,不改会话存的 P1
文件重读而非常驻 大文件用时 read,不长期占窗口 P1

一句话收束:上下文工程的本质是"在有限的窗口里,决定模型每一轮该看到什么、不该看到什么"。 它比模型选择更决定 Agent 质量------因为再强的模型,喂错上下文也会胡说。


知识卡片(本节体系归档)

text 复制代码
┌──────────────────────────────────────────────────────────┐
│ 知识节点:上下文工程(Agent 的真正 CPU)                     │
│                                                          │
│ What  Context Assembly 流水线 + Budget(谁被扔)+           │
│       Compaction(压过程留结论)+ 五层记忆分离               │
│                                                          │
│ Why   一般原理:模型看到什么,决定模型做什么。不变量:         │
│       ① 上下文只尾增长(KV cache 不失效)                   │
│       ② 预算前置:发请求前算好,超了就分类处理               │
│       ③ compaction 压的是过程性的旧对话,留的是结论和约束     │
│                                                          │
│ How   校验动作:                                          │
│       画上下文流水线 + PaiFlow 预算前置                      │
│                                                          │
│ Pits  坑点:                                              │
│       越跑越傻 / 上下文灌爆 / 记忆混筐                      │
│                                                          │
│ Transfer 到 PaiFlow:ContextBuilder 预算前置 + 分级压缩     │
└──────────────────────────────────────────────────────────┘
相关推荐
天空属于哈夫克33 天前
企业微信二次开发:精准实现关键词自动回复
架构·企业微信
子兮曰3 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰3 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万4 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝4 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋4 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
晨米酱4 天前
AGENTS.md:Agent 的上下文策略层
面试·架构·agent
这个DBA有点耶4 天前
MVCC深入:Read View、版本链与快照读——InnoDB并发控制的内核
数据库·mysql·架构
卡布鲁4 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
码流子4 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构