🧩 上下文工程
F2 章说过 context window 有限,历史会撑爆它。这是所有长任务 Agent 的核心难题。本章看 OpenCode 如何工程化地管理上下文:compaction(压缩历史)、子 Agent(分流任务)、按模型动态选 system prompt。这是生产 Agent 的"记忆艺术"。
本章目标
- 理解 compaction 如何压缩历史消息(对应 F2 的记忆管理)
- 掌握子 Agent 派生机制(task 工具,对应 LG3 的 Send)
- 看清 system prompt 如何按模型动态选择
- 理解 overflow 检测和自动 compaction 触发
- 对比 LG2 的 RemoveMessage,看生产级方案的成熟度
上下文管理:生产 Agent 的核心难题
回顾 F2 章的核心约束图:
一个编码 Agent 可能跑几十上百步(读很多文件、改很多代码、执行很多命令),历史消息可能几十万 token。必须主动管理,否则:① 超出窗口报错;② 费用爆炸;③ 模型被旧信息干扰。OpenCode 用三大手段解决:
compaction 压缩
token 溢出时,让专门 agent 总结历史,用摘要替换旧消息。
子 Agent 分流
把独立子任务交给子 agent,结果只回传摘要,不污染主上下文。
动态 system prompt
按模型特性选不同提示,注入环境信息,精准控制 agent 行为。
① compaction:历史压缩
这是 OpenCode 处理溢出的核心机制。源码在 session/compaction.ts:
// compaction 模块的核心方法
interface Interface {
isOverflow(input: { tokens; model }): Effect<boolean> // 检测是否溢出
create(input: { sessionID; agent; model; auto }): ... // 创建压缩任务
process(input: { messages; sessionID; ... }): ... // 执行压缩
prune(input: { sessionID }): ... // 清理旧压缩记录
}
export const PRUNE_MINIMUM = 20_000 // 触发清理的最小 token
export const PRUNE_PROTECT = 40_000 // 保护阈值
compaction 的工作原理
// runLoop 内(prompt.ts:1149-1168):
// 显式压缩任务
if (task?.type === "compaction") {
const result = yield* compaction.process({ messages: msgs, ... })
if (result === "stop") break
continue
}
// 自动检测溢出 → 触发压缩
if (lastFinished && lastFinished.summary !== true
&& (yield* compaction.isOverflow({ tokens: lastFinished.tokens, model }))) {
yield* compaction.create({ sessionID, agent, model, auto: true })
continue // 压缩后继续循环
}
// handle.process 返回 "compact" 时也触发
if (result === "compact") {
yield* compaction.create({ sessionID, ..., overflow: !handle.message.finish })
}
注意 compaction 用的不是主 agent,而是专门的 compaction agent(agent.ts:220 定义,hidden)。它的提示词(compaction.txt)专门优化为"忠实总结对话"。用小模型跑(省钱),产出摘要替换旧消息。这是"用 AI 管理 AI 上下文"的精妙设计。
② 子 Agent:分流任务
对应 LG3 的 Send(map-reduce)。OpenCode 的 task 工具派生子 Agent,把独立子任务隔离:
// task 工具:派生子 agent 执行独立子任务
export const TaskTool = Tool.define("task", Effect.gen(function* () {
return {
description: DESCRIPTION, // task.txt:告诉模型何时派生子任务
parameters: Schema.Struct({
description: Schema.String, // 子任务描述
agent: Schema.String, // 用哪个子 agent(explore/general...)
prompt: Schema.String, // 给子 agent 的指令
}),
execute: (params, ctx) => Effect.gen(function* () {
// ① 创建子会话
const childSession = yield* sessions.create({
parentID: ctx.sessionID,
agent: params.agent,
...
})
// ② 派生子 agent 权限(OC4:继承 deny + 禁止递归)
const childPermission = deriveSubagentSessionPermission(session.permission)
// ③ 子 agent 独立跑(有自己的上下文,不污染主会话!)
const result = yield* prompt.loop({
sessionID: childSession.id,
...
})
// ④ 只把【结果摘要】回传主 agent(不是完整历史)
return { output: result.summary }
}),
}
}))
如果主 Agent 自己探索代码库,要读 10 个文件,每个文件几百行——主上下文瞬间爆炸。用子 Agent:探索在子上下文里发生,主 Agent 只收到一段摘要。这是上下文管理的结构性方案,比 compaction(事后压缩)更主动。对应 LG3 的 Send,但目标是上下文隔离而非并行加速。
③ 动态 system prompt
不同模型(Claude/GPT/Gemini)特性不同,最优提示也不同。OpenCode 按模型动态选择 system prompt:
export function provider(model: Provider.Model) {
if (model.api.id.includes("gpt-4") || ...o1...o3) return [PROMPT_BEAST] // GPT-4/o系列
if (model.api.id.includes("gpt")) {
if (model.api.id.includes("codex")) return [PROMPT_CODEX] // Codex
return [PROMPT_GPT] // 其他 GPT
}
if (model.api.id.includes("gemini-")) return [PROMPT_GEMINI] // Gemini
if (model.api.id.includes("claude")) return [PROMPT_ANTHROPIC] // Claude
if (...kimi...) return [PROMPT_KIMI]
return [PROMPT_DEFAULT] // 兜底
}
对应的提示文件在 session/prompt/(anthropic.txt 105行、gpt.txt 107行、gemini.txt 155行 等)。每个都是针对该模型特性精心调优的指令。
system prompt 的完整组装
OC2 看到 runLoop 这样组装 system(prompt.ts:1256-1268):
// 并发获取四部分(Effect.all)
const [skills, env, instructions, mcpInstructions, modelMsgs] = yield* Effect.all([
sys.skills(agent), // ① 可用 skill 列表
sys.environment(model), // ② 环境信息(工作目录/git/平台/日期)
instruction.system(), // ③ AGENTS.md/CLAUDE.md 等指令文件
sys.mcp(agent, ...), // ④ MCP server 指令
MessageV2.toModelMessagesEffect(msgs, model),
])
const system = [
...env, // 环境信息
...instructions, // 项目指令
...(mcpInstructions ? [mcpInstructions] : []), // MCP
...(skills ? [skills] : []), // skills
]
// 外层还会加 provider(model) 选的模型专属提示
其他内置 Agent
| Agent | 用途 | hidden |
|---|---|---|
| build | 默认主 agent,全部权限 | 否 |
| plan | 规划模式,禁用编辑工具,只写 .opencode/plans | 否 |
| explore | 代码库探索子 agent,只读 + bash/webfetch | 否 |
| general | 通用子 agent | 否 |
| compaction | 上下文压缩(用小模型) | 是 |
| title | 生成会话标题 | 是 |
| summary | 生成会话摘要 | 是 |
这体现了"多 Agent 协作":不同任务用不同特化的 agent,各自有独立上下文和权限。
与框架对照
| OpenCode 概念 | 框架对应 | |
|---|---|---|
| compaction 历史压缩 | → | LG2 RemoveMessage 裁剪(更初级) |
| isOverflow 溢出检测 | → | 需自己实现(框架不管) |
| compaction agent 总结 | → | 需自己写总结节点 |
| task 子 agent 派生 | → | LG3 Send(map-reduce)/ 子图 |
| 子 agent 上下文隔离 | → | 子图的独立 state |
| 按模型选 system prompt | → | LC2 ChatPromptTemplate(但通常固定) |
| 动态注入 environment | → | 需自己组装(工作目录/git 等) |
| 多内置 agent 协作 | → | 多图/supervisor 模式 |
教学框架的 Agent 通常不管上下文长度——演示用,历史短。但真实产品跑长任务,上下文工程是核心能力:compaction、子 agent、智能裁剪。OpenCode 在这方面做得很完整——这也是它比"玩具 Agent"强大的关键。学完本章你应理解:会写 Agent 循环只是入门,会管理上下文才是产品级。
小结
- compaction:token 溢出时用专门 agent 总结历史,用摘要替换旧消息。自动检测 + 手动触发。
- 子 Agent(task 工具):把独立子任务交给子 agent,上下文隔离,只回传摘要——保护主上下文。
- 动态 system prompt:按模型选提示文件 + 注入环境/指令/MCP/skills 四部分。
- 多内置 agent 协作:build/plan/explore/compaction 各司其职。
- 上下文工程是生产 Agent 的核心能力,比"写循环"更难、更重要。
最后一章 OC6 · 概念对照表——本教程的收官!用一张大表把所有概念两两对照:基础理论 ↔ LangChain ↔ LangGraph ↔ OpenCode。这是你复习和查阅的终极参考,也是验证"是否真正理解"的试金石。