<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>OpenClaw on Last Stand</title><link>https://amatsuzero.github.io/LastStand/tags/openclaw/</link><description>Recent content in OpenClaw on Last Stand</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Sat, 02 May 2026 22:32:27 +0800</lastBuildDate><atom:link href="https://amatsuzero.github.io/LastStand/tags/openclaw/index.xml" rel="self" type="application/rss+xml"/><item><title>OpenClaw 源码导读（三）：Agent Harness — 为"每一家 LLM 都能兜住"而生的执行管线</title><link>https://amatsuzero.github.io/LastStand/posts/interview/ai/openclaw-agent-harness/</link><pubDate>Sat, 02 May 2026 22:32:27 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/interview/ai/openclaw-agent-harness/</guid><description>&lt;blockquote&gt;
&lt;p&gt;系列第一篇把 OpenClaw 的架构地图摊开，第二篇深入 Gateway 控制平面。这一篇，我们钻进整个项目的心脏——&lt;code&gt;src/agents/&lt;/code&gt;，看 OpenClaw 怎么处理最复杂的一件事：&lt;strong&gt;当 Gateway 接到一条用户消息之后，怎么把它变成一轮真正的 LLM turn + Tool 循环&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这一层代码的体量和复杂度非常惊人：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;src/agents/pi-embedded-runner/run.ts&lt;/code&gt; 单文件 &lt;strong&gt;2160 行&lt;/strong&gt;，是整个 agent 循环的驱动器。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src/agents/pi-embedded-runner/compact.ts&lt;/code&gt; &lt;strong&gt;1148 行&lt;/strong&gt;，专门处理上下文压缩。&lt;/li&gt;
&lt;li&gt;整个 &lt;code&gt;src/agents/&lt;/code&gt; 目录有 &lt;strong&gt;830+ 个文件&lt;/strong&gt;，光 &lt;code&gt;anthropic-*.ts&lt;/code&gt; 就有近 20 个（为了 Messages API 的各种 edge case）。&lt;/li&gt;
&lt;li&gt;同时支持 &lt;strong&gt;Anthropic、OpenAI、Google Gemini、Bedrock、Vertex、OpenRouter、Z.AI/GLM、MiniMax、Qwen、Ollama、Kilocode&lt;/strong&gt; 等十多个 provider。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但这些&amp;quot;量&amp;quot;的背后，是一个&lt;strong&gt;只有一个接口的核心抽象：AgentHarness&lt;/strong&gt;。本文就从这个抽象出发，一路看到 2160 行循环如何处置真实世界里十几类失败。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="一harness-抽象只有一个-runattempt"&gt;一、Harness 抽象：只有一个 &lt;code&gt;runAttempt&lt;/code&gt;&lt;/h2&gt;
&lt;h3 id="1-为什么不用-langchain-风格的agent-framework"&gt;1. 为什么不用 LangChain 风格的&amp;quot;Agent Framework&amp;quot;&lt;/h3&gt;
&lt;p&gt;读过 LangChain 或 CrewAI 的人第一眼会期望看到 &lt;code&gt;AgentExecutor&lt;/code&gt;、&lt;code&gt;AgentChain&lt;/code&gt;、&lt;code&gt;Memory&lt;/code&gt;、&lt;code&gt;Callback&lt;/code&gt;、&lt;code&gt;Tool&lt;/code&gt; 这些类。OpenClaw 里&lt;strong&gt;一个都没有&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;它的选择是：&lt;strong&gt;把整个 agent 循环当成一个黑盒&lt;/strong&gt;，对外只暴露一个接口：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-30:39:src/agents/harness/types.ts" data-lang="30:39:src/agents/harness/types.ts"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;type&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AgentHarness&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;label&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;pluginId?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;supports&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;ctx&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;AgentHarnessSupportContext&lt;/span&gt;)&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;AgentHarnessSupport&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;runAttempt&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;params&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;AgentHarnessAttemptParams&lt;/span&gt;)&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Promise&lt;/span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;AgentHarnessAttemptResult&lt;/span&gt;&amp;gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;compact&lt;/span&gt;&lt;span style="color:#f92672"&gt;?&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;params&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;AgentHarnessCompactParams&lt;/span&gt;)&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Promise&lt;/span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;AgentHarnessCompactResult&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;undefined&lt;/span&gt;&amp;gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;reset&lt;/span&gt;&lt;span style="color:#f92672"&gt;?&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;params&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;AgentHarnessResetParams&lt;/span&gt;)&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Promise&lt;/span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;void&lt;/span&gt;&amp;gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;dispose&lt;/span&gt;&lt;span style="color:#f92672"&gt;?&lt;/span&gt;()&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Promise&lt;/span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;void&lt;/span&gt;&amp;gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;五个方法，其中三个可选：&lt;/p&gt;</description></item><item><title>OpenClaw 源码导读（四）：Channels、Nodes 与扩展生态</title><link>https://amatsuzero.github.io/LastStand/posts/interview/ai/openclaw-channels%E4%B8%8E%E6%89%A9%E5%B1%95%E7%94%9F%E6%80%81/</link><pubDate>Sat, 02 May 2026 22:32:27 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/interview/ai/openclaw-channels%E4%B8%8E%E6%89%A9%E5%B1%95%E7%94%9F%E6%80%81/</guid><description>&lt;blockquote&gt;
&lt;p&gt;前三篇我们看清了 OpenClaw 的三副骨架：架构全景、Gateway 控制平面、Agent Harness 执行管线。这一篇是压轴篇，我们看它的&amp;quot;血肉&amp;quot;——&lt;strong&gt;&lt;code&gt;channels/&lt;/code&gt;、&lt;code&gt;node-host/&lt;/code&gt;、&lt;code&gt;canvas-host/&lt;/code&gt;、&lt;code&gt;plugins/&lt;/code&gt;、&lt;code&gt;skills/&lt;/code&gt;、&lt;code&gt;sandbox/&lt;/code&gt;&lt;/strong&gt;。OpenClaw 真正&amp;quot;好用&amp;quot;的感觉，是从这一层来的：一条消息从 WhatsApp 进来、触发 iOS 上的摄像头、在 Android 上渲染一张 Canvas、最后把结果发回 Slack 线程——整个过程中 Gateway 本身一行代码都不需要改。&lt;/p&gt;
&lt;p&gt;本文涵盖 &lt;strong&gt;118 个 channel/provider extension、53 个 skill、iOS/macOS/Android node 客户端、A2UI Canvas 协议、以及 Docker sandbox 的整套隔离模型&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="一channels118-个-extension-是怎么插进来的"&gt;一、Channels：118 个 extension 是怎么&amp;quot;插&amp;quot;进来的&lt;/h2&gt;
&lt;h3 id="1-channelplugin-接口"&gt;1. ChannelPlugin 接口&lt;/h3&gt;
&lt;p&gt;OpenClaw 所有 channel（微信、Telegram、Slack、iMessage、IRC、Discord……）本质上都是实现同一个接口 &lt;code&gt;ChannelPlugin&lt;/code&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-53:96:src/channels/plugins/types.plugin.ts" data-lang="53:96:src/channels/plugins/types.plugin.ts"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;export&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;type&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ChannelPlugin&lt;/span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;ResolvedAccount&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;any&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Probe &lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;unknown&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Audit &lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;unknown&lt;/span&gt;&amp;gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelId&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;meta&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelMeta&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;capabilities&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelCapabilities&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;defaults&lt;/span&gt;&lt;span style="color:#f92672"&gt;?:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;queue&lt;/span&gt;&lt;span style="color:#f92672"&gt;?:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;debounceMs?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;number&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;reload&lt;/span&gt;&lt;span style="color:#f92672"&gt;?:&lt;/span&gt; { &lt;span style="color:#a6e22e"&gt;configPrefixes&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;[]; &lt;span style="color:#a6e22e"&gt;noopPrefixes?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;[] };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;setupWizard?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelPluginSetupWizard&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;config&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelConfigAdapter&lt;/span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;ResolvedAccount&lt;/span&gt;&amp;gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;configSchema?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelConfigSchema&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;setup?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelSetupAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;pairing?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelPairingAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;security?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelSecurityAdapter&lt;/span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;ResolvedAccount&lt;/span&gt;&amp;gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;groups?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelGroupAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;mentions?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelMentionAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;outbound?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelOutboundAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;status?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelStatusAdapter&lt;/span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;ResolvedAccount&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Probe&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Audit&lt;/span&gt;&amp;gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;gatewayMethods?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;[];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;gateway?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelGatewayAdapter&lt;/span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;ResolvedAccount&lt;/span&gt;&amp;gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;auth?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelAuthAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;approvalCapability?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelApprovalCapability&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;elevated?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelElevatedAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;commands?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelCommandAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;lifecycle?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelLifecycleAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;secrets?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelSecretsAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;allowlist?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelAllowlistAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;doctor?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelDoctorAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;bindings?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelConfiguredBindingProvider&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;conversationBindings?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelConversationBindingSupport&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;streaming?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelStreamingAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;threading?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelThreadingAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;messaging?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelMessagingAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;agentPrompt?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelAgentPromptAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;directory?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelDirectoryAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;resolver?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelResolverAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;actions?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelMessageActionAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;heartbeat?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelHeartbeatAdapter&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;agentTools?&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;ChannelAgentToolFactory&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ChannelAgentTool&lt;/span&gt;[];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个接口一共有 &lt;strong&gt;40 多个可选的 adapter&lt;/strong&gt;，每个对应一种能力：&lt;/p&gt;</description></item><item><title>OpenClaw 源码导读（二）：Gateway 控制平面 — 一条 WebSocket 连上所有人</title><link>https://amatsuzero.github.io/LastStand/posts/interview/ai/openclaw-gateway%E6%8E%A7%E5%88%B6%E5%B9%B3%E9%9D%A2/</link><pubDate>Sat, 02 May 2026 22:32:27 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/interview/ai/openclaw-gateway%E6%8E%A7%E5%88%B6%E5%B9%B3%E9%9D%A2/</guid><description>&lt;blockquote&gt;
&lt;p&gt;在系列第一篇里我们看到，OpenClaw 的核心隐喻是 &lt;strong&gt;&amp;ldquo;Gateway 是控制平面&amp;rdquo;&lt;/strong&gt;。它不存转储消息、不排队、不持久化事件流，而是一个长在 127.0.0.1:18789 上的&lt;strong&gt;状态化 RPC 服务器&lt;/strong&gt;，所有客户端（CLI、macOS.app、iOS/Android Node、WebChat 浏览器、Canvas iframe）都靠一条 WebSocket 接入。&lt;/p&gt;
&lt;p&gt;这一篇拆开控制平面的五个层面：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;进程入口&lt;/strong&gt;：CLI 怎么从 &lt;code&gt;openclaw gateway&lt;/code&gt; 走到 Node 进程常驻&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WebSocket 协议&lt;/strong&gt;：握手帧、RPC 帧、Event 帧、状态快照与版本号&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;鉴权矩阵&lt;/strong&gt;：Token / Password / Device Token / Bootstrap Token / Tailscale Whois / Trusted Proxy&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pairing&lt;/strong&gt;：陌生客户端怎么合法加入（setup code、pairing store、allowFrom）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Session 模型&lt;/strong&gt;：Session Key 组成规则、多 Agent 路由、Session ID 模糊匹配&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;注：下文所有 &lt;code&gt;src/&lt;/code&gt; 路径都指 &lt;a href="https://github.com/openclaw/openclaw"&gt;openclaw/openclaw&lt;/a&gt; 仓库根下。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="一cli-到-gateway-的六段路径"&gt;一、CLI 到 Gateway 的六段路径&lt;/h2&gt;
&lt;p&gt;第一篇概览过 &lt;code&gt;src/entry.ts&lt;/code&gt; 和 &lt;code&gt;src/cli/run-main.ts&lt;/code&gt;，这里按调用序重新画一遍，把每个关键函数在 graph 上标出来：&lt;/p&gt;
&lt;pre class="mermaid"&gt;flowchart TD
A0["openclaw gateway --port 18789"] --&gt; A1
A1["entry.ts&lt;br/&gt;main guard + process.title"] --&gt; A2
A2["normalizeEnv + enableCompileCache()"] --&gt; A3
A3["ensureCliRespawnReady()&lt;br/&gt;按需 spawn 子 Node"] --&gt; A4
A4["parseCliContainerArgs&lt;br/&gt;parseCliProfileArgs"] --&gt; A5
A5["tryHandleRootVersionFastPath&lt;br/&gt;tryHandleRootHelpFastPath"] --&gt;|miss| A6
A6["runMainOrRootHelp → runCli(argv)"] --&gt; B0
B0["run-main.ts&lt;br/&gt;container? container in progress"] --&gt; B1
B1["loadCliDotEnv (~/.openclaw/.env)"] --&gt; B2
B2["assertSupportedRuntime (Node≥22.16)"] --&gt; B3
B3["tryRouteCli（fast-path 子命令路由）"] --&gt;|miss| B4
B4["buildProgram() + commander 注册"] --&gt; B5
B5["installUnhandledRejectionHandler"] --&gt; B6
B6["registerPluginCliCommandsFromValidatedConfig (lazy)"] --&gt; B7
B7["program.parseAsync"] --&gt; C0
C0["gateway 命令匹配 → gatewayCommandAction"] --&gt; C1
C1["lock 文件 + 端口 acquire"] --&gt; C2
C2["HTTP/WS server 启动"] --&gt; C3
C3["channels/plugins/cron/canvas-host 启动"] --&gt; C4["Ready"]
&lt;/pre&gt;
&lt;h3 id="1-为什么要自己-spawn-自己"&gt;1. 为什么要自己 &lt;code&gt;spawn&lt;/code&gt; 自己&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ensureCliRespawnReady()&lt;/code&gt; 是 &lt;code&gt;src/entry.ts&lt;/code&gt; 里最容易被忽视但非常重要的段落：&lt;/p&gt;</description></item><item><title>OpenClaw 源码导读（一）：架构总览 — 为"单个主人"而设计的 AI Gateway</title><link>https://amatsuzero.github.io/LastStand/posts/interview/ai/openclaw-%E6%9E%B6%E6%9E%84%E6%80%BB%E8%A7%88/</link><pubDate>Sat, 02 May 2026 22:32:27 +0800</pubDate><guid>https://amatsuzero.github.io/LastStand/posts/interview/ai/openclaw-%E6%9E%B6%E6%9E%84%E6%80%BB%E8%A7%88/</guid><description>&lt;blockquote&gt;
&lt;p&gt;2025 年 11 月，&lt;a href="https://steipete.me"&gt;Peter Steinberger&lt;/a&gt;（PSPDFKit 创始人、前著名 iOS 社区人物）把自己折腾出来的个人 AI 助手 Molty 开源为 &lt;strong&gt;OpenClaw&lt;/strong&gt;。短短几个月，仓库吃到 35 万+ star、93 个 release、360 多个 contributor，核心代码量在 TypeScript/Swift/Kotlin/Go/Python 之间横跨 40 多万行。它和 Claude Code、Codex CLI 乍看都是&amp;quot;跑在本地的 CLI Agent&amp;quot;，但设计哲学完全不同——Claude Code 是一个&lt;strong&gt;编程助手&lt;/strong&gt;，OpenClaw 是一个长在你设备上的&lt;strong&gt;私人秘书&lt;/strong&gt;：它挂在 WhatsApp/Telegram/iMessage/微信 的 IM 客户端后面，能自己发消息、自己开浏览器、自己调用 iOS/Android 上的摄像头和麦克风。&lt;/p&gt;
&lt;p&gt;本文是 OpenClaw 源码导读系列的第一篇，目标是把整个项目的&amp;quot;地图&amp;quot;摊开。先讲清楚它要解决什么问题、在怎样的信任模型下运行，再把仓库 100 多个顶层模块拎出来分类，最后给出后续系列文章的导航。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="一它到底是什么"&gt;一、它到底是什么&lt;/h2&gt;
&lt;h3 id="1-一句话定义"&gt;1. 一句话定义&lt;/h3&gt;
&lt;p&gt;OpenClaw 官方给自己的定位是 &lt;em&gt;&amp;ldquo;Personal AI Assistant. Any OS. Any Platform. The lobster way.&amp;rdquo;&lt;/em&gt; 翻译过来就是：&lt;strong&gt;一个在你自己设备上运行、从你自己现有的聊天工具里和你对话的个人 AI 助手&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这个定位里藏着三个关键差异：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Personal&lt;/strong&gt;：它不是多租户 SaaS，也不是团队协作工具，而是&lt;strong&gt;单一主人&lt;/strong&gt;的私人助手。整个信任模型就是为&amp;quot;只有一个 operator&amp;quot;优化的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Any OS / Any Platform&lt;/strong&gt;：Gateway 是 Node 进程，可以跑在 macOS/Linux/Windows(WSL2)、甚至 Fly.io/Docker/NAS 上；客户端包含 iOS/Android/macOS 原生 App，还有 Web Dashboard。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The lobster way&lt;/strong&gt;：作者把它拟人化成一只太空龙虾 Molty，这是一个品牌/吉祥物层面的设计，但也反映了项目的&amp;quot;玩心&amp;quot;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-和-claude-code-对比两个相似却完全不同的-cli-agent"&gt;2. 和 Claude Code 对比：两个相似却完全不同的 CLI Agent&lt;/h3&gt;
&lt;p&gt;由于作者 Steipete 本身是 Claude Code 的重度用户和 Anthropic 的合作者，社区最常问的问题就是&amp;quot;和 Claude Code 有啥区别&amp;quot;。这里先画一张对比表：&lt;/p&gt;</description></item></channel></rss>