Pi Agent 源代码流转分析
一、四层栈架构
┌─────────────────────────────────────────────────────────────┐ │ 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 差异渲染 │ └─────────────────────────────────────────────────────────────┘
二、启动流程
-
进程初始化 —
packages/coding-agent/src/cli.ts:1-20设process.title、PI_CODING_AGENTenv、屏蔽emitWarning、调configureHttpDispatcher配 undici、同步调main(process.argv.slice(2))。 -
顶层编排 —
packages/coding-agent/src/main.ts:mainpi package/pi config短路 →parseArgs→resolveAppMode(rpc > json > print/非 TTY > interactive)→ 非交互takeOverStdout防协议污染 →runMigrations。 -
会话分派 —
main.ts:createSessionManager按--no-session/--fork/--session/--resume/--continue/--session-id调SessionManager.inMemory/open/forkFrom/continueRecent/create。 -
服务装配 —
core/agent-session-services.ts:createAgentSessionServices装AuthStorage、SettingsManager、ModelRegistry、DefaultResourceLoader→resourceLoader.reload()加载扩展/技能/模板/主题 → 扩展 provider 注册进 modelRegistry。 -
模型解析 —
core/model-resolver.ts:resolveCliModel+main.ts:buildSessionOptions--model/--models/--thinking/--tools合成CreateAgentSessionOptions。 -
SDK 装配 —
core/sdk.ts:createAgentSession从 session 历史还原 model/messages →new Agent({streamFn, onPayload/onResponse 桥扩展, beforeToolCall/afterToolCall 桥 extensionRunner})→new AgentSession。 -
AgentSession 自装 —
core/agent-session.ts:325订阅 agent 事件 →_installAgentToolHooks绑钩 →_buildRuntime装工具/系统提示 → 默认激活[read,bash,edit,write]。 -
运行时包装 —
core/agent-session-runtime.ts:createAgentSessionRuntime工厂闭包跑一次出 runtime,供/new/fork/resume复用。 -
模式分派 —
main.ts末段 rpc →runRpcMode/ interactive →new InteractiveMode(runtime).run()/ print|json →runPrintMode。 -
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:
-
drain pendingMessages (steering)
-
streamAssistantResponse—transformContext→convertToLlm→streamFn(model,ctx,opts) -
for-await event → 翻译成
message_start/update/end -
executeToolCalls -
emit
turn_end -
prepareNextTurn重建 turnState -
shouldStopAfterTurn?退出 emitagent_end -
检查 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。
三段执行
-
prepare
agent-loop.ts:562—tools.find→prepareArguments→ Zod 校验 →beforeToolCall({block:true}可短路) -
execute
agent-loop.ts:628—tool.execute(id,args,signal,onUpdate)→onUpdate包tool_execution_update -
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 不重装。
五、上下文压缩
触发
-
每轮
agent_end后 —_handlePostAgentRun:947→_checkCompaction -
新 prompt 前预检 —
prompt:1067→_checkCompaction(lastAssistant, false)(覆盖 aborted)
判定 — _checkCompaction:1793
-
Case 1 overflow —
isContextOverflow(msg, contextWindow)→_runAutoCompaction('overflow', true) -
Case 2 阈值 —
calculateContextTokens(usage)(error fallbackestimateContextTokens) →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。
六、已驳回声明
-
cli.ts 顶层无 await —
cli.ts:20实为main(process.argv.slice(2));同步。 -
batch.terminate 不"直接退出" —
agent-loop.ts:210仅阻下一轮 tool 循环,仍 emitturn_end→prepareNextTurn→shouldStopAfterTurn→ 查 steering/followUp 队列才真退出。
七、残留未解
-
AppMode
'json'实为 print 输出格式变体(toPrintOutputMode),无独立runJsonMode。 -
SDK 模式无独立 AppMode,消费方
RpcClientspawncli --mode rpc复用 rpc 路径。 -
toolResult入context.messages的确切 push 位置未追到(疑在 runLoop 外层)。 -
sdk 注入 streamFn 时 onPayload/onResponse 与 ExtensionRunner 桥接细节未追完。
-
AbortController 互抢规则(compaction/autoCompaction/branchSummary/retry/bash 各独立 controller)。
-
shouldStopAfterTurn默认实现 + followUp/工具续触发的退出条件。 -
packages/agent/harness/compaction(Result 风格) vspackages/coding-agent/core/compaction(throw 风格) 两份并存,是否合并未知。 -
bash 无 allowlist/denylist;UI 层 per-call 确认估由扩展实现。
-
并行工具事件时序(start 按源序、end 按完成序、result message_start 又按源序)——监听器应按
toolCallId关联。 -
AgentHarness.createLoopConfig把getSteeringMessages/getFollowUpMessages桥到三个 queue 的优先级未拉源码。
更多推荐


所有评论(0)