Files
tokens-reef/deploy/docs-backup/MODEL_SUPPORT_DETAIL.md
Developer 349d783fd1 refactor: clean up project structure
- Remove old review reports (keep latest only)
- Move docs/ to deploy/docs-backup/
- Move performance-testing/ to deploy/
- Clean up test output files
- Organize root directory
2026-04-06 23:36:03 +08:00

22 KiB

Sub2API 国产模型接入详细方案

版本: v1.0
日期: 2026-03-26
状态: 详细设计


一、当前模型支持现状

1.1 已有模型支持

平台 状态 实现方式
OpenAI 完整 原生 client
Anthropic 完整 原生 client
Google Gemini 完整 原生 client
AWS Bedrock 完整 AWS SDK
自定义 Upstream 完整 HTTP 代理
Antigravity 完整 自定义
Sora (Claude Code) 完整 自定义

1.2 待接入模型

序号 模型 厂商 API 特点 优先级
1 文心一言 百度 REST API P0
2 通义千问 阿里 OpenAI 兼容 P0
3 讯飞星火 讯飞 私有协议 P1
4 混元 腾讯 OpenAI 兼容 P1
5 豆包 字节 OpenAI 兼容 P1
6 MiniMax MiniMax OpenAI 兼容 P1
7 DeepSeek DeepSeek OpenAI 兼容 P0
8 智谱清言 智谱 OpenAI 兼容 P2
9 百川智能 百川 OpenAI 兼容 P2

二、技术架构设计

2.1 整体架构

┌─────────────────────────────────────────────────────────────────────────┐
│                           模型接入架构                                   │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                     Gateway (API 网关)                          │   │
│  │  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐           │   │
│  │  │OpenAI兼容│ │Anthropic │ │ Gemini   │ │ Bedrock  │           │   │
│  │  └──────────┘ └──────────┘ └──────────┘ └──────────┘           │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                    │                                    │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                    Provider Adapter Layer                       │   │
│  │  ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐   │   │
│  │  │ 百川/智谱  │ │  DeepSeek  │ │  通义/豆包  │ │  讯飞/混元  │   │   │
│  │  │(OpenAI兼容)│ │(OpenAI兼容)│ │(OpenAI兼容)│ │ (自定义)   │   │   │
│  │  └────────────┘ └────────────┘ └────────────┘ └────────────┘   │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                    │                                    │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                    External APIs                                 │   │
│  │  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐           │   │
│  │  │  百度   │ │   阿里   │ │   腾讯   │ │   讯飞   │           │   │
│  │  │ ERNIE Bot│ │ Qwen API │ │Hunyuan API│ │ Spark API │           │   │
│  │  └──────────┘ └──────────┘ └──────────┘ └──────────┘           │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

2.2 目录结构

backend/internal/pkg/
├── openai/              // 现有 OpenAI 客户端
   ├── client.go
   ├── types.go
   └── stream.go
├── anthropic/           // 现有 Anthropic 客户端
   └── ...
├── models/              // 新增: 模型提供商适配器
   ├── factory.go      // 工厂模式创建 client
   ├── interface.go    // 统一接口定义
   ├── base.go        // 基础实现
   ├── openai_compat.go    // OpenAI 兼容适配器
      ├── deepseek/       // DeepSeek
      ├── qwen/          // 通义千问
      ├── doubao/        // 豆包
      ├── minimax/       // MiniMax
      ├── zhipu/         // 智谱清言
      └── baichuan/      // 百川智能
   └── custom/            // 自定义协议
       ├── baidu/         // 百度文心
       ├── tencent/       // 腾讯混元
       └── xfyun/         // 讯飞星火
└── oauth/               // 现有 OAuth 处理

三、接口设计

3.1 统一 Provider 接口

// backend/internal/pkg/models/interface.go

package models

// Provider 模型提供商接口
type Provider interface {
    // Name 返回提供商名称
    Name() string
    
    // BaseURL 返回 API 基础地址
    BaseURL() string
    
    // Models 返回支持的模型列表
    Models() []Model
    
    // Chat 发起聊天请求
    Chat(ctx context.Context, req *ChatRequest) (*ChatResponse, error)
    
    // ChatStream 发起流式聊天请求
    ChatStream(ctx context.Context, req *ChatRequest) (*StreamReader, error)
    
    // Embeddings 获取嵌入向量
    Embeddings(ctx context.Context, req *EmbeddingsRequest) (*EmbeddingsResponse, error)
    
    // ValidateKey 验证 API 密钥有效性
    ValidateKey(ctx context.Context, key string) error
}

