
🎬 个人主页 :艾莉丝努力练剑
❄专栏传送门 :《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 模型调用原始操作步骤
- 获取模型厂商 API‑Key
- 阅读厂商 HTTP API 文档
- 程序内部发起 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
- 将所有模型公共接口抽至顶层基类;
- 将接口声明为纯虚函数 ,
virtual 函数签名 = 0;,使LLMProvider成为抽象类。 - 抽象类禁止实例化:基类不绑定任何具体大模型,实例化无业务意义;只定义接口规范,由子类完成全部业务实现。
- 各个厂商模型实现类继承该抽象基类,重写全部纯虚函数。
继承关系:
- 基类:
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抽象基类,定义所有模型必须实现的接口契约 - 具体策略:
DeepSeekProvider、ChatGPTProvider等每一个模型实现类 - 上下文: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
关键修正点说明
- 增加虚析构函数
virtual ~LLMProvider() = default;,多态继承场景必不可少,防止子类析构不执行内存泄漏;- 将成员变量修改为
protected,子类可以直接访问,原文档写 private 子类无法读取配置;- 修正原文档多处拼写笔误
initodel → initModel、getodelDesc → getModelDesc;- 回调
std::function参数 const 引用,避免不必要拷贝;- 补充头文件保护宏
#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 架构优势总结
- 消除大量 if‑else:依靠 C++ 多态策略模式,运行时动态切换模型实现。
- 开闭原则 :新增大模型,只需要新增继承
LLMProvider的派生类,原有上层代码不需要修改。 - 统一 SDK 对外接口:第三方使用者只需要依赖抽象接口,不需要关心每家模型 HTTP 细节。
- 接口强制约束:抽象类纯虚函数强制派生类必须实现全部规定能力,不会出现遗漏功能。
- 兼容云端 API 模型、本地 Ollama 部署模型,同一套框架复用。
1.8 面试 / 考试易错考点
- 抽象类含有纯虚函数,不能实例化对象;
- 多态继承必须写虚析构函数,否则 delete 基类指针时子类析构不会执行;
- 纯虚函数语法:
virtual 返回类型 函数名(参数) = 0;; - 策略模式核心:封装可变行为,运行时替换,隔离调用方与具体实现;
- protected 访问限定符:基类成员希望子类可以访问、外部不能访问时使用,不能设置为 private。
结尾
uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ### 艾莉丝努力练剑 C/C++ & Linux 底层探索者 | 一个正在努力练剑的技术博主 *** ** * ** *** 👀 【关注】 跟随我一起深耕技术领域,见证每一次成长。 ❤️ 【点赞】 让优质内容被更多人看见,让知识传递更有力量。 ⭐ 【收藏】 把核心知识点存好,在需要时随时查、随时用。 💬 【评论】 分享你的经验或疑问,评论区一起交流避坑! 不要忘记给博主"一键四连"哦! "今日练剑达成!"
"技术之路难免有困惑,但同行的人会让前进更有方向。" |
结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主"一键四连"哦!
往期回顾:
🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა
