oh-my-pi 全景代码解析(七):Context Engineering——上下文工程系统
系列导读:本文深入 Pi 最具战略意义的架构层面——上下文工程系统。在 LLM 上下文窗口有限(即使 200K tokens 也是稀缺资源)的现实下,如何高效管理进入上下文窗口的信息,直接决定了 Agent 的性能上限。我们将从树形会话存储、Compaction 压缩系统、Skills 渐进式披露、AGENTS.md 层级加载、Prompt Templates、动态上下文注入、以及 Hindsight 长期记忆七个维度展开。
一、上下文窗口:Agent 的稀缺资源
在讨论 Pi 的上下文工程系统之前,必须理解一个基本约束:上下文窗口是 Agent 的稀缺资源。即使 Claude 3.7 Sonnet 提供 200K tokens 的上下文窗口,在以下场景中仍然捉襟见肘:
graph LR
subgraph "Context Window Consumption"
C1[Large Codebase
React src ~50K tokens]
C2[Long Session History
100 turns ~80K tokens]
C3[Multi-file Read
5-20K per read]
C4[Tool Output
Grep ~10K+ tokens]
C5[System Prompt
Claude Code ~10K
Pi ~200 tokens]
end
C1 --> T[Total Context Window]
C2 --> T
C3 --> T
C4 --> T
C5 --> T
T --> L[Limit: 200K tokens]
T --> P[Performance degrades near limit]当上下文窗口接近上限时,Agent 的性能会急剧下降:
- 模型开始"遗忘"早期对话内容
- 工具调用的精度下降(因为可用上下文减少)
- 响应延迟增加(处理长上下文需要更多计算)
- 成本激增(input tokens 按量计费)
Pi 的上下文工程系统正是为了解决这些问题而设计的。它不是单一的技术,而是一套分层、协作、可扩展的策略集合。
二、树形会话存储:非线性的对话历史
传统的聊天应用使用线性聊天记录:消息按时间顺序排列,用户只能"回退"到某一点。Pi 采用树形结构存储会话历史,这是其区别于所有其他编码 Agent 的核心 UX 创新。
2.1 数据模型
classDiagram
class SessionTree {
+string version
+string id
+number createdAt
+number updatedAt
+string rootId
+string[] currentBranch
+Map~string, SessionNode~ nodes
}
class SessionNode {
+string id
+string parentId
+NodeType type
+string role
+string content
+NodeMetadata metadata
+string[] children
+string[] bookmarks
+number createdAt
+number updatedAt
}
class NodeMetadata {
+string model
+string provider
+number temperature
+Cost cost
+Tokens tokens
+number latencyMs
+string toolName
+Record toolArgs
+string[] filesRead
+string[] filesModified
+string[] filesCreated
+string[] filesDeleted
+EditOperation[] editOperations
+boolean editSuccess
+number editAttempts
+string[] compactionSource
+string compactionStrategy
+string userReaction
+string userNotes
}
SessionTree --> SessionNode
SessionNode --> NodeMetadata2.2 会话树可视化
graph TD
Root["Turn 1: Implement auth
node-001"] --> A["Turn 2a: Use JWT
node-002
model: claude-3.7-sonnet"]
Root --> B["Turn 2b: Use OAuth
node-003
model: gpt-5.1-codex"]
Root --> C["Turn 2c: Use Session Cookies
node-006"]
A --> A1["Turn 3a: Generate token
node-005"]
A1 --> A2["Turn 4a: Verify token
node-010
bookmark: approach-decision"]
A2 --> A3["Turn 5a: Test pass
✓"]
B --> B1["Turn 3b: Setup provider
node-004"]
B1 --> B2["Turn 4b: Too complex
node-009
compaction: branch_summary"]
C --> C1["Turn 3c: Cookie config
node-008"]
C1 --> C2["Turn 4c: CSRF issue
node-011"]
C2 --> C3["Turn 5c: Fix CSRF
✓"]
style A3 fill:#c8e6c9
style B2 fill:#ffcdd2
style C3 fill:#c8e6c92.3 树操作 API
graph TD
subgraph "Tree Operations"
O1[fork(nodeId)] --> O1A[Create branch point]
O1A --> O1B[Switch to new branch]
O1B --> O1C[Save session]
O2[navigateTo(nodeId)] --> O2A[Get path to root]
O2A --> O2B[Set currentBranch]
O2B --> O2C[Load context]
O3[appendNode(node)] --> O3A[Add to current leaf]
O3A --> O3B[Update currentBranch]
O3B --> O3C[Save session]
O4[getAllBranches()] --> O4A[Find all leaves]
O4A --> O4B[Build paths to root]
O4B --> O4C[Return branch arrays]
end核心代码:
class SessionTreeManager {
fork(nodeId: string): string {
const newBranchId = generateUUID();
const node = this.tree.nodes[nodeId];
const branchPoint: SessionNode = {
id: newBranchId,
parentId: nodeId,
type: "branch_point",
role: "system",
content: `[Branch created from ${nodeId}]`,
metadata: { createdAt: Date.now() },
children: [],
bookmarks: [],
};
this.tree.nodes[newBranchId] = branchPoint;
node.children.push(newBranchId);
this.tree.currentBranch = this.getPathToRoot(newBranchId);
this.save();
return newBranchId;
}
getCurrentContext(): Context {
const path = this.getCurrentPath();
const messages = path
.filter(n => n.type === "message" || n.type === "tool_call" || n.type === "tool_result")
.map(n => ({ role: n.role, content: n.content, timestamp: n.metadata.createdAt }));
return { systemPrompt: this.getSystemPrompt(), messages };
}
}三、Compaction:上下文压缩系统
当会话历史接近上下文窗口上限时,Pi 的 Compaction 系统会自动或手动将旧消息压缩为结构化摘要。
3.1 触发条件与策略
graph TD
A[Compaction Trigger] --> B{Auto Trigger?}
B -->|Yes| C{Context > Threshold?}
C -->|Yes| D[Trigger Compaction]
C -->|No| E[Check Max Turns]
E -->|Exceeds| D
E -->|No| F[Continue]
B -->|No| G{Manual /compact?}
G -->|Yes| D
G -->|No| F
D --> H[Select Strategy]
H --> I[Default]
H --> J[Topic-based]
H --> K[Code-aware]
H --> L[Hierarchical]
H --> M[Custom Extension]3.2 默认压缩策略
graph TD
A[Compaction Start] --> B[Determine Compress Range]
B --> C[Preserve Recent N Turns]
C --> D[Extract Key Info]
D --> D1[Files Modified]
D --> D2[Key Decisions]
D --> D3[Errors]
D --> D4[Tool Results]
D1 --> E[Generate Summary with LLM]
D2 --> E
D3 --> E
D4 --> E
E --> F[Create CompactionEntry]
F --> G[Inject Key Info Messages]
G --> H[Assemble New Context]
H --> I[Verify Token Count]
I --> J{Still Over Limit?}
J -->|Yes| K[Recursive Compaction]
J -->|No| L[Return Compressed Context]
K --> H核心代码:
function compactContext(context, config, extensions) {
const totalTokens = estimateTokens(context);
const threshold = config.model.contextWindow * config.contextWindowThreshold;
if (totalTokens <= threshold) return context;
const preserveCount = config.preserveRecentTurns;
const compressStart = Math.max(0, messages.length - preserveCount - 1);
const toCompress = messages.slice(0, compressStart);
const toPreserve = messages.slice(compressStart);
const keyInfo = extractKeyInformation(toCompress);
const summary = generateSummary(toCompress, config);
const compressed = [
{ role: "system", content: `[Previous conversation summarized]:\n\n${summary}` },
{ role: "system", content: `[Files modified]:\n${keyInfo.filesModified.join("\n")}` },
{ role: "system", content: `[Key decisions]:\n${keyInfo.keyDecisions.map(d => `- ${d}`).join("\n")}` },
...toPreserve,
];
return { ...context, messages: compressed };
}3.3 与 Claude Code 的 Compaction 对比
graph LR
subgraph "Claude Code"
C1[Auto or manual /compact]
C2[Keep requests + key snippets + root memories]
C3[Clear old tool outputs]
C4[Summarize conversation]
C5[Fixed strategy]
C6[Linear history]
end
subgraph "Pi"
P1[Auto or manual
Configurable threshold]
P2[Keep recent tail + summaries + file tracking]
P3[Structured CompactionEntry]
P4[Branch-aware compression]
P5[Fully customizable via extensions]
P6[Tree structure]
end
C1 -.->|More flexible| P1
C2 -.->|More structured| P2
C3 -.->|Structured entries| P3
C4 -.->|Branch aware| P4
C5 -.->|Extensible| P5
C6 -.->|Non-linear| P6| 维度 | Claude Code | Pi |
|---|---|---|
| 触发方式 | 接近限制自动触发,或手动 /compact | 自动或手动,可配置阈值和策略 |
| 保留内容 | 请求、关键片段、根记忆 | 最近尾部、摘要、文件追踪、关键决策 |
| 压缩方式 | 清除旧工具输出 → 总结对话 | 结构化 CompactionEntry + 分支摘要 |
| 扩展性 | 固定策略 | 完全可定制(扩展可实现自定义策略) |
| 多轮压缩 | 支持(压缩的压缩) | 支持,且保留层级结构 |
| 分支感知 | 不支持(线性历史) | 支持(分支独立压缩) |
四、Skills 系统:渐进式披露的能力包
Skills 是 Pi 的模块化能力扩展机制。与将所有功能塞进系统提示不同,Skills 采用渐进式披露(progressive disclosure)设计——只有 skill 的名称和描述常驻系统提示,完整内容在需要时按需加载。
4.1 Skill 目录结构
graph TD
subgraph "Skill Directory"
S1[SKILL.md
Required: frontmatter + instructions]
S2[scripts/
Optional: helper scripts]
S3[references/
Detailed docs
Loaded on demand]
S4[assets/
Templates & configs]
S5[package.json
For npm publishing]
end
S1 --> S1A[---
name: brave-search
description: Web search...
---
# Setup
# Usage]
S3 --> S3A[api-reference.md]
S3 --> S3B[examples.md]
S3 --> S3C[troubleshooting.md]4.2 渐进式披露机制
graph TD
A[System Prompt] --> B[Skills List
Name + Description only]
B --> C[LLM decides to use skill]
C --> D[Load SKILL.md on demand]
D --> E[Execute skill scripts]
E --> F[Load references if needed]
G[Full Context Window] --> H[Skills: ~50 tokens]
G --> I[Loaded Skill: ~500 tokens]
G --> J[References: ~2000 tokens]
G --> K[Total: ~2550 tokens]
G --> L[Without progressive disclosure: ~5000+ tokens]核心代码:
class SkillManager {
private skills: Map<string, Skill> = new Map();
private loadedSkills: Set<string> = new Set();
buildSkillsPrompt(): string {
const lines = ["## Available Skills"];
for (const [name, skill] of this.skills) {
lines.push(`- ${name}: ${skill.description}`);
}
return lines.join("\n");
}
async loadSkill(name: string): Promise<string> {
const skill = this.skills.get(name);
if (!skill) throw new Error(`Skill "${name}" not found`);
if (this.loadedSkills.has(name)) return skill.fullContent!;
const content = await fs.readFile(
path.join(skill.path, "SKILL.md"), "utf-8"
);
const body = content.replace(/^---[\s\S]*?---\n/, "");
skill.fullContent = body;
this.loadedSkills.add(name);
return body;
}
}五、AGENTS.md 层级加载:项目指令的继承系统
AGENTS.md 是 Pi 的项目指令文件,其加载遵循从具体到一般的层级继承规则。
5.1 加载优先级
graph TD
subgraph "Priority Hierarchy (High to Low)"
P1[1. Current Directory AGENTS.md]
P2[2. Parent Directory AGENTS.md]
P3[3. ... Up to Git Root]
P4[4. ~/.pi/agent/AGENTS.md]
P5[5. Built-in Default System Prompt]
end
P1 -->|Overrides| P2
P2 -->|Overrides| P3
P3 -->|Overrides| P4
P4 -->|Overrides| P5这种层级加载的设计灵感来自 Git 的 .gitignore 机制——允许在仓库的不同层级定义不同的规则,子目录的规则覆盖父目录的规则。
5.2 合并规则
graph TD
A[Find AGENTS.md Files] --> B[Sort by Priority]
B --> C[Load Each File]
C --> D{Merge Strategy?}
D -->|append| E[Low priority first
High priority last]
D -->|prepend| F[High priority first]
D -->|replace| G[Last file replaces all]
D -->|smart| H[Merge sections intelligently]
E --> I[Assembled System Prompt]
F --> I
G --> I
H --> I六、Prompt Templates:可复用的提示模板
Prompt Templates 是 Pi 的提示工程基础设施,允许用户定义可复用的提示片段,通过 /name 命令展开。
6.1 模板文件格式
graph TD
subgraph "Template Structure"
T1[---
name: review-pr
description: Review a PR...
variables:
- pr_url
- focus
---]
T2[# PR Review]
T3[## Focus Areas
{{#if focus}}
- Pay attention to: {{focus}}
{{/if}}]
T4[## Review Format
For each file...]
T5[## Security Checklist
- [ ] No hardcoded secrets
- [ ] Input validation]
end
T1 --> T2
T2 --> T3
T3 --> T4
T4 --> T5
6.2 模板展开流程
graph TD
A[User Input: /review-pr https://...] --> B[Parse Command]
B --> C[Find Template "review-pr"]
C --> D[Extract Variables]
D --> E[Replace {{pr_url}}]
E --> F[Evaluate {{#if focus}}]
F --> G[Assemble Full Prompt]
G --> H[Inject into Context]
H --> I[Send to LLM]
七、动态上下文注入:扩展的消息拦截机制
Pi 的扩展系统允许在每次 LLM 调用前动态注入消息,这是实现 RAG(Retrieval-Augmented Generation)、长期记忆、实时数据获取等功能的基础。
7.1 扩展注入机制
sequenceDiagram
participant User
participant Agent
participant Ext1 as Hindsight Extension
participant Ext2 as RAG Extension
participant LLM
User->>Agent: Send message
Agent->>Ext1: onBeforeTurn()
Ext1->>Ext1: Embed query
Ext1->>Ext1: Search memories
Ext1-->>Agent: Inject relevant memories
Agent->>Ext2: onBeforeTurn()
Ext2->>Ext2: Search vector DB
Ext2-->>Agent: Inject relevant docs
Agent->>LLM: Context with injected messages
LLM-->>Agent: Response
Agent->>Ext1: onAfterTurn()
Ext1->>Ext1: Store key info
Agent->>User: Display response7.2 RAG 实现示例
export default function ragExtension(pi: ExtensionAPI) {
const vectorDB = new VectorDB({ path: "./.pi/vectors" });
pi.onBeforeTurn(async (ctx) => {
const query = ctx.messages[ctx.messages.length - 1].content;
const chunks = await vectorDB.search({ query, topK: 10 });
const reranked = await rerankChunks(query, chunks);
const selected = selectChunksWithinBudget(reranked, 4000);
return {
messages: [{
role: "system",
content: `## Relevant Documentation\n\n${selected.map(c =>
`### ${c.title}\n${c.content}\n(Source: ${c.source})`
).join("\n\n")}`
}]
};
});
}八、Hindsight 长期记忆:跨会话的持久化知识
Hindsight 是 Pi 生态中的长期记忆系统,通过可选的扩展集成。它解决了 Agent 的跨会话遗忘问题——每次新会话开始时,Agent 对之前的工作一无所知。
8.1 Hindsight 架构
graph TB
subgraph "Pi Agent Process"
A1[Hindsight Extension]
A2[onBeforeTurn: retrieve memories]
A3[onAfterTurn: store memories]
end
subgraph "Hindsight Server (Optional)"
B1[Vector Database
pgvector / qdrant / milvus]
B2[Embedding Model
local llama.cpp / OpenAI / Ollama]
B3[Memory Store
SQLite / PostgreSQL]
B4[Scope Manager
Project / User / Global]
end
A1 -->|HTTP API| B1
A2 -->|HTTP API| B1
A3 -->|HTTP API| B3
B1 --> B2
B3 --> B48.2 记忆作用域
graph TD
subgraph "Memory Scopes"
S1[Project Scope
Codebase structure
Project conventions
Resolved bugs]
S2[User Scope
Coding preferences
Tech stack habits
Workflow patterns]
S3[Global Scope
General programming
Common solutions
Best practices]
S4[Recall-Only
No storage
Privacy sensitive]
end
S1 -->|Isolated per repo| P1[Project A]
S1 -->|Isolated per repo| P2[Project B]
S2 -->|Shared across repos| U1[User Profile]
S3 -->|Shared across all| G1[Global Knowledge]
S4 -->|No persistence| R1[Session Only]8.3 记忆生命周期
graph TD
A[Memory Entry] --> B{Type?}
B -->|decision| C[Key decision made]
B -->|error| D[Error + solution]
B -->|pattern| E[Code pattern discovered]
B -->|api| F[API usage pattern]
B -->|architecture| G[Architecture decision]
B -->|preference| H[User preference]
B -->|fact| I[Factual knowledge]
C --> J[Store with embedding]
D --> J
E --> J
F --> J
G --> J
H --> J
I --> J
J --> K[Auto-recall on relevant query]
K --> L{Confidence > threshold?}
L -->|Yes| M[Inject into context]
L -->|No| N[Skip injection]九、设计原则总结
mindmap
root((Context Engineering 原则))
分层管理
系统提示
项目指令
Skills
对话历史
记忆
渐进式披露
按需加载
分层逐步
不浪费上下文
结构保留
文件修改记录
关键决策
API 签名
可扩展性
压缩策略可替换
检索机制可替换
注入逻辑可替换
本地优先
敏感数据本地存储
会话文件可审计
跨会话持久
Hindsight 记忆
项目级知识
用户级偏好Pi 的上下文工程系统体现了几个核心原则:
- 分层管理:不同信息(系统提示、项目指令、技能、对话历史、记忆)有不同的管理策略和生命周期
- 渐进式披露:不一次性加载所有信息,而是按需、分层、逐步披露
- 结构保留:压缩时保留结构(文件修改记录、关键决策、API 签名)而非简单丢弃
- 可扩展性:所有策略(压缩、检索、注入)都可以通过扩展自定义
- 本地优先:敏感数据(对话历史、记忆)默认存储在本地
- 跨会话持久:通过 Hindsight 等机制,将知识从会话级提升到项目级和用户级
这些原则的共同目标是:在有限的上下文窗口内,最大化信息的价值密度。不是简单地"塞入更多信息",而是"塞入更相关的信息,以更高效的方式组织"。
在下一篇(最终篇)中,我们将探讨 Pi 的扩展生态系统、多模式运行架构、生产部署策略、性能优化技巧、以及项目的未来演进方向。
本系列基于 oh-my-pi v2026.07 和 Pi Agent v0.66+ 的公开源码与文档编写。