跳转到内容

一条请求如何穿过 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

统一流包含起点、增量和终点事件:

start
text_start
text_delta ...
text_end
done

如果模型正在思考或生成工具调用,还会出现相应的 start、delta 与 end 事件。错误则走 error 结束。具体类型见 Pi AI 的流事件定义

统一这层协议以后,Agent Core 不需要认识每一家 API 的事件名称。

当前 Agent 没有直接依赖某一家 Provider,而是依赖 StreamFn

model + messages + tools
StreamFn
AssistantMessageEventStream

接口位于 packages/agent/src/types.tsAgent.prompt() 整理内存状态、模型、工具和配置,再启动 Loop;这一层的公开 API 可从 agent.ts 读起。

进入一次 turn 后,Loop 会:

  1. 把产品消息转换成模型能接收的 Context;
  2. 调用 streamFn
  3. 消费增量事件;
  4. 组装完整 assistant message;
  5. 判断消息中是否包含工具调用。

模型调用发生在 agent-loop.ts

模型流描述“一次 Provider 响应怎样到达”。Agent 事件描述“整次任务正走到哪一步”。Pi 的 Agent 会对外发布:

agent_start
turn_start
message_start / message_update / message_end
tool_execution_start / tool_execution_update / tool_execution_end
turn_end
agent_end

界面可以用 message update 增量渲染文字,用 tool execution 事件显示动作状态,用 agent end 恢复输入框。Trace 也可以订阅同一条事件链,而不必去解析终端文本。

my-pi-agent 使用 ScriptedModel 代替在线 Provider。它按预先写好的顺序返回 assistant message,并记录每次调用收到的 Context。这样既不需要 API Key,也没有模型随机性。

运行一个没有工具的场景:

Terminal window
cd agent-harness-lab
npm run lab -- 02

预期观察:

{
"terminal": "completed",
"eventTypes": ["run_start", "turn_start", "model_end", "run_end"],
"modelCalls": 1
}

四个事件分别说明任务进入执行器、第一轮开始、模型适配器返回完整消息、执行器写下终态。教学版合并了上游的若干细粒度事件,但保留了同样的生命周期骨架。

模型接口不能只返回字符串。工具调用、截断原因、错误和取消都需要结构化字段。下一章让脚本模型先提出一个 read 调用,再观察工具结果怎样进入第二次请求。