Provider分析与实现:实战应用指南

文章配图

🎬 个人主页 :艾莉丝努力练剑
❄专栏传送门 :《C语言》《数据结构与算法》《C/C++干货分享 &学习过程记录》
《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平


🎬 艾莉丝的简介:


文章目录

  • 1 ~> LLMProvider
    • 1.1 业务背景与需求分析
      • 1.1.1 业务场景
    • 1.1.2 模型调用原始操作步骤
    • 1.1.3 Provider 必须实现的能力集合
    • 1.1.4 共性与差异分析
    • 1.2 C++ 继承、多态与抽象类设计
      • 1.2.1 问题:直接硬编码各个模型类的弊端
    • 1.2.2 解决方案:抽取顶层抽象基类 LLMProvider
    • 1.2.3 C++ 多态调用
    • 1.3 底层设计模式:策略模式
      • 1.3.1 策略模式核心定义
    • 1.3.2 通俗示例代码(出行策略)
    • 1.3.3 映射到本项目
    • 1.4 LLMProvider 抽象基类完整头文件实现
    • 1.5 派生实现类工作逻辑(例如 DeepSeekProvider)
    • 1.6 整体架构分层总览
    • 1.7 架构优势总结
    • 1.8 面试 / 考试易错考点
  • 结尾


1 ~> LLMProvider

基于 C++ 策略模式实现多大模型统一接入 SDK。

1.1 业务背景与需求分析

1.1.1 业务场景

需要接入多款大模型(DeepSeek、ChatGPT、Gemini、Ollama 本地模型等),对外提供统一 SDK,上层业务不需要感知底层模型厂商 API 差异。 前期已完成公共数据结构、日志库封装,现在完成模型接入层架构设计。

1.1.2 模型调用原始操作步骤

  1. 获取模型厂商 API‑Key
  2. 阅读厂商 HTTP API 文档
  3. 程序内部发起 HTTP 请求调用模型接口

1.1.3 Provider 必须实现的能力集合

每一个模型接入实现类,都必须具备以下功能:

  • 模型初始化:加载配置(apikey、endpoint 等)
  • 初始化状态检测:判断模型是否配置合法、可用
  • 非流式消息发送:stream=false,全量一次性返回完整应答
  • 流式消息发送:stream=true,增量分片返回,流式回调输出
  • 获取模型名称
  • 获取模型描述信息

使用场景:前端页面展示可用模型列表,展示每个模型名称、简介、能力标签。

1.1.4 共性与差异分析

  • 公共部分 :初始化、可用性检测、发送消息接口定义、持有 apikey/endpoint/ 可用状态标记
  • 差异部分
    • 每个模型的 base‑url (endpoint) 不同
    • 模型名称、描述文本不同
    • HTTP 请求体参数结构不同
    • HTTP 响应 JSON 解析逻辑不同

1.2 C++ 继承、多态与抽象类设计

1.2.1 问题:直接硬编码各个模型类的弊端

如果每个模型独立写一套完整类,会出现大量重复代码;业务层调用需要大量if‑else分支判断模型类型:

// 糟糕的if‑else写法
if(modelName == "deepseek")
{
    DeepSeekProvider dp;
    dp.sendMessage(...);
}
else if(modelName == "chatgpt")
{
    ChatGPTProvider gp;
    gp.sendMessage(...);
}
// 新增模型必须修改此处业务判断代码,违反开闭原则

1.2.2 解决方案:抽取顶层抽象基类 LLMProvider

  1. 将所有模型公共接口 抽至顶层基类;
  2. 将接口声明为纯虚函数 ,virtual 函数签名 = 0;,使LLMProvider成为抽象类 。
  3. 抽象类禁止实例化:基类不绑定任何具体大模型,实例化无业务意义;只定义接口规范,由子类完成全部业务实现。
  4. 各个厂商模型实现类继承该抽象基类,重写全部纯虚函数。

