从源码泄露看AI Agent未来:深度对比Claude Code原生实现与OpenClaw开源方案

作者:半行代码日期:2026/4/8

Claude Code 是 Anthropic 推出的终端 AI 编程助手。与普通的聊天式 AI 不同,它直接在终端里工作,能够读取代码、执行命令、修改文件、管理 Git 操作。阅读其源码后,可以从 Agent 循环、上下文工程、提示词工程和多 Agent 协同几个维度梳理出它的设计脉络。

整体架构

Claude Code 的核心是一个典型的 ReAct Agent 架构,入口是 query() 函数,它内部委托给 queryLoop() —— 一个通过 while(true) 无限循环驱动的 AsyncGenerator。每一次循环就是一个 turn,完成从上下文准备、模型调用、响应处理到工具执行的完整闭环。

整体架构可以概括为以下几个层次:

转存失败,建议直接上传图片文件

下面按几个核心维度逐一拆解。

Agent 循环:queryLoop 的运转机制

设计思想

Claude Code 的 Agent 循环采用了 AsyncGenerator + while(true) 的组合。选择 AsyncGenerator 而非简单 async function 的原因很直接:每一轮循环都会向 UI 层 yield 流式事件,调用方可以在循环执行过程中实时渲染模型输出和工具执行进度。如果用普通 async function,只能等整个 turn 结束后才返回结果,用户体验会断掉。

状态管理上,Claude Code 没有把状态分散在各个局部变量里,而是定义了一个 State 对象,在每次 continue 时整体替换:

1type State = {
2  messages: Message[]
3  toolUseContext: ToolUseContext
4  autoCompactTracking: AutoCompactTrackingState
5  maxOutputTokensRecoveryCount: number
6  hasAttemptedReactiveCompact: boolean
7  maxOutputTokensOverride: number | undefined
8  pendingToolUseSummary: Promise<ToolUseSummaryMessage | null> | undefined
9  stopHookActive: boolean | undefined
10  turnCount: number
11  transition: Continue | undefined    // 记录上一次循环继续的原因
12}
13

这种设计的优势在于:continue 站点只需要构造一个新的 State 对象然后赋值 + continue,不需要分别更新 9 个变量。同时 transition 字段记录了上一次循环继续的原因,让后续迭代能够基于前一次决策做判断(比如判断上一次是否已经做过 collapse_drain_retry,避免重复操作)。

实现过程

循环的执行流程如下:

第一步:循环前预启动(只执行一次)

startRelevantMemoryPrefetch()startSkillDiscoveryPrefetch() 在循环外启动。这两个操作返回 Promise 句柄,内部异步执行,不阻塞主流程。后续 turn 中通过检查 settledAt 字段判断是否就绪,就绪了就消费。

第二步:上下文准备

每轮 turn 进入后,按顺序执行:

  1. applyToolResultBudget() — 工具结果瘦身,把超大的 tool_result 替换为摘要
  2. snipCompactIfNeeded() — 历史裁剪,移除过旧的消息
  3. microcompact() — 微压缩,清理过期的 tool result
  4. applyCollapsesIfNeeded() — 上下文折叠,分段生成摘要
  5. autocompact() — 自动压缩,最后的兜底策略
  6. calculateTokenWarningState() — 检查是否触及硬上限

这些步骤不是互斥的。snip 在 microcompact 之前执行,因为两者都可能运行。collapse 在 autocompact 之前执行,因为如果折叠能把上下文压到 autocompact 阈值以下,autocompact 就变成 no-op,从而保留更细粒度的上下文。

第三步:模型调用

通过 callModel() 将组装好的消息发给 API。流式返回的消息逐条 yield 给 UI,同时收集到 assistantMessages 数组中。遇到 tool_use 块就记录到 toolUseBlocks,设置 needsFollowUp = true

第四步:工具执行

needsFollowUp 为 true 时进入工具执行阶段,通过 runTools()StreamingToolExecutor 执行工具调用。

第五步:附件注入

工具执行完毕后,调用 getAttachmentMessages() 注入各类附件:文件变更通知、任务通知、memory 预取结果、skill 预取结果。

第六步:决策

如果没有 follow-up(模型没有请求工具),进入决策层:执行 Stop Hooks、检查 Token Budget、判断 Max Turns,然后决定是否继续。

核心循环的伪代码:

1async function* queryLoop(params):
2  state = initialState(params)
3  pendingMemoryPrefetch = startRelevantMemoryPrefetch(state.messages)
4
5  while true:
6    // --- 上下文准备 ---
7    pendingSkillPrefetch = startSkillDiscoveryPrefetch(messages)
8    messagesForQuery = applyToolResultBudget(messages)
9    messagesForQuery = snipCompactIfNeeded(messagesForQuery)
10    messagesForQuery = microcompact(messagesForQuery)
11    messagesForQuery = applyCollapsesIfNeeded(messagesForQuery)
12    messagesForQuery, compactionResult = autocompact(messagesForQuery)
13
14    // --- 硬上限拦截 ---
15    if isAtBlockingLimit(messagesForQuery):
16      yield APIErrorMessage
17      return { reason: 'blocking_limit' }
18
19    // --- 模型调用 ---
20    for await message in callModel(messagesForQuery, systemPrompt, tools):
21      yield message
22      if message.type == 'assistant':
23        assistantMessages.push(message)
24        toolUseBlocks.extend(extractToolUseBlocks(message))
25
26    // --- 工具执行 ---
27    if toolUseBlocks:
28      for update in runTools(toolUseBlocks):
29        yield update.message
30        toolResults.push(update.message)
31
32    // --- 附件注入 ---
33    for attachment in getAttachmentMessages(...):
34      yield attachment
35      toolResults.push(attachment)
36
37    // 消费 memory prefetch(如果已就绪)
38    if pendingMemoryPrefetch.settledAt and not consumed:
39      for mem in pendingMemoryPrefetch.promise:
40        yield createAttachmentMessage(mem)
41        toolResults.push(mem)
42
43    // 消费 skill prefetch
44    for skill in collectSkillDiscoveryPrefetch(pendingSkillPrefetch):
45      yield createAttachmentMessage(skill)
46      toolResults.push(skill)
47
48    // --- 决策 ---
49    if not needsFollowUp:
50      // 处理 413 恢复、max_output_tokens 恢复
51      // 执行 Stop Hooks
52      // 检查 Token Budget / Max Turns
53      if shouldContinue:
54        state = newStateWithContinueReason(...)
55        continue
56      return { reason: 'completed' }
57
58    //  follow-up,构建下一轮 state
59    state = {
60      messages: [...messagesForQuery, ...assistantMessages, ...toolResults],
61      turnCount: turnCount + 1,
62      ...
63    }
64

流式 fallback 与错误恢复

模型调用阶段有一个 streaming fallback 机制。如果流式调用过程中触发了 FallbackTriggeredError(通常是模型负载过高),系统会:

  1. 清空当前轮收集的所有 assistantMessages 和 toolResults
  2. 将模型切换到 fallbackModel
  3. 丢弃 StreamingToolExecutor 的 pending 结果,重建新的 executor
  4. 重新发起完整请求

fallback 触发时,还会 yield 一个 tombstone 消息给 UI 层,让前端移除上次流式调用产生的不完整内容。

