跳转到内容

第 13 课:LLM Provider 层

从阶段二的模型配置实践进入 Pi 源码,理解 provider abstraction、streaming、tool calling、thinking events、model handoff 和 OpenAI-compatible provider。

Pi 的 LLM Provider 层不是调用某个模型 API 的薄封装,而是一层把不同模型供应商统一成同一种 assistant event stream 的适配层。

为什么 agent loop 不应该直接知道 OpenAI、Anthropic、本地模型或内部 gateway 的请求差异?

  • ModelRegistry 先解析模型和认证。
  • Agent loop 只提交统一的消息、工具和推理配置。
  • Provider adapter 把供应商响应转成统一事件。
  • 上层只消费 text、thinking、tool call、done 和 error。

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

async function* streamAssistant(request: LlmRequest) {
const raw = provider.stream(request);
for await (const chunk of raw) {
if (chunk.kind === "text") yield { type: "text_delta", text: chunk.text };
if (chunk.kind === "reasoning") yield { type: "thinking_delta", text: chunk.text };
if (chunk.kind === "tool") yield { type: "toolcall_delta", call: normalizeToolCall(chunk) };
}
yield { type: "done" };
}
  • 省略版源码展示了 provider 层的核心价值:归一化。
  • 不同供应商的 chunk 格式可以不同,但进入 agent loop 前要变成统一事件。
  • 接入新 gateway 时,优先判断能否通过兼容配置解决,不能时才新增 adapter。

设计一个 provider event union,覆盖文本、thinking、工具调用、完成和错误。

  • 能说明 provider adapter 和 agent loop 的分工。
  • 能解释 streaming 事件为什么要归一化。
  • 能判断新增 provider 与使用兼容配置的取舍。
  1. 实践题:基于本课的省略版源码,补出一个最小实现草图,要求写清输入、输出、副作用和错误处理。
  2. 思考题:本课机制如果只靠 prompt 约束,而不放进 harness 或 runtime,会出现什么工程风险?