AI大模型接入SDK—LLMProvider实现

LLMProvider 接口与四类模型提供者实现

一、策略模式设计概述

项目采用**策略模式(Strategy Pattern)**设计 LLM 提供者层,将不同模型的调用逻辑封装到独立的实现类中,通过统一的接口对外暴露。

设计目标

  • 统一接口:所有模型提供者遵循相同的调用契约
  • 解耦扩展:新增模型只需实现 LLMProvider 接口,无需修改已有代码
  • 多态调用:上层代码通过基类指针调用,运行时动态分发到具体实现

1.1 类图关系

                    ┌─────────────────────┐
                    │    LLMProvider      │  (抽象策略接口)
                    │ ─────────────────── │
                    │ + initModel()       │
                    │ + isProviderAvailable() │
                    │ + getProviderName() │
                    │ + getProviderDesc() │
                    │ + sendMessage()     │
                    │ + sendMessageStream() │
                    │ ─────────────────── │
                    │ # _isAvailable      │
                    │ # _apiKey           │
                    │ # _endpoint         │
                    └──────────┬──────────┘
                               │
           ┌───────────────────┼───────────────────┐
           │                   │                   │
           ▼                   ▼                   ▼
    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
    │ChatGPT      │    │DeepSeek     │    │Gemini       │
    │Provider     │    │Provider     │    │Provider     │
    └─────────────┘    └─────────────┘    └─────────────┘
                                              
                                    ┌─────────────┐
                                    │OllamaLLM    │
                                    │Provider     │
                                    │ ─────────── │
                                    │ # _modelName│
                                    │ # _modelDesc│
                                    └─────────────┘

二、LLMProvider 抽象接口

2.1 接口定义

class LLMProvider
{
public:
    // 初始化模型(纯虚函数)
    virtual bool initModel(const std::map<std::string, std::string>& modelConfig) = 0;
    
    // 检查提供方是否可用
    virtual bool isProviderAvailable() const = 0;
    
    // 获取提供方名称/描述
    virtual std::string getProviderName() const = 0;
    virtual std::string getProviderDesc() const = 0;
    
    // 发送消息——全量返回
    virtual std::string sendMessage(const std::vector<Message>& messages,
                                    const std::map<std::string, std::string>& requestParams) = 0;
    
    // 发送消息——流式返回
    virtual std::string sendMessageStream(const std::vector<Message>& 												messages,
                                          const std::map<std::string, 												std::string>& requestParams,
                                          std::function<void(const 													std::string&, bool)> callback) = 0;

protected:
    bool _isAvailable = false;      // 提供方是否可用
    std::string _apiKey;            // API 密钥
    std::string _endpoint;          // API 端点地址
};

2.2 接口方法说明

方法说明返回值
initModel()初始化模型,设置 API Key、Endpoint 等配置bool,初始化是否成功
isProviderAvailable()检查模型是否已初始化并可用bool
getProviderName()获取模型名称标识std::string
getProviderDesc()获取模型描述信息std::string
sendMessage()发送消息并等待完整响应std::string,完整回复内容
sendMessageStream()流式发送消息,通过回调逐步返回std::string,聚合的完整回复

2.3 纯虚函数与保护成员

