一、四层栈架构

 ┌─────────────────────────────────────────────────────────────┐
 │ packages/coding-agent (CLI 应用)                             │
 │   cli.ts:main → AgentSession → InteractiveMode/Print/RPC     │
 │   工具: read/write/edit/bash/grep/find/ls                    │
 └─────────────────────────────────────────────────────────────┘
                             ↓ 持有
 ┌─────────────────────────────────────────────────────────────┐
 │ packages/agent (代理运行时)                                  │
 │   agent.ts:Agent → agent-loop.ts:runLoop                     │
 │   钩子: beforeToolCall / afterToolCall / prepareNextTurn     │
 └─────────────────────────────────────────────────────────────┘
                             ↓ 调 streamFn
 ┌─────────────────────────────────────────────────────────────┐
 │ packages/ai (LLM API)                                        │
 │   stream.ts:stream → api-registry → providers/*              │
 │   AssistantMessageEventStream 统一事件协议                   │
 └─────────────────────────────────────────────────────────────┘
                             ↑ 渲染
 ┌─────────────────────────────────────────────────────────────┐
 │ packages/tui (终端 UI)                                       │
 │   tui.ts:TUI → components/*  CSI 2026 差异渲染               │
 └─────────────────────────────────────────────────────────────┘

二、启动流程

  1. 进程初始化packages/coding-agent/src/cli.ts:1-20process.titlePI_CODING_AGENT env、屏蔽 emitWarning、调 configureHttpDispatcher 配 undici、同步调 main(process.argv.slice(2))

  2. 顶层编排packages/coding-agent/src/main.ts:main pi package/pi config 短路 → parseArgsresolveAppMode(rpc > json > print/非 TTY > interactive)→ 非交互 takeOverStdout 防协议污染 → runMigrations

  3. 会话分派main.ts:createSessionManager--no-session/--fork/--session/--resume/--continue/--session-idSessionManager.inMemory/open/forkFrom/continueRecent/create

  4. 服务装配core/agent-session-services.ts:createAgentSessionServicesAuthStorageSettingsManagerModelRegistryDefaultResourceLoaderresourceLoader.reload() 加载扩展/技能/模板/主题 → 扩展 provider 注册进 modelRegistry。

  5. 模型解析core/model-resolver.ts:resolveCliModel + main.ts:buildSessionOptions --model/--models/--thinking/--tools 合成 CreateAgentSessionOptions

  6. SDK 装配core/sdk.ts:createAgentSession 从 session 历史还原 model/messages → new Agent({streamFn, onPayload/onResponse 桥扩展, beforeToolCall/afterToolCall 桥 extensionRunner})new AgentSession

  7. AgentSession 自装core/agent-session.ts:325 订阅 agent 事件 → _installAgentToolHooks 绑钩 → _buildRuntime 装工具/系统提示 → 默认激活 [read,bash,edit,write]

  8. 运行时包装core/agent-session-runtime.ts:createAgentSessionRuntime 工厂闭包跑一次出 runtime,供 /new /fork /resume 复用。

  9. 模式分派main.ts 末段 rpc → runRpcMode / interactive → new InteractiveMode(runtime).run() / print|json → runPrintMode

  10. TUI 启动modes/interactive/interactive-mode.ts:init→run 装 chat/pending/status/editor/footer → ui.start()subscribeToAgent 接 AgentSession 事件 → while(true) { input = await getUserInput(); await session.prompt(input); }

三、一次 Prompt 的生命周期

输入聚合editor.ts:Editor.submitValue:1194onSubmit → resolve getUserInput Promise。

AgentSession.promptcore/agent-session.ts:986

  • / 开头 → _tryExecuteExtensionCommand 短路

  • emitInput 让扩展改写文本/图像

  • 展开 /skill: /template

  • 流中 → _queueSteer / _queueFollowUp

  • _checkCompaction 预检

  • messages=[{role:'user',content:[text,...images]}]

  • emitBeforeAgentStart 收扩展 messages + systemPrompt

  • _runAgentPromptagent.prompt → while _handlePostAgentRunagent.continue

Agent 主循环packages/agent/src/agent-loop.ts:runLoop:155,每 tick:

  1. drain pendingMessages (steering)

  2. streamAssistantResponsetransformContextconvertToLlmstreamFn(model,ctx,opts)

  3. for-await event → 翻译成 message_start/update/end

  4. executeToolCalls

  5. emit turn_end

  6. prepareNextTurn 重建 turnState

  7. shouldStopAfterTurn?退出 emit agent_end

  8. 检查 steering/followUp 队列决定续跑

LLM 调用packages/ai/src/stream.ts:streamSimple resolveApiProvider(model.api) 查 registry → withEnvApiKey 回填 env → 委托 provider.streamSimpleAssistantMessageEventStream

Provider(举 openai-completions)— providers/openai-completions.ts:111 buildParamsonPayloadclient.chat.completions.create.withResponse()onResponse → for-await SSE → 累 textBlock/thinkingBlock/toolCallBlocksByIndex → 流末 finishBlock*_end + done

回流到 UI Agent.processEventsAgentSession._handleAgentEvent:476 → JSONL 持久化 + _emitInteractiveMode.handleEvent:2679AssistantMessageComponent.updateContentTUI.requestRender → 差异渲染。

四、工具调用往返

LLM 侧识别(provider 内部) chunk.delta.tool_calls → ensureToolCallBlockpartialArgs += deltaparseStreamingJson 容错 → 流末 push toolcall_endToolCall{id,name,arguments}

Agent 调度agent-loop.ts:executeToolCalls:373content.filter(type==='toolCall') → 串/并行分派(任一 executionMode='sequential' → 串行)→ 并行:源顺序 emit tool_execution_startPromise.all → 按源顺序入 messages。

三段执行

  1. prepare agent-loop.ts:562tools.findprepareArguments → Zod 校验 → beforeToolCall{block:true} 可短路)

  2. execute agent-loop.ts:628tool.execute(id,args,signal,onUpdate)onUpdatetool_execution_update

  3. finalize agent-loop.ts:665afterToolCall 可覆盖 content/details/isError/terminate

底层

  • bash tools/bash.ts:282spawn + OutputAccumulator 节流 100ms

  • edit tools/edit.ts:308withFileMutationQueue 锁 → read/normalizeLF/applyEdits/write/diff

  • 无内置权限拦截,唯一拦截点 = beforeToolCall 扩展钩子

结果回流 createToolResultMessage:727{role:'toolResult',toolCallId,content,details,isError} → emit message_start/end → SessionManager 持久化 → 入 currentContext.messages → 下轮 provider convertMessages 编码(OpenAI role:'tool' / Anthropic tool_result)。

关键钩子AgentSession._installAgentToolHooks:403 agent.beforeToolCall/afterToolCall 永久绑 ExtensionRunner,扩展热重载只换 runner 不重装。

五、上下文压缩

触发

  1. 每轮 agent_end 后 — _handlePostAgentRun:947_checkCompaction

  2. 新 prompt 前预检 — prompt:1067_checkCompaction(lastAssistant, false)(覆盖 aborted)

判定_checkCompaction:1793

  • Case 1 overflow — isContextOverflow(msg, contextWindow)_runAutoCompaction('overflow', true)

  • Case 2 阈值 — calculateContextTokens(usage) (error fallback estimateContextTokens) → shouldCompact_runAutoCompaction('threshold', false)

阈值core/compaction/compaction.ts:shouldCompact:219 contextTokens > contextWindow - settings.reserveTokens,默认 reserveTokens=16384, keepRecentTokens=20000

流程core/compaction/compaction.ts:compact:747 prepareCompaction → 流式 generateSummarySUMMARIZATION_SYSTEM_PROMPT),splitTurn 并行 generateTurnPrefixSummary---formatFileOperations → 返 {summary,firstKeptEntryId,tokensBefore,details}

落盘续跑agent-session.ts:compact:1717 sessionManager.appendCompaction不改旧条目)→ buildSessionContext() 重建 → 赋 agent.state.messages → emit session_compact + compaction_endfinally _reconnectToAgent() → auto 路径带 willRetry:true → 续 agent.continue()

扩展接管 session_before_compact 钩子可返自定义 CompactionResult,标 fromExtension=true

六、已驳回声明

  1. cli.ts 顶层无 awaitcli.ts:20 实为 main(process.argv.slice(2)); 同步。

  2. batch.terminate 不"直接退出"agent-loop.ts:210 仅阻下一轮 tool 循环,仍 emit turn_endprepareNextTurnshouldStopAfterTurn → 查 steering/followUp 队列才真退出。

七、残留未解

  1. AppMode 'json' 实为 print 输出格式变体(toPrintOutputMode),无独立 runJsonMode

  2. SDK 模式无独立 AppMode,消费方 RpcClient spawn cli --mode rpc 复用 rpc 路径。

  3. toolResultcontext.messages 的确切 push 位置未追到(疑在 runLoop 外层)。

  4. sdk 注入 streamFn 时 onPayload/onResponse 与 ExtensionRunner 桥接细节未追完。

  5. AbortController 互抢规则(compaction/autoCompaction/branchSummary/retry/bash 各独立 controller)。

  6. shouldStopAfterTurn 默认实现 + followUp/工具续触发的退出条件。

  7. packages/agent/harness/compaction (Result 风格) vs packages/coding-agent/core/compaction (throw 风格) 两份并存,是否合并未知。

  8. bash 无 allowlist/denylist;UI 层 per-call 确认估由扩展实现。

  9. 并行工具事件时序(start 按源序、end 按完成序、result message_start 又按源序)——监听器应按 toolCallId 关联。

  10. AgentHarness.createLoopConfiggetSteeringMessages/getFollowUpMessages 桥到三个 queue 的优先级未拉源码。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