跳转到内容

第 14 课:Agent Loop

从阶段二 trace 实践进入 Pi 源码,沿一次真实 turn 理解 message state、tool call loop、retry、abort、wait idle 和 compaction 触发点。

Agent Loop 是 Pi runtime 的心脏:它把用户 prompt 扩展成模型响应、流式更新、工具执行、工具结果回填、下一轮模型调用、结束或继续的循环。

agent loop 在模型响应、工具调用和上下文之间循环的教学图

当 assistant message 里出现 tool call 时,Pi 为什么会继续请求模型,而不是直接结束?

  • transcript state 保存已完成消息和当前 streaming message。
  • turn lifecycle 把一次 assistant response 和它触发的 tool results 视作一轮。
  • tool-call loop 执行工具并把结果回填上下文。
  • session-level policy 在外层处理 retry、abort、compaction 和持久化。

下面不是完整源码,而是把本课主线压缩成可以在文档里直接阅读的关键形状。读者即使不打开本地源码,也应该能看出运行时如何组织职责。

async function runLoop(context: Message[]) {
while (true) {
const assistant = await streamAssistantResponse(context);
context.push(assistant.message);
if (assistant.toolCalls.length === 0) break;
const results = await executeToolCalls(assistant.toolCalls);
context.push(...results.map(toToolResultMessage));
if (shouldStopAfterTools(results)) break;
}
}
  • 这段代码补足了原文只列路径时缺失的主循环。
  • 关键点是 context.push(...toolResults):下一次模型请求能看到工具结果,所以它可以继续解释、修复或调用下一个工具。
  • abort 和 retry 不应该随意塞进内层循环;它们更适合作为 session policy 包住这条主线。

根据省略版循环,标出 message_update、tool_execution_start、tool_execution_end、turn_end 应该在哪些位置发出。

  • 能解释一次 turn 为什么包含 assistant message 和 tool results。
  • 能说明工具结果如何进入下一次模型请求。
  • 能区分低层 loop 和 session policy 的职责。
  1. 实践题:基于本课的省略版源码,补出一个最小实现草图,要求写清输入、输出、副作用和错误处理。
  2. 思考题:本课机制如果只靠 prompt 约束,而不放进 harness 或 runtime,会出现什么工程风险?