转存失败,建议直接上传图片文件

Agent 架构细节

工具结果预算控制

设计思想

工具调用的返回结果(比如 BashTool 执行 ls -la / 的输出、FileReadTool 读取一个大文件的内容)可能非常庞大。如果不加限制地全部塞进上下文,几次工具调用就能吃光整个 token 预算。

Claude Code 的设计思路是:不直接丢弃原始内容,而是替换为摘要,同时在摘要里保留原始内容的文件路径。模型如果觉得摘要不够,可以主动通过 Read 工具去读取全文。这本质上是一种 lazy loading 策略。

实现过程

applyToolResultBudget() 的执行过程分四步:

  1. collectCandidatesByMessage — 按 API 消息序列分组,把同一轮流式返回的多个 tool_result 候选收集到一起共享预算
  2. partitionByPriorDecision — 对每组候选按历史决策分区:
    • mustReapply:之前已经被替换成摘要的结果,直接用缓存的 preview 重放,不需要重新决策
    • frozen:之前已经见过但模型已经读取过的结果,标记为冻结,不能再改动。改动会破坏 prompt cache 的字节匹配
    • fresh:首次见到的结果,可以做替换决策
  3. selectFreshToReplace — 如果 frozen + fresh 的总大小超过 200KB 阈值,从 fresh 中选最大的先替换。选择策略是贪心的:按大小降序排列,从最大的开始替换,直到总大小降到阈值以下
  4. persistToolResult + buildLargeToolResultMessage — 被选中的结果先写入磁盘文件,然后生成 preview 字符串替换原始内容。preview 中会包含完整内容的文件路径
1applyToolResultBudget(messages, contentReplacementState)
2  
3  ├─ state === undefined  直接返回(feature 关闭)
4  
5  └─ enforceToolResultBudget()
6       
7       ├─ 1. collectCandidatesByMessage()
8             API 消息序列分组,同一序列的 tool_result 共享预算
9       
10       ├─ 2. 对每组消息执行:
11            ├─ partitionByPriorDecision()
12               ├─ mustReapply: 之前替换过  复用缓存的 preview
13               ├─ frozen: 之前见过但模型已读取  冻结不动
14               └─ fresh: 首次见到  进入替换决策
15            
16            ├─ frozen + fresh 总量 > 200KB?
17               ├─ YES  selectFreshToReplace()
18                        按大小降序,从最大的开始替换
19               └─ NO   全部标记为 seen,跳过
20            
21            └─ 被选中的结果:
22                 ├─ persistToolResult()  写磁盘
23                 └─ buildLargeToolResultMessage()  生成 preview
24       
25       ├─ 3. replaceToolResultContents()
26             preview 字符串替换原始内容
27       
28       └─ 4. 返回 { messages, newlyReplaced }
29

缓存持久化(contentReplacementState)是关键设计。因为下一轮循环时,同一批 tool_result 会再次经过 applyToolResultBudget(),系统需要知道哪些已经被替换过(mustReapply),哪些是模型读取过的(frozen)。这个状态只在 agentIdrepl_main_thread 的 querySource 下才会持久化到磁盘,确保 resume 会话能恢复状态。

多级压缩策略

设计思想

Claude Code 的压缩体系不是一刀切的,而是按影响范围和成本分了四个层次。设计的核心原则是:能用便宜操作的就不用贵的,能保留细粒度上下文的就不做全局摘要。

四个层次从轻量到重量排列:

  1. snipCompact — 裁剪旧消息,直接删除,零成本
  2. microcompact — 清理过期的 tool result,利用 cache_edits API 保持 KVCache
  3. collapse — 分段折叠,每段生成独立摘要,保留部分结构
  4. autocompact — 全局压缩,生成整个对话的摘要替换历史,成本最高

实现过程

microcompact 的处理逻辑:

  1. 检查距离上一次 AI 消息的时间是否超过阈值
  2. 如果超时,直接清空过期的 tool result
  3. 如果启用了 CACHED_MICROCOMPACT,走 cachedMicrocompactPath
    • 依赖 Claude Code 的 cache_edits API
    • 将压缩操作作为缓存编辑提交给服务端
    • 服务端返回实际的 cache_deleted_input_tokens
    • 客户端用实际值替换预估的 token 删除数
    • 边界消息(boundary message)延迟到 API 响应后才 yield,确保使用真实的 token 数据

**为什么使用 ****cache_edits**会又块又节省 token?

要理解这一点,需要先了解 Anthropic API 的 prompt caching 机制。当请求中包含 cache_control: { type: 'ephemeral' } 标记时,API 服务端会缓存该标记位置之前所有消息的 KVCache(Key-Value Cache,即 transformer 模型中 attention 计算产生的中间状态)。下一轮请求如果前缀字节完全一致,服务端可以直接复用缓存的 KV,不需要重新 prefill,从而显著降低延迟和 token 消耗。

问题是:当我们要删除某些旧的 tool result 时,如果直接修改消息内容,整个前缀的字节序列就变了,KVCache 全部失效,服务端必须重新 prefill 全部内容。这就是传统压缩的代价——你节省了上下文 token 数量,但浪费了 cache 命中率。

cache_edits API 解决了这个矛盾。它的工作方式是:

  1. 客户端在请求体中额外附带一个 cache_edits 块,声明要删除哪些内容
  2. 服务端收到请求后,先匹配前缀 KVCache(此时前缀未变,cache 命中),然后再执行删除操作
  3. 删除操作是在 cache 命中之后进行的,所以不会影响前缀的 cache 命中率

autocompact 的处理逻辑:

  1. 守卫检查:DISABLE_COMPACT 开启则跳过
  2. 熔断器检查:连续失败次数达到阈值则跳过
  3. shouldAutoCompact() 判断是否需要压缩
  4. 优先尝试 trySessionMemoryCompaction() — 将对话信息提取到 session memory 中,然后删除旧消息
  5. 如果 session memory 方式不可用,兜底执行 compactConversation() — 生成整个对话的摘要替换全部历史
  6. 失败时递增失败计数,触发熔断

执行顺序也很讲究。collapse 在 autocompact 之前执行,因为 collapse 是一个 read-time 的 view projection,不会真正删除消息。如果 collapse 能把上下文压到 autocompact 阈值以下,autocompact 就变成 no-op,从而保留更细粒度的上下文而不是一个粗粒度的摘要。

这些压缩机制都通过 bun:bundlefeature() 函数进行编译期树摇。外部构建可以完全剔除未启用的功能代码,减小包体积。


设计思想

当上下文超出模型限制,API 返回 413 (prompt too long) 错误时,Claude Code 不是简单报错退出,而是设计了渐进式的恢复路径。核心思路是:先做便宜的恢复(保留更多上下文),不行再做重量级恢复。

实现过程

恢复流程分两层:

  1. recoverFromOverflow() — 折叠释放
    • 将所有 staged 的 context collapses 提交
    • 这是一个相对便宜的操作,只释放之前已经计算好的折叠摘要
    • 保留了细粒度的上下文结构
    • 如果没有 staged collapses 或已经执行过(通过 transition.reason === 'collapse_drain_retry' 判断),则跳过
  2. tryReactiveCompact() — 反应式压缩
    • 当折叠释放不够用时触发
    • 做一次完整的摘要压缩,替换历史消息
    • hasAttemptedReactiveCompact 守卫,防止重复执行
    • 如果 oversized 的是媒体内容(图片/PDF),跳过 collapse drain 直接走这层,因为 collapse 不处理图片