继承关系:

  • 基类:LLMProvider(抽象类 / 接口类,只定义契约)
  • 派生实现类:DeepSeekProvider / ChatGPTProvider / GeminiProvider / OllamaDeepSeekProvider

1.2.3 C++ 多态调用

基类引用 / 基类指针可以指向派生类对象。业务函数只依赖抽象基类,运行时传入不同子类对象,自动调用对应子类实现,消除大量 if‑else。

// 业务层统一发送消息函数,只依赖抽象基类
void SendMessage(LLMProvider& provider)
{
    // 根据传入对象的实际类型,自动调用子类重写的sendMessage
    provider.sendMessage(...);
}

1.3 底层设计模式:策略模式

1.3.1 策略模式核心定义

定义一系列算法 / 行为,将每一套行为封装为独立类,各个实现类可以互相替换;通过抽象接口隔离调用方与具体实现,运行时动态选择策略,消除 if‑else 分支,符合开闭原则。

  • 抽象策略:抽象基类,定义统一接口
  • 具体策略:各个派生子类,实现具体逻辑
  • 上下文:业务调用方,只操作抽象策略,不感知底层实现

1.3.2 通俗示例代码(出行策略)

#include <iostream>

// 抽象策略
class TransportStrategy{
public:
    virtual void go() = 0;
};

// 具体策略:走路
class WalkStrategy : public TransportStrategy{
public:
    void go() override{
        std::cout << "走路去往目的地\n";
    }
};

// 具体策略:自行车
class BikeStrategy : public TransportStrategy{
public:
    void go() override{
        std::cout << "骑自行车去往目的地\n";
    }
};

文章配图

// 具体策略:巴士
class BusStrategy : public TransportStrategy{
public:
    void go() override{
        std::cout << "坐巴士去往目的地\n";
    }
};

// 上下文:使用者
class Student{
private:
    TransportStrategy* m_strategy;
public:
    void setStrategy(TransportStrategy* s){
        m_strategy = s;
    }
    void goToLab(){
        m_strategy->go();
    }
};

int main()
{
    Student me;
    me.setStrategy(new WalkStrategy());
    me.goToLab();

    me.setStrategy(new BusStrategy());
    me.goToLab();
    return 0;
}

1.3.3 映射到本项目

  • 抽象策略:LLMProvider抽象基类,定义所有模型必须实现的接口契约
  • 具体策略:DeepSeekProvider、ChatGPTProvider等每一个模型实现类
  • 上下文:SDK 上层、LLMManager管理器,只和抽象基类交互,运行时切换不同模型策略
  • 开闭原则:新增模型只需要新增派生类,不需要修改原有上层业务代码

1.4 LLMProvider 抽象基类完整头文件实现

文件路径:sdk/include/LLMProvider.h

#ifndef LLM_PROVIDER_H
#define LLM_PROVIDER_H

#include <string>
#include <map>
#include <vector>
#include <functional>

namespace ai_chat_sdk {

// 消息结构体,对话历史单元,前置公共数据结构
struct Message{
std::string role;
std::string content;
};

/**
* @brief LLMProvider 大模型抽象基类(抽象接口)
* 所有大模型接入都继承该类,实现全部纯虚函数
* 不允许实例化,定义统一的模型访问契约
*/
class LLMProvider
{
public:
// 虚析构:多态继承场景必须提供虚析构,保证子类析构被正确调用
virtual ~LLMProvider() = default;

/**
* @brief 初始化模型,传入模型配置键值对
* @param modelConfig key‑value配置:apikey、endpoint等
*/
virtual void initModel(const std::map<std::string, std::string>& modelConfig) = 0;

/**
* @brief 检测模型是否初始化完成、可用
* @return true可用;false不可用
*/
virtual bool isAvailable() const = 0;

/// @brief 获取模型名称
virtual std::string getModelName() const = 0;

/// @brief 获取模型描述信息
virtual std::string getModelDesc() const = 0;

/**
* @brief 发送消息,非流式,全量一次性返回结果
* @param messages 完整对话上下文
* @param requestParam 模型请求参数 temperature/max_tokens等
*/
virtual void sendMessage(const std::vector<Message>& messages,
const std::map<std::string, std::string>& requestParam) = 0;

/**
* @brief 发送消息,流式增量返回
* @param messages 对话上下文
* @param requestParam 请求参数
* @param callback 回调函数:参数1分片字符串,参数2是否为最后一块数据
* bool参数=true代表还有后续分片;false代表流式结束
*/
virtual void sendMessageStream(const std::vector<Message>& messages,
const std::map<std::string, std::string>& requestParam,
std::function<void(const std::string&, bool)> callback) = 0;

protected:
// 基类保护成员,派生类可以直接访问
bool m_isAvailable = false; ///< 标记模型是否初始化可用
std::string m_apiKey; ///< 模型厂商API密钥
std::string m_endpoint; ///< API base‑url请求端点
};

} // namespace ai_chat_sdk

