【AI大模型接入SDK】 —— 数据结构设计  日志库封装  Provider分析与实现
欢迎来到实战项目专栏 ~~ 从零实现AI大模型接入SDK博客主页张小姐的猫~江湖背景所属专栏C项目 ~ AI大模型接入SDK作者水平很有限如果发现错误可在评论区指正感谢AI大模型接入SDK欢迎来到实战项目专栏 ~~ 从零实现AI大模型接入SDK数据结构设计消息结构模型的公共配置信息 接入方式模型信息 会话信息日志库概念与封装Provider分析与实现策略模式LLMProvider写在最后数据结构设计虽然各个模型不同但有⼀些公共的配置和描述信息比如通过api调用模型时需要模型名称、温度值、最大tokens数、apikey等在和模型聊天时聊天信息需要管理每次开启和模型的新一轮对话都是一次新的会话将来可能需要实现会话管理。这些数据在多个文件中都会用到因此提前先将这些数据结构定义好以方便后续使用消息结构消息结构的构造方法只需要给定两个参数即可消息ID和时间戳是我们在进行创建的时候才会进行填充的而消息ID通常由系统/会话管理器自动生成不应由调用方手填_timestamp一般是「创建时取当前时间」也不该让调用方传入。创建一条「已知内容」的用户消息//消息结构structMessage{std::string _messageId;//消息IDstd::string _role;//角色std::string _content;//消息内容std::time_t _timestamp;//时间戳//构造函数Message()default;Message(conststd::stringrole,conststd::stringcontent):_role(role),_content(content){}};模型的公共配置信息 接入方式接入方式有两种通过API方式接入云端模型——继承Config通过ollma方式接入本地模型 - 不需要api配置//模型的公共配置信息structConfig{std::string _modelName;//模型名称double_temperature0.7;//温度参数,用来控制模型的输出随机性int_maxTokens2048;//最大输出token数};//通过API方式接入云端模型structApiConfig:publicConfig{std::string _apiKey;//API密钥};//通过ollma方式接入本地模型 - 不需要api配置模型信息 会话信息//LLM模型信息structLLMInfo{std::string _modelName;//模型名称std::string _modelDesc;//模型描述std::string _provider;//模型提供方std::string _endpoint;//模型endpoint base urlbool_isAvailablefalse;//模型是否有效//构造函数LLMInfo()default;LLMInfo(conststd::stringmodelName,conststd::stringmodelDesc,conststd::stringprovider,conststd::stringendpoint):_modelName(modelName),_modelDesc(modelDesc),_provider(provider),_endpoint(endpoint){}};//会话信息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){}};日志库概念与封装C中可以通过cout将信息打印到控制台为什么还要封装日志库呢日志级别管理日志格式化日志存储管理线程安全性能优势因此本项目采用google的spdlog日志库进行日志管理为了使用方便对spdlog库采用单例模式进行简单封装myLog.h日志类的头文件fmt库的使用方式hello,{}, world10s表示左对齐宽度为104d表示右对齐宽度为4VA_ARGS后续要拼接上的参数FILE会自动获取当前文件名LINE会自动获取当前行号二者都是宏#pragmaonce#includemutex#includespdlog/spdlog.hnamespacebite{classLogger{public://spdlog::level::level_enum 枚举类型用于设置日志级别默认是debugstaticvoidinitLogger(conststd::stringloggerName,conststd::stringloggerFile,spdlog::level::level_enum levelspdlog::level::level_enum::info);staticstd::shared_ptrspdlog::loggergetLogger();private:Logger();Logger(constLogger)delete;//拷贝构造函数被禁用Loggeroperator(constLogger)delete;//赋值运算符被禁用private:staticstd::shared_ptrspdlog::logger_logger;staticstd::mutex _mutex;};//fmt库 使用方式hello,{}, world// {:10} 表示右对齐宽度10文件名{:4} 表示左对齐宽度4行号// __FILE__ 会自动获取当前文件名, __LINE__ 会自动获取当前行号// ##__VA_ARGS__ 是GNU扩展可变参数为空时自动吞掉前面的逗号#defineTRACE(formar,...)Logger::getLogger()-trace(std::string([{:10s}:{:4d}]formar),__FILE__,__LINE__,##__VA_ARGS__)#defineDBG(formar,...)Logger::getLogger()-debug(std::string([{:10s}:{:4d}]formar),__FILE__,__LINE__,##__VA_ARGS__)//##__VA_ARGS__:其他的可变参数#defineINFO(formar,...)Logger::getLogger()-info(std::string([{:10s}:{:4d}]formar),__FILE__,__LINE__,##__VA_ARGS__)#defineERR(formar,...)Logger::getLogger()-error(std::string([{:10s}:{:4d}]formar),__FILE__,__LINE__,##__VA_ARGS__)#defineWARN(formar,...)Logger::getLogger()-warn(std::string([{:10s}:{:4d}]formar),__FILE__,__LINE__,##__VA_ARGS__)#defineCRITICAL(formar,...)Logger::getLogger()-trace(std::string([{:10s}:{:4d}]formar),__FILE__,__LINE__,##__VA_ARGS__)}//end bite那么后续我们如何使用这个DBG来打印出一串日志呢DBG(Database open successfully: {},localhost)预处理器做参数匹配formar ←Database open successfully: {}...可变参数←localhost即 __VA_ARGS__localhost替换进myLog.h的宏体得到Logger::getLogger()-debug(std::string([{:10s}:{:4d}]Database open successfully: {}),__FILE__,// 预处理器替换为当前源文件名如 /home/lbw/.../main.cpp__LINE__,// 预处理器替换为当前行号如 42localhost);交给spdlog输出套上 myLog.cpp 的pattern时间戳、级别后最终得到[2026:09:0115:30:12][debug][main.cpp:42]Database open successfully:localhostmyLog.cpp日志类的实现#include../include/util/myLog.h#includespdlog/spdlog.h#includespdlog/sinks/stdout_color_sinks.h#includespdlog/sinks/basic_file_sink.h#includespdlog/async.hnamespacebite{std::shared_ptrspdlog::loggerLogger::_loggernullptr;std::mutex Logger::_mutex;Logger::Logger(){}voidLogger::initLogger(conststd::stringloggerName,conststd::stringloggerFile,spdlog::level::level_enum logLevel){std::lock_guardstd::mutexlock(Logger::_mutex);//出了作用域就解锁if(_loggernullptr){//设置全局自动刷新级别当日志等级达到指定级别时自动刷新日志文件spdlog::flush_on(logLevel);// 启动异步日志将日志存放在队列里由后台线程负责写入文件//参数1队列大小参数2线程数spdlog::init_thread_pool(32768,1);if(stdoutloggerFile){//创建一个带颜色的输出到控制台的日志记录器_loggerspdlog::stdout_color(loggerName);}else{//创建一个文件日志记录器将日志写入指定文件_loggerspdlog::basic_logger_mtspdlog::async_factory(loggerName,loggerFile);}}//格式设置//[%H:%M:%S] 时分秒//[%n] 日志记录器名称//[%-7l] 日志等级左对齐宽度为7//%v 日志消息_logger-set_pattern([%H:%M:%S] [%n][%-7l] %v);_logger-set_level(logLevel);}std::shared_ptrspdlog::loggerLogger::getLogger(){return_logger;}}Provider分析与实现策略模式假设你现在要从宿舍去学校图书馆但宿舍到图书馆之间有⼀段距离你可以采用下面三种方法去实现策略方式实现定义⼀个接口TransportStrategy出行策略分别实现WalkStrategy、BikeStrategy、TaxiStrategy在运行时你可以随时切换策略classTransportStrategy{public:virtualvoidgo()0;};classWalkStrategy:publicTransportStrategy{public:virtualvoidgo()override{cout⾛路去机房;}};classBikeStrategy:publicTransportStrategy{public:virtualvoidgo()override{cout骑⻋去机房;}};classBusStrategy:publicTransportStrategy{public:virtualvoidgo()override{cout打⻋去机房;}};classStudent{private:TransportStrategy*strategy;public:voidsetStrategy(TransportStrategy*s){strategys;}voidgoToLab(){strategy-go();}};intmain(){Student me;me.setStrategy(newWalkStrategy());me.goToLab();// 输出: ⾛路去机房me.setStrategy(newBusStrategy());me.goToLab();// 输出: 打⻋去机房return0;}程序非常美观且灵活在使用时只需和TransportStrategy打交道不需要知道背后到底是WalkStrategy、BikeStrategy或BusStrategy。如果想更换模式只需要更换⼀个具体的策略对象即可程序基本不需要改动。策略模式是设计模式的⼀种它的核心思想是它定义了一些列算法将每一个算法(或行为)封装起来使它们可以相互替换而不用再代码中写一堆if-else/switch来决定用哪个算法。即把“做事的方式”抽象出来运行时根据需要选择哪种方式去执行。LLMProvider#pragmaonce#includemap#includestring#includevector#includefunctionnamespaceai_chat_sdk{classLLMProvider{public://初始化模型virtualboolinitModel(conststd::mapstd::string,std::stringconfig)0;//检查模型是否可用virtualboolisAvailable()const0;//获取模型名称virtualstd::stringgetModelName()const0;//获取模型描述virtualstd::stringgetModelDesc()const0;//发送消息 —— 全量返回virtualvoidsendMessage(conststd::vectorMessagemessages,conststd::mapstd::string,std::stringrequestParam)0;//发送消息 —— 增量返回, 流式返回//messages: 消息列表//requestParam: 请求参数//callback: 对模型返回的增量内容如何进行处理第一个为增量数据第二个为是否是最后一个增量virtualvoidsendMessageStream(conststd::vectorMessagemessages,conststd::mapstd::string,std::stringrequestParam,std::functionvoid(conststd::string,bool)callback)0;};protected:boolis_available_false;//标记模型是否可用std::string _apikey;//模型的API密钥std::string _endpoint;//模型的 base url}先实现这个LLMProvider的抽象类后续再用具体的大模型比如DeepSeek来继承它即可写在最后接下来登场的是deepseek接入封装