max_output_tokens 错误也有类似的恢复路径:

  1. 首先尝试 escalation:如果使用了 capped 8k 默认值,将 maxOutputTokensOverride 提升到 64K,用同样的消息重试
  2. 如果仍然超出,进入 multi-turn recovery:注入一条 meta 消息告诉模型 "Output token limit hit. Resume directly — no apology, no recap",最多重试 3 次
  3. 3 次重试后用 withheld 的错误消息终止循环

上下文工程:精细化控制每一点 token

Memory 预取机制

设计思想

Memory 系统的设计目标很明确:让 Agent 能在跨会话中积累知识并复用。但注入太多记忆会浪费 token,注入不相关的记忆会干扰模型判断。所以需要在检索的准确性和注入的成本之间做平衡。

实现过程

startRelevantMemoryPrefetch() 在循环外启动,返回一个 MemoryPrefetch 句柄。整个流程包括:

  1. 功能开关检查 — 判断 isAutoMemoryEnabled() 和对应的 feature flag
  2. 提取用户消息 — 获取最后一条真实用户消息,跳过 isMeta 标记的系统注入消息
  3. 词长过滤 — 单词数少于 2 个的查询直接跳过,不浪费检索资源
  4. 累计熔断 — 检查会话累计已注入字节是否 >= 60KB,达到上限后停止预取
  5. 异步搜索 — 启动不阻塞主流程的后台检索

检索过程的具体实现:

  1. 扫描 memory 目录,读取所有文件的 header
  2. 用 Sonnet 模型的 sideQuery 做相关性评分,选出最多 5 个相关记忆
  3. 读取每个记忆的内容,单文件上限 4KB,超出截断

消费阶段在 getAttachmentMessages() 中进行:

  1. 检查 pendingMemoryPrefetch.settledAt !== null 判断是否就绪
  2. 检查 consumedOnIteration === -1 避免重复消费
  3. 通过 filterDuplicateMemoryAttachments() 过滤已读取过的文件(基于 readFileState 判断)
  4. 将过滤后的记忆转为 AttachmentMessage 注入
1startRelevantMemoryPrefetch(messages, toolUseContext)
2  
3  ├─ 1. isAutoMemoryEnabled() + feature flag?
4       └─ NO  返回 null
5  
6  ├─ 2. 提取最后一条非 isMeta 用户消息
7       └─ 单词数 < 2?  返回 null
8  
9  ├─ 3. 会话累计注入字节 >= 60KB?
10       └─ YES  返回 null
11  
12  ├─ 4. 启动异步搜索(fire-and-forget)
13       └─ getRelevantMemoryAttachments()
14             ├─ 扫描 memory 目录,读取文件 header
15             ├─ Sonnet sideQuery 评分  最多选 5 
16             └─ 读取内容(单文件 4KB 上限)
17  
18  └─ 5. 返回 MemoryPrefetch 句柄
19        后续 turn 中通过 settledAt  consumedOnIteration 判断消费时机
20

这套设计有几个值得注意的细节。已读取过的文件通过 readFileState 过滤,这是 cumulative 的状态,跨 turn 累加,确保同一份记忆不会被重复注入。Sonnet 筛选保证了选出的 5 个文件与当前上下文的相关度最高。每个文件只读取 4KB,这个量级大致只够做开头摘要,至于是否读取全文留给后续工具决策。

Skill 发现预取

设计思想

Skill 是 Claude Code 中自定义的命令集合(类似 .claude/commands/ 目录下的命令)。如果每次 turn 都阻塞式搜索哪些 skill 适用,生产数据显示 97% 的情况下搜不到匹配项,白白浪费时间。所以设计为异步预取。

实现过程

startSkillDiscoveryPrefetch() 每轮 turn 启动一次(与 Memory prefetch 不同,skill prefetch 是每轮都启动的,因为它依赖 findWritePivot 守卫来判断是否是非 write 迭代)。

发现搜索在模型流式返回和工具执行的窗口期内运行,等到工具执行结束后与 memory prefetch 结果一起消费。只有 turn-0 的用户输入 discovery 才会阻塞在 userInputAttachments 中,因为那是唯一一个没有前置工作可以隐藏等待时间的信号。

Command Queue

设计思想

在多 Agent 场景下,子 Agent 完成任务后需要通知父 Agent,用户也可以在中途发送新的消息。Claude Code 用一个进程全局的 CommandQueue 单例来管理这些异步消息。

关键设计是消息的作用域隔离。队列是进程全局的,Coordinator 和所有 in-process 子 Agent 共享同一个队列。每个 Agent 在循环中只 drain 属于自己的消息:

  • 主线程(agentId === undefined):获取所有非 slash-command 的通知
  • 子 Agent:只获取 mode === 'task-notification'agentId 匹配自己的通知
  • 用户提示(mode: 'prompt')始终只发送给主线程,子 Agent 永远不会看到用户的 prompt 流

实现过程

query.ts 的每轮 turn 中,工具执行后、附件注入前,会从队列中取出消息:

1const sleepRan = toolUseBlocks.some(b => b.name === SLEEP_TOOL_NAME)
2const queuedCommandsSnapshot = getCommandsByMaxPriority(
3  sleepRan ? 'later' : 'next'
4).filter(cmd => {
5  if (isSlashCommand(cmd)) return false       // slash command 不走这个路径
6  if (isMainThread) return cmd.agentId === undefined  // 主线程拿所有通知
7  return cmd.mode === 'task-notification' && cmd.agentId === currentAgentId
8})
9

Sleep 工具在这里起调度作用。如果本轮执行了 Sleep,队列优先级切换到 later,否则用 next。这使得不同类型的任务通知可以有不同的调度优先级。

提示词工程

系统提示词的分层构建

设计思想

系统提示词不是单一的字符串,而是由多层组件拼装而成。这种设计让不同来源的配置可以独立演进,互不干扰。

实现过程

构建分三个阶段:

第一阶段:获取基础组件fetchSystemPromptParts()

  1. defaultSystemPrompt — 默认系统提示,定义 Agent 的基本行为
  2. userContext — 来自 getUserContext(),包含 CLAUDE.md 内容和当前日期
  3. systemContext — 来自 getSystemContext(),包含 git 状态(分支、近期提交、用户等)

getUserContextgetSystemContext 都用 memoize 缓存,在对话开始时计算一次,整个对话周期复用。

第二阶段:在 query.ts 中拼接

1const systemPrompt = asSystemPrompt([
2  ...(customPrompt ? [customPrompt] : defaultSystemPrompt),
3  ...(memoryMechanicsPrompt ? [memoryMechanicsPrompt] : []),
4  ...(appendSystemPrompt ? [appendSystemPrompt] : []),
5])
6

自定义 prompt 存在时替换默认系统提示。memory mechanics prompt 和 append system prompt 作为可选层追加。

第三阶段:在 claude.ts 的 queryModel 中最终组装

