跳转至

第 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 可能包含 RemoteHost 与 RemoteModel。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 作为显式的安全分支处理。