// Model 模型信息
type Model struct {
    ID          string   `json:"id"`           // 模型 ID
    Name        string   `json:"name"`         // 显示名称
    Provider    string   `json:"provider"`     // 提供商
    Type        string   `json:"type"`         // chat, embedding, image
    ContextSize int      `json:"context_size"` // 上下文长度
    MaxTokens   int      `json:"max_tokens"`   // 最大输出 tokens
    Capabilities []string `json:"capabilities"` // streaming, vision, function_call
}

// ChatRequest 聊天请求
type ChatRequest struct {
    Model       string                 `json:"model"`
    Messages    []ChatMessage          `json:"messages"`
    Temperature float64                `json:"temperature,omitempty"`
    MaxTokens   int                    `json:"max_tokens,omitempty"`
    Stream      bool                   `json:"stream,omitempty"`
    Tools       []Tool                 `json:"tools,omitempty"`
    // ... 其他参数
}

// ChatMessage 聊天消息
type ChatMessage struct {
    Role    string `json:"role"`    // system, user, assistant, tool
    Content string `json:"content"`
    // ...
}

3.2 工厂模式

// backend/internal/pkg/models/factory.go

package models

import (
    "errors"
)

var (
    ErrUnknownProvider = errors.New("unknown provider")
    
    // provider registry
    providers = make(map[string]func(cfg *ProviderConfig) Provider)
)

// RegisterProvider 注册模型提供商
func RegisterProvider(name string, factory func(cfg *ProviderConfig) Provider) {
    providers[name] = factory
}

// NewProvider 创建模型提供商实例
func NewProvider(name string, cfg *ProviderConfig) (Provider, error) {
    factory, ok := providers[name]
    if !ok {
        return nil, ErrUnknownProvider
    }
    return factory(cfg), nil
}

// ProviderConfig 提供商配置
type ProviderConfig struct {
    APIKey      string
    BaseURL     string
    Organization string
    HTTPClient  *http.Client
    Timeout     time.Duration
}

// Init 初始化内置提供商
func Init() {
    // OpenAI 兼容系列 (使用通用适配器)
    RegisterProvider("deepseek", NewOpenAICompatProvider)
    RegisterProvider("qwen", NewOpenAICompatProvider)
    RegisterProvider("doubao", NewOpenAICompatProvider)
    RegisterProvider("minimax", NewOpenAICompatProvider)
    RegisterProvider("zhipu", NewOpenAICompatProvider)
    RegisterProvider("baichuan", NewOpenAICompatProvider)
    RegisterProvider("anthropic", NewOpenAICompatProvider) // 复用
    
    // 自定义协议系列
    RegisterProvider("baidu", NewBaiduProvider)
    RegisterProvider("tencent", NewTencentProvider)
    RegisterProvider("xfyun", NewXfyunProvider)
}

四、模型配置

4.1 模型映射配置

# backend/config.yaml

