一個系統最容易被誤解的地方,通常不是它怎麼做事,而是它怎麼「開始做事」。
你想像一個新人第一天進公司。
他不是一進門就跑去改程式、寄信、開會,而是先去櫃檯報到,確認今天有人找他做什麼,然後才知道要把事情送去哪個部門。AI Agent 也一樣。
如果你直接把需求丟給它,它不應該只是硬生生回你一句答案。真正有系統的做法,應該是先接住需求,再判斷這件事要不要拆、要不要查記憶、要不要進 session、要不要走固定流程。
所以第 3 天,我想先看最前面的那一關。
不是工具,不是記憶,不是子代理。
而是它怎麼把第一個任務接進來。
agentCommandFromIngress 在做什麼?allowModelOverride?agentCommandInternal 怎麼把訊息變成 session run?如果把 OpenClaw 的任務流畫成一條線,入口這一段大概長這樣:
外部入口 / CLI / Gateway
-> agentCommandFromIngress
-> agentCommandInternal
-> prepareAgentCommandExecution
-> session admission
-> skills / model / delivery
這裡最重要的不是「它會不會做」,而是「它先確認再做」。
因為一個成熟的 Agent 系統,不會把所有事情都塞給同一個角色硬幹。它會先想清楚這件事屬於哪一類,然後再把工作往下送。
先看 ingress 的門口。agentCommandFromIngress 不會直接跳進執行,它會先檢查必要條件,再把控制權交給 internal runner。
📄 原始碼:
src/agents/agent-command.ts:3004-3004
async function agentCommandFromIngress(opts, runtime = defaultRuntime, deps) {
if (typeof opts.allowModelOverride !== "boolean") throw new Error("allowModelOverride must be explicitly set for ingress agent runs.");
const lifecycleGeneration = opts.lifecycleGeneration ?? captureAgentRunLifecycleGeneration(opts.runId ?? "");
return await withAgentRunLifecycleGeneration(lifecycleGeneration, async () => {
const result = await agentCommandInternal({
...opts,
lifecycleGeneration,
senderIsOwner: opts.senderIsOwner === true
}, runtime, deps);
if (result) emitIngressModelUsageDiagnostic(result, opts);
return result;
});
}
這段其實很誠實。
它先做了一件很重要的事:確認 allowModelOverride 有沒有明確傳進來。
這表示 ingress 不是「隨便什麼人進來都可以改模型」,而是有明確 gate 的。
再往下看,真正的入口邏輯都落在 agentCommandInternal。
📄 原始碼:
src/agents/agent-command.ts:955-961
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;
...
}
這段一開始就把所有關鍵上下文拿出來:
body / transcriptBody
cfg
sessionId / sessionKey
sessionStore / storePath
workspaceDir / agentDir
agentCfg
runId
也就是說,OpenClaw 接到第一個任務時,不是先想答案,而是先把任務環境組完。
再往下看,session admission 會先確認工作能不能開始。
📄 原始碼:
src/agents/agent-command.ts:1042-1074
const sessionWorkAdmission = await beginSessionWorkAdmission({
scope: storePath ?? `agent:${sessionAgentId}`,
identities: [sessionKey, sessionId],
signal: opts.abortSignal,
onInterrupt: () => lifecycleAbortController.abort(createAgentRunRestartAbortError()),
assertAllowed: () => {
const currentEntry = sessionStoreRuntime && storePath && sessionKey ? sessionStoreRuntime.loadSessionEntry({
storePath,
sessionKey,
readConsistency: "latest"
}) : sessionEntry;
if (!currentEntry && preparedSessionId) throw new Error(`Session "${sessionKey ?? sessionId}" changed while starting work. Retry.`);
const matchesIntentionalRollover = isNewSession && currentEntry?.sessionId === preparedSessionId;
if (currentEntry && currentEntry.sessionId !== sessionId && !matchesIntentionalRollover) throw new Error(`Session "${sessionKey ?? sessionId}" changed while starting work. Retry.`);
const archivedSessionError = resolveSessionWorkStartError(sessionKey ?? sessionId, currentEntry);
if (archivedSessionError) throw new Error(archivedSessionError);
sessionEntry = currentEntry;
if (sessionStore && sessionKey) if (currentEntry) sessionStore[sessionKey] = currentEntry;
else delete sessionStore[sessionKey];
}
});
這一段就是入口層的真功夫。
它不是在說「任務來了」,而是在說:
如果答案不對,OpenClaw 直接要求重試。
這就是為什麼入口不能亂做,因為你接錯的不是一個字串,而是一整個工作狀態。
我把這個過程想成一間公司的櫃檯加門禁。
agentCommandFromIngress 像前台前台不負責做完工作,但它負責確認:
這層的重點是先守規矩,再把案子往下送。
agentCommandInternal 像作業中心進到內部後,就不是「收到一句話」而已了。
它會把整個 run 的背景補齊:
這樣後面的模型、工具、記錄,才知道自己到底在處理哪一個工作。
這段最有感。
系統不是把 session key 有看到就算,而是會看它現在是不是有效、是不是變動中的那一個、是不是已經封存。
如果你把這想成機場登機口,就很好懂:
OpenClaw 的 session admission 就是這種感覺。
因為 AI 系統最可怕的事情,不是慢,而是「跑錯上下文」。
如果一開始就直接做,後面你會很難知道:
先判斷再行動,就是在幫後面的除錯留路。
allowModelOverride 這種高風險能力有明確檢查但這正是我喜歡 OpenClaw 的地方。
它不假裝自己很簡單,而是老老實實把每一層責任都攤開。
agentCommandFromIngress 會先檢查必要權限與 lifecycleagentCommandInternal 先把執行環境、session、workspace 都準備好beginSessionWorkAdmission 負責確認這個 session 現在真的能開始工作如果說第 2 天是在看腦袋長什麼樣,那第 3 天就是在看它怎麼把第一個任務真的接住。
接下來第 4 天,我會繼續往下看它怎麼分派工作,而不是只把任務停在入口。