Open Code Review 完全指南 — LLM 协议与 Provider#
本章深入解析 Open Code Review 的 LLM 集成层——LLMClient 接口契约、三种协议实现、19 个内置 Provider、四策略 Endpoint 解析链、Token 计数机制与自定义 Provider 扩展。
1. LLMClient 接口:极简契约#
OCR 的 LLM 集成层遵循"最小接口原则"——整个 LLMClient 接口只有一个方法。
1.1 接口定义#
// internal/llm/client.go
type LLMClient interface {
CompletionsWithCtx(ctx context.Context, req CompletionRequest) (*CompletionResponse, error)
}
为什么只有一个方法?
设计选择 |
理由 |
|---|---|
单方法接口 |
降低实现成本,新 Provider 只需实现一个方法 |
|
强制传入 context,支持超时和取消 |
请求/响应结构体 |
隔离协议差异,上层调用方无需关心是 Anthropic 还是 OpenAI |
1.2 CompletionRequest 与 CompletionResponse#
type CompletionRequest struct {
Model string
Messages []Message
Tools []ToolDef
MaxTokens int
Temperature float64
SystemPrompt string
}
type CompletionResponse struct {
Content string
ToolCalls []ToolCall
Usage Usage
StopReason string
}
关键洞察:单方法接口让"协议适配"和"业务调用"完全解耦。上层 Agent 只看到
CompletionsWithCtx,底层可以是 Anthropic、OpenAI 或任何自定义协议。这是"确定性工程"对"协议多样性"的治理。
2. 三种协议详解#
OCR 支持三种 LLM 协议,覆盖主流厂商的 API 规范。
2.1 协议常量#
// internal/llm/protocol.go
const (
ProtocolAnthropic = "anthropic"
ProtocolOpenAIChatCompletions = "openai"
ProtocolOpenAIResponses = "openai-responses"
)
2.2 三种协议对比#
graph LR
subgraph Anthropic协议
A1["POST /v1/messages"]
A2["x-api-key 鉴权"]
A3["cache_control 支持"]
end
subgraph OpenAI协议
O1["POST /v1/chat/completions"]
O2["Authorization: Bearer 鉴权"]
O3["streaming 支持"]
end
subgraph OpenAI-Responses协议
R1["POST /v1/responses"]
R2["Authorization: Bearer 鉴权"]
R3["Responses API"]
end
协议 |
URL 结尾 |
鉴权方式 |
特有功能 |
|---|---|---|---|
|
|
|
|
|
|
|
streaming、reasoning_content |
|
|
|
Responses API |
2.3 NormalizeProtocol 与 ValidateProtocol#
// internal/llm/protocol.go
func NormalizeProtocol(p string) string {
switch strings.ToLower(strings.TrimSpace(p)) {
case "anthropic", "claude":
return ProtocolAnthropic
case "openai", "gpt", "chat":
return ProtocolOpenAIChatCompletions
case "openai-responses", "responses":
return ProtocolOpenAIResponses
default:
return p // 未知协议原样返回
}
}
func ValidateProtocol(p string) error {
switch p {
case ProtocolAnthropic, ProtocolOpenAIChatCompletions, ProtocolOpenAIResponses:
return nil
default:
return fmt.Errorf("unsupported protocol: %s", p)
}
}
两个函数的分工:
NormalizeProtocol:宽容归一化,接受 "claude"、"gpt" 等别名ValidateProtocol:严格校验,只接受三个标准常量
设计要点:归一化与校验分离,让用户配置更宽容(可用别名),同时保证内部使用严格常量。这是"宽容输入、严格内部"的常见工程模式。
3. NewLLMClient 工厂模式#
OCR 使用工厂函数根据协议创建对应的 Client 实现。
3.1 工厂函数#
// internal/llm/factory.go
func NewLLMClient(cfg ClientConfig) (LLMClient, error) {
protocol := NormalizeProtocol(cfg.Protocol)
if err := ValidateProtocol(protocol); err != nil {
return nil, err
}
switch protocol {
case ProtocolAnthropic:
return NewAnthropicClient(cfg)
case ProtocolOpenAIChatCompletions:
return NewOpenAIClient(cfg)
case ProtocolOpenAIResponses:
return NewOpenAIResponsesClient(cfg)
default:
return nil, fmt.Errorf("unsupported protocol: %s", protocol)
}
}
3.2 工厂模式 Mermaid 图#
flowchart TD
Cfg["ClientConfig<br/>(protocol, url, token, model)"] --> Normalize["NormalizeProtocol"]
Normalize --> Validate["ValidateProtocol"]
Validate --> Switch{"switch protocol"}
Switch -->|"anthropic"| Anthropic["NewAnthropicClient"]
Switch -->|"openai"| OpenAI["NewOpenAIClient"]
Switch -->|"openai-responses"| Responses["NewOpenAIResponsesClient"]
Anthropic --> Impl1["AnthropicClient 实现"]
OpenAI --> Impl2["OpenAIClient 实现"]
Responses --> Impl3["OpenAIResponsesClient 实现"]
Impl1 --> LLMClient["LLMClient 接口"]
Impl2 --> LLMClient
Impl3 --> LLMClient
4. OpenAIClient 实现#
OpenAIClient 是 OpenAI Chat Completions 协议的实现,也是使用最广泛的 Client。
4.1 核心配置#
// internal/llm/openai_client.go
func NewOpenAIClient(cfg ClientConfig) (*OpenAIClient, error) {
client := openai.NewClient(
cfg.Token,
openai.WithBaseURL(cfg.BaseURL),
openai.WithHTTPClient(&http.Client{
Timeout: 5 * time.Minute, // 默认超时 5 分钟
}),
openai.WithMaxRetries(5), // 默认重试 5 次
)
return &OpenAIClient{client: client, model: cfg.Model}, nil
}
配置项 |
值 |
理由 |
|---|---|---|
默认超时 |
5 分钟 |
LLM 长上下文响应可能较慢,5 分钟覆盖大多数场景 |
最大重试 |
5 次 |
应对 429 限流和临时网络错误 |
BaseURL |
可配置 |
支持代理和兼容 OpenAI 协议的厂商 |
4.2 Streaming 支持#
OpenAIClient 使用 openai.ChatCompletionAccumulator 处理流式响应:
// internal/llm/openai_client.go
func (c *OpenAIClient) CompletionsWithCtx(ctx context.Context, req CompletionRequest) (*CompletionResponse, error) {
stream, err := c.client.CreateChatCompletionStream(ctx, convertRequest(req))
if err != nil {
return nil, err
}
defer stream.Close()
var acc openai.ChatCompletionAccumulator
for {
resp, err := stream.Recv()
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return nil, err
}
acc.AddChunk(resp)
}
return c.parseResponse(acc)
}
Streaming 的价值:
更早响应:首个 Token 到达即可开始处理,无需等待全部生成
超时友好:只要持续有 Token,连接就不会超时
内存高效:增量累积,避免大响应的内存峰值
4.3 reasoning_content 提取#
OpenAI 的 o 系列模型会返回 reasoning_content(推理过程),OCR 会单独提取:
func (c *OpenAIClient) parseResponse(acc openai.ChatCompletionAccumulator) (*CompletionResponse, error) {
choice := acc.Choices[0]
resp := &CompletionResponse{
Content: choice.Message.Content,
ToolCalls: choice.Message.ToolCalls,
StopReason: string(choice.FinishReason),
}
// 提取 reasoning_content(o 系列模型的推理过程)
if reasoning, ok := choice.Message.CustomFields["reasoning_content"]; ok {
resp.Reasoning = reasoning
}
resp.Usage = Usage{
PromptTokens: acc.Usage.PromptTokens,
CompletionTokens: acc.Usage.CompletionTokens,
TotalTokens: acc.Usage.TotalTokens,
}
return resp, nil
}
关键洞察:
reasoning_content是 o1/o3/o4 等推理模型的"思考过程",提取它可用于调试和可观测性,但不送入上下文(避免污染对话历史)。
5. AnthropicClient 实现#
AnthropicClient 是 Anthropic Claude API 的实现,针对 Claude 的特性做了多项适配。
5.1 URL 自动补全#
Anthropic API 的标准端点是 /v1/messages,OCR 会自动补全:
// internal/llm/anthropic_client.go
func normalizeAnthropicURL(baseURL string) string {
baseURL = strings.TrimSuffix(baseURL, "/")
if !strings.HasSuffix(baseURL, "/v1/messages") {
if strings.HasSuffix(baseURL, "/v1") {
baseURL += "/messages"
} else {
baseURL += "/v1/messages"
}
}
return baseURL
}
5.2 cache_control ephemeral 设置#
Claude 支持 prompt caching,OCR 自动为系统提示设置 cache_control: ephemeral:
// internal/llm/anthropic_client.go
func buildAnthropicRequest(req CompletionRequest) anthropic.MessagesRequest {
return anthropic.MessagesRequest{
Model: req.Model,
MaxTokens: req.MaxTokens, // 默认 8192
System: anthropic.System{
Text: req.SystemPrompt,
CacheControl: &anthropic.CacheControl{
Type: "ephemeral", // 自动缓存系统提示
},
},
Messages: convertMessages(req.Messages),
Tools: convertTools(req.Tools),
}
}
cache_control ephemeral 的价值:
系统提示缓存 5 分钟,重复请求只计少量 CacheReadInputTokens
对 OCR 这种"系统提示固定、Diff 变化"的场景,缓存收益巨大
成本可降低 50% 以上
5.3 MaxTokens 默认值#
const DefaultAnthropicMaxTokens = 8192
Claude API 要求显式指定 MaxTokens,OCR 默认 8192(覆盖大多数审查场景的输出长度)。
5.4 tool 角色 message 合并#
Claude API 要求连续的 tool 角色消息合并为单个 tool_result block,OCR 用 NewToolResultBlock 处理:
// internal/llm/anthropic_client.go
func convertMessages(msgs []Message) []anthropic.Message {
var result []anthic.Message
for i, msg := range msgs {
if msg.Role == "tool" && i > 0 && result[len(result)-1].Role == "tool" {
// 合并到前一个 tool 消息
last := &result[len(result)-1]
last.Content = append(last.Content, anthropic.NewToolResultBlock(
msg.ToolCallID, msg.Content, false,
))
} else {
result = append(result, convertSingleMessage(msg))
}
}
return result
}
设计要点:Claude 的"连续 tool 消息合并"是协议级要求,违反会报错。OCR 在 Client 层自动处理,上层无需感知。
5.5 Usage 计算含 CacheReadInputTokens#
Claude 的 Usage 包含缓存读取的 Token,OCR 完整记录:
type Usage struct {
PromptTokens int
CompletionTokens int
TotalTokens int
CacheReadInputTokens int // Claude 特有:缓存读取
CacheCreationTokens int // Claude 特有:缓存创建
}
resp.Usage = Usage{
PromptTokens: anthropicResp.Usage.InputTokens,
CompletionTokens: anthropicResp.Usage.OutputTokens,
CacheReadInputTokens: anthropicResp.Usage.CacheReadInputTokens,
CacheCreationTokens: anthropicResp.Usage.CacheCreationInputTokens,
}
resp.Usage.TotalTokens = resp.Usage.PromptTokens + resp.Usage.CompletionTokens
6. 19 个内置 Provider#
OCR 内置 19 个 Provider,覆盖国际和国内主流 LLM 服务。
6.1 Provider 定义结构#
// internal/llm/providers.go
type Provider struct {
Name string
BaseURL string
Protocol string
EnvVar string // Token 环境变量名
Models []string // 推荐模型列表
}
6.2 19 个 Provider 完整表格#
# |
Provider 名 |
BaseURL |
EnvVar (Token) |
协议 |
|---|---|---|---|---|
1 |
|
|
|
anthropic |
2 |
|
|
|
openai |
3 |
|
|
|
openai |
4 |
|
|
|
openai |
5 |
|
|
|
openai |
6 |
|
|
|
openai |
7 |
|
|
|
openai |
8 |
|
|
|
openai |
9 |
|
|
|
openai |
10 |
|
|
|
openai |
11 |
|
|
|
openai |
12 |
|
|
|
openai |
13 |
|
|
|
openai |
14 |
|
|
|
openai |
15 |
|
|
|
openai |
16 |
|
|
|
openai |
17 |
|
|
|
openai |
18 |
|
|
|
openai |
19 |
|
用户指定 |
用户指定 |
用户指定 |
6.3 anthropic 的 6 个推荐模型#
// internal/llm/providers.go
"BuiltinProviders": []Provider{
{
Name: "anthropic",
Models: []string{
"claude-opus-5",
"claude-opus-5-20250610",
"claude-sonnet-5",
"claude-sonnet-5-20250610",
"claude-haiku-5",
"claude-haiku-5-20250610",
},
},
}
6.4 openai 的 6 个推荐模型#
{
Name: "openai",
Models: []string{
"gpt-5.6-sol",
"gpt-5.6",
"gpt-5.5",
"o3",
"o4-mini",
"gpt-4o",
},
}
设计要点:19 个 Provider 中 17 个使用 OpenAI 协议——这反映了 OpenAI Chat Completions 已成为事实标准。OCR 通过协议归一化,让所有兼容厂商"开箱即用"。
7. Endpoint 解析四策略链#
OCR 的 Endpoint 解析是"多源发现"的典型实现,按优先级尝试四种来源。
7.1 四策略链 Mermaid 图#
flowchart TD
Start["Endpoint 解析启动"] --> S1["策略 1: tryOCRConfig<br/>读取 config.json"]
S1 --> Q1{"完整三元组<br/>(URL, Token, Model)?"}
Q1 -->|"是"| Win1["胜出"]
Q1 -->|"否"| S2["策略 2: tryOCREnv<br/>读取 OCR_LLM_* 环境变量"]
S2 --> Q2{"完整三元组?"}
Q2 -->|"是"| Win2["胜出"]
Q2 -->|"否"| S3["策略 3: tryCCEnv<br/>读取 ANTHROPIC_* 环境变量"]
S3 --> Q3{"完整三元组?"}
Q3 -->|"是"| Win3["胜出"]
Q3 -->|"否"| S4["策略 4: tryShellRC<br/>读取 shell 配置文件"]
S4 --> Q4{"完整三元组?"}
Q4 -->|"是"| Win4["胜出"]
Q4 -->|"否"| Fail["解析失败<br/>提示用户配置"]
7.2 策略 1: tryOCRConfig#
读取 OCR 自己的配置文件 ~/.config/ocr/config.json:
// internal/llm/resolver.go
func tryOCRConfig() (*Endpoint, error) {
cfg, err := loadOCRConfig()
if err != nil {
return nil, err
}
if cfg.LLMURL != "" && cfg.LLMToken != "" && cfg.LLMModel != "" {
return &Endpoint{
URL: cfg.LLMURL,
Token: cfg.LLMToken,
Model: cfg.LLMModel,
Source: "ocr-config",
}, nil
}
return nil, errIncomplete
}
7.3 策略 2: tryOCREnv#
读取 OCR 专用的 8 个环境变量:
// internal/llm/resolver.go
func tryOCREnv() (*Endpoint, error) {
url := os.Getenv("OCR_LLM_URL")
token := os.Getenv("OCR_LLM_TOKEN")
model := os.Getenv("OCR_LLM_MODEL")
// 还支持:OCR_LLM_PROTOCOL, OCR_LLM_MAX_TOKENS,
// OCR_LLM_TEMPERATURE, OCR_LLM_TIMEOUT, OCR_LLM_BASE_URL
if url != "" && token != "" && model != "" {
return &Endpoint{
URL: url,
Token: token,
Model: model,
Source: "ocr-env",
}, nil
}
return nil, errIncomplete
}
8 个 OCR 环境变量:
环境变量 |
用途 |
|---|---|
|
LLM API 端点 |
|
鉴权 Token |
|
默认模型名 |
|
协议(anthropic/openai/openai-responses) |
|
最大输出 Token |
|
采样温度 |
|
请求超时 |
|
Base URL(与 URL 语义略有不同) |
7.4 策略 3: tryCCEnv#
复用 Claude Code 的环境变量,实现"零配置迁移":
// internal/llm/resolver.go
func tryCCEnv() (*Endpoint, error) {
url := os.Getenv("ANTHROPIC_BASE_URL")
token := os.Getenv("ANTHROPIC_AUTH_TOKEN")
model := os.Getenv("ANTHROPIC_MODEL")
if url != "" && token != "" && model != "" {
return &Endpoint{
URL: url + "/v1/messages",
Token: token,
Model: model,
Source: "cc-env",
}, nil
}
return nil, errIncomplete
}
关键洞察:
tryCCEnv让 Claude Code 用户可以"零配置"使用 OCR——只要设置了ANTHROPIC_*环境变量,OCR 自动复用。这是"生态友好"的设计。
7.5 策略 4: tryShellRC#
读取 shell 配置文件,提取环境变量定义:
// internal/llm/resolver.go
func tryShellRC() (*Endpoint, error) {
candidates := []string{
"~/.zshrc",
"~/.bashrc",
"~/.bash_profile",
"~/.profile",
}
for _, f := range candidates {
env := parseShellEnv(f)
if env["ANTHROPIC_BASE_URL"] != "" && env["ANTHROPIC_AUTH_TOKEN"] != "" {
return &Endpoint{
URL: env["ANTHROPIC_BASE_URL"] + "/v1/messages",
Token: env["ANTHROPIC_AUTH_TOKEN"],
Model: env["ANTHROPIC_MODEL"],
Source: "shell-rc",
}, nil
}
}
return nil, errIncomplete
}
为什么读 shell 配置文件?
很多开发者只在
.zshrc/.bashrc中设置环境变量非交互式进程(如 cron、CI)不会加载这些文件
OCR 主动解析,让这些场景也能"自动发现"配置
7.6 "首个完整三元组胜出"原则#
func ResolveEndpoint() (*Endpoint, error) {
strategies := []func() (*Endpoint, error){
tryOCRConfig,
tryOCREnv,
tryCCEnv,
tryShellRC,
}
for _, strategy := range strategies {
ep, err := strategy()
if err == nil && ep.IsComplete() {
return ep, nil // 首个完整三元组胜出
}
}
return nil, fmt.Errorf("no complete endpoint configuration found")
}
优先级 |
策略 |
来源 |
适用场景 |
|---|---|---|---|
1 |
|
config.json |
正式配置 |
2 |
|
OCR_LLM_* 环境变量 |
CI/CD、临时覆盖 |
3 |
|
ANTHROPIC_* 环境变量 |
Claude Code 用户复用 |
4 |
|
shell 配置文件 |
本地开发环境 |
设计要点:四策略链体现了"渐进发现"——从最正式到最临时,依次尝试。这种设计让 OCR 在各种环境下都能"开箱即用",同时保持配置层级清晰。
8. 自定义 Provider#
当 19 个内置 Provider 不够用时,OCR 支持自定义 Provider。
8.1 自定义 Provider 要求#
// internal/llm/providers.go
type CustomProvider struct {
URL string // 必须提供
Protocol string // 必须提供:anthropic/openai/openai-responses
Token string // 可选:不提供则无鉴权
Model string // 必须提供
}
关键规则:
必须提供
url和protocol:这是协议级要求,无法猜测无环境变量回退:自定义 Provider 不参与 Endpoint 解析链
Token 可选:某些自部署场景(如 Ollama)无需鉴权
8.2 自定义 Provider 示例#
// ~/.config/ocr/config.json
{
"providers": {
"my-local-llm": {
"url": "http://localhost:11434/v1",
"protocol": "openai",
"model": "llama3"
}
}
}
# 使用自定义 Provider
ocr review --provider my-local-llm
9. Token 计数:tiktoken#
OCR 使用 tiktoken 进行 Token 计数,用于预算控制和压缩触发。
9.1 CountTokens 实现#
// internal/llm/tokens.go
func CountTokens(model, text string) int {
encoding := getEncoding(model)
return len(encoding.Encode(text))
}
func getEncoding(model string) *tiktoken.Encoding {
if isReasoningModel(model) {
// o1/o3/o4 系列使用 o200k_base
return tiktoken.MustGetEncoding("o200k_base")
}
// 其他模型使用 cl100k_base
return tiktoken.MustGetEncoding("cl100k_base")
}
func isReasoningModel(model string) bool {
prefixes := []string{"o1", "o3", "o4"}
for _, p := range prefixes {
if strings.HasPrefix(model, p) {
return true
}
}
return false
}
9.2 两种 Encoding 对比#
Encoding |
适用模型 |
词表大小 |
特点 |
|---|---|---|---|
|
GPT-4/GPT-4o/Claude 等 |
~100k |
主流模型默认 |
|
o1/o3/o4 推理模型 |
~200k |
支持更多语言和符号 |
9.3 Token 计数的用途#
graph LR
CountTokens["CountTokens"] --> Budget["预算前瞻<br/>估算 Subtask 成本"]
CountTokens --> Compress["压缩触发<br/>计算上下文占用"]
CountTokens --> Manifest["Manifest<br/>记录实际 Token 消耗"]
CountTokens --> Limit["上下文限制<br/>防止超出模型窗口"]
关键洞察:Token 计数是"确定性工程"的基础设施——预算控制、压缩触发、Manifest 记录都依赖准确的 Token 计数。tiktoken 的本地计算避免了"调用 API 才知道 Token 数"的滞后。
10. 配置示例#
OCR 支持三种配置方式,适应不同场景。
10.1 交互式配置#
$ ocr config provider
? Select provider: anthropic
? Enter API key: ********************************
? Select model: claude-sonnet-5
✓ Configuration saved to ~/.config/ocr/config.json
10.2 命令行配置#
# 设置单个值
ocr config set llm.url https://api.anthropic.com
ocr config set llm.token sk-ant-xxx
ocr config set llm.model claude-sonnet-5
ocr config set llm.protocol anthropic
# 批量设置
ocr config set --from-file my-config.json
10.3 环境变量配置#
# 临时使用(CI/CD 场景)
export OCR_LLM_URL=https://api.deepseek.com
export OCR_LLM_TOKEN=sk-xxx
export OCR_LLM_MODEL=deepseek-chat
ocr review
10.4 三种方式对比#
方式 |
持久性 |
适用场景 |
优先级 |
|---|---|---|---|
交互式 |
持久 |
首次配置 |
1(config.json) |
命令行 |
持久 |
脚本化配置 |
1(config.json) |
环境变量 |
临时 |
CI/CD、临时切换 |
2(覆盖 config.json) |
11. extra_body 和 extra_headers#
OCR 支持通过 extra_body 和 extra_headers 传递厂商特有参数。
11.1 配置格式#
{
"llm": {
"extra_body": {
"thinking": {
"type": "enabled",
"budget_tokens": 10000
}
},
"extra_headers": {
"X-Custom-Header": "value"
}
}
}
11.2 保留头拒绝列表#
某些 HTTP 头由 OCR 自动管理,用户配置会被拒绝:
// internal/llm/headers.go
var reservedHeaders = map[string]bool{
"authorization": true,
"x-api-key": true,
"content-type": true,
"user-agent": true,
}
func validateHeaders(headers map[string]string) error {
for k := range headers {
if reservedHeaders[strings.ToLower(k)] {
return fmt.Errorf("header %s is reserved and cannot be overridden", k)
}
}
return nil
}
为什么拒绝这些头?
保留头 |
拒绝理由 |
|---|---|
|
鉴权由 Token 字段统一管理,覆盖会导致安全风险 |
|
Anthropic 鉴权头,同上 |
|
由协议决定(JSON),覆盖会破坏请求解析 |
|
用于 OCR 自身标识和 API 调用追踪 |
设计要点:保留头拒绝列表是"安全护栏"——防止用户无意中覆盖关键 HTTP 头,导致鉴权失败或请求格式错误。
11.3 extra_body 的合并逻辑#
// internal/llm/openai_client.go
func buildRequestBody(req CompletionRequest, cfg ClientConfig) map[string]interface{} {
body := map[string]interface{}{
"model": req.Model,
"messages": convertMessages(req.Messages),
"tools": convertTools(req.Tools),
"max_tokens": req.MaxTokens,
}
// 合并 extra_body(用户配置覆盖默认值)
for k, v := range cfg.ExtraBody {
body[k] = v
}
return body
}
12. 协议适配总结#
12.1 协议适配决策树#
flowchart TD
URL["BaseURL"] --> Check1{"包含<br/>/v1/messages?"}
Check1 -->|"是"| Anthropic["使用 AnthropicClient"]
Check1 -->|"否"| Check2{"包含<br/>/v1/chat/completions?"}
Check2 -->|"是"| OpenAI["使用 OpenAIClient"]
Check2 -->|"否"| Check3{"包含<br/>/v1/responses?"}
Check3 -->|"是"| Responses["使用 OpenAIResponsesClient"]
Check3 -->|"否"| Default["按 protocol 字段决定"]
12.2 三种 Client 实现对比#
特性 |
AnthropicClient |
OpenAIClient |
OpenAIResponsesClient |
|---|---|---|---|
默认超时 |
5 分钟 |
5 分钟 |
5 分钟 |
MaxTokens 默认 |
8192 |
不设上限 |
不设上限 |
Streaming |
✅ |
✅ |
✅ |
Prompt Caching |
✅ ephemeral |
❌ |
❌ |
reasoning_content |
❌ |
✅ |
✅ |
Tool 消息合并 |
✅ NewToolResultBlock |
❌ |
❌ |
CacheReadInputTokens |
✅ |
❌ |
❌ |
12.3 设计哲学#
OCR 的 LLM 集成层体现了三个设计哲学:
接口最小化:单方法 LLMClient 接口,降低实现成本
协议归一化:19 个 Provider 中 17 个用 OpenAI 协议,统一适配
渐进发现:四策略 Endpoint 解析,让配置"开箱即用"
这三个哲学共同让 OCR 的 LLM 集成层既"简单"(上层接口简单)又"灵活"(底层支持多协议多厂商)。
13. 源码索引#
模块 |
文件路径 |
核心符号 |
|---|---|---|
LLMClient 接口 |
|
|
协议定义 |
|
|
工厂函数 |
|
|
OpenAI Client |
|
|
Anthropic Client |
|
|
Provider 列表 |
|
|
Endpoint 解析 |
|
|
Token 计数 |
|
|
头部校验 |
|
|
下一章:05-tools-mcp.md 将深入解析 6 个内置工具、code_comment 评论机制、MCP 集成与工具自定义扩展。