models:
  # 现有模型
  - platform: openai
    name: GPT-4
    model_id: gpt-4
    enabled: true
    
  - platform: anthropic
    name: Claude 3.5
    model_id: claude-3-5-sonnet-20241022
    enabled: true

  # 新增国产模型
  
  # DeepSeek (OpenAI 兼容)
  - platform: deepseek
    name: DeepSeek Chat
    model_id: deepseek-chat
    base_url: https://api.deepseek.com/v1
    enabled: true
    
  - platform: deepseek
    name: DeepSeek Coder
    model_id: deepseek-coder
    base_url: https://api.deepseek.com/v1
    enabled: true

  # 通义千问 (OpenAI 兼容)
  - platform: qwen
    name: Qwen Turbo
    model_id: qwen-turbo
    base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
    enabled: true
    
  - platform: qwen
    name: Qwen Plus
    model_id: qwen-plus
    base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
    enabled: true
    
  - platform: qwen
    name: Qwen Max
    model_id: qwen-max
    base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
    enabled: true

  # 百度文心一言 (自定义)
  - platform: baidu
    name: ERNIE 4.0
    model_id: ernie-4.0-8k
    base_url: https://qianfan.baidubce.com/v2
    enabled: true
    
  - platform: baidu
    name: ERNIE 3.5
    model_id: ernie-3.5-8k
    base_url: https://qianfan.baidubce.com/v2
    enabled: true

  # 讯飞星火 (自定义)
  - platform: xfyun
    name: Spark Max
    model_id: spark-max
    base_url: https://spark-api.xf-yun.com/v3.5
    enabled: true

  # 腾讯混元 (OpenAI 兼容)
  - platform: tencent
    name: Hunyuan Turbo
    model_id: hunyuan-turbo
    base_url: https://hunyuan-api.tencentcloudapi.com/v1
    enabled: true

  # 豆包 (OpenAI 兼容)
  - platform: doubao
    name: Doubao Pro
    model_id: doubao-pro-32k
    base_url: https://ark.cn-beijing.volces.com/api/v3
    enabled: true
    
  # MiniMax (OpenAI 兼容)
  - platform: minimax
    name: MiniMax Text
    model_id: abab6.5s-chat
    base_url: https://api.minimax.chat/v1
    enabled: true
    
  # 智谱清言 (OpenAI 兼容)
  - platform: zhipu
    name: GLM-4
    model_id: glm-4
    base_url: https://open.bigmodel.cn/api/paas/v4
    enabled: true

4.2 数据库模型

// backend/ent/schema/platform.go

// Platform 模型平台
type Platform struct {
    ent.Schema
    
    Fields []ent.Field {
        String("name").Unique().NotEmpty(),      // 平台名称
        String("display_name").NotEmpty(),       // 显示名称
        String("api_base_url").Optional(),      // API 基础地址
        String("documentation").Optional(),     // 文档链接
        Bool("enabled").Default(true),          // 是否启用
        JSON("capabilities", []string{}),        // 能力列表
        JSON("auth_config", map[string]string{}),// 认证配置
        Time("created_at"),
        Time("updated_at"),
    }
    
    Edges []ent.Edge {
        OneToMany("models", Model.Type),
    }
}

五、各模型接入实现

5.1 DeepSeek 接入 (OpenAI 兼容)

// backend/internal/pkg/models/openai_compat/deepseek/deepseek.go

package deepseek

import (
    "context"
    "github.com/Wei-Shaw/sub2api/internal/pkg/models"
)

type DeepSeekProvider struct {
    *models.OpenAICompatProvider  // 嵌入通用 OpenAI 兼容实现
}

func New(cfg *models.ProviderConfig) models.Provider {
    baseURL := "https://api.deepseek.com/v1"
    if cfg.BaseURL != "" {
        baseURL = cfg.BaseURL
    }
    
    return &DeepSeekProvider{
        OpenAICompatProvider: models.NewOpenAICompatProvider(
            "deepseek",
            baseURL,
            []models.Model{
                {ID: "deepseek-chat", Name: "DeepSeek Chat", Type: "chat", ContextSize: 32*1024},
                {ID: "deepseek-coder", Name: "DeepSeek Coder", Type: "chat", ContextSize: 16*1024},
            },
            cfg,
        ),
    }
}

// 注册到工厂
func init() {
    models.RegisterProvider("deepseek", New)
}

5.2 百度文心接入 (自定义协议)

// backend/internal/pkg/models/custom/baidu/baidu.go

package baidu

import (
    "bytes"
    "context"
    "encoding/json"
    "errors"
    "net/http"
    "time"
    
    "github.com/Wei-Shaw/sub2api/internal/pkg/models"
)

type BaiduProvider struct {
    config     *models.ProviderConfig
    baseURL    string
    accessToken string
    tokenExpiry time.Time
}

func New(cfg *models.ProviderConfig) models.Provider {
    baseURL := "https://qianfan.baidubce.com/v2"
    if cfg.BaseURL != "" {
        baseURL = cfg.BaseURL
    }
    
    return &BaiduProvider{
        config:  cfg,
        baseURL: baseURL,
    }
}

func (p *BaiduProvider) Name() string { return "baidu" }
func (p *BaiduProvider) BaseURL() string { return p.baseURL }