1systemPrompt = asSystemPrompt([
2  getAttributionHeader(fingerprint),        // 归属头
3  getCLISyspromptPrefix(...),              // CLI 前缀
4  ...systemPrompt,                          // 上面传下来的系统提示词
5  ...(advisorModel ? [ADVISOR_TOOL_INSTRUCTIONS] : []),
6  ...(injectChromeHere ? [CHROME_TOOL_SEARCH_INSTRUCTIONS] : []),
7])
8

上下文注入

消息最终发给模型时,通过 prependUserContext(messagesForQuery, userContext) 将用户上下文插入到消息数组的最前面。系统上下文通过 appendSystemContext(systemPrompt, systemContext) 追加到系统提示词的末尾。

这种 prepend/append 的设计保证了用户上下文(CLAUDE.md 等)出现在消息序列的最前面,离用户输入最近,注意力权重最高。而系统上下文(git 状态等)放在系统提示词最后,作为背景信息。

工具 Schema 构建

工具 schema 的构建流程:

  1. filteredTools — 按当前权限和可用性过滤工具列表
  2. toolToAPISchema — 将每个工具的定义转换为 API 可接受的 schema 格式
  3. toolSchemas + extraToolSchemas — 合并基础工具 schema 和额外工具 schema
  4. allTools — 最终发送给模型的完整工具列表

在 Coordinator 模式下,Coordinator 只能看到 4 个工具(Agent、SendMessage、TaskStop、SyntheticOutput),而 Worker 能看到完整的工具池。这个过滤在 toolUseContext.options.tools 中完成,由 coordinatorMode 的配置决定。

多 Agent 架构

Coordinator 模式:编排者与工作者

设计思想

Coordinator 模式是最经典的多 Agent 架构,核心思想是角色分离:Coordinator 负责理解和规划,Worker 负责执行和探索。Coordinator 被刻意限制只能使用 4 个工具,防止它越俎代庖去做具体的代码工作。

工作流分为四个阶段:Research → Synthesis → Implementation → Verification,支持并行研究。

Coordinator 的工具限制

Coordinator 只能使用以下 4 个工具:

  1. Agent — 启动新的 Worker
  2. SendMessage — 继续已有的 Worker(复用其上下文)
  3. TaskStop — 停止运行中的 Worker
  4. SyntheticOutput — 合成输出

在 Coordinator 模式下,模型参数被忽略,不允许自选模型。Worker 使用默认模型进行实质性任务。

AgentTool 的调用逻辑

AgentTool.call() 的执行流程:

  1. 参数解析和前置校验
    • Coordinator 模式下忽略 model 参数
    • 检查 team_name 参数
  2. 路由分发
    • 如果指定了 team_name,走 spawnTeammate 路径,直接进入 team 模式
    • 如果没有指定 subagent_type 且 FORK_SUBAGENT 开启,fork 子 Agent(禁止递归 fork)
    • 否则在已定义的 agent 列表中查找匹配的 agent
  3. Agent 配置解析
    • 等待 MCP 服务器连接
    • 解析模型并记录
    • 解析 isolation 参数(worktree 或 remote)
    • 构建 system prompt 和 prompt messages:
      * fork 的 Agent 继承父 Agent 的 system prompt、上下文、tool result 和指令
      * 普通 Agent 重新组装,只传递一条单独的简单 user message
    • 组装 runAgentParams 对象
  4. 异步/同步 Agent 执行
    • 异步 Agent:注册到后台,调用 runWithAgentContext,启用每 30 秒生成进度摘要,返回 async_launched 状态
    • 同步 Agent:保留中途异步化的能力
1AgentTool.call()
2  
3  ├─ 1. 参数解析 & 前置校验
4       ├─ Coordinator 模式  忽略 model 参数
5       └─ 检查 team_name
6  
7  ├─ 2. 路由分发
8       ├─ team_name 存在  spawnTeammate  return
9       ├─  subagent_type + FORK_SUBAGENT  forkSubagent(禁止递归)
10       └─  subagent_type  查找匹配的 agent 定义
11  
12  ├─ 3. Agent 配置解析
13       ├─ 等待 MCP 服务器连接
14       ├─ 解析模型
15       ├─ 解析 isolation(worktree / remote)
16       └─ 构建 system prompt  messages
17            ├─ fork: 继承父 Agent 的完整上下文
18            └─ 普通: 单条 user message
19  
20  ├─ 4. 执行
21       ├─ 异步: runAsyncAgentLifecycle()  fire-and-forget
22              后台执行,实时更新 UI 进度
23              stream 结束后  finalizeAgentTool()
24                            completeAsyncAgent()
25                            enqueueAgentNotification()
26       └─ 同步: 阻塞等待结果
27  
28  └─ 5. 返回 { status: 'async_launched', agentId }
29

异步 Agent 的通知机制