设计要点

  • 所有方法声明为纯虚函数= 0),强制子类实现
  • _isAvailable_apiKey_endpoint 声明为 protected,子类可直接访问
  • 子类可根据需要扩展额外的成员变量(如 OllamaLLMProvider_modelName

三、四类 Provider 实现详解

3.1 DeepSeekProvider(标准 OpenAI 兼容)

3.1.1 基本信息
属性
模型名称deepseek-chat
API 端点https://api.deepseek.com
API 路径/v1/chat/completions
协议格式OpenAI 兼容格式
3.1.2 initModel 实现
bool DeepSeekProvider::initModel(const std::map<std::string, std::string>& modelConfig)
{
    // 1. 提取 api_key
    auto it = modelConfig.find("api_key");
    if(it == modelConfig.end()) {
        ERR("api_key not found");
        return false;
    }
    _apiKey = it->second;

    // 2. 提取 endpoint(可选,有默认值)
    it = modelConfig.find("endpoint");
    if(it == modelConfig.end()) {
        _endpoint = "https://api.deepseek.com";  // 默认端点
    } else {
        _endpoint = it->second;
    }
    
    _isAvailable = true;
    return true;
}

特点

  • endpoint 参数可选,提供默认值
  • 只需 api_key 即可完成初始化
3.1.3 sendMessage(全量返回)实现流程
┌──────────────────────────────────────────────────────────────┐
│                    sendMessage 全量流程                       │
├──────────────────────────────────────────────────────────────┤
│  1. 检查 _isAvailable                                         │
│     │                                                         │
│     ▼                                                         │
│  2. 提取请求参数(temperature, max_tokens)                    │
│     │                                                         │
│     ▼                                                         │
│  3. 构造 JSON 请求体                                           │
│     {                                                         │
│       "model": "deepseek-chat",                                │
│       "messages": [...],                                      │
│       "temperature": 0.7,                                     │
│       "max_tokens": 2048                                      │
│     }                                                         │
│     │                                                         │
│     ▼                                                         │
│  4. 序列化 JSON                                               │
│     │                                                         │
│     ▼                                                         │
│  5. 创建 httplib::Client,设置超时                             │
│     │                                                         │
│     ▼                                                         │
│  6. POST /v1/chat/completions                                 │
│     │                                                         │
│     ▼                                                         │
│  7. 检查响应状态码(200)                                       │
│     │                                                         │
│     ▼                                                         │
│  8. 解析响应 JSON,提取 choices[0].message.content             │
│     │                                                         │
│     ▼                                                         │
│  9. 返回回复内容                                               │
└──────────────────────────────────────────────────────────────┘

请求体格式

{
    "model": "deepseek-chat",
    "messages": [
        {"role": "user", "content": "你好"}
    ],
    "temperature": 0.7,
    "max_tokens": 2048
}

响应体格式

{
    "choices": [
        {
            "message": {
                "role": "assistant",
                "content": "你好!有什么我可以帮助你的吗?"
            }
        }
    ]
}
3.1.4 sendMessageStream(流式返回)实现流程
┌──────────────────────────────────────────────────────────────┐
│                sendMessageStream 流式流程                     │
├──────────────────────────────────────────────────────────────┤
│  1. 构造请求体,添加 "stream": true                            │
│     │                                                         │
│     ▼                                                         │
│  2. 设置请求头 Accept: text/event-stream                       │
│     │                                                         │
│     ▼                                                         │
│  3. 创建 httplib::Request 对象                                 │
│     │                                                         │
│     ▼                                                         │
│  4. 设置 response_handler(检查状态码)                         │
│     │                                                         │
│     ▼                                                         │
│  5. 设置 content_receiver(数据接收器)                         │
│     │                                                         │
│     ├──▶ 追加数据到 buffer                                     │
│     │                                                         │
│     ├──▶ 按 "\n\n" 分割数据块                                  │
│     │                                                         │
│     ├──▶ 解析 "data: " 前缀的 SSE 事件                         │
│     │                                                         │
│     ├──▶ 检测 "[DONE]" 结束标记                                │
│     │                                                         │
│     ├──▶ 提取 delta.content 增量内容                           │
│     │                                                         │
│     └──▶ 调用 callback(content, false)                         │
│     │                                                         │
│     ▼                                                         │
│  6. 流结束,调用 callback("", true)                            │
│     │                                                         │
│     ▼                                                         │
│  7. 返回聚合的完整响应                                         │
└──────────────────────────────────────────────────────────────┘

SSE 数据格式

data: {"choices":[{"delta":{"content":"你"}}]}

data: {"choices":[{"delta":{"content":"好"}}]}

data: {"choices":[{"delta":{"content":"!"}}]}

data: [DONE]

流式处理关键代码

// 设置数据接收处理器
req.content_receiver = [&](const char* data, size_t len, size_t offset, size_t totallength)
{
    buffer.append(data, len);
    
    // 按 "\n\n" 分割 SSE 事件
    size_t pos = 0;
    while((pos = buffer.find("\n\n", pos)) != std::string::npos)
    {
        std::string chunk = buffer.substr(0, pos);
        buffer.erase(0, pos + 2);
        
        if(chunk.compare(0, 6, "data: ") == 0)
        {
            std::string modelData = chunk.substr(6);
            
            if(modelData == "[DONE]") {
                streamFinished = true;
                return true;
            }
            
            // 解析 JSON 提取增量内容
            Json::Value modelDataJson;
            // ...
            std::string content = modelDataJson["choices"][0]["delta"]["content"].asString();
            fullResponse += content;
            callback(content, false);
        }
    }
    return true;
};

3.2 ChatGPTProvider(OpenAI 新 API 格式)

3.2.1 基本信息
属性
模型名称gpt-4o-mini
API 端点https://api.openai.com
API 路径/v1/responses(新 API)
代理设置http://127.0.0.1:7890(硬编码)
3.2.2 与 DeepSeek 的主要差异
差异点ChatGPTProviderDeepSeekProvider
API 路径/v1/responses/v1/chat/completions
请求字段input(数组)messages(数组)
响应字段output[0].content[0].textchoices[0].message.content
流式事件response.output_text_deltadata: + delta.content
代理强制设置代理无代理
3.2.3 请求体格式差异

ChatGPT 请求体

{
    "model": "gpt-4o-mini",
    "input": [                          // 注意:使用 input 而非 messages
        {"role": "user", "content": "你好"}
    ],
    "temperature": 0.7,
    "max_output_tokens": 2048           // 注意:字段名不同
}

响应体格式

{
    "output": [
        {
            "content": [
                {"text": "你好!有什么我可以帮助你的吗?"}
            ]
        }
    ]
}
3.2.4 流式事件类型

ChatGPT 使用**事件类型(event type)**区分不同阶段:

事件类型说明
response.output_text_delta增量文本内容
response.output_item.done单个输出项完成(包含完整文本)
response.completed整个响应完成

SSE 格式

event: response.output_text_delta
data: {"delta": "你"}

event: response.output_item.done
data: {"item": {"content": [{"text": "你好!"}]}}

event: response.completed
data: {}
3.2.5 代理设置(硬编码)
// 硬编码代理地址
client.set_proxy("http://127.0.0.1", 7890);

问题:代理地址硬编码,不同网络环境需要修改源码。


3.3 GeminiProvider(Google OpenAI 兼容模式)

3.3.1 基本信息
属性
模型名称gemini-2.0-flash
API 端点https://generativelanguage.googleapis.com
API 路径/v1beta/openai/chat/completions
协议格式OpenAI 兼容模式
3.3.2 与 DeepSeek 的差异
差异点GeminiProviderDeepSeekProvider
API 路径/v1beta/openai/chat/completions/v1/chat/completions
代理强制设置代理无代理
响应格式OpenAI 兼容(相同)OpenAI 兼容(相同)
3.3.3 initModel 特殊点
// 注意:配置键名是 "api_Key"(大小写敏感)
auto apiKey_it = modelConfig.find("api_Key");
auto endpointIt = modelConfig.find("endpoint");

if(apiKey_it == modelConfig.end() || endpointIt == modelConfig.end()) {
    ERR("api_Key or endpoint not found");
    return false;
}

问题:键名 "api_Key" 与其他 Provider 的 "api_key" 不一致(大小写差异)。

3.3.4 流式响应

与 DeepSeek 相同,使用标准 SSE 格式,通过 delta.content 提取增量内容。


3.4 OllamaLLMProvider(本地模型)

3.4.1 基本信息
属性
模型名称用户指定(如 deepseek-r1:1.5b
API 端点用户指定(如 http://localhost:11434
API 路径/api/chat
认证方式无需 API Key
3.4.2 特殊成员变量
protected:
    std::string _modelName;     // 模型名称(动态)
    std::string _modelDesc;     // 模型描述信息

特点:不使用 _apiKey,模型名称通过配置动态指定。

3.4.3 initModel 实现
bool OllamaLLMProvider::initModel(const std::map<std::string, std::string>& modelConfig)
{
    // 必需参数:model_name
    if(modelConfig.find("model_name") == modelConfig.end()) {
        ERR("model_name not found");
        return false;
    }
    _modelName = modelConfig.at("model_name");
    
    // 必需参数:model_desc
    if(modelConfig.find("model_desc") == modelConfig.end()) {
        ERR("model_desc not found");
        return false;
    }
    _modelDesc = modelConfig.at("model_desc");
    
    // 必需参数:endpoint
    if(modelConfig.find("endpoint") == modelConfig.end()) {
        ERR("endpoint not found");
        return false;
    }
    _endpoint = modelConfig.at("endpoint");
    
    _isAvailable = true;
    return true;
}

必需配置项

  • model_name:Ollama 模型名称(如 deepseek-r1:1.5b
  • model_desc:模型描述(用于展示)
  • endpoint:Ollama 服务地址(如 http://localhost:11434
3.4.4 请求体格式
{
    "model": "deepseek-r1:1.5b",
    "messages": [
        {"role": "user", "content": "你好"}
    ],
    "options": {
        "temperature": 0.7,
        "num_ctx": 1024
    },
    "stream": false
}

差异

  • 使用 options 包装温度等参数
  • 使用 num_ctx 而非 max_tokens
3.4.5 响应体格式
{
    "message": {
        "role": "assistant",
        "content": "你好!有什么我可以帮助你的吗?"
    }
}

差异:响应结构与其他 API 不同,没有 choices 数组。

3.4.6 流式响应格式(非 SSE)

关键差异:Ollama 流式响应不使用 SSE 格式,而是每行一个 JSON 对象:

{"message":{"content":"你"},"done":false}
{"message":{"content":"好"},"done":false}
{"message":{"content":"!"},"done":true}

流式处理代码

// 按 "\n" 分割,而非 "\n\n"
while((pos = buffer.find("\n", pos)) != std::string::npos)
{
    std::string chunk = buffer.substr(0, pos);
    buffer.erase(0, pos + 1);
    
    // 解析 JSON
    Json::Value chunkJson;
    // ...
    
    // 检查结束标记:done 字段
    if(chunkJson.get("done", false).asBool()) {
        streamFinish = true;
        callback("", true);
        return true;
    }
    
    // 提取内容
    std::string delta = chunkJson["message"]["content"].asString();
    fullData += delta;
    callback(delta, false);
}

四、四种 Provider 对比总结

4.1 基础信息对比

Provider模型名称API 路径协议格式认证方式
DeepSeekProviderdeepseek-chat/v1/chat/completionsOpenAI 标准Bearer Token
ChatGPTProvidergpt-4o-mini/v1/responsesOpenAI 新格式Bearer Token
GeminiProvidergemini-2.0-flash/v1beta/openai/chat/completionsOpenAI 兼容Bearer Token
OllamaLLMProvider用户指定/api/chatOllama 原生

4.2 初始化参数对比

Provider必需参数可选参数
DeepSeekProviderapi_keyendpoint(有默认值)
ChatGPTProviderapi_key, endpoint
GeminiProviderapi_Key, endpoint
OllamaLLMProvidermodel_name, model_desc, endpoint

4.3 请求体格式对比

// DeepSeek / Gemini(OpenAI 标准)
{
    "model": "deepseek-chat",
    "messages": [...],
    "temperature": 0.7,
    "max_tokens": 2048,
    "stream": true
}

// ChatGPT(新 API)
{
    "model": "gpt-4o-mini",
    "input": [...],                    // 字段名不同
    "temperature": 0.7,
    "max_output_tokens": 2048,         // 字段名不同
    "stream": true
}

// Ollama(原生格式)
{
    "model": "deepseek-r1:1.5b",
    "messages": [...],
    "options": {                       // 嵌套结构
        "temperature": 0.7,
        "num_ctx": 1024                // 字段名不同
    },
    "stream": true
}

4.4 流式响应处理对比

Provider数据分割符结束标记增量内容路径
DeepSeekProvider\n\ndata: [DONE]choices[0].delta.content
ChatGPTProvider\n\nevent: response.completeddeltaoutput_item.done
GeminiProvider\n\ndata: [DONE]choices[0].delta.content
OllamaLLMProvider\ndone: truemessage.content
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hehelm

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值