摘要 :Brain(大脑) 是 OpenClaw 的 核心推理引擎 ,负责与 LLM 交互、执行工具调用循环、管理上下文窗口。本文深入探讨 Brain 层的三大工程实践:Prompt Engineering (提示工程)------如何组装 System Prompt;Context Engineering (上下文工程)------如何管理 Token 预算和渐进式披露;Harness Engineering (执行框架工程)------如何编排工具调用循环和上下文压缩。基于 src/agents/ 和 src/auto-reply/reply/agent-runner.ts 源码,揭示 OpenClaw 如何在不牺牲能力的前提下,高效地利用 LLM。
关键词:OpenClaw;Brain;Prompt Engineering;Context Engineering;Harness Engineering;工具调用循环;上下文压缩;Compaction;多 Provider
系列文章:
- OpenClaw 深度解析与源代码导读 · 第1篇:系列导读------术语、版本与读源码方法
- OpenClaw 深度解析与源代码导读 · 第2篇:Skills------能力扩展平面与源码中的「目录即技能」
- OpenClaw 深度解析与源代码导读 · 第3篇:Gateway------常驻控制面、单端口多协议与进程骨架
- OpenClaw 深度解析与源代码导读 · 第4篇:Router------入站消息的分发中枢与决策逻辑
源码版本说明 :本文引用路径基于 openclaw/openclaw 仓库;本地阅读使用的 commit 为 0dd4958bc8a78d26b3b526b1f2e63b15110c64a2(2026-04-11)。GitHub 上可按该 SHA 查看对应版本的源码。
1 Brain 在架构中的位置:从 Router 到回复的核心链路
在第4篇(Router)中,我们了解了 Router 如何将消息分发给 Brain。现在让我们深入 Brain 内部,看看它是如何工作的。
1.1 Brain 的核心职责
输出/执行层
Brain 核心
输入层
需要工具
结果回灌
无需工具/最终回复
Router
决策入口
用户消息
历史对话
Prompt Engineering
System Prompt 组装
Context Engineering
Token 预算管理
Harness Engineering
工具调用循环
Tools
技能/工具执行
生成回复
| 工程维度 | 核心问题 | 源码入口 | 产出 |
|---|---|---|---|
| Prompt Engineering | "给 LLM 看什么?" | system-prompt.ts |
结构化 System Prompt |
| Context Engineering | "给 LLM 看多少?" | context.ts, workspace.ts |
Token 预算内的最优上下文 |
| Harness Engineering | "如何与 LLM 协作?" | agent-runner.ts, pi-embedded*.ts |
工具调用循环、执行编排 |
💡 理解要点 :Brain 不是简单的"把消息发给 LLM",而是一整套工程实践------从提示设计到上下文管理,再到执行编排,确保 LLM 在可控成本内发挥最大能力。
2 Prompt Engineering:System Prompt 的分层组装
OpenClaw 的 Prompt Engineering 核心在 src/agents/system-prompt.ts,它通过分层组装的方式构建 System Prompt。
2.1 System Prompt 的八层结构
System Prompt 分层结构
- Base Identity
基础身份
2. Tooling
工具说明
3. Skills
技能目录
4. Memory
记忆能力
5. Project Context
项目上下文
─── Cache Boundary ───
6. Dynamic Context
动态上下文
7. Runtime
运行时信息
| 层级 | Section | 来源 | 稳定性 | 说明 |
|---|---|---|---|---|
| 1 | Base Identity | 硬编码 + 配置 | 🟢 稳定 | Agent 身份、行为准则 |
| 2 | Tooling | 运行时检测 | 🟢 稳定 | 可用工具列表及说明 |
| 3 | Skills | skills/ 扫描 |
🟢 稳定 | <available_skills> XML |
| 4 | Memory | Memory 子系统 | 🟢 稳定 | 记忆读写能力说明 |
| 5 | Project Context | 上下文文件 | 🟢 稳定 | agents.md, soul.md 等 |
| --- | Cache Boundary | --- | --- | 缓存分界 |
| 6 | Dynamic Context | 动态文件 | 🔴 动态 | heartbeat.md 等 |
| 7 | Runtime | 实时生成 | 🔴 动态 | 模型、OS、Channel 等 |
2.2 源码实现:buildSystemPrompt
ts
// src/agents/system-prompt.ts(节选,概念结构)
export function buildSystemPrompt(params: BuildSystemPromptParams): string {
const lines: string[] = [];
// L1: Base Identity
lines.push(...buildBaseIdentitySection(params));
// L2: Tooling
lines.push(...buildToolingSection(params));
// L3: Skills(L1 Catalog - 仅 frontmatter)
lines.push(...buildSkillsSection({ skillsPrompt: params.skillsPrompt }));
// L4: Memory
lines.push(...buildMemorySection(params));
// L5: Project Context(稳定上下文文件)
const contextFiles = sortContextFilesForPrompt(params.contextFiles ?? []);
const stableFiles = contextFiles.filter(f => !isDynamicContextFile(f.path));
lines.push(...buildProjectContextSection({ files: stableFiles, dynamic: false }));
// Cache Boundary: 之上可被 Anthropic 缓存复用
lines.push(SYSTEM_PROMPT_CACHE_BOUNDARY);
// L6: Dynamic Context
const dynamicFiles = contextFiles.filter(f => isDynamicContextFile(f.path));
lines.push(...buildProjectContextSection({ files: dynamicFiles, dynamic: true }));
// L7: Runtime
lines.push(...buildRuntimeSection(params));
return lines.join("\n");
}
2.3 上下文文件优先级
ts
// src/agents/system-prompt.ts(节选)
const CONTEXT_FILE_ORDER = new Map<string, number>([
["agents.md", 10], // 最高优先级:Agent 配置
["soul.md", 20], // 人格/语气定义
["identity.md", 30], // 身份标识
["user.md", 40], // 用户信息
["tools.md", 50], // 工具配置
["bootstrap.md", 60], // 启动配置
["memory.md", 70], // 记忆配置
]);
优先级逻辑 :数字越小优先级越高。agents.md 可以覆盖 soul.md,soul.md 可以覆盖 identity.md,以此类推。
2.4 Cache Boundary 机制
ts
// src/agents/system-prompt.ts(节选)
// Keep large stable prompt context above this seam so Anthropic-family
// transports can reuse it across labs and turns.
lines.push(SYSTEM_PROMPT_CACHE_BOUNDARY);
| Cache Boundary | 内容 | Anthropic 缓存 | 更新频率 |
|---|---|---|---|
| 之上 | Base, Tooling, Skills, Memory, Project Context | ✅ 可缓存 | 低(文件变更时) |
| 之下 | Dynamic Context, Runtime | ❌ 不缓存 | 高(每轮都可能变) |
💡 理解要点:Cache Boundary 是 Anthropic 模型特有的优化------稳定内容可以被缓存,降低 API 成本;动态内容每轮重新生成,保证时效性。
2.5 真实示例:新会话的默认 System Prompt
让我们看一个真实的 System Prompt 示例------当你刚启动 OpenClaw 会话时,Brain 默认会生成什么:
You are a personal assistant running inside OpenClaw.
## Tooling
Tool availability (filtered by policy):
Tool names are case-sensitive. Call tools exactly as listed.
- read: Read file contents
- write: Create or overwrite files
- edit: Make precise edits to files
- apply_patch: Apply multi-file patches
- grep: Search file contents for patterns
- find: Find files by glob pattern
- ls: List directory contents
- exec: Run shell commands (pty available for TTY-required CLIs)
- process: Manage background exec sessions
- web_search: Search the web (Brave API)
- web_fetch: Fetch and extract readable content from a URL
- browser: Control web browser
- canvas: Present/eval/snapshot the Canvas
- nodes: List/describe/notify/camera/screen on paired nodes
- cron: Manage cron jobs and wake events (use for reminders; when scheduling a reminder, write the systemEvent text as something that will read like a reminder when it fires, and mention that it is a reminder depending on the time gap between setting and firing; include recent context in reminder text if appropriate)
- message: Send messages and channel actions
- gateway: Restart, apply config, or run updates on the running OpenClaw process
- agents_list: List OpenClaw agent ids allowed for sessions_spawn
- sessions_list: List other sessions (incl. sub-agents) with filters/last
- sessions_history: Fetch history for another session/sub-agent
- sessions_send: Send a message to another session/sub-agent
- sessions_spawn: Spawn an isolated sub-agent session
- subagents: List, steer, or kill sub-agent runs for this requester session
- session_status: Show a /status-equivalent status card (usage + time + Reasoning/Verbose/Elevated)
- image: Analyze an image with the configured image model
- image_generate: Generate images with the configured image-generation model
TOOLS.md does not control tool availability; it is user guidance for how to use external tools.
For long waits, avoid rapid poll loops: use exec with enough yieldMs or process(action=poll, timeout=<ms>).
If a task is more complex or takes longer, spawn a sub-agent. Completion is push-based: it will auto-announce when done.
Do not poll `subagents list` / `sessions_list` in a loop; only check status on-demand (for intervention, debugging, or when explicitly asked).
## Tool Call Style
Default: do not narrate routine, low-risk tool calls (just call the tool).
Narrate only when it helps: multi-step work, complex/challenging problems, sensitive actions (e.g., deletions), or when the user explicitly asks.
Keep narration brief and value-dense; avoid repeating obvious steps.
Use plain human language for narration unless in a technical context.
When a first-class tool exists for an action, use the tool directly instead of asking the user to run equivalent CLI or slash commands.
## Execution Bias
If the user asks you to do the work, start doing it in the same turn.
Use a real tool call or concrete action first when the task is actionable; do not stop at a plan or promise-to-act reply.
Commentary-only turns are incomplete when tools are available and the next action is clear.
If the work will take multiple steps or a while to finish, send one short progress update before or while acting.
## OpenClaw CLI Quick Reference
OpenClaw is controlled via subcommands. Do not invent commands.
To manage the Gateway daemon service (start/stop/restart):
- openclaw gateway status
- openclaw gateway start
- openclaw gateway stop
- openclaw gateway restart
If unsure, ask the user to run `openclaw help` (or `openclaw gateway --help`) and paste the output.
## Skills (mandatory)
Before replying: scan <available_skills> <description> entries.
- If exactly one skill clearly applies: read its SKILL.md at <location>, then follow it.
- If multiple could apply: choose the most specific one, then read/follow it.
- If none clearly apply: do not read any SKILL.md.
Constraints: never read more than one skill up front; only read after selecting.
- When a skill drives external API writes, assume rate limits: prefer fewer larger writes, avoid tight one-item loops, serialize bursts when possible, and respect 429/Retry-After.
<available_skills>
<skill>
<name>weather-query</name>
<description>查询指定城市的天气...</description>
</skill>
<skill>
<name>reminder</name>
<description>设置定时提醒...</description>
</skill>
</available_skills>
## Memory
You have access to tools that can read and write to memory stores...
────────────────────────────────────────────────
[SYSTEM_PROMPT_CACHE_BOUNDARY]
────────────────────────────────────────────────
## Runtime
Runtime: agent=main | model=gpt-4 | os=linux | shell=bash | channel=webchat | capabilities=none | thinking=off
Reasoning: off (hidden unless on/stream). Toggle /reasoning; /status shows Reasoning when enabled.
这个示例展示了:
- L1 Base Identity :
"You are a personal assistant running inside OpenClaw." - L2 Tooling : 20+ 个工具的详细说明,包括
read,write,exec,browser等 - Tool Call Style: 指导 LLM 如何描述工具调用("do not narrate routine")
- Execution Bias: 鼓励 LLM 立即行动而非只给计划
- OpenClaw CLI: 内置帮助文档
- L3 Skills :
<available_skills>XML 格式,包含已安装技能的 name + description(L1) - L4 Memory: 记忆能力说明
- Cache Boundary: 分隔稳定内容和动态内容
- L7 Runtime: 实时信息(模型、OS、Shell、Channel 等)
Token 开销估算:
- L1-L5(稳定内容,可缓存):~3000 tokens
- L6-L7(动态内容):~200 tokens
- 总计:~3200 tokens(不含 Skills 和 Project Context 文件)
💡 理解要点:即使是"默认"的 System Prompt,也包含了丰富的指令------工具使用方式、行为规范、技能发现协议------这些都是为了让 LLM 能够自主地与 OpenClaw 生态协作。
2.6 Prompt Mode 控制
Brain 根据使用场景选择不同的提示模式:
| Mode | 使用场景 | 省略的 Section | 目的 |
|---|---|---|---|
| "full" | 主 Agent 对话 | 无 | 完整能力 |
| "minimal" | Subagent、Cron | Messaging、Execution Bias 等 | 减少 Token |
| "none" | 特殊场景 | 大部分 | 极简(仅保留 "You are a personal assistant running inside OpenClaw.") |
3 Context Engineering:Token 预算与渐进式披露
Context Engineering 解决"给 LLM 看多少 "的问题。OpenClaw 通过渐进式披露 和Token 预算管理来优化上下文。
3.1 设置上下文窗口大小:四层优先级机制
OpenClaw 通过四层优先级机制确定模型的上下文窗口大小,确保在不同场景下都能获得准确的 Token 上限。
3.1.1 四层优先级解析
ts
// src/agents/context.ts(节选,resolveContextTokensForModel)
export function resolveContextTokensForModel(params: {
cfg?: OpenClawConfig;
provider?: string;
model?: string;
contextTokensOverride?: number; // 第1层:运行时覆盖
fallbackContextTokens?: number; // 第4层:最终回退
}): number | undefined {
// 第1层:运行时覆盖(最高优先级)
if (typeof params.contextTokensOverride === "number" && params.contextTokensOverride > 0) {
return params.contextTokensOverride;
}
const ref = resolveProviderModelRef({ provider: params.provider, model: params.model });
if (ref) {
// 特殊处理:Anthropic 1M 模型(context1m 标志)
const modelParams = resolveConfiguredModelParams(params.cfg, ref.provider, ref.model);
if (modelParams?.context1m === true && isAnthropic1MModel(ref.provider, ref.model)) {
return ANTHROPIC_CONTEXT_1M_TOKENS; // 1,048,576
}
// 第2层:配置文件(openclaw.json)
if (explicitProvider) {
const configuredWindow = resolveConfiguredProviderContextTokens(
params.cfg, explicitProvider, ref.model
);
if (configuredWindow !== undefined) return configuredWindow;
}
}
// 第3层:模型发现缓存(models.json / pi-model-discovery)
// 3a. Provider-qualified 查找(优先)
if (params.provider && ref && !ref.model.includes("/")) {
const qualifiedResult = lookupContextTokens(
`${normalizeProviderId(ref.provider)}/${ref.model}`
);
if (qualifiedResult !== undefined) return qualifiedResult;
}
// 3b. Bare key 回退
const bareResult = lookupContextTokens(params.model);
if (bareResult !== undefined) return bareResult;
// 第4层:默认值(最低优先级)
return params.fallbackContextTokens ?? DEFAULT_CONTEXT_TOKENS; // 200,000
}
| 优先级 | 来源 | 配置方式 | 适用场景 |
|---|---|---|---|
| 1 | 运行时覆盖 | contextTokensOverride 参数 |
临时调整、测试、紧急限制 |
| 2 | 配置文件 | openclaw.json → models.providers.<provider>.models[].contextTokens |
用户自定义模型窗口 |
| 3 | 模型发现缓存 | models.json / pi-model-discovery 自动发现 |
标准模型、新模型自动识别 |
| 4 | 硬编码默认值 | DEFAULT_CONTEXT_TOKENS = 200_000 |
未知模型时的保守回退 |
3.1.2 配置示例
方式一:openclaw.json 配置(推荐)
json
{
"models": {
"providers": {
"openai": {
"models": [
{
"id": "gpt-4",
"contextTokens": 8192
},
{
"id": "gpt-4-turbo",
"contextTokens": 128000
}
]
},
"anthropic": {
"models": [
{
"id": "claude-opus-4",
"contextTokens": 1048576,
"context1m": true
}
]
}
}
}
}
方式二:运行时参数(Agent Runner)
ts
// 在 runReplyAgent 中临时覆盖
const contextTokens = resolveContextTokensForModel({
cfg,
provider: "openai",
model: "gpt-4",
contextTokensOverride: 4000, // 临时限制为 4K
});
3.1.3 特殊模型处理:Anthropic 1M 窗口
ts
// src/agents/context.ts(节选)
const ANTHROPIC_1M_MODEL_PREFIXES = ["claude-opus-4", "claude-sonnet-4"];
const ANTHROPIC_CONTEXT_1M_TOKENS = 1_048_576;
function isAnthropic1MModel(provider: string, model: string): boolean {
if (provider !== "anthropic") return false;
const normalized = normalizeLowercaseStringOrEmpty(model);
const modelId = normalized.includes("/")
? normalized.split("/").at(-1)
: normalized;
return ANTHROPIC_1M_MODEL_PREFIXES.some(prefix => modelId.startsWith(prefix));
}
1M 窗口的触发条件:
- Provider 必须是
"anthropic" - Model ID 必须以
"claude-opus-4"或"claude-sonnet-4"开头 - 配置中设置了
"context1m": true
3.1.4 模型发现机制
OpenClaw 通过 pi-model-discovery 自动发现并缓存模型元数据:
ts
// src/agents/context.ts(节选,ensureContextWindowCacheLoaded)
async function ensureContextWindowCacheLoaded(): Promise<void> {
// 1. 从配置预加载
const cfg = primeConfiguredContextWindows();
// 2. 确保 models.json 存在
await (await loadModelsConfigRuntime()).ensureOpenClawModelsJson(cfg);
// 3. 发现可用模型
const { discoverAuthStorage, discoverModels } = await import("./pi-model-discovery-runtime.js");
const agentDir = resolveOpenClawAgentDir();
const authStorage = discoverAuthStorage(agentDir);
const modelRegistry = discoverModels(authStorage, agentDir);
// 4. 应用到缓存
applyDiscoveredContextWindows({
cache: MODEL_CONTEXT_TOKEN_CACHE,
models: modelRegistry.getAvailable(),
});
}
缓存键设计:
- Qualified key :
"anthropic/claude-opus-4"→ 1048576 - Bare key :
"claude-opus-4"→ 1048576 - 优先使用 qualified key 避免跨 provider 冲突
3.1.5 实际使用场景
| 场景 | 解析结果 | 来源 |
|---|---|---|
| 标准 GPT-4 | 8192 | 内置发现 / 配置 |
| GPT-4 Turbo | 128000 | 内置发现 / 配置 |
| Claude Opus 4 | 1048576 | Anthropic 1M 特殊处理 |
| 自定义模型 | 用户配置 | openclaw.json |
| 未知模型 | 200000 | DEFAULT_CONTEXT_TOKENS 保守回退 |
💡 理解要点 :上下文窗口管理是层层递进的防御性设计------运行时覆盖用于紧急情况,配置文件用于用户自定义,模型发现用于标准模型,默认值用于未知场景。这种设计确保了在任何情况下都不会因为"不知道窗口大小"而导致请求失败。
3.2 Skills 的 Token 预算控制:三层渐进式截断策略
OpenClaw 采用三层渐进式截断策略来管理 Skills 的 Token 预算,确保即使在技能数量庞大时,也能在有限的提示窗口内保留最关键的信息。
3.2.1 三层截断策略详解
ts
// src/agents/skills/workspace.ts(节选,applySkillsPromptLimits)
function applySkillsPromptLimits(params: {
skills: Skill[];
config?: OpenClawConfig
}): {
skillsForPrompt: Skill[]; // 最终进入提示的技能
truncated: boolean; // 是否发生了数量截断
compact: boolean; // 是否使用了紧凑格式
} {
const limits = resolveSkillsLimits(params.config);
const total = params.skills.length;
// ╔══════════════════════════════════════════════════════════════╗
// ║ 第一层:按数量截断(硬限制) ║
// ╚══════════════════════════════════════════════════════════════╝
const byCount = params.skills.slice(0, Math.max(0, limits.maxSkillsInPrompt));
let skillsForPrompt = byCount;
let truncated = total > byCount.length; // 是否因为数量限制被截断
let compact = false;
// 检查是否适合完整格式(name + description + location)
const fitsFull = (skills: Skill[]): boolean =>
formatSkillsForPrompt(skills).length <= limits.maxSkillsPromptChars;
// ╔══════════════════════════════════════════════════════════════╗
// ║ 第二层:格式降级(保留数量,减少信息密度) ║
// ╚══════════════════════════════════════════════════════════════╝
if (!fitsFull(skillsForPrompt)) {
// Full format 超出预算,尝试 Compact format(省略 descriptions)
const compactBudget = limits.maxSkillsPromptChars - COMPACT_WARNING_OVERHEAD;
const fitsCompact = (skills: Skill[]): boolean =>
formatSkillsCompact(skills).length <= compactBudget;
if (fitsCompact(skillsForPrompt)) {
// ✅ Compact 模式容纳得下,保留所有技能,仅降级格式
compact = true;
// truncated 保持原值(可能已在第一层被截断)
} else {
// ╔══════════════════════════════════════════════════════════╗
// ║ 第三层:二分查找截断(迫不得已丢弃技能) ║
// ╚══════════════════════════════════════════════════════════╝
compact = true;
let lo = 0;
let hi = skillsForPrompt.length;
// 二分查找最大可容纳的技能数量
while (lo < hi) {
const mid = Math.ceil((lo + hi) / 2);
if (fitsCompact(skillsForPrompt.slice(0, mid))) {
lo = mid; // mid 可以容纳,尝试更大的
} else {
hi = mid - 1; // mid 太大,尝试更小的
}
}
skillsForPrompt = skillsForPrompt.slice(0, lo);
truncated = true; // 明确标记发生了截断
}
}
return { skillsForPrompt, truncated, compact };
}
3.2.2 两种格式对比:Full vs Compact
| 格式 | 函数 | 每个 Skill 包含 | description 处理 | Token 开销 |
|---|---|---|---|---|
| Full | formatSkillsForPrompt |
name + description + location | ✅ 保留 description | ~150-300 tokens/skill |
| Compact | formatSkillsCompact |
name + location | ❌ 删除 description(所有 skill 都没有 description) | ~50-100 tokens/skill |
关键澄清 :Compact 模式下,所有 skills 的 description 都会被删除,不是"部分删除"。这是格式层面的变更,不是数据层面的筛选。
Full 格式示例(3 个 skills):
xml
<available_skills>
<skill>
<name>weather-query</name>
<description>查询指定城市的天气信息,包括温度、湿度、风速等</description> ✅ 保留
<location>/home/user/.openclaw/skills/weather-query/SKILL.md</location>
</skill>
<skill>
<name>reminder</name>
<description>设置定时提醒,支持一次性提醒和周期性提醒</description> ✅ 保留
<location>/home/user/.openclaw/skills/reminder/SKILL.md</location>
</skill>
<skill>
<name>web-search</name>
<description>使用搜索引擎查找网络信息</description> ✅ 保留
<location>/home/user/.openclaw/skills/web-search/SKILL.md</location>
</skill>
</available_skills>
Compact 格式示例(同样的 3 个 skills):
xml
<available_skills>
<skill>
<name>weather-query</name>
<location>/home/user/.openclaw/skills/weather-query/SKILL.md</location>
</skill>
<skill>
<name>reminder</name>
<location>/home/user/.openclaw/skills/reminder/SKILL.md</location>
</skill>
<skill>
<name>web-search</name>
<location>/home/user/.openclaw/skills/web-search/SKILL.md</location>
</skill>
</available_skills>
<!-- 注意:所有 description 都被删除了!LLM 只能通过 name 猜测技能用途 -->
Compact 格式源码(确认删除所有 description):
ts
// src/agents/skills/workspace.ts(节选)
export function formatSkillsCompact(skills: Skill[]): string {
if (skills.length === 0) return "";
const lines = [
"\n\nThe following skills provide specialized instructions for specific tasks.",
"Use the read tool to load a skill's file when the task matches its name.", // 注意:提示 LLM 按 name 匹配
"<available_skills>",
];
for (const skill of skills) {
lines.push(" <skill>");
lines.push(` <name>${escapeXml(skill.name)}</name>`);
lines.push(` <location>${escapeXml(skill.filePath)}</location>`);
// ❌ 完全没有 description 字段!所有 skill 都没有 description
lines.push(" </skill>");
}
lines.push("</available_skills>");
return lines.join("\n");
}
对 LLM 的影响:
| 格式 | LLM 如何决策使用哪个 skill | 准确性 | 适用场景 |
|---|---|---|---|
| Full | 基于 description 理解技能用途 | 高 | 正常场景 |
| Compact | 基于 name 猜测技能用途 | 低(可能误匹配) | Token 紧张时 |
💡 理解要点 :Compact 模式是"信息换空间"------用 description 的丢失换取保留更多 skills 的能力。LLM 只能通过 skill name(如 "weather-query")来猜测用途,而不是通过详细的 description。
3.2.3 三层策略的决策流程
原始 Skills 列表(假设 50 个技能)
│
▼
╔═════════════════════════════════════════════════════╗
║ 第一层:按数量截断(硬限制) ║
║ maxSkillsInPrompt = 20 ║
║ 结果:保留前 20 个技能 ║
║ truncated = false(还未真正截断) ║
╚═════════════════════════════════════════════════════╝
│
▼
╔═════════════════════════════════════════════════════╗
║ 检查:full 格式是否超出预算? ║
║ formatSkillsForPrompt(20 skills).length ║
║ <= maxSkillsPromptChars (假设 8000)? ║
║ ║
║ Full 格式 = name + description + location ║
║ 每个 skill ~200 tokens ║
║ 20 个 skills ~4000 tokens ║
╚═════════════════════════════════════════════════════╝
│
├─ ✅ 未超出 ──▶ 使用 full 格式
│ • 20 个 skills
│ • 每个都有 description
│ • truncated = false
│
└─ ❌ 超出 ──▶ 进入第二层
│
▼
╔══════════════════════════════════════════════╗
║ 第二层:格式降级(Compact 模式) ║
║ formatSkillsCompact(20 skills).length ║
║ <= compactBudget (8000 - 150 = 7850)? ║
║ ║
║ Compact 格式 = name + location(无 description)║
║ 每个 skill ~80 tokens(节省 60%) ║
║ 20 个 skills ~1600 tokens ║
╚══════════════════════════════════════════════╝
│
├─ ✅ 可以容纳 ──▶ 使用 compact 格式
│ • 保留全部 20 个 skills
│ • **所有 skill 都没有 description**
│ • truncated = false(数量未截断)
│
└─ ❌ 仍然超出 ──▶ 进入第三层(迫不得已丢弃)
│
▼
╔══════════════════════════════╗
║ 第三层:二分查找截断 ║
║ 减少 skills 数量(仍用 Compact)║
║ ║
║ lo=0, hi=20 ║
║ mid=10: fitsCompact(10)? ║
║ ├─ ✅ yes ──▶ lo=10 ║
║ └─ ❌ no ──▶ hi=9 ║
║ ... ║
║ 最终:lo=12 可以容纳 ║
╚══════════════════════════════╝
│
▼
使用 compact 格式,只保留 12 个技能
• **这 12 个 skill 都没有 description**
• 8 个 skills 被丢弃
• truncated = true(明确标记发生了数量截断)
第二层 vs 第三层的关键区别:
| 层级 | 格式 | Skills 数量 | Description | 结果 |
|---|---|---|---|---|
| 第二层 | Compact | 完整(20 个) | 全部删除 | 保留全部,但信息密度降低 |
| 第三层 | Compact | 截断(12 个) | 全部删除 | 不仅信息密度低,数量也减少了 |
3.2.4 配置参数与实际场景
| 配置项 | 作用 | 典型值 | 说明 |
|---|---|---|---|
maxSkillsInPrompt |
第一层:数量硬限制 | 20-50 | 即使 Token 允许,也不超过这个数量 |
maxSkillsPromptChars |
第二层/第三层:字符预算 | 8000-16000 | 转换为 Token 约 2000-4000 |
COMPACT_WARNING_OVERHEAD |
紧凑模式警告预留 | 150 | 为截断提示预留的空间 |
配置示例(openclaw.json):
json
{
"skills": {
"limits": {
"maxSkillsInPrompt": 25,
"maxSkillsPromptChars": 12000,
"maxSkillFileBytes": 1048576
}
}
}
3.2.5 实际场景案例
| 场景 | 技能数量 | Token 预算 | 处理结果 | 说明 |
|---|---|---|---|---|
| 小型项目 | 5 个 | 充足 | Full 格式,无截断 | 所有技能完整展示 |
| 中型项目 | 20 个 | 紧张 | Compact 格式,无截断 | 降级格式保留全部 |
| 大型项目 | 50 个 | 严重不足 | Compact + 截断至 15 个 | 丢弃低优先级技能 |
| 极限场景 | 100+ 个 | 严重不足 | Compact + 截断至 10 个 | 仅保留最核心的 |
3.2.6 截断时的用户提示
当发生截断时,OpenClaw 会在 System Prompt 中添加警告:
ts
// src/agents/skills/workspace.ts(节选,resolveWorkspaceSkillPromptState)
const truncationNote = truncated
? `⚠️ Skills truncated: included ${skillsForPrompt.length} of ${resolvedSkills.length}${compact ? " (compact format, descriptions omitted)" : ""}. Run \`openclaw skills check\` to audit.`
: compact
? `⚠️ Skills catalog using compact format (descriptions omitted). Run \`openclaw skills check\` to audit.`
: "";
示例输出:
⚠️ Skills truncated: included 12 of 50 (compact format, descriptions omitted).
Run `openclaw skills check` to audit.
💡 关键洞察:三层截断策略体现了**"能保则保,不得已才丢"**的设计哲学:
- 第一层(数量限制):预防性保护,避免技能过多影响性能
- 第二层(格式降级):信息密度换数量,让 LLM 至少知道"有哪些技能"
- 第三层(二分查找):迫不得已时才丢弃技能,且通过二分查找最大化保留数量
这种渐进式策略确保了即使在高负载场景下,LLM 仍能获得最有价值的技能目录信息。
3.3 渐进式披露(Progressive Disclosure)
第2篇已详细介绍,这里从 Brain 视角总结:
| 阶段 | Skill 部分 | Token 开销 | 触发时机 |
|---|---|---|---|
| L1 Catalog | frontmatter(name + description) | ~500 | 每次请求初始化 |
| L2 Instructions | body(完整说明书) | ~2000 | LLM 输出 read_skill 时 |
| L3 Resources | references, scripts | 按需 | 执行阶段 |
💡 理解要点 :Context Engineering 的核心是"摘要进提示,正文走工具"------L1 让 LLM 知道"有什么",L2/L3 在需要时才加载,避免 Token 浪费。
详细逻辑参考文章 OpenClaw 深度解析与源代码导读 · 第2篇:Skills------能力扩展平面与源码中的「目录即技能」。
4 Harness Engineering:工具调用循环与执行编排
Harness Engineering(执行框架工程)解决"如何与 LLM 协作 "的问题。OpenClaw 通过 Agent Runner 和 PI-Embedded 框架实现。
4.1 工具调用循环(Tool Call Loop)
Tools/Skills LLM Brain (Agent Runner) 用户 Tools/Skills LLM Brain (Agent Runner) 用户 alt [返回工具调用] [返回文本] loop [工具调用循环] 发送消息 组装 System Prompt (PE) 管理 Token 预算 (CE) 发送 Prompt + 历史 返回:文本 或 工具调用 解析 tool call 执行工具 返回结果 将结果回灌到上下文 生成回复
4.2 Agent Runner 架构
ts
// src/auto-reply/reply/agent-runner.ts(节选,概念结构)
export async function runReplyAgent(params: RunReplyAgentParams): Promise<ReplyPayload> {
// 1. 准备阶段
const sessionEntry = refreshSessionEntryFromStore(params);
await runPreflightCompactionIfNeeded(params); // 预压缩
// 2. 主循环
const result = await runAgentTurnWithFallback({
// LLM 请求参数
model: params.defaultModel,
systemPrompt: buildSystemPrompt(params),
messages: buildMessageHistory(params),
// 工具配置
tools: resolveAvailableTools(params),
toolChoice: "auto",
// 回调处理
onToolCall: async (toolCall) => {
// 执行工具并返回结果
return await executeTool(toolCall, params);
},
// 流式处理
onStreamChunk: (chunk) => {
params.typing.onChunk(chunk); // 打字效果
},
});
// 3. 后处理
await runMemoryFlushIfNeeded(params); // 记忆刷新
return buildReplyPayloads(result);
}
4.3 工具调用处理流程
ts
// src/agents/pi-embedded-subscribe.handlers.tools.ts(节选,概念结构)
export async function handleToolCall(
ctx: EmbeddedPiSubscribeContext,
evt: AgentToolCallEvent
): Promise<void> {
const { toolCallId, toolName, args } = evt;
// 1. 记录工具调用开始
toolStartData.set(buildToolStartKey(runId, toolCallId), {
startTime: Date.now(),
args,
});
// 2. 执行前钩子(Plugin Hook)
const hookRunner = getGlobalHookRunner();
await hookRunner?.runBeforeToolCall({ toolName, args });
// 3. 执行工具
const result = await executeTool({ toolName, args, context: ctx });
// 4. 结果处理
const sanitizedResult = sanitizeToolResult(result);
// 5. 执行后钩子
await hookRunner?.runAfterToolCall({
toolName,
args,
result: sanitizedResult
});
// 6. 发送结果回 LLM
await ctx.sendToolResult({ toolCallId, result: sanitizedResult });
}
4.4 Provider 抽象层
OpenClaw 支持多 Provider(OpenAI、Anthropic、Ollama 等),通过统一的抽象层处理:
ts
// 概念结构(基于 transport 和 transcript-policy.ts)
interface ProviderTransport {
// 发送请求
sendRequest(params: RequestParams): Promise<Response>;
// 处理流式响应
handleStreaming(response: Response, callbacks: StreamCallbacks): void;
// 工具调用 ID 处理(不同 Provider 格式不同)
normalizeToolCallId(id: string): string;
// 消息格式转换
transformMessages(messages: Message[]): ProviderSpecificFormat;
}
| Provider | API 格式 | Tool Call ID | 特殊处理 |
|---|---|---|---|
| Anthropic | Messages API | 严格模式 | Cache Boundary 优化 |
| OpenAI | Completions/Responses | 宽松模式 | 兼容 GPT-3.5/GPT-4 |
| Ollama | OpenAI-compatible | 本地模型 | 无需 API Key |
5 上下文压缩(Compaction):长对话的救星
当对话历史过长时,Brain 会触发 Compaction 机制,将早期对话压缩为摘要,释放 Token 空间。
5.1 Compaction 触发条件与判断逻辑
OpenClaw 在每次 LLM 请求前都会检查是否需要触发 Compaction,确保不会因上下文溢出而导致请求失败。
5.1.1 触发条件的类型
| 触发类型 | 说明 | 配置项 | 触发方式 |
|---|---|---|---|
| 自动触发(预算阈值) | Token 数接近窗口上限 | reserveTokens |
每次请求前自动检查 |
| 自动触发(消息数) | 消息数超过配置阈值 | minMessages |
SDK 内部判断 |
| 手动触发 | 用户或系统显式请求 | /compact 命令 |
用户输入命令 |
| 强制触发 | 忽略阈值立即压缩 | trigger: "manual" |
编程调用 |
5.1.2 自动触发的核心判断逻辑
ts
// src/auto-reply/reply/memory-flush.ts(节选,shouldRunPreflightCompaction)
export function shouldRunPreflightCompaction(params: {
entry?: SessionEntry;
tokenCount?: number; // 当前 Token 计数
contextWindowTokens: number; // 模型上下文窗口大小
reserveTokensFloor: number; // 预留 Token 数(默认 32000)
softThresholdTokens: number; // 软阈值(提前触发)
}): boolean {
// 计算有效阈值
const threshold = Math.max(
params.reserveTokensFloor, // 最小预留:32000 tokens
params.contextWindowTokens - params.softThresholdTokens // 窗口 - 缓冲
);
// 触发条件:当前 Token >= 阈值
return params.tokenCount >= threshold;
}
触发公式:
阈值 = max(reserveTokens, contextWindow - softThreshold)
示例(GPT-4 8K 窗口):
- contextWindow = 8192
- reserveTokens = 32000(但受限于窗口,实际取 8192 - 缓冲)
- softThreshold = 2000
- 阈值 = 8192 - 2000 = 6192 tokens
当 currentToken >= 6192 时触发 Compaction
5.1.3 实际触发场景示例
| 模型 | 窗口大小 | 预留 Token | 软阈值 | 触发阈值 | 触发时机 |
|---|---|---|---|---|---|
| GPT-3.5 | 4K | 32K(受限) | 2K | ~3.5K | 使用 87% 时 |
| GPT-4 | 8K | 32K(受限) | 2K | ~6K | 使用 75% 时 |
| GPT-4 Turbo | 128K | 32K | 2K | 126K | 使用 98% 时 |
| Claude Opus 4 | 1M | 32K | 2K | ~1M | 使用 97% 时 |
💡 关键观察 :对于大窗口模型(128K+),预留 32K 相对较小,触发阈值接近窗口上限;对于小窗口模型(8K),实际阈值由 contextWindow - softThreshold 决定。
5.1.4 手动触发:/compact 命令
用户可以通过命令显式触发 Compaction:
User: /compact
Assistant: ⚙️ Compaction in progress...
[Compaction 完成后]
Assistant: ✅ Context compacted: reduced from 15000 to 3000 tokens (80% reduction).
手动触发的特点:
- 无视当前 Token 计数,立即执行
- 可用于强制整理会话,即使未达阈值
- 需要用户授权(
isAuthorizedSender检查)
5.1.5 触发流程时序
LLM Compaction Brain Router 用户 LLM Compaction Brain Router 用户 alt [Token >= 阈值] [Token < 阈值] 发送消息 路由到 Brain 检查 Token 计数 shouldRunPreflightCompaction() return true 执行 compaction 分块摘要 合并摘要 返回 summary 替换历史消息 return false 发送请求(含 summary) 返回响应 回复用户
5.1.6 避免重复触发
OpenClaw 通过 compactionCount 防止同一轮次内重复触发:
ts
// 检查是否已在当前 compaction 周期执行过
export function hasAlreadyFlushedForCurrentCompaction(entry: SessionEntry): boolean {
const compactionCount = entry.compactionCount ?? 0;
const lastFlushAt = entry.memoryFlushCompactionCount;
return typeof lastFlushAt === "number" && lastFlushAt === compactionCount;
}
应用场景:
- Compaction 完成后,LLM 请求可能再次接近阈值
- 为避免无限循环,同一次 compaction 周期内不会重复触发
- 只有当新的用户消息到来后,才可能再次触发
5.2 Compaction 的核心 Prompt
Compaction 的关键在于如何指导 LLM 生成高质量的摘要。OpenClaw 使用分阶段摘要策略 和精心设计的 System Prompt。
5.2.1 合并摘要的 Prompt 指令
当对话太长需要分块处理时,OpenClaw 使用以下指令指导 LLM 合并多个部分摘要:
ts
// src/agents/compaction.ts(节选)
const MERGE_SUMMARIES_INSTRUCTIONS = [
"Merge these partial summaries into a single cohesive summary.",
"",
"MUST PRESERVE:",
"- Active tasks and their current status (in-progress, blocked, pending)",
"- Batch operation progress (e.g., '5/17 items completed')",
"- The last thing the user requested and what was being done about it",
"- Decisions made and their rationale",
"- TODOs, open questions, and constraints",
"- Any commitments or follow-ups promised",
"",
"PRIORITIZE recent context over older history. The agent needs to know",
"what it was doing, not just what was discussed.",
].join("\n");
5.2.2 标识符保留指令
为避免摘要过程中丢失关键标识符(如 UUID、文件名等),OpenClaw 添加了特殊指令:
ts
// src/agents/compaction.ts(节选)
const IDENTIFIER_PRESERVATION_INSTRUCTIONS =
"Preserve all opaque identifiers exactly as written (no shortening or reconstruction), " +
"including UUIDs, hashes, IDs, tokens, API keys, hostnames, IPs, ports, URLs, and file names.";
实际效果:
- ✅ 保留 :
file_abc123.txt,https://api.example.com/v1/users/42,error-code-0x5F3 - ❌ 不保留:长段落的详细描述、中间推理步骤、已完成的无关对话
5.3 Compaction 工作流程
替换阶段
分阶段摘要
准备阶段
触发阶段
是
检查 Token 使用率
超过阈值?
执行 before_compaction 钩子
将消息分块
剪枝历史消息
第1块摘要
第2块摘要
第3块摘要
合并摘要
丢弃原始消息
插入 summary 消息
执行 after_compaction 钩子
5.4 分阶段摘要算法(summarizeInStages)
ts
// src/agents/compaction.ts(节选,summarizeInStages)
export async function summarizeInStages(params: {
messages: AgentMessage[];
model: NonNullable<ExtensionContext["model"]>;
apiKey: string;
reserveTokens: number;
maxChunkTokens: number;
contextWindow: number;
customInstructions?: string;
previousSummary?: string;
parts?: number; // 分块数量,默认 2
}): Promise<string> {
const { messages } = params;
// 1. 决定是否分块
const parts = normalizeParts(params.parts ?? DEFAULT_PARTS, messages.length);
const totalTokens = estimateMessagesTokens(messages);
if (parts <= 1 || messages.length < minMessagesForSplit || totalTokens <= params.maxChunkTokens) {
// 消息不多,直接一次摘要
return summarizeWithFallback(params);
}
// 2. 分块处理
const splits = splitMessagesByTokenShare(messages, parts).filter(chunk => chunk.length > 0);
// 3. 对每个块生成部分摘要
const partialSummaries: string[] = [];
for (const chunk of splits) {
partialSummaries.push(
await summarizeWithFallback({
...params,
messages: chunk,
previousSummary: undefined, // 部分摘要不使用 previous summary
})
);
}
// 4. 合并部分摘要
const summaryMessages: AgentMessage[] = partialSummaries.map(summary => ({
role: "user",
content: summary,
timestamp: Date.now(),
}));
const mergeInstructions = customInstructions
? `${MERGE_SUMMARIES_INSTRUCTIONS}\n\n${customInstructions}`
: MERGE_SUMMARIES_INSTRUCTIONS;
// 5. 递归调用合并
return summarizeWithFallback({
...params,
messages: summaryMessages,
customInstructions: mergeInstructions,
});
}
分块策略:
| 参数 | 默认值 | 说明 |
|---|---|---|
parts |
2 | 分块数量(2 = 二分) |
maxChunkTokens |
上下文窗口的 40% | 每块最大 Token 数 |
reserveTokens |
4096 | 为摘要 prompt 预留的空间 |
SAFETY_MARGIN |
1.2 (20%) | 安全缓冲,防止低估 |
5.5 消息分块算法
ts
// src/agents/compaction.ts(节选,chunkMessagesByMaxTokens)
export function chunkMessagesByMaxTokens(
messages: AgentMessage[],
maxTokens: number,
): AgentMessage[][] {
// 应用安全缓冲(SAFETY_MARGIN = 1.2)
const effectiveMax = Math.max(1, Math.floor(maxTokens / SAFETY_MARGIN));
const chunks: AgentMessage[][] = [];
let currentChunk: AgentMessage[] = [];
let currentTokens = 0;
for (const message of messages) {
const messageTokens = estimateCompactionMessageTokens(message);
// 如果当前块已满,开启新块
if (currentChunk.length > 0 && currentTokens + messageTokens > effectiveMax) {
chunks.push(currentChunk);
currentChunk = [];
currentTokens = 0;
}
currentChunk.push(message);
currentTokens += messageTokens;
// 单条消息过大时,单独成块
if (messageTokens > effectiveMax) {
chunks.push(currentChunk);
currentChunk = [];
currentTokens = 0;
}
}
if (currentChunk.length > 0) {
chunks.push(currentChunk);
}
return chunks;
}
5.6 实际 Compaction 示例
原始对话(Token 数:15000,超出 8K 窗口):
User: 帮我分析这个日志文件 /var/log/app.log
Assistant: [读取文件,发现错误] 我发现很多 Connection refused 错误
User: 是什么原因?
Assistant: [分析] 可能是数据库连接池满了
User: 怎么解决?
Assistant: [提供解决方案] 可以调整 max_connections 参数...
[... 30 轮对话 ...]
User: 现在我想查看系统状态
Assistant: 好的,我来检查
Compaction 后(Token 数:3000):
[Compaction Summary]:
用户请求分析 /var/log/app.log,发现 Connection refused 错误。
根因:数据库连接池满。解决方案:调整 max_connections 参数。
后续讨论:实施了配置更改,验证了连接数下降。
当前任务:用户要求查看系统状态(待执行)。
最近的对话:
User: 现在我想查看系统状态
Assistant: 好的,我来检查
关键观察:
- ✅ 保留了文件路径
/var/log/app.log - ✅ 保留了关键错误 "Connection refused"
- ✅ 保留了解决方案 "调整 max_connections"
- ✅ 保留了当前任务 "查看系统状态"
- ❌ 省略了中间 30 轮的详细讨论过程
5.7 Compaction 与 Hooks
ts
// src/agents/pi-embedded-subscribe.handlers.compaction.ts(节选)
export function handleAutoCompactionEnd(ctx, evt) {
ctx.state.compactionInFlight = false;
if (hasResult && !wasAborted) {
ctx.incrementCompactionCount();
}
if (willRetry) {
// Compaction 后重试 LLM 请求
ctx.noteCompactionRetry();
ctx.resetForCompactionRetry();
}
// after_compaction 钩子
const hookRunner = getGlobalHookRunner();
if (hookRunner?.hasHooks("after_compaction")) {
hookRunner.runAfterCompaction({
messageCount: ctx.params.session.messages?.length ?? 0,
compactedCount: ctx.getCompactionCount(),
});
}
}
| Hook | 触发时机 | 用途 |
|---|---|---|
before_compaction |
开始压缩前 | 记录状态、备份数据 |
after_compaction |
压缩完成后 | 清理资源、记录日志 |
5.8 配置参数
json
{
"agents": {
"defaults": {
"compaction": {
"mode": "default", // "default" | "safeguard" | "disabled"
"reserveTokens": 32000, // 预留 Token 数
"keepRecentTokens": 20000, // 保留最近对话的 Token 数
"minMessages": 4, // 触发压缩的最小消息数
"parts": 2, // 分块数量
"summarizationInstructions": {
"identifierPolicy": "strict" // "strict" | "off" | "custom"
}
}
}
}
}
💡 理解要点 :Compaction 是"不失真地遗忘"------通过精心设计的 prompt 指导 LLM 保留关键信息(标识符、任务状态、承诺),同时丢弃次要细节,从而在不丢失上下文的前提下释放 Token 空间。
6 三大工程的关系与协同
| 工程维度 | 解决的核心问题 | 关键技术 | 与 Brain 的关系 |
|---|---|---|---|
| Prompt Engineering | "给 LLM 看什么?" | 分层 System Prompt、Cache Boundary | 输入侧优化 |
| Context Engineering | "给 LLM 看多少?" | Token 预算、渐进式披露、Skills 截断 | 容量侧优化 |
| Harness Engineering | "如何与 LLM 协作?" | 工具调用循环、Provider 抽象、Compaction | 执行侧优化 |
Harness Engineering
释放 Token
触发 Skills L2/L3
Context Engineering
Token 预算管理
渐进式披露
L1/L2/L3
Prompt Engineering
System Prompt
分层组装
工具调用循环
上下文压缩
多 Provider 适配
用户输入
LLM 回复
7 源码走读:Brain 核心链路
7.1 入口:runReplyAgent
ts
// src/auto-reply/reply/agent-runner.ts(节选)
export async function runReplyAgent(params) {
// 1. 会话状态刷新
const sessionEntry = refreshSessionEntryFromStore(params);
// 2. 预压缩检查
await runPreflightCompactionIfNeeded(params);
// 3. 构建 System Prompt(Prompt Engineering)
const systemPrompt = buildSystemPrompt({
contextFiles: params.contextFiles,
skillsPrompt: params.skillsPrompt, // L1 Catalog
runtimeInfo: params.runtimeInfo,
promptMode: params.promptMode, // full/minimal/none
});
// 4. 主执行循环(Harness Engineering)
const result = await runAgentTurnWithFallback({
model: params.defaultModel,
systemPrompt,
messages: buildMessageHistory(params),
tools: resolveAvailableTools(params),
onToolCall: handleToolCall,
});
// 5. 后处理
await runMemoryFlushIfNeeded(params);
return buildReplyPayloads(result);
}
7.2 System Prompt 构建:buildSystemPrompt
ts
// src/agents/system-prompt.ts(节选)
export function buildSystemPrompt(params) {
const lines = [];
// L1-L5: 稳定内容(可被缓存)
lines.push(...buildBaseIdentitySection(params));
lines.push(...buildToolingSection(params));
lines.push(...buildSkillsSection(params)); // L1 Skills
lines.push(...buildMemorySection(params));
lines.push(...buildProjectContextSection(params, false));
// Cache Boundary
lines.push(SYSTEM_PROMPT_CACHE_BOUNDARY);
// L6-L7: 动态内容(不缓存)
lines.push(...buildProjectContextSection(params, true));
lines.push(...buildRuntimeSection(params));
return lines.join("\n");
}
7.3 上下文 Token 解析:resolveContextTokensForModel
ts
// src/agents/context.ts(节选)
export function resolveContextTokensForModel(params) {
// 1. 配置优先
const configTokens = params.config?.models?.providers?.[params.provider]?.models?.[params.model]?.contextTokens;
if (configTokens) return configTokens;
// 2. 缓存查找
const cached = lookupCachedContextTokens(params.model);
if (cached) return cached;
// 3. 特殊模型处理
if (isAnthropic1MModel(params.model)) {
return 1_048_576; // 1M tokens
}
// 4. 默认值
return DEFAULT_CONTEXT_TOKENS; // 通常 128K
}
8 本篇小结与下一篇预告
-
小结:
- Prompt Engineering = 分层组装 System Prompt(8层结构)+ Cache Boundary 优化
- Context Engineering = Token 预算管理 + 渐进式披露(L1/L2/L3)+ Skills 截断
- Harness Engineering = 工具调用循环 + Provider 抽象 + 上下文压缩(Compaction)
- 三大工程协同工作,让 Brain 在可控成本内处理复杂任务
-
关键洞察:
- Brain 不是简单的"LLM 包装器",而是一整套工程实践
- Prompt/Context/Harness 三层优化缺一不可
- Compaction 机制让长对话成为可能
-
下一篇(第6篇) :Hands------工具执行、沙箱安全、权限边界,探索 OpenClaw 如何安全地执行代码和操作。
9 参考文献与链接
- OpenClaw 主仓库:https://github.com/openclaw/openclaw
- Brain & Hands 架构文档(D1):http://clawdocs.org/architecture/brain-and-hands
- Context Engine 文档(D2):docs/concepts/context-engine.md
- Compaction 文档(D2):docs/concepts/compaction.md
- 源码入口:
src/agents/system-prompt.ts------ System Prompt 构建src/agents/context.ts------ 上下文窗口管理src/agents/skills/workspace.ts------ Skills Token 预算src/auto-reply/reply/agent-runner.ts------ Agent 执行引擎src/agents/pi-embedded-subscribe.handlers.tools.ts------ 工具调用处理src/agents/pi-embedded-subscribe.handlers.compaction.ts------ 上下文压缩
- D3 参考:managemyclaw.com - How OpenClaw Works