异步 Agent 通过以下流程与父 Agent 通信:

  1. runAsyncAgentLifecycle() 在后台执行,通过 for await (msg of runAgent()) 逐条消费 Agent 输出
  2. 实时更新 UI 进度(updateAsyncAgentProgress()
  3. Stream 结束后,finalizeAgentTool() 提取运行结果
  4. completeAsyncAgent() 标记任务状态为 completed
  5. enqueueAgentNotification() 构造 XML 通知推入全局 commandQueue

父 Agent 在下一个 turn 开始时从队列中 drain 这些通知,作为 AttachmentMessage 注入。模型看到 <task-notification> XML 后处理结果并决定下一步。

1AgentTool.call()
2  
3  +--[异步] void runAsyncAgentLifecycle()   ←── fire-and-forget
4  |    
5  |    +-- for await (msg of runAgent())    ←── 后台执行,逐条消费
6  |    |     updateAsyncAgentProgress()     ←── 实时更新 UI 进度
7  |    
8  |    +-- finalizeAgentTool()              ←── stream 结束
9  |    +-- completeAsyncAgent()             ←── task.status = 'completed'
10  |    +-- enqueueAgentNotification()       ←── 构造 XML
11  |         
12  |         +-- enqueuePendingNotification()  ←── 推入全局 commandQueue
13  |              
14  |              +-- notifySubscribers()      ←── 唤醒订阅者
15  
16  +--[立即] return { status: 'async_launched', agentId }
17
18                    ...  Agent 继续做其他事 ...
19
20 Agent 下一个 turn:
21  query.ts 主循环
22  
23  +-- getCommandsByMaxPriority()            ←──  commandQueue drain
24  +-- getQueuedCommandAttachments()         ←── 转为 attachment
25  +-- yield AttachmentMessage               ←── 作为 user message 注入
26  
27  Claude 看到 <task-notification> XML       ←── 处理结果,决定下一步
28

Coordinator 的提示词设计

Coordinator 的系统提示词有一个非常关键的设计:它被明确告知 "Workers can't see your conversation",所以每个 Worker prompt 必须是自包含的。这防止了 Coordinator 写出 "基于我们刚才讨论的内容" 这种依赖对话历史的无效 prompt。

Research 完成后,Coordinator 被要求自己做 Synthesis —— 不是简单地把研究结果转发给下一个 Worker,而是自己理解后发现,写出具体的文件路径、行号和变更内容。提示词中给出了明确的反例和正例:

1// 反例 —— 懒惰委托
2AgentTool({ prompt: "Based on your findings, fix the auth bug" })
3
4// 正例 —— 自己理解后合成规格
5AgentTool({ prompt: "Fix the null pointer in src/auth/validate.ts:42.
6  The user field on Session (src/auth/types.ts:15) is undefined when
7  sessions expire. Add a null check before user.id access." })
8

Fork Subagent 模式:上下文继承与并行扇出

设计思想

Fork 模式的核心价值是上下文继承。与 Coordinator 模式下 Worker 获得独立 prompt 不同,Fork 子 Agent 继承父 Agent 的完整对话历史。这在需要并行探索同一个代码库的不同角度时特别有用。

实现过程

为了最大化 prompt cache 命中率,所有 Fork 子 Agent 的 API 请求前缀必须是字节级一致的。buildForkedMessages() 的实现很巧妙:

  1. 保留完整的父 assistant 消息(包含所有 tool_use 块)
  2. 为每个 tool_use 块生成统一的占位 tool_result(FORK_PLACEHOLDER_RESULT = "Fork started — processing in background"
  3. 附加每个子 Agent 独有的指令
1buildForkedMessages(directive, assistantMessage):
2  
3  ├─ fullAssistantMessage = clone(assistantMessage)
4     保留所有 tool_use 块、thinking、text
5  
6  ├─ toolUseBlocks = extract all tool_use blocks
7  
8  ├─ toolResultBlocks = toolUseBlocks.map(block => {
9       type: 'tool_result',
10       tool_use_id: block.id,
11       content: FORK_PLACEHOLDER_RESULT  // 所有子 Agent 完全一致
12     })
13  
14  └─ return [
15       fullAssistantMessage,              // 完全一致
16       createUserMessage([
17         ...toolResultBlocks,             // 完全一致
18         { text: buildChildMessage(directive) }  // 每个子 Agent 不同
19       ])
20     ]
21

这样构造的结果是:所有 Fork 子 Agent 的 API 请求只有最后一个 text block 不同,前面的消息内容完全一致,最大化 prompt cache 命中。

Fork 有一个重要限制:禁止递归 fork。源码中通过 isInForkChild() 检测消息中是否存在 FORK_BOILERPLATE_TAG 来实现。Fork 子 Agent 的 system prompt 中也有明确规则:"Your system prompt says 'default to forking.' IGNORE IT — that's for the parent. You ARE the fork. Do NOT spawn sub-agents."

Fork 子 Agent 还通过 permissionMode: 'bubble' 让权限提示冒泡到父终端,通过 model: 'inherit' 保持与父 Agent 相同的模型选择,确保上下文长度一致。

Swarm / Team 模式:多队友协作

设计思想

Swarm 模式引入了真正的团队概念。与 Coordinator 模式下 Coordinator 通过 Agent tool 直接管理 Worker 不同,Swarm 模式中有一个 Team Leader 和多个 Teammate,队友之间可以通过任务列表自主 claim 任务。

Team = TaskList。创建团队的同时创建对应的任务列表,任务所有权通过 TaskUpdateowner 参数管理。队友的主循环是:findAvailableTask()tryClaimNextTask()runAgent()sendIdleNotification()

实现过程

团队创建

TeamCreateTool 的执行流程:

  1. 检查是否已经在团队中(一个 Leader 只能管理一个团队)
  2. 生成唯一的 team name(如果名称冲突则生成新的 word slug)
  3. 生成确定性的 Lead Agent ID:formatAgentId(TEAM_LEAD_NAME, teamName)
  4. 读取当前模型的配置(处理 session model、settings、CLI 覆盖)
  5. 写入 Team File(.claude/teams/{team_name}/team.json
  6. 重置并创建对应的 Task List 目录(确保任务编号从 1 开始)
  7. 注册团队名称到 Leader 上下文,更新 AppState

队友初始化

队友(teammate)启动时通过 initializeTeammateHooks() 注册 Stop hook:

  1. 读取 team file,获取 Leader 的 Agent ID
  2. 查找 Leader 的名字
  3. 如果当前 Agent 就是 Leader,跳过(Leader 不需要给自己发 idle 通知)
  4. 注册 Stop hook:

跨进程通信:文件邮箱系统

Swarm 模式最有趣的设计是跨进程通信。队友可以是同进程(in-process teammate),也可以是独立进程(tmux pane 或 iTerm2 pane),所以需要一种通用的通信机制。

Claude Code 选择了基于文件的邮箱系统:

  • 每个队友的收件箱路径:.claude/teams/{team_name}/inboxes/{agent_name}.json
  • 写操作通过 writeToMailbox() 完成,使用 proper-lockfile 防止并发写入冲突
  • 锁重试配置:最多 10 次,退避时间 5ms-100ms
1writeToMailbox(recipientName, message, teamName):
2  
3  ├─ ensureInboxDir(teamName)
4  
5  ├─ inboxPath = .claude/teams/{team}/inboxes/{agent}.json
6  ├─ lockPath = inboxPath + .lock
7  
8  ├─ 确保收件箱文件存在(flag: 'wx',已存在则忽略)
9  
10  ├─ lockfile.lock(inboxPath, { retries: 10, minTimeout: 5, maxTimeout: 100 })
11  
12  ├─ readMailbox()  messages(获取锁后重新读取最新状态)
13  
14  ├─ messages.push({ ...message, read: false })
15  
16  ├─ writeFile(inboxPath, JSON.stringify(messages))
17  
18  └─ release lock
19

Team Leader 的轮询消费

Team Leader 通过 useInboxPoller 每 1 秒轮询自己的邮箱:

1useInboxPoller(poll):
2  
3  ├─  1000ms 执行一次 poll()
4  
5  ├─ poll():
6       ├─ agentName = getAgentNameToPoll(appState)
7            跳过 in-process teammate(有自己的轮询机制)
8            Teammate 返回 getAgentName()
9            Team Lead 返回 teamContext.teammates[leadAgentId].name
10       
11       ├─ unread = readUnreadMessages(agentName, teamName)
12       
13       ├─ 按消息类型分类:
14            ├─ permissionRequests  路由到 ToolUseConfirmQueue
15            ├─ permissionResponses  处理权限回调
16            ├─ sandboxPermissionRequests  workerSandboxPermissions 队列
17            ├─ sandboxPermissionResponses  处理沙箱回调
18            ├─ shutdownRequests  传递给 UI 组件
19            ├─ shutdownApprovals  关闭 pane + 清理团队状态
20            ├─ teamPermissionUpdates  应用权限更新
21            ├─ modeSetRequests  设置队友模式
22            ├─ planApprovalRequests  自动审批并写回
23            └─ regularMessages  常规消息
24       
25       ├─ 如果没有 regularMessages  markRead() + return
26       
27       ├─ 格式化 regularMessages  XML:
28            <teammate_message teammate_id="..." color="..." summary="...">
29              {message text}
30            </teammate_message>
31       
32       ├─ 如果 idle(!isLoading && !focusedInputDialog):
33            onSubmitTeammateMessage(formatted)  立即作为新 turn 提交
34       
35       └─ 如果 busy:
36             消息入队 AppState.inbox  等空闲时投递
37  
38  └─ 空闲时交付 pending messages(useEffect)
39        格式化  onSubmitTeammateMessage  清除已提交的消息
40

邮箱系统的协议消息

邮箱不只传递简单文本,还承载了多种结构化协议消息,通过 JSON 序列化后以 type 字段区分:

  1. PermissionRequest / PermissionResponse — 工具权限请求/响应。Teammate 需要执行需要审批的工具时,写入 PermissionRequest 到 Leader 的邮箱。Leader 通过 UI 审批后,写回 PermissionResponse
  2. SandboxPermissionRequest / SandboxPermissionResponse — 沙箱网络访问权限。当沙箱运行时检测到非允许主机的网络连接时触发
  3. PlanApprovalRequest / PlanApprovalResponse — 计划审批。Teammate 在 plan 模式下完成计划后,请求 Leader 审批
  4. ShutdownRequest / ShutdownApproved / ShutdownRejected — 关闭请求。Leader 发送 shutdown_request 给队友,队友可以批准或拒绝
  5. TeamPermissionUpdate — 团队权限广播。Leader 修改权限后广播给所有队友
  6. ModeSetRequest — 模式设置。Leader 修改队友的 permission mode
  7. IdleNotification — 空闲通知。队友 turn 结束时通过 Stop hook 写入

转存失败,建议直接上传图片文件

与 OpenClaw 的设计对比

Claude Code 和 OpenClaw 都是 Agent 驱动的产品,但阅读两者的源码会发现,它们在 Agent 循环、上下文管理和多 Agent 通信上的设计思路有显著差异。这些差异不是技术能力的差距,而是产品定位不同导致的架构分叉。关于 OpenClaw 的具体介绍可以看:OpenClaw架构与实现解析

定位差异:专用工具 vs 通用平台

Claude Code 是 Anthropic 自家的产品,深度绑定 Claude API。它的设计目标是把一个 Agent 做到极致——极致的上下文控制、极致的 cache 利用、极致的单用户体验。所有压缩策略都围绕 cache_edits API 设计,所有消息格式都围绕 Anthropic 的 message schema 构建。

OpenClaw 是一个通用 Agent 平台,需要对接所有主流厂商(Anthropic、OpenAI、Google、Ollama 等),支持多种消息通道(Discord、Telegram、WhatsApp、Slack、Web UI),并且允许插件注入自定义的上下文管理逻辑和安全守卫。它的设计目标是可扩展性和多厂商适配,而非对单一 API 的深度优化。

Agent 实现的差异:手写工具 vs 框架包装

Claude Code:手写完整的 Agent 循环

Claude Code 的 queryLoop() 从零手写了整个 Agent 循环。query.ts 里 1700 多行代码完整实现了:上下文准备管线(6 层压缩)、模型调用、工具执行、附件注入、停止决策、错误恢复。没有任何外部 Agent 框架参与。

这背后的原因是 Claude Code 的定位——它是一个工具,不是一个平台。Anthropic 控制从 API 到客户端的整个栈,有动机也有能力对每个环节做深度优化。手写循环让他们可以在精确的位置插入精确的逻辑:在 snipCompactmicrocompact 之间插入 applyCollapses,在工具执行后注入 getAttachmentMessages,在 413 错误时走 recoverFromOverflow 而不是直接报错。每一行代码都为 Claude API 服务。

OpenClaw:包装 PI 框架,提供平台能力

OpenClaw 的 Agent 核心不是自己写的,而是复用了 @mariozechner/pi-coding-agent 库(简称 PI 框架)。它的职责不是实现 Agent 循环,而是围绕 PI 框架做包装和适配:

  1. 多厂商 API 适配层runEmbeddedPiAgent() 内部通过 streamSimple(来自 @mariozechner/pi-ai)统一调用不同厂商的 API。Claude、OpenAI、Google、Ollama 的流式接口各不相同,PI 框架把它们统一成一致的 AsyncGenerator 接口。OpenClaw 在此基础上再做模型选择、鉴权、重试和降级
  2. SessionManager 生命周期管理:PI 框架的 SessionManager 负责管理对话历史、工具注册、消息流转。OpenClaw 负责创建、持久化、预热 SessionManager,处理 session 文件锁、transcript 修复、compaction 重试
  3. 插件和扩展点:OpenClaw 在 PI 框架外围构建了完整的插件体系——before_prompt_buildbefore_agent_start 钩子,ContextEngine 可插拔接口,安全守卫注入。这些都不是 PI 框架提供的,而是 OpenClaw 作为平台的核心价值
1Claude Code  Agent 层次:
2  ┌─────────────────────────────────┐
3    queryLoop() (手写, 1700+ 行)      完整实现
4    ├── 上下文管线 (6层)            
5    ├── callModel()                   直接调 Anthropic API
6    ├── runTools()                 
7    ├── getAttachmentMessages()    
8    └── 决策层 (stop hooks)        
9  └─────────────────────────────────┘
10       直接面向 Claude API
11
12
13OpenClaw  Agent 层次:
14  ┌──────────────────────────────────┐
15    OpenClaw 平台层                     包装 + 适配
16    ├── 多厂商 API 适配              
17    ├── SessionManager 生命周期      
18    ├── 插件体系 (钩子/ContextEngine)│
19    ├── 多通道路由 (Discord/Slack..) 
20    └── Gateway 事件总线             
21  ├──────────────────────────────────┤
22    PI 框架 (@mariozechner/...)        Agent 核心
23    ├── SessionManager              
24    ├── streamSimple (统一 API 调用) 
25    └── 工具注册/消息流转            
26  └──────────────────────────────────┘
27       面向所有厂商 API
28

本质差异:Claude Code 的 Agent 就是它自己——整个 queryLoop 就是 Agent 循环。OpenClaw 的 Agent 是 PI 框架——OpenClaw 提供的是平台能力:多厂商适配、插件扩展、多通道路由、Gateway 调度。Claude Code 是"工具",把一件事做到极致;OpenClaw 是"平台",让不同的 Agent 实现和插件都能跑起来。

上下文管理的差异

Claude Code:极致精细的内置管线

Claude Code 的上下文管理完全内置,包含 6 层递进管线:

applyToolResultBudgetsnipCompactmicrocompactapplyCollapsesautocompact → 硬上限拦截。

每一层都有精确的阈值控制和状态跟踪。

关键特点:

  1. 深度适配 Claude API 的 cache_edits,利用 KVCache 保持能力
  2. 工具结果分三类(mustReapply/frozen/fresh),每类处理策略不同
  3. 200KB 工具结果预算、4KB memory 截断、60KB 累计熔断
  4. 413 错误两层渐进恢复

OpenClaw:插件化的 ContextEngine 接口

OpenClaw 通过 ContextEngine 接口实现上下文管理的可插拔:

1interface ContextEngine {
2  assemble(params): AssembleResult    // 组装上下文
3  compact(params): CompactResult      // 压缩
4  ingest(params): IngestResult        // 摄入新信息
5  maintenance(params): ContextEngineMaintenanceResult  // 维护
6}
7

用户可以注册自定义的 ContextEngine 实现,替换默认的压缩和组装逻辑。插件还可以通过 before_prompt_buildbefore_agent_start 钩子注入上下文或修改消息。

关键特点:

  1. 多厂商适配:不同 API 有不同的上下文限制和错误模式。OpenClaw 的 pi-embedded-helpers 中有专门的 guard 逻辑:isContextOverflowErrorisCompactionFailureErrorisBillingErrorMessage,针对不同厂商返回不同的错误分类
  2. 安全守卫resolveContextWindowGuard 检查上下文窗口是否低于安全阈值(CONTEXT_WINDOW_HARD_MIN_TOKENS),防止在极端情况下发起请求
  3. 插件注入:安全插件可以通过钩子在 prompt 构建前插入指令,比如注入 rate limit 提示、安全警告等
  4. Bootstrap 注入bootstrap-budget 管理启动时的上下文注入量,根据剩余预算决定注入多少 bootstrap 文件内容
1OpenClaw 上下文组装流程:
2  
3  ├─ 1. 加载 Session 历史
4       limitHistoryTurns(sessionKey)  根据通道类型限制 turn 
5  
6  ├─ 2. Bootstrap 注入
7       resolveBootstrapContextForRun()  按预算截断
8  
9  ├─ 3. ContextEngine.assemble()
10       可插拔的实现
11       默认: LegacyContextEngine
12       自定义: 用户注册的插件引擎
13  
14  ├─ 4. 插件钩子: before_prompt_build
15       可修改 prompt  messages
16  
17  ├─ 5. 插件钩子: before_agent_start
18       可做最终修改
19  
20  ├─ 6. Context Window Guard
21       evaluateContextWindowGuard()  低于 MIN 则拒绝
22  
23  └─ 7. 发起请求
24       多厂商 API 适配层统一调用
25

核心差异总结:Claude Code 的上下文管理是"深度优化"——针对单一 API 做到极致。OpenClaw 的上下文管理是"广度适配"——用接口抽象让不同厂商和插件都能工作。Claude Code 选择深度是因为 Anthropic 控制整个栈,可以做字节级优化;OpenClaw 选择广度是因为它面对的是碎片化的 AI 生态。

多 Agent 通信的差异

在异步 Agent 通信方面,两者的协调模式有一个共同点:Coordinator/主 Agent 启动子 Agent 后都继续做自己的事,子 Agent 完成后通过某种机制把结果送回给父 Agent。但它们的"turn 结束"语义和通信通道截然不同。

Claude Code:循环不结束,轮询消费

Claude Code 的 Coordinator 调用 Agent tool 启动异步 Worker 后,queryLoop 继续运行——它不会 yield 或暂停。Worker 完成后通过 enqueueAgentNotification() 把 XML 通知推入全局 CommandQueue,Coordinator 在下一轮 turn 的开头通过 getCommandsByMaxPriority() 从队列中取出通知,作为 AttachmentMessage 注入。

团队模式下,队友(Teammate)可能运行在不同进程(tmux pane / iTerm2 pane),通过文件邮箱系统通信:写入 .claude/teams/{team}/inboxes/{agent}.json,Leader 每 1 秒轮询一次。

OpenClaw:session_yield 让出,Gateway 事件唤醒

OpenClaw 的主 Agent 调用 sessions_yield 后,当前 turn 直接结束queueSessionsYieldInterruptMessage() 通过 agent.steer() 注入一个自定义中断消息,persistSessionsYieldContextMessage() 写入 yield 原因到 session 上下文。整个 Agent 进入等待状态,不再消耗资源。

子 Agent(或外部事件)完成后,通过 Gateway 的事件系统(emitAgentEvent)通知父 Agent。Gateway 检测到父 Agent 处于 yield 等待状态,将其唤醒并注入子 Agent 的结果作为下一条消息,触发新一轮 turn。

转存失败,建议直接上传图片文件

通信通道的本质区别

Claude Code 的文件邮箱系统是"同机优先"的设计。它假设所有 Agent 运行在同一台机器上,通过共享文件系统通信就够了。这在实际使用中是合理的——Claude Code 的目标用户是单机开发者,Team 模式最多扩展到几个 tmux pane。文件系统通信的好处是零依赖:不需要启动额外的服务,不需要网络连接,开箱即用。

OpenClaw 的 Gateway 是"分布式优先"的设计。Gateway 是一个独立的 WebSocket 服务,所有 Agent 实例(无论是同进程、同机不同进程、还是远程机器)都通过 Gateway 通信。这使得 OpenClaw 可以部署在云服务器上,Agent 可以分布在不同的节点。代价是必须维护 Gateway 基础设施,增加了系统复杂度。

相同点

两者在异步 Agent 的核心思想上是一致的:父 Agent 启动子 Agent 后不阻塞等待,子 Agent 完成后通过某种通知机制把结果送回。这都体现了"非阻塞优先"的设计理念。两者的 CommandQueue 和 Gateway 事件总线在功能上是等价的——都是解耦生产者和消费者的消息通道。

不同点的根源

这些差异都可以追溯到产品定位。Claude Code 是一个精心打磨的单机编程工具,Anthropic 控制整个技术栈,可以做深度优化。OpenClaw 是一个通用的 Agent 平台,需要适配多厂商、多通道、多部署场景,必须在灵活性和深度优化之间做取舍。Claude Code 选择了深度,OpenClaw 选择了广度。两者没有对错之分,只是各自服务于不同的用户群体和使用场景。

总结

Claude Code 的架构设计中,有几个贯穿始终的设计原则:

非阻塞优先。Memory 预取、Skill 发现、工具执行总结都是异步启动、按需消费的模式。主流程绝不等待可能没有结果的后台操作。97% 的 skill 搜索命中率意味着阻塞式搜索几乎总是浪费时间。

精细化控制。从 tool result 的三类分区(mustReapply/frozen/fresh),到 memory 文件的 4KB 截断和已读过滤,到 60KB 的累计熔断,到 fork 子 Agent 的字节级 cache 对齐,每个环节都在精打细算 token 和 cache 的使用。

Agent 间通信解耦。Coordinator 模式的 XML 通知、Fork 模式的继承上下文、Swarm 模式的文件邮箱,都保持了 Agent 间的松耦合。每个 Agent 不需要知道对方是谁,只需要按照协议读写。文件邮箱系统通过 JSON 类型区分、文件锁序列化、轮询投递,实现了跨进程的可靠消息传递。

一些个人见解-关于Claude Code的“遥遥领先“

Claude Code的代码泄漏让我们得以窥见其内部运作的精妙,但这并不意味着它就是AI编程领域的唯一真理。Claude Code的设计优秀,更多体现的是Anthropic在特定技术路径上的深耕,而非AI编程助手的终极形态。

这恰恰是我们在评估AI工具时最需要警惕的认知陷阱。今天也有不少人因为看到Claude Code使用体验不错,Agent设计又用优秀之处,就对 Claude Code 大吹特吹,甚至贬低其他的 AI Coding 工具。但实际上也没有几个人会去分析它的代码和设计,也不会同时深度使用多个 AI Coding 工具。就像几年前新能源汽车爆发时,很多人不认识几个汽车零件,就盲目吹捧某个品牌"天下第一"一样。

真正的智慧在于理解差异的本质。Prompt工程的差异决定了AI如何理解任务,Agent架构的差异决定了AI如何执行任务,而模型差异只是底层的能力支撑。对于开发者而言,与其纠结于哪个工具"更好",不如从原理出发,理解不同设计选择背后的权衡,然后在实际使用中验证这些权衡是否适合自己的需求。

多用、多总结、多学习,这才是AI时代技术人的正确打开方式。无论是Claude Code的精工细作,还是OpenClaw的宏大架构,都值得我们深入研究和实践。因为最终,定义我们生产力的不是某个工具,而是我们驾驭工具的能力。


从源码泄露看AI Agent未来:深度对比Claude Code原生实现与OpenClaw开源方案》 是转载文章,点击查看原文


相关推荐


OpenClaw 接入 Telegram:BotFather 实战
七夜zippoe2026/3/31

目录 摘要1. 引言2. Telegram Bot API 介绍2.1 什么是 Telegram Bot API2.2 Bot 与普通用户的区别2.2 Bot 的核心特性2.3 API 通信模式2.4 消息类型与格式2.5 API 请求示例 3. 通过 BotFather 创建机器人3.1 BotFather 简介3.2 创建 Bot 的详细步骤3.3 Bot 配置选项3.4 配置命令示例3.5 Bot 头像与品牌设置3.6 多语言支持 4. 获取 Bot Token 与安全实践4


Agent Skills:让 AI 一次学会、永远记住的能力扩展方案
草捏子2026/3/23

导语 程序员阿明最近发现一个让他崩溃的事——他的 AI 助手明明昨天才学会怎么写周报,今天换个对话窗口又全忘了。"这 AI 跟金鱼一样,7 秒钟记忆。"他跟同事吐槽。直到同事给他发了一个叫 Agent Skills 的东西,从此阿明再也没有复制粘贴过那段周报格式说明。 Agent Skills 到底是什么?它解决了什么痛点?怎么用?今天我们彻底搞明白。 1. 从"金鱼记忆"到"活的员工手册" 先讲阿明的故事。他每次让 AI 写周报,都要先花十分钟描述格式:分"本周完成""进行中""下周计划"三个


微信小程序开发01:XR-FRAME的快速上手
海石2026/3/15

一、前言 最近要基于微信小程序实现一个具备AR功能的APP,在进行技术选型时,发现小程序本身自带了XR-FRAME这个框架, 从描述上来看: 没有比它更“合适”的,用来进行AR功能开发的框架了 本来想使用 Vibe Coding 无痛完成开发,但是却在实际使用中,发现大模型写不太来 wxml 和<xr-...>相关的代码 于是在此开了一个系列文章,用来记录我遇到的坑 😓 二、从 1 到 1.x 个人的建议,一开始不从0到1,而是从1到1.x,即基于现有的demo二次开发一个 否则,如果想在


ubuntu + Docker + piper + 实现TTS自由
Android小码家2026/3/6

文章目录 前言启动脚本启动容器模型下载使用方式 前言 为什么要使用这种框架,原因很简单,分离环境和工作区间,因为我不可能只跑一个应用,因此docker就是最好的选择。 背景是实现文字转语音的简单AI功能,实现转化自由,为什么叫ai因为它集成了hugeface的语音ai模型。 启动脚本 # 使用 Ubuntu 22.04 LTS(你指定的版本) FROM ubuntu:22.04 ENV DEBIAN_FRONTEND=noninteractive # 安


AI 原生应用开源开发者沙龙·深圳站精彩回顾 & PPT下载
阿里云云原生2026/2/26

作者:盈楹 近日,AI 原生应用开源开发者沙龙·深圳站圆满落幕。本场活动吸引了 140+ 名技术从业者深度参与,聚焦 AI 原生应用架构领域的开源技术与落地实践, 围绕 AgentScope、RocketMQ、HiMarket、Higress、LoongSuite、Agent 技术实践等议题展开深度分享,并设置了动手实操环节。 关注「阿里云云原生」公众号,后台回复:0210 免费获得深圳站讲师 PPT 合辑 精彩回顾 议题一:AgentScope:迈向 Agentic 智能体应用丨高大伟(大玮)


TypeScript 类型体操练习笔记(二)
我不吃饼干2026/2/18

进度(90 /188) 其中标记 ※ 的是我认为比较难或者涉及新知识点的题目 刷题也许没有什么意义,但是喜欢一个人思考一整天的灵光一现,也喜欢看到新奇的答案时的恍然大悟,仅此而已。 42. Medium - 1130 - ReplaceKeys ※ 实现一个类型 ReplaceKeys,用于替换联合类型中的键,如果某个类型不包含该键则跳过替换。该类型接受三个参数。 一开始我只是想这么写,我想分布式条件类型 + Pick + Omit 来实现。 type ReplaceKeys<U, T, Y>


【Kubernetes专项】K8s 配置管理中心 ConfigMap 实现微服务配置管理
.Kaser.2026/2/9

十六、K8s 配置管理中心 ConfigMap 实现微服务配置管理 16.1 ConfigMap 相关概念及cm字段 16.1.1 ConfigMap 概述 ​ Configmap 是 k8s 中的资源对象,用于保存非机密性的配置的,数据可以用 key/value键值对 的形式保存,也可通过 文件 的形式保存。 Configmap 是 k8s 中的资源, 相当于配置文件,可以有一个或者多个 Configmap;Configmap 可以做成 Volume,k8s pod 启动之后,通过 volu


VScode引入claude+deepseek
何亚告2026/1/31

最近由于项目需求以及效率需要,在vscode引入claude进行代码整理,现将引入过程记录,将相关踩坑问题复盘: 1. 安装CC-Switch ccSwitch(CC-Switch)是基于 Rust+Tauri 开发的跨平台桌面应用,核心作用是一键管理与切换 Claude Code、Codex、Gemini CLI 等 AI 编程工具的 API 配置,替代手动修改 JSON / 环境变量,大幅提升配置效率。以下是核心功能与价值 安装包下载地址:https://github.com


多标签页强提醒不重复打扰:从“弹框轰炸”到“共享待处理队列”的实战
_Jude2026/1/22

场景:我在多标签页里“接力”处理紧急待办 这篇文章讨论的不是“消息列表怎么做”,而是紧急待办的强提醒体验应该如何落地。我的核心需求很明确: 紧急消息必须强制弹框提醒(不能靠用户自己去小铃铛里找) 弹框不能手动关闭,只能通过“去处理/已读”等业务动作逐条消解 刷新后仍要继续弹:只要还有“高优先级且未处理”的消息,就必须再次弹框 多标签页不重复打扰:同一时间只允许一个标签页弹;未处理的消息能跨标签页接力,不丢失 ✅ 问题 1:多标签页重复强弹(“弹框轰炸”)💥 现象 A 中点“去处理”打开


10分钟复刻爆火「死了么」App:vibe coding 实战(Expo+Supabase+MCP)
mCell2026/1/14

视频链接:10分钟复刻爆火「死了么」App:vibe coding 实战 仓库地址:github.com/minorcell/s… 最近“死了么”App 突然爆火:内容极简——签到 + 把紧急联系人邮箱填进去。 它的产品形态很轻,但闭环很完整: 你每天打卡即可;如果你连续两天没打,系统就给紧急联系人发邮件。 恰好我最近在做 Supabase 相关调研,就顺手把它当成一次“极限验证”: 我想看看:Expo + Supabase 能不能把后端彻底“抹掉” 我也想看看:Codex + MCP 能

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客