架构总览¶
关于本节
「源码解析」一节面向贡献者和好奇的读者,讲解 llm 包内部如何工作。公开 API 的用法见 LLM 一节;本节关注的是实现。
or/llm 是一个无状态的翻译层。它只决定一次请求该发送什么、以及如何解读流式响应,而把历史存储、上下文压缩和工具循环编排留给调用方。同一段对话可以发往任意一种协议下的任意模型,目标模型还能在轮次之间切换;本库会按请求重新适配历史。
包结构¶
这里没有分开的「门面」与「核心」:公开类型和实现都在同一个包 llm 里。协议适配器各自独立成子包,import 时自行注册,因此应用只会链接它真正用到的厂商 SDK。
| 路径 | 职责 |
|---|---|
llm/ |
整个中立核心与公开 API:模型、消息、选项、流式、迁移、适配器与 provider 两张注册表,以及默认 client |
llm/openai/ |
openai-completions 与 openai-responses 适配器;在 init 中同时注册 |
llm/openai/internal/chatcompletions/ |
Chat Completions 请求转换、兼容方言与流状态 |
llm/openai/internal/responses/ |
Responses input item 转换与事件状态机 |
llm/openai/internal/transport/ |
HTTP client、请求 Hook 与共享 SSE 过滤 |
llm/anthropic/ |
anthropic-messages 适配器;在 init 中自行注册 |
llm/all/ |
空导入两个 provider 包,供想要全部内置协议的调用方使用 |
llm/internal/ |
jsonx(宽容的 JSON 辅助)与 genmodels(模型清单生成器) |
适配器通过副作用被引入:
import (
"github.com/ktsoator/or/llm"
_ "github.com/ktsoator/or/llm/anthropic" // 注册 anthropic-messages
)
两张注册表、适配器与 client¶
调度由几个小部件组成,全部在核心包内:
ProtocolAdapter—— 一个接口,含Protocol()(它在注册表中的键)和Stream()(单个协议的请求生命周期)。见协议适配器。AdapterRegistry—— 一个并发安全的map[Protocol]ProtocolAdapter。厂商的init函数调用llm.Register把自己加入包默认注册表;偏好显式接线的调用方用NewAdapterRegistry和AdapterRegistry.Register自建。ProviderRegistry—— 一张并发安全的 per-vendor 配置表:凭证来源、静态 header,以及可选的ProviderOverride。它的ResolveRequest在派发前为每次请求填充 API key 并套用 override;默认是从内置模型清单填充的NewBuiltInProviderRegistry。见 provider。Client—— 同时持有两张注册表,为每次请求路由:先经ProviderRegistry解析 provider 配置,再派发到模型协议对应的适配器。llm.Stream和llm.Complete只是绑定到默认注册表的默认 client 的薄封装。
flowchart LR
subgraph core["package llm"]
R["AdapterRegistry"]
P["ProviderRegistry"]
C["Client"]
end
OA["llm/openai · init()"] -->|Register| R
AN["llm/anthropic · init()"] -->|Register| R
C -->|"ResolveRequest(model, options)"| P
C -->|"Get(model.Protocol)"| R
请求的数据流¶
flowchart TD
A["llm.Complete / Stream"] --> B["Client.Stream"]
B --> V["options.Validate(protocol, tools)"]
V --> RR["providers.ResolveRequest<br/>key · override · headers"]
RR --> C{"adapters.Get(model.Protocol)"}
C -->|anthropic-messages| D["Anthropic 适配器"]
C -->|openai-completions| E["OpenAI 适配器"]
C -->|openai-responses| F["OpenAI Responses 适配器"]
D --> T["TransformMessages → convert → SDK 请求"]
E --> T
F --> T
T --> G["StreamWriter: Emit / Done / Fail"]
G --> H["chan Event → 调用方"]
模型上的 Protocol 字段是判别器:Client.Stream 用它从注册表中选出适配器。适配器左侧的一切都与厂商无关;适配器内部则可以讲一种具体的线路协议。
逐步解读一次请求¶
- 协议专属选项会在构建任何 HTTP 请求之前先对目标协议做校验,因此不匹配会尽早失败。
ProviderRegistry.ResolveRequest填充 API key——依次取自StreamOptions、provider override、厂商环境变量——并套用 per-provider 的 base-URL 与 header override。若模型的 provider 未注册,则回退到旧版环境变量查找。Protocol选定适配器。同一段对话可以发往任意一种协议;本库会按请求重新适配历史。
源码:llm/client.go、llm/adapters.go、llm/default.go。