func (p *BaiduProvider) Models() []models.Model {
    return []models.Model{
        {ID: "ernie-4.0-8k", Name: "ERNIE 4.0", Type: "chat", ContextSize: 8*1024, MaxTokens: 8*1024},
        {ID: "ernie-3.5-8k", Name: "ERNIE 3.5", Type: "chat", ContextSize: 8*1024, MaxTokens: 8*1024},
        {ID: "ernie-speed-8k", Name: "ERNIE Speed", Type: "chat", ContextSize: 8*1024, MaxTokens: 8*1024},
        {ID: "ernie-text-embedding-v1", Name: "ERNIE Embedding", Type: "embedding", ContextSize: 8*1024},
    }
}

// 获取 Access Token (百度需要 OAuth)
func (p *BaiduProvider) getAccessToken(ctx context.Context) (string, error) {
    if p.accessToken != "" && time.Now().Before(p.tokenExpiry) {
        return p.accessToken, nil
    }
    
    // 调用百度 OAuth 获取 token
    // POST https://aip.baidubce.com/oauth/2.0/token
    // grant_type=client_credentials&client_id=xxx&client_secret=xxx
    
    // 这里需要从 config 中获取 client_id 和 client_secret
    // 暂时简化处理,实际需要完善
    return p.accessToken, nil
}

func (p *BaiduProvider) Chat(ctx context.Context, req *models.ChatRequest) (*models.ChatResponse, error) {
    token, err := p.getAccessToken(ctx)
    if err != nil {
        return nil, errors.New("failed to get access token: " + err.Error())
    }
    
    // 构建请求
    url := p.baseURL + "/chat/completions"
    
    // 转换消息格式
    messages := make([]map[string]interface{}, len(req.Messages))
    for i, m := range req.Messages {
        messages[i] = map[string]interface{}{
            "role":    m.Role,
            "content": m.Content,
        }
    }
    
    body := map[string]interface{}{
        "model":   req.Model,
        "messages": messages,
    }
    if req.Temperature > 0 {
        body["temperature"] = req.Temperature
    }
    if req.MaxTokens > 0 {
        body["max_tokens"] = req.MaxTokens
    }
    
    jsonBody, _ := json.Marshal(body)
    
    httpReq, _ := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonBody))
    httpReq.Header.Set("Content-Type", "application/json")
    httpReq.Header.Set("Authorization", "Bearer "+token)
    
    // 发送请求...
    // 处理响应...
    
    return nil, nil // 简化,实际需要完整实现
}

func (p *BaiduProvider) ValidateKey(ctx context.Context, key string) error {
    // 验证 API Key 有效性
    // 可以调用 /me 接口或获取 token 测试
    return nil
}

// 注册到工厂
func init() {
    models.RegisterProvider("baidu", New)
}

5.3 通义千问接入 (OpenAI 兼容)

// backend/internal/pkg/models/openai_compat/qwen/qwen.go

package qwen

import (
    "github.com/Wei-Shaw/sub2api/internal/pkg/models"
)

type QwenProvider struct {
    *models.OpenAICompatProvider
}

func New(cfg *models.ProviderConfig) models.Provider {
    baseURL := "https://dashscope.aliyuncs.com/compatible-mode/v1"
    if cfg.BaseURL != "" {
        baseURL = cfg.BaseURL
    }
    
    return &QwenProvider{
        OpenAICompatProvider: models.NewOpenAICompatProvider(
            "qwen",
            baseURL,
            []models.Model{
                {ID: "qwen-turbo", Name: "Qwen Turbo", Type: "chat", ContextSize: 8*1024},
                {ID: "qwen-plus", Name: "Qwen Plus", Type: "chat", ContextSize: 32*1024},
                {ID: "qwen-max", Name: "Qwen Max", Type: "chat", ContextSize: 8*1024},
                {ID: "qwen-long", Name: "Qwen Long", Type: "chat", ContextSize: 320*1024},
            },
            cfg,
        ),
    }
}

func init() {
    models.RegisterProvider("qwen", New)
}

六、前端集成

6.1 模型选择下拉框

<!-- frontend/src/components/common/ModelSelect.vue -->

<template>
  <Select
    v-model="selectedModel"
    filterable
    :loading="loading"
    @change="handleChange"
  >
    <OptionGroup :label="t('model.platform.openai')">
      <Option
        v-for="model in openaiModels"
        :key="model.id"
        :value="model.id"
        :label="model.name"
      >
        <div class="model-option">
          <span>{{ model.name }}</span>
          <Tag v-if="model.status === 'beta'" size="small">Beta</Tag>
        </div>
      </Option>
    </OptionGroup>
    
    <OptionGroup :label="t('model.platform.anthropic')">
      <Option
        v-for="model in anthropicModels"
        :key="model.id"
        :value="model.id"
        :label="model.name"
      />
    </OptionGroup>
    
    <!-- 新增国产模型 -->
    <OptionGroup :label="t('model.platform.chinese')">
      <Option
        v-for="model in chineseModels"
        :key="model.id"
        :value="model.id"
        :label="model.name"
      />
    </OptionGroup>
  </Select>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'

const { t } = useI18n()

// 按平台分组
const chineseModels = computed(() => 
  props.models.filter(m => 
    ['deepseek', 'qwen', 'baidu', 'xfyun', 'tencent', 'doubao', 'minimax', 'zhipu'].includes(m.platform)
  )
)
</script>

6.2 模型定价配置

# backend/resources/model-pricing/model-pricing.yaml

models:
  # OpenAI
  gpt-4:
    input: 0.03    # $/1M tokens
    output: 0.06
  gpt-4-turbo:
    input: 0.01
    output: 0.03
    
  # Anthropic
  claude-3-5-sonnet:
    input: 0.003
    output: 0.015
    
  # 国产模型
  deepseek-chat:
    input: 0.14
    output: 0.14
    
  qwen-max:
    input: 0.20
    output: 0.60
    
  ernie-4.0-8k:
    input: 0.20
    output: 0.60
    
  spark-max:
    input: 0.03
    output: 0.05

七、任务拆分

7.1 开发任务清单

序号 任务 模块 预估工时 依赖
1 创建 Provider 接口和工厂 core 2天 -
2 实现 OpenAI 兼容基类 base 2天 1
3 接入 DeepSeek provider 1天 2
4 接入通义千问 provider 1天 2
5 接入豆包 provider 1天 2
6 接入 MiniMax provider 1天 2
7 接入智谱清言 provider 1天 2
8 接入百川智能 provider 1天 2
9 接入百度文心 (自定义) provider 2天 1
10 接入腾讯混元 provider 1天 2
11 接入讯飞星火 provider 2天 1
12 更新前端模型选择器 frontend 2天 3-11
13 添加模型定价配置 config 1天 -
14 编写使用文档 docs 1天 -

7.2 实施顺序

Week 1: 基础设施 (接口 + 工厂 + 基类)
         ↓
Week 2: OpenAI 兼容系列 (DeepSeek, Qwen, 豆包, MiniMax, 智谱, 百川)
         ↓
Week 3: 自定义协议系列 (百度, 腾讯, 讯飞)
         ↓
Week 4: 前端集成 + 测试 + 文档

八、兼容性考虑

8.1 与官方 Sub2API 兼容

// 兼容性策略:
// 1. 不修改核心 gateway 逻辑
// 2. 保持 v1/models, v1/chat/completions API 兼容
// 3. 新增 provider 不影响现有功能
// 4. 通过配置开关控制是否启用

8.2 版本管理

v1.0.x - 基础功能 (当前)
v1.1.x - 国产模型接入
v1.2.x - 更多模型 + 高级特性

九、风险与挑战

风险 应对方案
各厂商 API 变更 版本化适配器,及时更新
认证方式差异 统一抽象认证层
响应格式不统一 标准化响应转换
限流/配额处理 实现统一的限流控制

十、总结

本方案详细规划了国产模型接入的完整路径:

  1. 架构设计: 工厂模式 + OpenAI兼容适配器 + 自定义协议适配器
  2. 优先级: DeepSeek/通义千问 → 豆包/MiniMax → 百度/腾讯/讯飞
  3. 工作量: 约 3-4 周完成核心接入
  4. 兼容性: 保持与官方 Sub2API 的兼容

需要我开始实现哪个模型接入吗?建议从 DeepSeek 开始,因为它是 OpenAI 兼容,实现难度最低。


文档版本: v1.0
最后更新: 2026-03-26