
🎬 个人主页 :艾莉丝努力练剑
❄专栏传送门 :《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录》
《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》
⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平
🎬 艾莉丝的简介:

文章目录
- [1 ~> ChatSDK 整体实现](#1 ~> ChatSDK 整体实现)
-
- [1.1 整体类结构](#1.1 整体类结构)
-
- [1.1.1 ChatSDK 类核心成员](#1.1.1 ChatSDK 类核心成员)
- [1.1.2 LLMManager 类职责](#1.1.2 LLMManager 类职责)
- [1.1.3 继承配置体系 common.h](#1.1.3 继承配置体系 common.h)
- [1.2 模型注册与初始化 initModels](#1.2 模型注册与初始化 initModels)
-
- [1.2.1 registerAllProvider 模型注册阶段](#1.2.1 registerAllProvider 模型注册阶段)
-
- [1.2.1.1 云端模型注册(deepseek‑chat /gpt‑4o‑mini /gemini‑2.0‑flash)](#1.2.1.1 云端模型注册(deepseek‑chat /gpt‑4o‑mini /gemini‑2.0‑flash))
- [1.2.1.2 dynamic_pointer_cast 向下转型知识点](#1.2.1.2 dynamic_pointer_cast 向下转型知识点)
- [1.2.2 initProviders 模型实例初始化阶段](#1.2.2 initProviders 模型实例初始化阶段)
-
- [1.2.2.1 initAPIModelProviders 云端模型初始化](#1.2.2.1 initAPIModelProviders 云端模型初始化)
- [1.2.2.2 initOllamaModelProviders Ollama 本地模型初始化](#1.2.2.2 initOllamaModelProviders Ollama 本地模型初始化)
- [1.3 Session 会话管理实现](#1.3 Session 会话管理实现)
-
- [1.3.1 createSession 创建会话](#1.3.1 createSession 创建会话)
- [1.3.2 getSession 获取会话对象](#1.3.2 getSession 获取会话对象)
- [1.3.3 getSessionLists 获取全部会话 ID 列表](#1.3.3 getSessionLists 获取全部会话 ID 列表)
- [1.3.4 deleteSession 删除会话](#1.3.4 deleteSession 删除会话)
- [1.4 消息发送接口实现](#1.4 消息发送接口实现)
-
- [1.4.1 sendMessage 全量一次性返回应答](#1.4.1 sendMessage 全量一次性返回应答)
- [1.4.2 sendMessageStream 流式增量回调返回](#1.4.2 sendMessageStream 流式增量回调返回)
- [2 ~> 关键 C++ 核心易错点面试总结](#2 ~> 关键 C++ 核心易错点面试总结)
-
- [2.1 unique_ptr 所有权转移](#2.1 unique_ptr 所有权转移)
- [2.2 shared_ptr 多态向下转型](#2.2 shared_ptr 多态向下转型)
- [2.3 云端模型 vs Ollama 本地模型差异对比](#2.3 云端模型 vs Ollama 本地模型差异对比)
- [3 ~> ChatSDK 整体编译交付流程](#3 ~> ChatSDK 整体编译交付流程)
- [4 ~> 完整业务调用时序(外部用户视角)](#4 ~> 完整业务调用时序(外部用户视角))
- 结尾

1 ~> ChatSDK 整体实现
ChatSDK 对外提供统一接口,屏蔽云端大模型、本地 Ollama 模型差异;核心依赖
LLMManager做模型管理,SessionManager管理会话上下文。整体工作流程:SDK 初始化模型 → 创建会话 session → 基于 session 发送消息(全量 / 流式增量返回)。
1.1 整体类结构
1.1.1 ChatSDK 类核心成员
- 私有成员
bool _initialized:SDK 整体初始化标记位LLMManager _llmManager:模型管理器,完成模型注册、实例初始化、消息下发SessionManager _sessionManager:会话管理器,负责会话创建、删除、历史消息持久化std::unordered_map<std::string, std::shared_ptr<Config>> _modelConfigs:保存各个模型对应的配置对象
- 对外公开接口
initModels():批量注册并初始化模型,SDK 入口初始化函数createSession():创建会话,返回 sessionIdgetSession():根据 sessionId 获取会话对象getSessionLists():获取全部会话 id 列表deleteSession():删除指定会话getAvailableModels():获取当前已经就绪可用模型列表sendMessage():发送消息,全量一次性返回应答sendMessageStream():发送消息,流式增量返回,回调推送分片数据
- 私有内部辅助函数
registerAllProvider():遍历配置,注册全部模型 ProviderinitProviders():遍历配置,调用对应初始化逻辑完成模型实例初始化initAPIModelProviders():云端 API 模型初始化辅助函数initOllamaModelProviders():本地 Ollama 模型初始化辅助函数
1.1.2 LLMManager 类职责
- 维护
_providers:std::unordered_map<std::string, std::unique_ptr<LLMProvider>>,保存模型名称与 provider 实例独占指针 - 维护
_modelInfos:保存每个模型运行状态、描述信息 - 核心接口
registerProvider():注册 LLM 提供者,接收模型名 +unique_ptr<LLMProvider>,发生资源转移 std::moveinitModel():对已注册模型执行 provider 的initModel,完成模型实例初始化isModelAvailable():判断模型是否注册且初始化完成可用getAvailableModels():获取全部可用模型信息sendMessage():全量模式下发消息给底层 providersendMessageStream():流式模式下发消息给底层 provider,传入回调
1.1.3 继承配置体系 common.h
多态配置,
std::vector<std::shared_ptr<Config>>容器可以存放不同子类配置;基类必须定义虚析构,保证 dynamic_pointer_cast 安全向下转型
cpp
//模型公共基类配置
struct Config{
std::string _modelName; //模型名称
double _temperature = 0.7; //生成随机性温度参数
int _maxTokens = 2048; //最大输出token
virtual ~Config() = default; //虚析构,多态向下转型dynamic_cast必备
};
//云端API模型配置:deepseek‑chat / gpt‑4o‑mini / gemini‑2.0‑flash
struct APIConfig : public Config{
std::string _apikey;
};
//本地Ollama模型配置,不需要apikey,需要本地服务端点
struct OllamaConfig : public Config{
std::string _modelDesc; //模型描述
std::string _endpoint; //ollama服务地址,如http://127.0.0.1:11434
};
1.2 模型注册与初始化 initModels
ChatSDK::initModels()外部调用入口,分为两大阶段:注册 Provider、初始化 Provider。
cpp
bool ChatSDK::initModels(const std::vector<std::shared_ptr<Config>>& configs)
{
registerAllProvider(configs); //第一步:注册所有模型provider对象
initProviders(configs); //第二步:执行每个provider的初始化
_initialized = true;
return true;
}
1.2.1 registerAllProvider 模型注册阶段
1.2.1.1 云端模型注册(deepseek‑chat /gpt‑4o‑mini /gemini‑2.0‑flash)
- 关键点:
LLMManager::registerProvider入参是std::unique_ptr<LLMProvider>,unique_ptr 禁止拷贝,必须使用std::move将左值转为右值完成独占所有权转移。 - 即使子类
DeepSeekProvider公有继承基类LLMProvider,unique_ptr 不会执行隐式转换,必须显式 std::move。
cpp
void ChatSDK::registerAllProvider(const std::vector<std::shared_ptr<Config>>& configs)
{
//注册deepseek‑chat
if(!_llmManager.isModelAvailable("deepseek‑chat")){
auto deepseekProvider = std::make_unique<DeepSeekProvider>();
//std::move转移独占所有权,不发生拷贝
_llmManager.registerProvider("deepseek‑chat", std::move(deepseekProvider));
INFO("deepseek‑chat provider registered successed");
}
//注册gpt‑4o‑mini
if(!_llmManager.isModelAvailable("gpt‑4o‑mini")){
auto gpt4oProvider = std::make_unique<ChatGPTProvider>();
_llmManager.registerProvider("gpt‑4o‑mini", std::move(gpt4oProvider));
INFO("gpt‑4o‑mini provider registered successed");
}
//注册gemini‑2.0‑flash
if(!_llmManager.isModelAvailable("gemini‑2.0‑flash")){
auto geminiProvider = std::make_unique<GeminiProvider>();
_llmManager.registerProvider("gemini‑2.0‑flash", std::move(geminiProvider));
INFO("gemini‑2.0‑flash provider registered successed");
}
//Ollama本地模型批量注册,来源于外部传入configs
std::unordered_set<std::string> modelNames; //去重,避免重复注册同一个模型
for(const auto& config : configs)
{
//安全向下转型:基类shared_ptr → 子类OllamaConfig shared_ptr
auto ollamaConfig = std::dynamic_pointer_cast<OllamaConfig>(config);
if(ollamaConfig)
{
std::string modelName = ollamaConfig->_modelName;
if(modelNames.find(modelName) == modelNames.end())
{
modelNames.insert(modelName);
if(!_llmManager.isModelAvailable(modelName))
{
auto ollamaProvider = std::make_unique<OllamaLLMProvider>();
_llmManager.registerProvider(modelName, std::move(ollamaProvider));
INFO("OllamaLLMProvider {} registered successed", modelName);
}
}
}
}
}
1.2.1.2 dynamic_pointer_cast 向下转型知识点
static_pointer_cast:不安全向下转型,不做运行时校验;如果原始对象不是目标子类,得到非法指针,UB 未定义行为。dynamic_pointer_cast:安全多态向下转型,运行时 RTTI 校验;基类必须存在虚函数(虚析构);类型不匹配返回 nullptr。- 使用场景:容器存放基类
shared_ptr<Config>,需要区分是APIConfig还是OllamaConfig。
1.2.2 initProviders 模型实例初始化阶段
遍历 configs,dynamic_cast 区分配置类型,分别调用initAPIModelProviders / initOllamaModelProviders。
cpp
void ChatSDK::initProviders(const std::vector<std::shared_ptr<Config>>& configs)
{
for(const auto& config : configs)
{
//尝试转型云端API配置
if(auto apiConfig = std::dynamic_pointer_cast<APIConfig>(config))
{
if(apiConfig->_modelName == "deepseek‑chat"
|| apiConfig->_modelName == "gpt‑4o‑mini"
|| apiConfig->_modelName == "gemini‑2.0‑flash")
{
initAPIModelProviders(apiConfig->_modelName, apiConfig);
}
else
{
ERR("Model {} is not supported", apiConfig->_modelName);
}
}
//尝试转型Ollama本地模型配置
else if(auto ollamaConfig = std::dynamic_pointer_cast<OllamaConfig>(config))
{
initOllamaModelProviders(ollamaConfig->_modelName, ollamaConfig);
}
else
{
ERR("Config {} is not supported", config->_modelName);
}
}
}
1.2.2.1 initAPIModelProviders 云端模型初始化
- 将 APIConfig 配置提取组装为
std::map<std::string,std::string>参数,传给LLMManager::initModel - 云端模型 endpoint 可以内置默认值,用户传入配置可覆盖默认地址;必须校验 apikey 不为空。
cpp
bool ChatSDK::initAPIModelProviders(const std::string& modelName, const std::shared_ptr<APIConfig>& apiConfig)
{
if(modelName.empty()){
ERR("ChatSDK::initAPIModelProviders:modelName is empty");
return false;
}
if(!apiConfig || apiConfig->_apikey.empty()){
ERR("ChatSDK::initAPIModelProviders:apikey is empty");
return false;
}
if(!_llmManager.isModelAvailable(modelName)){
ERR("ChatSDK::initAPIModelProviders:model {} is not available", modelName);
return false;
}
std::map<std::string, std::string> modelParams;
modelParams["api_key"] = apiConfig->_apikey;
//endpoint:优先取用户配置,没有则使用内置厂商默认域名
//modelParams["endpoint"] = apiConfig->_endpoint;
if(!_llmManager.initModel(modelName, modelParams)){
ERR("ChatSDK::initAPIModelProviders: init model failed {}", modelName);
return false;
}
_modelConfigs[modelName] = apiConfig; //保存配置
INFO("ChatSDK::initAPIModelProviders:model {} init successed", modelName);
return true;
}
1.2.2.2 initOllamaModelProviders Ollama 本地模型初始化
- Ollama 没有内置固定服务地址,
model_name、model_desc、endpoint全部由外部配置传入,缺一不可。
cpp
bool ChatSDK::initOllamaModelProviders(const std::string& modelName, const std::shared_ptr<OllamaConfig>& ollamaConfig)
{
if(modelName.empty()){
ERR("ChatSDK::initOllamaModelProviders:modelName is empty");
return false;
}
if(!ollamaConfig || ollamaConfig->_endpoint.empty()){
ERR("ChatSDK::initOllamaModelProviders:endpoint is empty");
return false;
}
if(!_llmManager.isModelAvailable(modelName)){
ERR("ChatSDK::initOllamaModelProviders:model {} is not available", modelName);
return false;
}
std::map<std::string, std::string> modelParams;
modelParams["model_name"] = modelName;
modelParams["model_desc"] = ollamaConfig->_modelDesc;
modelParams["endpoint"] = ollamaConfig->_endpoint;
if(!_llmManager.initModel(modelName, modelParams)){
ERR("ChatSDK::initOllamaModelProviders:init model failed {}", modelName);
return false;
}
_modelConfigs[modelName] = ollamaConfig;
INFO("ChatSDK::initOllamaModelProviders: model {} init successed", modelName);
return true;
}
1.3 Session 会话管理实现
ChatSDK 会话相关接口全部转发调用
SessionManager,每一个会话绑定一个模型,维护该会话完整消息历史。
1.3.1 createSession 创建会话
cpp
std::string ChatSDK::createSession(const std::string& modelName)
{
if(!_initialized){
ERR("ChatSDK::createSession:SDK is not initialized");
return "";
}
std::string sessionId = _sessionManager.createSession(modelName);
if(sessionId.empty()){
ERR("ChatSDK::createSession:create session failed");
return "";
}
INFO("ChatSDK::createSession create session {} successed", sessionId);
return sessionId;
}
1.3.2 getSession 获取会话对象
cpp
std::shared_ptr<Session> ChatSDK::getSession(const std::string& sessionId)
{
if(!_initialized){
ERR("ChatSDK::getSession:SDK is not initialized");
return nullptr;
}
auto session = _sessionManager.getSession(sessionId);
if(!session){
ERR("ChatSDK::getSession: session {} not found", sessionId);
}
return session;
}
1.3.3 getSessionLists 获取全部会话 ID 列表
cpp
std::vector<std::string> ChatSDK::getSessionLists() const
{
if(!_initialized){
ERR("ChatSDK::getSessionLists: SDK is not initialized");
return {};
}
return _sessionManager.getSessionLists();
}
1.3.4 deleteSession 删除会话
cpp
bool ChatSDK::deleteSession(const std::string& sessionId)
{
if(!_initialized){
ERR("ChatSDK::deleteSession:SDK is not initialized");
return false;
}
bool result = _sessionManager.deleteSession(sessionId);
if(!result){
ERR("ChatSDK::deleteSession:delete session failed {}", sessionId);
return false;
}
INFO("ChatSDK::deleteSession:delete session {} successed", sessionId);
return true;
}
1.4 消息发送接口实现
两个接口:全量返回
sendMessage;流式增量返回sendMessageStream。 公共前置逻辑:
- 校验 SDK 初始化标记
_initialized- 根据 sessionId 获取会话 Session 对象
- 将用户输入 user 消息添加到会话
- 从 session 拿到绑定的 modelName,在
_modelConfigs取出温度、max_tokens,组装请求参数- 从 Session 读取完整历史消息
- 调用
_llmManager底层接口发送请求- 将 assistant 应答写入会话,更新会话时间戳
1.4.1 sendMessage 全量一次性返回应答
cpp
std::string ChatSDK::sendMessage(const std::string& sessionId, const std::string& message)
{
if(!_initialized){
ERR("ChatSDK::sendMessage: SDK is not initialized");
return "";
}
auto session = _sessionManager.getSession(sessionId);
if(!session){
ERR("ChatSDK::sendMessage: session not found {}", sessionId);
return "";
}
// 1. 将用户消息加入会话
Message userMessage{"user", message};
_sessionManager.addMessage(sessionId, userMessage);
// 2. 获取会话全部历史消息
std::vector<Message> historyMessages = _sessionManager.getHistoryMessages(sessionId);
// 3. 获取模型配置,组装请求参数
auto it = _modelConfigs.find(session->_modelName);
if(it == _modelConfigs.end()){
ERR("ChatSDK::sendMessage:model {} not found", session->_modelName);
return "";
}
std::map<std::string, std::string> requestParam;
requestParam["temperature"] = std::to_string(it->second->_temperature);
requestParam["max_tokens"] = std::to_string(it->second->_maxTokens);
// 4. 调用LLMManager下发消息
std::string response = _llmManager.sendMessage(session->_modelName, historyMessages, requestParam);
if(response.empty()){
ERR("ChatSDK::sendMessage: send message to model {} failed", session->_modelName);
return "";
}
// 5. 将助手回复写入会话,更新会话时间戳
Message assistantMessage{"assistant", response};
_sessionManager.addMessage(sessionId, assistantMessage);
_sessionManager.updateSessionTimestamp(sessionId);
INFO("ChatSDK::sendMessage send message to model {} successed", session->_modelName);
return response;
}
1.4.2 sendMessageStream 流式增量回调返回
- 入参回调
std::function<void(const std::string&, bool)> callback- 第一个参数:模型返回的增量分片字符串
- 第二个 bool 参数:标记是否为最后一块分片
- 底层 LLMManager 收到分片数据,反复调用回调推送数据;函数返回完整拼接后的应答文本。
cpp
std::string ChatSDK::sendMessageStream(const std::string& sessionId, const std::string& message,
std::function<void(const std::string&, bool)> callback)
{
if(!_initialized){
ERR("ChatSDK::sendMessageStream: SDK is not initialized");
return "";
}
auto session = _sessionManager.getSession(sessionId);
if(!session){
ERR("ChatSDK::sendMessageStream:session not found {}", sessionId);
return "";
}
Message userMessage{"user", message};
_sessionManager.addMessage(sessionId, userMessage);
std::vector<Message> historyMessages = _sessionManager.getHistoryMessages(sessionId);
auto it = _modelConfigs.find(session->_modelName);
if(it == _modelConfigs.end()){
ERR("ChatSDK::sendMessageStream:model {} not found", session->_modelName);
return "";
}
std::map<std::string, std::string> requestParam;
requestParam["temperature"] = std::to_string(it->second->_temperature);
requestParam["max_tokens"] = std::to_string(it->second->_maxTokens);
std::string response = _llmManager.sendMessageStream(session->_modelName, historyMessages, requestParam, callback);
if(response.empty()){
ERR("ChatSDK::sendMessageStream:send message to model {} failed", session->_modelName);
return "";
}
Message assistantMessage{"assistant", response};
_sessionManager.addMessage(sessionId, assistantMessage);
_sessionManager.updateSessionTimestamp(sessionId);
INFO("ChatSDK::sendMessageStream send message to model {} successed", session->_modelName);
return response;
}
2 ~> 关键 C++ 核心易错点面试总结
2.1 unique_ptr 所有权转移
unique_ptr是独占所有权智能指针,禁止拷贝构造、拷贝赋值 ,只允许移动语义std::move。- 即使子类指针可以隐式转换为基类裸指针,
unique_ptr<SubClass>不会隐式转换 为unique_ptr<BaseClass>,必须显式std::move。 std::move只是将左值强制转换为右值引用,本身不做内存释放;移动之后源对象变为空。
2.2 shared_ptr 多态向下转型
dynamic_pointer_cast<T>()- 运行时 RTTI 检查,安全向下转型;要求基类必须存在虚函数(虚析构)。
- 类型不匹配,返回
nullptr;可以用 if 判断转型结果。
static_pointer_cast<T>()- 编译期强制转换,无运行时检查;如果原始对象并不是目标子类,产生未定义行为 UB。
本项目
Config基类添加virtual ~Config()=default;,目的就是为了支持dynamic_pointer_cast安全向下转型。
2.3 云端模型 vs Ollama 本地模型差异对比
| 项目 | 云端 API 模型(deepseek/gpt/gemini) | Ollama 本地模型 |
|---|---|---|
| 鉴权 | 必须 apikey | 不需要 apikey |
| 服务端点 endpoint | 代码内置默认值,用户可覆盖 | 必须外部配置传入,无默认值 |
| 模型名称 | 代码写死固定名称 | 完全由外部 config 配置传入,可以多个 ollama 模型 |
| 配置结构体 | APIConfig |
OllamaConfig |
3 ~> ChatSDK 整体编译交付流程
- 完成全部类代码实现:ChatSDK、LLMManager、SessionManager、各个 LLMProvider 实现类
- 将 SDK 编译为静态库 (.a)
- 对外交付:头文件 + 编译产出静态库
- 用户侧:引入头文件,链接静态库,构造
vector<shared_ptr<Config>>配置,调用ChatSDK::initModels完成初始化,之后调用会话、发消息接口。
4 ~> 完整业务调用时序(外部用户视角)
- 构造
std::vector<std::shared_ptr<Config>> configs,填入需要使用的模型配置(APIConfig/OllamaConfig) ChatSDK sdk; sdk.initModels(configs);完成模型注册、初始化std::string sid = sdk.createSession("模型名称");创建会话- 调用
sdk.sendMessage(sid, "用户提问")或者sdk.sendMessageStream(sid, "提问", callback)进行对话 - 可选:
getSessionLists查询会话,deleteSession销毁会话。
结尾
uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ### 艾莉丝努力练剑 C/C++ & Linux 底层探索者 | 一个正在努力练剑的技术博主 *** ** * ** *** 👀 【关注】 跟随我一起深耕技术领域,见证每一次成长。 ❤️ 【点赞】 让优质内容被更多人看见,让知识传递更有力量。 ⭐ 【收藏】 把核心知识点存好,在需要时随时查、随时用。 💬 【评论】 分享你的经验或疑问,评论区一起交流避坑! 不要忘记给博主"一键四连"哦! "今日练剑达成!"
"技术之路难免有困惑,但同行的人会让前进更有方向。" |
结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主"一键四连"哦!
往期回顾:
🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა
