【AI大模型接入SDK】Provider分析与实现

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

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


🎬 艾莉丝的简介:


文章目录

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

C++ 复制代码
// 糟糕的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。

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

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

1.3.1 策略模式核心定义

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

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

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

C++ 复制代码
#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抽象基类,定义所有模型必须实现的接口契约
  • 具体策略:DeepSeekProviderChatGPTProvider等每一个模型实现类
  • 上下文:SDK 上层、LLMManager管理器,只和抽象基类交互,运行时切换不同模型策略
  • 开闭原则:新增模型只需要新增派生类,不需要修改原有上层业务代码

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

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

C++ 复制代码
#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 → initModelgetodelDesc → getModelDesc
  4. 回调std::function参数 const 引用,避免不必要拷贝;
  5. 补充头文件保护宏#ifndef / #define / #endif

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

Bash 复制代码
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 整体架构分层总览

Bash 复制代码
┌─────────────── 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们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!

|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ### 艾莉丝努力练剑 C/C++ & Linux 底层探索者 | 一个正在努力练剑的技术博主 *** ** * ** *** 👀 【关注】 跟随我一起深耕技术领域,见证每一次成长。 ❤️ 【点赞】 让优质内容被更多人看见,让知识传递更有力量。 ⭐ 【收藏】 把核心知识点存好,在需要时随时查、随时用。 💬 【评论】 分享你的经验或疑问,评论区一起交流避坑! 不要忘记给博主"一键四连"哦! "今日练剑达成!" "技术之路难免有困惑,但同行的人会让前进更有方向。" |

结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主"一键四连"哦!

往期回顾

【AI大模型接入SDK】spdlog日志库封装

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა

相关推荐
磁场转动100万匹12 分钟前
PyTorch 手写数字识别实战:从数据加载到模型训练(零基础详解版)
人工智能·pytorch·python
TaoMetrix6 小时前
TaoMetrix:AI 硬件创业公司如何做好原型制造
人工智能·制造
智圣新创016 小时前
面向多级组织协同场景 高校第二课堂一站式管理中枢落地全场景实操答疑
大数据·人工智能
cd_949217216 小时前
AI纹理和Substance Painter手绘纹理哪个更适合游戏资产制作?
人工智能·游戏·substance painter
sali-tec6 小时前
C# 基于OpenCv的视觉工作流-章106-差值追踪
图像处理·人工智能·opencv·算法·计算机视觉
Shockang7 小时前
用 AI 打造高品质 Web 应用
人工智能
l1258657 小时前
# LangGraph Memory机制深度解析:短期记忆与长期记忆的工程实践
前端·人工智能·python·langchain·bootstrap
手写码匠7 小时前
Dify 多 Agent 工具权限与安全沙箱实战:让智能体“有能力,但不越权“
人工智能·深度学习·算法·aigc
ZGIAI7 小时前
ZGI Skill Loop:给工具调用设边界
人工智能·架构