跳转至

第 11 章:兼容层——OpenAI、Anthropic 与 Cloud Passthrough

一、兼容的目标不是模拟所有细节

Ollama 选择一个内部规范:api.ChatRequest / api.Message / api.Tool / api.ChatResponse。OpenAI 和 Anthropic 兼容层只承诺把常见调用语义映射到这个规范,再把结果翻译回对方的传输格式。

外部协议
  → 请求 adapter
  → Ollama internal API
  → common inference path
  → 响应 writer/adapter
  → 外部协议

这样做的边界也很清楚:如果外部协议有 Ollama 内部没有的语义,就必须在 middleware、openai 或 Anthropic 包中明确降级,而不是偷偷丢字段。

二、OpenAI Chat 的关键映射

OpenAI middleware 要处理:

  • messages 的 role/content 结构。
  • tools 与 tool calls。
  • stream、stream options、usage。
  • response format/JSON schema。
  • model list/retrieve。
  • embeddings 的 encoding format。
  • audio transcription 的兼容入口。

响应侧的 ChatWriter 保存 toolCallSent 状态,因为 tool call 的 SSE chunk 与普通文本 chunk 的边界不同;结束时再根据 IncludeUsage 追加 usage event。

三、Anthropic Messages 的特殊性

Anthropic /v1/messages 与 OpenAI 最大的差别不是 URL,而是消息 block、thinking、tool use/tool result 和 streaming event 命名。middleware/anthropic.go 负责把这些 block 转成内部 api.Message,再把 Ollama response 重新包装成 Anthropic event stream。

这也是为什么 Anthropic 兼容不能简单复用 OpenAI middleware:两个协议都“像 chat”,但其消息和 stream 状态机不同。

四、Cloud passthrough 的两种情况

显式 cloud model

当模型引用带 cloud source 时,ChatHandler 会把模型名归一化,再通过 cloud proxy 转发。请求不应尝试在本地 GetModel 后加载不存在的权重。

本地 manifest 指向 remote model

某些 local model metadata 可能包含 RemoteHostRemoteModel。handler 会检查 remote hostname 是否在 envconfig.Remotes() allowlist 中,云被禁用时拒绝请求。

model ref / manifest
  → local? → scheduler
  → explicit cloud? → signed cloud proxy
  → remote config? → allowlist + cloud status + proxy

五、签名与安全边界

server/cloud_proxy.go 会构造 challenge,对请求进行签名,并复制允许的请求/响应 headers;同时过滤 hop-by-hop headers,维护 JSONL framing。它还会处理 cloud unauthorized,使客户端拿到 sign-in URL。

这里的原则是:

  • 本地 server 不把任意 URL 当作 remote target。
  • cloud 请求需要明确身份与签名。
  • 代理不能盲目转发连接级 headers。
  • 流式响应要维持原协议的 framing。

六、为什么兼容层仍共享 ChatHandler

如果 OpenAI/Anthropic 各自实现 prompt、scheduler、工具 parser,会导致:

  • 同一个模型三种协议行为不同。
  • OOM、keep-alive、thinking 支持不一致。
  • bug 修复要复制三次。

共享 handler 把差异收敛到“协议进出口”;代价是 internal API 必须足够表达多协议能力,middleware 也会变复杂。

七、边界案例

兼容层需要特别关注:

场景 风险
streaming + tool call chunk 顺序和终止事件错误
usage 非 streaming 与 streaming 位置不同
thinking 不能把隐藏思考误当普通 content
cloud model 本地找不到模型不应返回普通 local 404
remote host SSRF/任意转发风险
压缩请求体 解压后大小限制,避免资源耗尽

八、设计取舍

兼容层是一种“语义翻译器”,而不是第二套产品。它尽量复用内部生命周期,明确在 protocol adapter 里承担差异,并将 remote/cloud 作为显式的安全分支处理。