函数 runLoopfunction runLoop(核心循环引擎) (core loop engine)// ── 原料 ──// ── Ingredients ──currentContextagent 的记忆(系统提示 + 全部对话历史 + 可用工具列表)The agent's memory (system prompt + full conversation history + available tools list)newMessages收集器:本次运行新产生的消息(最终返回给调用者)Collector: new messages produced in this run (returned to caller)config配置(模型、hooks、消息队列……)Configuration (model, hooks, message queue...)signal取消按钮(外部随时可以按下中断)Cancel button (external can press to abort anytime)emit广播器(每发生一件事就通知外部)Broadcaster (notifies external on every event)streamFn调用 LLM 的函数(可替换成不同 AI)Function to call the LLM (swappable for different AIs)// ── 开始 ──// ── Start ──外层循环(while true)Outer loop (while true)// 处理 followUp 追加任务// handles followUp append tasks││内层循环(while 还有工具调用 或 有插队消息)Inner loop (while has tool calls OR has queued messages)│ ││ │// ── 一轮(turn)的完整流程 ──// ── Complete flow of one turn ──│ ││ ├─ ❶注入插队消息Inject queued messages│ │如果用户在 AI 工作期间发了新消息If user sent new messages while AI was working│ │→ 加入 context(AI 下次能看到)→ Add to context (AI sees it next call)│ │→ 加入 newMessages(记录本次新增)→ Add to newMessages (record as new this run)│ │[Pi 特有机制][Pi-specific mechanism]│ ││ ├─ ❷调用 LLMCall LLM← 循环的心跳← The loop's heartbeat│ │调用 streamAssistantResponse()(展开见下方)Calls streamAssistantResponse() (expanded below)│ │→ 返回 AI 的回复(可能含文字、工具调用请求、或错误)→ Returns AI's response (may contain text, tool call requests, or errors)│ │→ 回复自动加入 context + newMessages→ Response automatically added to context + newMessages│ ││ ├─ ❸出错/取消?→ 立即结束Error/Cancel? → Exit immediately│ │如果 AI 回复的 stopReason 是 "error" 或 "aborted"If AI response's stopReason is "error" or "aborted"│ │→ return(退出整个函数)→ return (exit entire function)│ ││ ├─ ❹检查并执行工具Check and execute tools← 决定循环是否继续的核心← The core that decides if loop continues│ │从 AI 回复中找出所有工具调用请求Extract all tool call requests from AI response│ │如果有 → 调用 executeToolCalls()(展开见下方)If found → call executeToolCalls() (expanded below)│ │→ 工具结果加入 context + newMessages→ Tool results added to context + newMessages│ │→ 标记"还有工具调用" → 内层循环继续→ Mark "has tool calls" → inner loop continues│ │如果没有 → 标记"没有更多工具调用" → 内层循环将退出If none → mark "no more tool calls" → inner loop will exit│ ││ ├─ ❺安全阀:强制停止检查Safety valve: forced stop check│ │调用 config.shouldStopAfterTurn()(可选)Calls config.shouldStopAfterTurn() (optional)│ │如果返回 true → return(强制结束,防止死循环)If returns true → return (force stop, prevents infinite loop)│ ││ └─ ❻检查新的插队消息Check for new queued messages│有 → 下一轮处理;没有 → 内层循环条件判断Found → process next turn; None → inner loop condition check││// ── 内层循环结束 ──// ── Inner loop ends ──│├─ ❼检查 followUp 追加任务Check followUp append tasks│有 → 重新进入内层循环Found → re-enter inner loop│没有 → break 退出外层循环None → break out of outer loop│外层循环结束Outer loop ends// 广播 agent_end 事件,函数返回// Emit agent_end event, function returns
三、streamAssistantResponse()——调用 LLM 的具体步骤3. streamAssistantResponse() — Steps for Calling the LLM
函数 streamAssistantResponsefunction streamAssistantResponse(调用 LLM 获取 AI 回复) (call LLM to get AI response)├─ ❶transformContext│对消息列表做预处理:压缩旧对话、删不重要的、裁剪到窗口大小以内Preprocess message list: compress old conversations, remove unimportant ones, trim to context window size│(可选步骤——如果配置了才执行)(Optional step — only runs if configured)│├─ ❷convertToLlm│agent 内部格式 → LLM API 要求的格式Agent internal format → LLM API required format│(过滤掉 LLM 不需要看的内部字段:时间戳、元数据等)(Filter out internal fields the LLM doesn't need: timestamps, metadata, etc.)│├─ ❸组装完整请求Assemble full request│把三样东西打包在一起发给 LLM:Bundle three things together and send to LLM:│ • systemPrompt — "你是谁、怎么行为"的总指令 • systemPrompt — master instruction for "who you are and how to behave"│ • messages — 全部对话历史(用户消息 + AI回复 + 工具结果) • messages — full conversation history (user messages + AI replies + tool results)│ • tools — 可用工具的说明书列表(名字 + 参数定义) • tools — list of available tool specs (name + parameter definitions)│├─ ❹调用 LLM APICall LLM API│通过 streamFn 发送请求,开始接收流式响应Send request via streamFn, begin receiving streaming response│└─ ❺处理流式响应Process streaming responseAI 一个字一个字回复,每收到新内容就通知外部(emit)AI responds word by word; each new chunk triggers a notification (emit)最终返回一条完整的 AI 回复消息Finally returns one complete AI response message
补充说明:context 和 newMessages 的关系Supplementary: Relationship Between context and newMessages
循环中每产生一条新消息(AI 回复、工具结果、插队消息),都会做两件事:
Every time a new message is produced in the loop (AI response, tool result, queued message), two things happen:
加入 currentContext.messages — 让 AI 下次调用时能看到(喂给 LLM 的记忆)
Added to currentContext.messages — so the AI can see it next call (the LLM's memory)
加入 newMessages — 单独记录"本次运行新增的"(返回给调用者用)
Added to newMessages — separately tracks "new in this run" (returned to the caller)
Agent = a loop, not a single Q&A
Call LLM → tool calls? → yes → execute → feed results back → call LLM again → ... → no more tools → done
停止条件有三层保护
① 出错/取消 → 立即停
② 外部强制(安全阀:max turns / token limit)→ 兜底保护
③ AI 自然完成(没有更多工具调用)→ 正常结束
Stop conditions have three layers of protection
① Error/cancel → stop immediately
② External force (safety valve: max turns / token limit) → fallback protection
③ AI finishes naturally (no more tool calls) → normal end
A "transform pipeline" runs before calling the LLM
Raw messages → transformContext (compress/trim) → convertToLlm (format conversion) → assemble the trio (systemPrompt + messages + tools) → send to LLM
This is where "context engineering" lives — deeper study in later chapters
工具执行前有"权限关卡"
beforeToolCall hook 可以拦截任何工具调用 → Claude Code 的"Allow?"弹窗就是这个机制
A "permission checkpoint" exists before tool execution
The beforeToolCall hook can intercept any tool call → Claude Code's "Allow?" dialog is exactly this mechanism
The event system lets the outside world observe loop state
agent_start → turn_start → message_start → tool_execution_start → ... → agent_end
UI, logs, and monitoring all rely on emit events to know "what the agent is doing"
和你日常体验的对应Mapping to Your Daily Experience
你看到 Claude Code 在做的事What You See Claude Code Doing
对应模块Corresponding Module
AI 说"我要读文件" → 读了 → "要编辑" → 编辑了 → "完成了"
runLoop 内层循环的多次迭代(每次 = 一轮 turn)
AI says "I'll read this file" → reads → "I'll edit" → edits → "done"
Multiple iterations of runLoop's inner loop (each = one turn)
弹出"Allow Read file X?"对话框
工具执行 ❶准备 里的 beforeToolCall
"Allow Read file X?" dialog appears
beforeToolCall in tool execution step ❶ Prepare
AI 一个字一个字打出来
streamAssistantResponse ❺处理流式响应
AI types out word by word
streamAssistantResponse step ❺ Process streaming response
你在 AI 工作时打字补充指令
runLoop ❶注入插队消息
You type additional instructions while AI is working
runLoop step ❶ Inject queued messages
AI 说"完成了"然后停下来
runLoop ❹没有工具调用 → 内层循环退出 → ❼没有 followUp → break
AI says "done" and stops
runLoop ❹ no tool calls → inner loop exits → ❼ no followUp → break