ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

【AI大模型接入SDK】 —— 模型管理 会话管理

【AI大模型接入SDK】 —— 模型管理  会话管理 欢迎来到实战项目专栏 ~~ 从零实现AI大模型接入SDK博客主页张小姐的猫~江湖背景所属专栏C项目 ~ AI大模型接入SDK作者水平很有限如果发现错误可在评论区指正感谢AI大模型接入SDK欢迎来到实战项目专栏 ~~ 从零实现AI大模型接入SDK 模型管理 会话管理会话介绍会话管理数据结构设计会话实现写在最后 模型管理现在deepseek-chat、gpt-5.6-sol 、gemini-3.7-flash模型已经接入成功每个模型都对应单独的Provider类为了后续使用简单再封装⼀个LLMManager类将模型管理起来后续通过多态的方式实现模型路由LLMManager.h 的实现#pragmaonce#includeLLMProvider.h#includemap#includememorynamespaceai_chat_sdk{classLLMManager{public://注册LLM提供者voidregisterProvider(conststd::unique_ptrLLMProviderprovider);//初始化指定模型boolinitModel(conststd::stringmodelName,conststd::mapstd::string,std::stringmodelParams);//获取可用的模型std::vectorLLMInfogetAvailableModels()const;//检测模型是否可用boolisModelAvailable(conststd::stringmodelName)const;//发送全量信息给指定模型 (模型名历史消息请求参数)std::stringsendMessage(conststd::stringmodelName,conststd::vectorMessagemessages,conststd::mapstd::string,std::stringrequestParams);//发送流式信息给指定模型std::stringsendMessageStream(conststd::stringmodelName,conststd::vectorMessagemessages,conststd::mapstd::string,std::stringrequestParams,std::functionvoid(conststd::string,bool)callback);private://key:模型名称, value: 模型提供器 ~ 基类的指针std::mapstd::string,std::unique_ptrLLMProviderproviders;//key:模型名称, value: 模型信息std::mapstd::string,LLMInfomodelInfos;};}LLMManager.cpp 的实现#include../include/util/LLMManager.h#include../include/util/myLog.h#include../include/util/Common.hnamespaceai_chat_sdk{//注册LLM提供者boolLLMManager::registerProvider(conststd::stringmodelName,std::unique_ptrLLMProviderprovider){//参数检测if(!provider){ERR(can not register a null provider, modelname {},modelName);returnfalse;}//因为unique_ptr是不允许赋值的, 但是可以进行移动构造//std::move把一个左值强制转成右值引用说明不再需要这个对象了_providers[modelName]std::move(provider);//添加模型信息_modelInfos[modelName]LLMInfo(modelName);//模型初始化成功INFO(register provider success, modelname {},modelName);returntrue;}//初始化指定模型boolLLMManager::initModel(conststd::stringmodelName,conststd::mapstd::string,std::stringmodelParams){//检测模型是否注册了autoit_providers.find(modelName);if(it_providers.end()){ERR(model not registered, modelname {},modelName);returnfalse;}//模型注册成功,可以开始初始化了 ———— 调用模型自己的initModel方法boolisSuccessit-second-initModel(modelParams);if(!isSuccess){ERR(model init failed, modelname {},modelName);}else{INFO(model init success, modelname {},modelName);_modelInfos[modelName]._modelDescit-second-getModelDesc();_modelInfos[modelName]._isAvailabletrue;}returnisSuccess;}//获取可用的模型std::vectorLLMInfoLLMManager::getAvailableModels()const{std::vectorLLMInfoavailableModels;// 可用的模型数组for(constautopair:_modelInfos){// 遍历所有模型信息if(pair.second._isAvailable){// 如果模型可用availableModels.push_back(pair.second);// 添加到可用模型数组}}returnavailableModels;}//检测模型是否可用boolLLMManager::isModelAvailable(conststd::stringmodelName)const{autoit_modelInfos.find(modelName);if(it_modelInfos.end()){returnfalse;}returnit-second._isAvailable;// 返回模型是否可用}//发送全量信息给指定模型 (模型名历史消息请求参数)std::stringLLMManager::sendMessage(conststd::stringmodelName,conststd::vectorMessagemessages,conststd::mapstd::string,std::stringrequestParams){//检测模型是否注册了autoit_providers.find(modelName);if(it_providers.end()){ERR(model not registered, modelname {},modelName);return;}//检测模型是否可用if(!it-second-isAvailable()){ERR(model not available, modelname {},modelName);return;}//模型注册并且可用,发送消息returnit-second-sendMessage(messages,requestParams);}std::stringLLMManager::sendMessageStream(conststd::stringmodelName,conststd::vectorMessagemessages,conststd::mapstd::string,std::stringrequestParams,std::functionvoid(conststd::string,bool)callback){//检测模型是否注册了autoit_providers.find(modelName);if(it_providers.end()){ERR(model not registered, modelname {},modelName);return;}//检测模型是否可用if(!it-second-isAvailable()){ERR(model not available, modelname {},modelName);return;}//模型注册并且可用,发送消息returnit-second-sendMessageStream(messages,requestParams,callback);return;}} 会话管理假设现在借助LLMManager搭建⼀个大模型后端服务用户和模型进行了多轮会话每个会话中都包含了好多条消息在某个会话中和模型聊天的多轮消息该如何管理多个会话该如何管理解决该问题的⼀种方式是引入Session每次和模型开启新一轮聊天都可以看成是一次会话。在一次会话中会和模型进行多次聊天这些聊天信息可以保存在本次会话中除了保存会话信息外还需要保存模型名称、会话创建时间等都需要保存会话介绍会话是用户与大语言模型之间的⼀系列连续交互它通过维护上下⽂和状态信息确保对话的连贯性和⼀致性。由于大模型不会为用户管理会话信息因此需要程序员手动完成会话管理会话管理涉及以下内容注意会话管理模块会保存所有的会话在同一时刻可能会对多个会话进行操作因此创建会话、更新会话、删除会话等时需要考虑线程安全问题会话管理数据结构设计//会话信息structSession{std::string _sessionId;//会话IDstd::string _modelName;//模型名称std::vectorMessage_messages;//会话中的消息列表std::time_t _updatedAt;//会话最后更新时间戳std::time_t _createdAt;//会话创建时间戳//构造函数Session()default;Session(conststd::stringmodelName)//创建时间是要我们创建会话时填入:_modelName(modelName){}};会话实现SessionManager.h实现实现有个细节有几个函数是设置成const只读函数的但是为了保证线程安全仍需要加锁mutable修饰锁即使在 const 函数中也可以被修改加锁/解锁而其他成员变量仍然受 const 保护不会被意外修改#pragmaonce#includeatomic#includemutex#includememory#includeunordered_map#includeCommon.hnamespaceai_chat_sdk{classSessionManager{public://创建会话提供模型返回会话idstd::stringcreateSession(conststd::stringmodelName);//通过会话id获取会话信息std::shared_ptrSessiongetSession(conststd::stringSessionId)const;//往某个会话中添加消息booladdMessage(conststd::stringSessionId,constMessagemessage);//获取某个会话的所有历史信息std::vectorMessagegetHistoryMessages(conststd::stringSessionId)const;//更新会话的时间戳voidupdateSessionTimestamp(conststd::stringSessionId);//获取所有会话列表std::vectorstd::stringgetSessionLists()const;//删除某个会话booldelteSession(conststd::string sessionId);//清空所有会话voidclearAllSessions();//获取会话总数size_tgetSessionCount()const;private:std::stringgenerateSessionId();//生成会话idstd::stringgenerateMessageId(size_t messageCount);//生成消息idprivate://管理所有会话信息key会话idvalue会话信息std::unordered_mapstd::string,std::shared_ptrSession_sessions;mutablestd::mutex _mutex;//表明在当前成员变量_mutex在const成员函数当中也是允许被修改的std::atomicint64_t_sessionCounter{0};//记录所有会话的总数};}SessionManager.cpp实现fetch_add_sessionCounter— 是类的成员变量被多个线程共享所以需要 std::atomic 保证线程安全一般在调用generateMessageId前就已经是加过锁了所以这里不需要原子操作创建会话是用智能指针去进行管理的避免会话太多时数据过大往会话中添加消息因为message是const的不能修改其_messageId所以要创建一个复制体来更新其_messageId//生成会话id 格式session_时间戳_会话计数std::stringSessionManager::generateSessionId(){_sessionCounter.fetch_add(1);//原子操作会话计数加1std::time_t timestd::time(nullptr);//获取当前时间戳std::ostringstream oss;//字符串流写入将数据拼接成字符串// session_1672531200_0001设置会话计数的宽度为4不足4位的用0填充osssession_time_std::setw(4)std::setfill(0)_sessionCounter;returnoss.str();}//生成消息idstd::stringSessionManager::generateMessageId(size_t messageCount){messageCount;std::time_t timestd::time(nullptr);//格式同上std::ostringstream oss;ossmsg_time_std::setw(4)std::setfill(0)messageCount;returnoss.str();}//创建会话提供模型返回会话idstd::stringSessionManager::createSession(conststd::stringmodelName){std::lock_guardstd::mutexlock(_mutex);//生成会话idstd::string sessionIdgenerateSessionId();//创建会话设置会话id、模型名称autosessionstd::make_sharedSession(modelName);session-_sessionIdsessionId;//加入到会话列表_sessions[sessionId]session;returnsessionId;}//通过会话id获取会话信息std::shared_ptrSessionSessionManager::getSession(conststd::stringsessionId)const{std::lock_guardstd::mutexlock(_mutex);autoit_sessions.find(sessionId);if(it_sessions.end()){returnnullptr;}returnit-second;}//往某个会话中添加消息boolSessionManager::addMessage(conststd::stringsessionId,constMessagemessage){std::lock_guardstd::mutexlock(_mutex);//通过会话id获取会话信息autoit_sessions.find(sessionId);if(it_sessions.end()){returnfalse;}//添加消息 —— 因为message是const的不能修改其_messageIdMessagemsg(message._role,message._content);msg._messageIdgenerateMessageId(it-second-_messages.size());//消息添加到会话中it-second-_messages.push_back(msg);//添加消息it-second-_updatedAtstd::time(nullptr);//更新会话时间戳INFO(Add message to session successfully: message_id {}, message_content {},msg._messageId,msg._content);returntrue;}//获取某个会话的所有历史信息std::vectorMessageSessionManager::getHistoryMessages(conststd::stringsessionId)const{std::lock_guardstd::mutexlock(_mutex);autoit_sessions.find(sessionId);if(it_sessions.end()){return{};}returnit-second-_messages;}//更新会话的时间戳voidSessionManager::updateSessionTimestamp(conststd::stringsessionId){std::lock_guardstd::mutexlock(_mutex);autoit_sessions.find(sessionId);if(it_sessions.end()){return;}it-second-_updatedAtstd::time(nullptr);}获取会话列表实现获取所有会话列表, 实际返回的是所有会话的会话id能否直接返回会话的对象呢可以的但是不够友好原因如下直接返回会话对象在返回时候会增加内存拷贝的开销影响程序的运行效率SessionManager中基本都是通过会话id来操作对应的会话对象的如果要返回会话对象的话其他模块就要对会话对象进行操作SessionManager和其他模块的耦合度增加不便于后期维护所以返回会话对象不友好并且会话信息存储在sqlite中获取会话历史消息、删除会话等都是通过会话id来进行的因此返回会话id更利于CURD返回会话id会更好注意在返回的这里会话id中最好将会话按照更新的时间戳进行降序排列更加关心最近的聊天//获取所有会话列表, 实际返回的是所有会话的会话idstd::vectorstd::stringSessionManager::getSessionLists()const{std::lock_guardstd::mutexlock(_mutex);//构造一个临时的会话列表将其内容按照更新时间降序排列std::vectorstd::pairstd::time_t,std::shared_ptrSessiontemp;temp.reserve(_sessions.size());//将所有会话信息复制到临时会话列表中for(constautopair:_sessions){temp.emplace_back(pair.second-_updatedAt,pair.second);}//按照更新时间降序排列自定义比较器std::sort(temp.begin(),temp.end(),[](constautoa,constautob){returna.firstb.first;});std::vectorstd::stringsessionList;sessionList.reserve(temp.size());for(constautopair:temp){sessionList.push_back(pair.second-_sessionId);}returnsessionList;}剩下的函数实现 删除会话、清理会话、获取会话总数//删除会话boolSessionManager::delteSession(conststd::string sessionId){std::lock_guardstd::mutexlock(_mutex);autoit_sessions.find(sessionId);if(it_sessions.end())returnfalse;_sessions.erase(it);returntrue;}//清空所有会话voidSessionManager::clearAllSessions(){std::lock_guardstd::mutexlock(_mutex);_sessions.clear();}//获取会话总数size_tSessionManager::getSessionCount()const{std::lock_guardstd::mutexlock(_mutex);return_sessions.size();}写在最后接下来登场的是
返回列表