我很喜歡把 AI Agent 想成一個人。
不是因為它真的有情緒,而是因為這樣比較好理解它到底在幹嘛。你如果把它當成一個會回答問題的黑盒子,很多行為會看不懂;但如果你把它當成一個有腦袋、有工作區、有記憶、還有執行邊界的角色,你就會慢慢看出它的設計邏輯。
假設今天你第一次走進一間新公司。
你不會先問印表機在哪裡,你會先問:
OpenClaw 也是一樣。
第 1 天我們知道它不是單純聊天,而是會工作、會分派、會留下紀錄的系統。那第 2 天就要往前一步,看看這個系統的腦袋到底怎麼切層。
main 不是唯一主角?如果把 OpenClaw 的整體架構縮成一句話,我會這樣說:
它是一個把「工作怎麼分工」翻成「AI 怎麼跑」的系統。
這句話不是口號,因為從 source code 看,它真的把責任拆得很明確。
一個 agent 不是只有模型而已,它還有:
這些東西合起來,才是一個完整的 agent。
先看 runtime 入口怎麼把這些東西組起來。agentCommandInternal 會先準備執行環境,再進入 session admission,接著才處理 skills、模型、與送達。
📄 原始碼:
src/agents/agent-command.ts:955-1001
async function agentCommandInternal(initialOpts, runtime = defaultRuntime, deps) {
const resolvedDeps = await resolveAgentCommandDeps(deps);
const isRawModelRun = initialOpts.modelRun === true || initialOpts.promptMode === "none";
const suppressVisibleSessionEffects = initialOpts.sessionEffects === "internal";
const preserveUserFacingSessionModelState = initialOpts.preserveUserFacingSessionModelState === true;
const prepared = await prepareAgentCommandExecution(initialOpts, runtime);
const lifecycleAbortController = new AbortController();
const opts = {
...prepared.opts,
abortSignal: prepared.opts.abortSignal ? AbortSignal.any([prepared.opts.abortSignal, lifecycleAbortController.signal]) : lifecycleAbortController.signal
};
const { body, transcriptBody, cfg, configuredThinkingCatalog, normalizedSpawned, agentCfg, thinkOverride, thinkOnce, verboseOverride, timeoutMs, runTimeoutOverrideMs, sessionId, sessionKey, sessionStore, storePath, isNewSession, persistedThinking, persistedVerbose, sessionAgentId, outboundSession, workspaceDir, cwd, agentDir, runId, isSubagentLane, acpManager, acpResolution, pluginsEnabled, manifestMetadataSnapshot, modelManifestContext } = prepared;
let lifecycleGeneration = opts.lifecycleGeneration ?? captureAgentRunLifecycleGeneration(runId);
...
}
這段一開始就告訴你:OpenClaw 的 agent 不是只有一個模型呼叫。
它先準備一整個執行包,裡面有:
workspaceDir
agentDir
sessionStore
storePath
sessionKey
sessionId
agentCfg
cfg
也就是說,這不是「模型要不要回答」而已,而是「這次 run 的整個生態系要怎麼組」。
再往下看,它會先把 skills snapshot 拉出來,然後再決定要用哪些能力。
📄 原始碼:
src/agents/agent-command.ts:1400-1428
const skillFilter = resolveEffectiveAgentSkillFilter(cfg, sessionAgentId);
const currentSkillsSnapshot = sessionEntry?.skillsSnapshot;
const [{ getRemoteSkillEligibility, resolveReusableWorkspaceSkillSnapshot }, { canExecRequestNode }] = await Promise.all([loadSkillsRuntime(), loadExecDefaultsRuntime()]);
const skillSnapshotState = resolveReusableWorkspaceSkillSnapshot({
workspaceDir,
config: cfg,
agentId: sessionAgentId,
existingSnapshot: isNewSession ? void 0 : currentSkillsSnapshot,
skillFilter,
eligibility: { remote: getRemoteSkillEligibility({ advertiseExecNode: canExecRequestNode({
cfg,
sessionEntry,
sessionKey,
agentId: sessionAgentId
}) }) },
watch: false
});
const needsSkillsSnapshot = isNewSession || !currentSkillsSnapshot || skillSnapshotState.shouldRefresh;
const skillsSnapshot = skillSnapshotState.snapshot;
if (skillsSnapshot && sessionStore && sessionKey && needsSkillsSnapshot && !suppressVisibleSessionEffects) {
...
}
這段很關鍵。
因為它證明 OpenClaw 的技能不是臨時查表而已,而是會形成一份可重用的 snapshot,並且跟 session 綁在一起。
這代表:
再看模型與可見性控制。
📄 原始碼:
src/agents/agent-command.ts:1502-1564
const configuredDefaultRef = resolveDefaultModelForAgent({
cfg,
agentId: sessionAgentId,
allowPluginNormalization: pluginsEnabled,
...modelManifestContext
});
const runContext = resolveAgentRunContext(opts);
const { provider: defaultProvider, model: defaultModel } = normalizeAgentCommandDefaultModelRef(cfg, configuredDefaultRef.provider, configuredDefaultRef.model, modelManifestContext);
let provider = defaultProvider;
let model = defaultModel;
const hasAllowlist = agentCfg?.models && Object.keys(agentCfg.models).length > 0;
let visibilityPolicy = createModelVisibilityPolicy({
cfg,
catalog: [],
defaultProvider,
defaultModel,
allowManifestNormalization: true,
allowPluginNormalization: pluginsEnabled,
...modelManifestContext
});
if (hasAllowlist) {
modelCatalog = pluginsEnabled ? loadManifestModelCatalog({
config: cfg,
workspaceDir
}) : [];
visibilityPolicy = createModelVisibilityPolicy({
cfg,
catalog: modelCatalog,
defaultProvider,
defaultModel,
agentId: sessionAgentId,
allowManifestNormalization: true,
allowPluginNormalization: pluginsEnabled,
...modelManifestContext
});
}
這段在告訴你:OpenClaw 每個 agent 看到的模型集合不一定一樣。
它會根據 agent config、workspace、manifest、plugin normalization 來決定:
這就是為什麼它是 multi-agent 系統,而不是單一 prompt 包裝器。
我常常把這一層想成一間公司裡的「部門制度」。
每個 agent 都有自己的 workspace。
這很像每個部門都有自己的辦公室、文件櫃、SOP、筆記本。你不會期待財務和研發共用同一個抽屜還完全不出事。
OpenClaw 把 workspace 當成 agent 的基本工作地盤,這樣每個角色的背景資訊、備註、bootstrap files 才能真的分開。
session 不是只記聊天內容,它還記狀態。
這包括:
所以 session 不是訊息容器而已,而是工作狀態容器。
不是所有工具每次都現查。
OpenClaw 會把可用 skills 先整理成 snapshot,這樣 agent 在執行時知道自己能用哪些工具。這很像每個部門的工具箱不同,而且工具箱內容會被版本化。
不是所有 agent 都能看到所有模型。
有些模型被 allowlist 限制,有些可以靠 manifest 或 plugin normalization 進來,有些只能在特定情境可見。
這種設計的本質不是麻煩,而是避免 agent 一時失控把不該用的模型也用掉。
workspaceDir、sessionStore、agentDir、skillsSnapshot 這些東西要一起理解但這就是成熟系統的樣子。
它不是只有一個黑盒,而是每一層都有責任。
agentCommandInternal 先準備執行環境,再處理 session 與技能第 3 天我會接著看第一個任務是怎麼被接進來的。
因為把腦袋分層看懂之後,下一個問題就會變成:
入口進來的訊息,到底是怎麼落到真正的 session 與 run 上的?
那就是下一篇要看的重點。