相关新闻

ESP-IDF+vscode开发ESP32 联网篇第五讲——Smart_config 配网

ESP-IDF+vscode开发ESP32 联网篇第五讲——Smart_config 配网

目录 前言 一、WIFI配网 1.1 为什么需要配网 1.2 常见配网方式 二、SmartConfig配网 2.1 基本工作原理 2.2 SmartConfig 的协议类型 2.2.1 ESPTouch 2.2.2 AirKiss 2.2.3 ESPTouch v2 的改进 支持加密 支持自定义数据 更适合产品化 2.2.4 手机操作 三、工程实现…

2026/10/24 21:30:55 阅读更多 →
基于SpringBoot的智能停车位预约系统设计实现毕业设计项目源码文档

基于SpringBoot的智能停车位预约系统设计实现毕业设计项目源码文档

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

2026/11/8 19:39:06 阅读更多 →
ATmega16智能插座实战:过零检测+固态继电器+EEPROM状态保持

ATmega16智能插座实战:过零检测+固态继电器+EEPROM状态保持

简介:本资源是一套基于ATmega16单片机的简易智能插座完整开发资料,面向嵌入式初学者与课程设计实践者,解决交流电参数监测、实时时钟显示、人机交互及继电器远程控制等典型物联网终端功能实现问题。压缩包共114个文件,含16个C源码…

2026/11/13 17:45:05 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/14 5:13:29 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/14 5:13:29 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/14 5:13:29 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/13 22:08:41 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/14 2:41:37 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/14 5:13:29 阅读更多 →

日新闻

周新闻

月新闻