OpenClaw 深度解析与源代码导读 · 第5篇:Brain——Prompt/Context/Harness Engineering 与执行框架

摘要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/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 分层结构

  1. 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.mdsoul.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.

这个示例展示了

  1. L1 Base Identity : "You are a personal assistant running inside OpenClaw."
  2. L2 Tooling : 20+ 个工具的详细说明,包括 read, write, exec, browser
  3. Tool Call Style: 指导 LLM 如何描述工具调用("do not narrate routine")
  4. Execution Bias: 鼓励 LLM 立即行动而非只给计划
  5. OpenClaw CLI: 内置帮助文档
  6. L3 Skills : <available_skills> XML 格式,包含已安装技能的 name + description(L1)
  7. L4 Memory: 记忆能力说明
  8. Cache Boundary: 分隔稳定内容和动态内容
  9. 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.jsonmodels.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 窗口的触发条件

  1. Provider 必须是 "anthropic"
  2. Model ID 必须以 "claude-opus-4""claude-sonnet-4" 开头
  3. 配置中设置了 "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.

💡 关键洞察:三层截断策略体现了**"能保则保,不得已才丢"**的设计哲学:

  1. 第一层(数量限制):预防性保护,避免技能过多影响性能
  2. 第二层(格式降级):信息密度换数量,让 LLM 至少知道"有哪些技能"
  3. 第三层(二分查找):迫不得已时才丢弃技能,且通过二分查找最大化保留数量

这种渐进式策略确保了即使在高负载场景下,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 RunnerPI-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 参考文献与链接

  1. OpenClaw 主仓库:https://github.com/openclaw/openclaw
  2. Brain & Hands 架构文档(D1):http://clawdocs.org/architecture/brain-and-hands
  3. Context Engine 文档(D2):docs/concepts/context-engine.md
  4. Compaction 文档(D2):docs/concepts/compaction.md
  5. 源码入口:
    • 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 ------ 上下文压缩
  6. D3 参考:managemyclaw.com - How OpenClaw Works
相关推荐
过河卒_zh15667661 小时前
技术狂奔之后:数字虚拟人走向规则时代
人工智能·算法·aigc·生成式人工智能·算法备案
笑小枫1 小时前
当智能眼镜遇上了AI——使用灵珠搭建【镜中食谱】智能体
人工智能
听你说321 小时前
中节能晶和科技亮相道路照明论坛:以EMC模式破局行业热潮 做智慧照明高质量发展引领者
大数据·人工智能·科技
ai大模型中转api测评2 小时前
Claude Opus 4.7 深度拆解:自验证架构与 1M 上下文,全方位对标 GPT-5.4
人工智能·gpt·自动化·api
脑极体2 小时前
智能体落地零售,带来了哪些新可能?
大数据·人工智能·零售
摸鱼仙人~2 小时前
企业级 AI Coding 设计规范
人工智能·设计规范
徐礼昭|商派软件市场负责人2 小时前
效率提升75%!AI赋能ECShopX系统开发特辑:使用Cursor、Codex、Figma等AI辅助工具高效完成商城创新功能开发
人工智能·figma
广州创科水利2 小时前
揭阳龙颈下水库除险加固:智慧监测护航,筑牢安全防线
人工智能
码云数智-大飞2 小时前
AI写单元测试的现状与挑战:覆盖率不等于有效性
人工智能