一条请求如何穿过 Pi
用户输入“概括这个目录”。几秒后,终端显示答案。
如果程序只保存最后一段文字,卡住时你看不出任务到底停在哪里。要定位问题,得把调用路径走一遍,分别看任务开始、Provider 返回、消息截断和 Loop 结束的位置。
Model 只保存信息,Provider 负责请求
Section titled “Model 只保存信息,Provider 负责请求”在 pi-ai 中,Model 保存模型 ID、Provider 名称、上下文长度、能力和价格等元数据。它描述“准备使用哪个模型”,自身不会发网络请求。
实际请求由 Provider 实现发送。Anthropic、OpenAI 和其他 Provider 的原始流格式不一样,Pi 把它们转换成统一的 assistant message 与事件。模型注册、Provider 查找和鉴权委托集中在 packages/ai/src/models.ts。
统一流包含起点、增量和终点事件:
starttext_starttext_delta ...text_enddone如果模型正在思考或生成工具调用,还会出现相应的 start、delta 与 end 事件。错误则走 error 结束。具体类型见 Pi AI 的流事件定义。
统一这层协议以后,Agent Core 不需要认识每一家 API 的事件名称。
StreamFn 把模型层接到 Core
Section titled “StreamFn 把模型层接到 Core”当前 Agent 没有直接依赖某一家 Provider,而是依赖 StreamFn:
model + messages + tools ↓ StreamFn ↓AssistantMessageEventStream接口位于 packages/agent/src/types.ts。Agent.prompt() 整理内存状态、模型、工具和配置,再启动 Loop;这一层的公开 API 可从 agent.ts 读起。
进入一次 turn 后,Loop 会:
- 把产品消息转换成模型能接收的 Context;
- 调用
streamFn; - 消费增量事件;
- 组装完整 assistant message;
- 判断消息中是否包含工具调用。
模型调用发生在 agent-loop.ts。
两套事件分别记录什么
Section titled “两套事件分别记录什么”模型流描述“一次 Provider 响应怎样到达”。Agent 事件描述“整次任务正走到哪一步”。Pi 的 Agent 会对外发布:
agent_startturn_startmessage_start / message_update / message_endtool_execution_start / tool_execution_update / tool_execution_endturn_endagent_end界面可以用 message update 增量渲染文字,用 tool execution 事件显示动作状态,用 agent end 恢复输入框。Trace 也可以订阅同一条事件链,而不必去解析终端文本。
用教学项目把路径缩短
Section titled “用教学项目把路径缩短”my-pi-agent 使用 ScriptedModel 代替在线 Provider。它按预先写好的顺序返回 assistant message,并记录每次调用收到的 Context。这样既不需要 API Key,也没有模型随机性。
运行一个没有工具的场景:
cd agent-harness-labnpm run lab -- 02预期观察:
{ "terminal": "completed", "eventTypes": ["run_start", "turn_start", "model_end", "run_end"], "modelCalls": 1}四个事件分别说明任务进入执行器、第一轮开始、模型适配器返回完整消息、执行器写下终态。教学版合并了上游的若干细粒度事件,但保留了同样的生命周期骨架。
模型接口不能只返回字符串。工具调用、截断原因、错误和取消都需要结构化字段。下一章让脚本模型先提出一个 read 调用,再观察工具结果怎样进入第二次请求。