#endif

关键修正点说明

  1. 增加虚析构函数virtual ~LLMProvider() = default;,多态继承场景必不可少,防止子类析构不执行内存泄漏;
  2. 将成员变量修改为protected,子类可以直接访问,原文档写 private 子类无法读取配置;
  3. 修正原文档多处拼写笔误 initodel → initModel、getodelDesc → getModelDesc;
  4. 回调std::function参数 const 引用,避免不必要拷贝;
  5. 补充头文件保护宏#ifndef / #define / #endif。

1.5 派生实现类工作逻辑(例如 DeepSeekProvider)

DeepSeekProvider : public LLMProvider
├─ initModel:从map读取apikey、endpoint,赋值给继承而来的m_apiKey/m_endpoint,设置m_isAvailable状态
├─ isAvailable:返回 m_isAvailable
├─ getModelName:返回固定字符串 "deepseek‑chat"
├─ getModelDesc:返回该模型的描述文本
├─ sendMessage:组装DeepSeek HTTP请求体,发起HTTP调用,解析完整JSON响应
└─ sendMessageStream:组装请求开启stream,读取SSE流式响应,分片调用callback回调

1.6 整体架构分层总览

┌─────────────── LLMManager(管理器)───────────────┐
│   业务层只操作抽象基类LLMProvider,不感知具体模型    │
└───────────────────────┬──────────────────────────┘
                        ↓
        ┌────────── LLMProvider(抽象基类) ──────────┐
        │ 纯虚接口定义:initModel / sendMessage / ...│
        └───┬───────────────┬───────────────┬──────┘
            ↓               ↓               ↓
    DeepSeekProvider  ChatGPTProvider  GeminiProvider
实现厂商HTTP交互细节,封装API差异

1.7 架构优势总结

  1. 消除大量 if‑else :依靠 C++ 多态策略模式,运行时动态切换模型实现。
  2. 开闭原则 :新增大模型,只需要新增继承LLMProvider的派生类,原有上层代码不需要修改。
  3. 统一 SDK 对外接口 :第三方使用者只需要依赖抽象接口,不需要关心每家模型 HTTP 细节。

  4. 接口强制约束 :抽象类纯虚函数强制派生类必须实现全部规定能力,不会出现遗漏功能。
  5. 兼容云端 API 模型、本地 Ollama 部署模型,同一套框架复用。

1.8 面试 / 考试易错考点

  1. 抽象类含有纯虚函数,不能实例化对象 ;
  2. 多态继承必须写虚析构函数 ,否则 delete 基类指针时子类析构不会执行;
  3. 纯虚函数语法:virtual 返回类型 函数名(参数) = 0;;
  4. 策略模式核心:封装可变行为,运行时替换,隔离调用方与具体实现;
  5. protected 访问限定符:基类成员希望子类可以访问、外部不能访问时使用,不能设置为 private。

结尾

uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!

0

评论0

请先
显示验证码
没有账号?注册  忘记密码?