一、四层栈架构

 ┌─────────────────────────────────────────────────────────────┐
 │ 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-20 设 process.title、PI_CODING_AGENT env、屏蔽 emitWarning、调 configureHttpDispatcher 配 undici、同步调 main(process.argv.slice(2))。

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

  3. 会话分派 — main.ts:createSessionManager 按 --no-session/--fork/--session/--resume/--continue/--session-id 调 SessionManager.inMemory/open/forkFrom/continueRecent/create。

  4. 服务装配 — core/agent-session-services.ts:createAgentSessionServices 装 AuthStorage、SettingsManager、ModelRegistry、DefaultResourceLoader → resourceLoader.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:1194 → onSubmit → resolve getUserInput Promise。

AgentSession.prompt — core/agent-session.ts:986

  • / 开头 → _tryExecuteExtensionCommand 短路

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

  • 展开 /skill: /template

  • 流中 → _queueSteer / _queueFollowUp

  • _checkCompaction 预检

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

  • emitBeforeAgentStart 收扩展 messages + systemPrompt

  • _runAgentPrompt → agent.prompt → while _handlePostAgentRun 续 agent.continue

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

  1. drain pendingMessages (steering)

  2. streamAssistantResponse — transformContext → convertToLlm → streamFn(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.streamSimple 返 AssistantMessageEventStream。

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

回流到 UI Agent.processEvents → AgentSession._handleAgentEvent:476 → JSONL 持久化 + _emit → InteractiveMode.handleEvent:2679 → AssistantMessageComponent.updateContent → TUI.requestRender → 差异渲染。

四、工具调用往返

LLM 侧识别(provider 内部) chunk.delta.tool_calls → ensureToolCallBlock → partialArgs += delta → parseStreamingJson 容错 → 流末 push toolcall_end 带 ToolCall{id,name,arguments}。

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

三段执行

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

  2. execute agent-loop.ts:628 — tool.execute(id,args,signal,onUpdate) → onUpdate 包 tool_execution_update

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

底层

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

  • edit tools/edit.ts:308 — withFileMutationQueue 锁 → 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 → 流式 generateSummary(SUMMARIZATION_SYSTEM_PROMPT),splitTurn 并行 generateTurnPrefixSummary 拼 --- → formatFileOperations → 返 {summary,firstKeptEntryId,tokensBefore,details}。

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

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

六、已驳回声明

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

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

七、残留未解

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

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

  3. toolResult 入 context.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.createLoopConfig 把 getSteeringMessages/getFollowUpMessages 桥到三个 queue 的优先级未拉源码。

Logo

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

更多推荐