AI大模型接入SDK—通用模块设计
模块设计详解:myLog 与 common
一、日志模块(myLog)
1.1 设计概述
日志模块基于spdlog库封装,采用单例模式提供全局唯一的日志实例,支持同步/异步日志写入、多级别日志输出、文件/stdout 双输出方式。
设计目标:
- 统一日志接口,简化使用
- 线程安全的初始化与访问
- 支持不同输出目标(控制台/文件)
- 日志格式统一,包含文件名和行号
1.2 myLog.h 接口设计
1.2.1 Logger 类(单例封装)
classLogger{public:staticvoidinitLogger(conststd::string&logName,conststd::string&logFile,spdlog::level::level_enum logLevel=spdlog::level::info);staticstd::shared_ptr<spdlog::logger>getLogger();private:Logger();Logger(constLogger&)=delete;Logger&operator=(constLogger&)=delete;private:staticstd::shared_ptr<spdlog::logger>_logger;staticstd::mutex _mutex;};| 成员 | 说明 |
|---|---|
initLogger() | 初始化日志器,设置名称、输出目标、日志级别 |
getLogger() | 获取日志器实例(返回shared_ptr) |
_logger | 静态成员,存储全局唯一的日志器实例 |
_mutex | 静态互斥锁,保护初始化过程的线程安全 |
| 私有构造函数 | 禁止外部实例化,强制通过静态方法访问 |
单例模式实现要点:
- 构造函数声明为
private,禁止外部创建实例 - 拷贝构造和赋值运算符使用
= delete删除,防止通过拷贝创建新实例 - 所有成员函数均为静态,无需实例化即可调用
1.2.2 日志宏定义
//fmt格式化输出跟踪日志#defineTRACE(fmt,...)bite::Logger::getLogger()->trace(std::string("[{:>10s}:{:<4d}]")+fmt,__FILE__,__LINE__,##__VA_ARGS__)//fmt格式化输出调试日志#defineDBG(fmt,...)bite::Logger::getLogger()->debug(std::string("[{:>10s}:{:<4d}]")+fmt,__FILE__,__LINE__,##__VA_ARGS__)//fmt格式化输出信息日志#defineINFO(fmt,...)bite::Logger::getLogger()->info(std::string("[{:>10s}:{:<4d}]")+fmt,__FILE__,__LINE__,##__VA_ARGS__)//fmt格式化输出警告日志#defineWARN(fmt,...)bite::Logger::getLogger()->warn(std::string("[{:>10s}:{:<4d}]")+fmt,__FILE__,__LINE__,##__VA_ARGS__)//fmt格式化输出错误日志#defineERR(fmt,...)bite::Logger::getLogger()->error(std::string("[{:>10s}:{:<4d}]")+fmt,__FILE__,__LINE__,##__VA_ARGS__)//fmt格式化输出严重错误日志#defineCRIT(fmt,...)bite::Logger::getLogger()->critical(std::string("[{:>10s}:{:<4d}]")+fmt,__FILE__,__LINE__,##__VA_ARGS__)宏设计亮点:
| 特性 | 实现方式 | 效果 |
|---|---|---|
| 文件行号追踪 | __FILE__、__LINE__宏 | 日志自动附带调用位置 |
| 格式化输出 | fmt库语法 | 支持{}占位符格式化 |
| 可变参数 | ##__VA_ARGS__ | 兼容 C99 可变参数宏 |
| 对齐美化 | {:>10s}、{:<4d} | 文件名右对齐10位,行号左对齐4位 |
日志输出格式:
[14:32:58][ChatSDK][INFO ][ChatSDK.cpp: 45] Initialize deepseek-chat success1.3 myLog.cpp 实现细节
文件:[myLog.cpp](file:///workspace/CPP_AI_Model_Access/AIModelAccess/sdk/src/util/myLog.cpp)
1.3.1 静态成员初始化
std::shared_ptr<spdlog::logger>Logger::_logger=nullptr;std::mutex Logger::_mutex;静态成员必须在类外部初始化,初始值为nullptr表示日志器尚未初始化。
1.3.2 initLogger() 核心实现
voidLogger::initLogger(conststd::string&logName,conststd::string&logFile,spdlog::level::level_enum logLevel){if(_logger==nullptr){std::lock_guard<std::mutex>lock(_mutex);if(_logger==nullptr){spdlog::flush_on(logLevel);spdlog::init_thread_pool(32768,1);if("stdout"==logFile)_logger=spdlog::stdout_color_mt(logName);else_logger=spdlog::basic_logger_mt(logName,logFile);}_logger->set_pattern("[%H:%M:%S][%n][%-7l]%v");_logger->set_level(logLevel);}}关键技术点:
| 技术点 | 实现方式 | 作用 |
|---|---|---|
| 双重检查锁 | 两次判断_logger == nullptr | 保证多线程环境下只初始化一次,避免竞态条件 |
| lock_guard | RAII 自动加锁/解锁 | 异常安全,防止死锁 |
| flush_on | 设置自动刷新级别 | 级别 >= logLevel 的日志立即刷新到文件 |
| init_thread_pool | 初始化异步日志线程池 | 线程池大小 32KB,1 个工作线程 |
| stdout_color_mt | 彩色控制台输出(多线程安全) | 区分不同级别日志颜色 |
| basic_logger_mt | 文件输出(多线程安全) | 将日志写入指定文件 |
1.3.3 日志格式设置
_logger->set_pattern("[%H:%M:%S][%n][%-7l]%v");格式说明:
[%H:%M:%S]- 时分秒时间戳(24小时制)[%n]- 日志器名称(初始化时指定)[%7l]- 日志级别,左对齐,占用7个字符%v- 具体日志内容(包含文件:行号信息)
1.3.4 getLogger() 实现
std::shared_ptr<spdlog::logger>Logger::getLogger(){return_logger;}直接返回静态成员_logger,调用者通过返回的shared_ptr访问日志方法。
1.4 使用方式
// 在程序入口初始化日志bite::Logger::initLogger("ChatServer","chat_server.log",spdlog::level::debug);// 在任意模块使用日志宏INFO("Server started on port {}",8080);ERR("Failed to connect to database: {}",errorMsg);WARN("API key not set, some models may not work");二、公共数据结构(common.h)
2.1 设计概述
common.h定义了项目中所有模块共享的数据结构,是整个 SDK 的数据契约层。设计遵循以下原则:
- 职责单一:每个结构体只负责一类数据
- 继承扩展:通过继承实现配置的多态性
- 默认构造:提供合理的默认值,便于对象创建
- 安全性:配置基类提供虚析构,支持安全的向下转型
2.2 数据结构体系
Config (基类) ├── ApiConfig (云端API配置) └── OllamaConfig (本地Ollama配置) Message (消息结构) ModelInfo (模型元信息) Session (会话结构)2.3 Message 结构
structMessage{std::string _messageId;// 消息唯一标识std::string _role;// 角色:"user" / "assistant" / "system"std::string _content;// 消息内容std::time_t _timestamp;// 消息发送时间戳Message(conststd::string&role="",conststd::string&content=""):_role(role),_content(content){}};设计要点:
_role字段遵循 OpenAI API 规范,支持 user/assistant/system 三种角色_messageId在SessionManager中生成,用于消息追踪和持久化- 提供默认参数的构造函数,便于快速创建用户消息
使用场景:
- LLMProvider 发送请求时,构造消息列表
- SessionManager 维护历史消息记录
- DataManager 持久化消息到 SQLite
2.4 Config 层次结构
2.4.1 Config 基类
structConfig{std::string _modelName;// 模型名称double_temperature;// 温度参数,控制输出随机性(默认0.7)int_maxTokens;// 最大令牌数,限制输出长度(默认2048)virtual~Config()=default;// 虚析构,支持安全的多态删除};设计要点:
| 字段 | 作用 | 取值范围 |
|---|---|---|
_modelName | 模型唯一标识 | 如 “deepseek-chat”, “gpt-4o-mini” |
_temperature | 控制输出随机性 | 0~2,越大越随机,越小越确定 |
_maxTokens | 限制输出长度 | 正整数,防止输出过长 |
虚析构的必要性:
// 如果没有虚析构,以下代码会导致未定义行为Config*config=newApiConfig();deleteconfig;// 只调用 Config 的析构函数,不会调用 ApiConfig 的析构2.4.2 ApiConfig(云端 API 模型配置)
structApiConfig:publicConfig{std::string _apiKey;// API 密钥,用于身份认证};设计要点:
- 继承自
Config,复用模型名称、温度、最大 token 配置 - 新增
_apiKey字段,用于云端模型的 API 认证 - 适用于 ChatGPT、DeepSeek、Gemini 等云端 API 模型
2.4.3 OllamaConfig(本地 Ollama 模型配置)
structOllamaConfig:publicConfig{std::string _modelName;// Ollama 模型名称(如 "deepseek-r1:1.5b")std::string _modelDesc;// 模型描述信息std::string _endpoint;// Ollama 服务地址(如 "http://localhost:11434")};设计要点:
- 继承自
Config,但覆盖了_modelName字段(Ollama 模型名格式不同) - 无需 API Key(本地模型)
- 需要指定 Ollama 服务的 endpoint 地址
- 包含模型描述,用于展示和区分不同模型
2.4.4 配置层次结构总结
| 配置类型 | 适用模型 | 特有字段 |
|---|---|---|
Config | 所有模型(基类) | _modelName,_temperature,_maxTokens |
ApiConfig | ChatGPT、DeepSeek、Gemini | _apiKey |
OllamaConfig | Ollama 本地模型 | _modelDesc,_endpoint |
多态使用示例:
// 在 ChatSDK 中统一处理不同类型的配置std::vector<std::shared_ptr<Config>>configs;// 添加云端模型配置autodeepseekConfig=std::make_shared<ApiConfig>();deepseekConfig->_modelName="deepseek-chat";deepseekConfig->_apiKey="sk-xxx";configs.push_back(deepseekConfig);// 添加本地模型配置autoollamaConfig=std::make_shared<OllamaConfig>();ollamaConfig->_modelName="deepseek-r1:1.5b";ollamaConfig->_endpoint="http://localhost:11434";configs.push_back(ollamaConfig);// 通过 dynamic_pointer_cast 区分处理for(constauto&config:configs){if(autoapiConfig=std::dynamic_pointer_cast<ApiConfig>(config)){// 处理云端模型}elseif(autoollamaConfig=std::dynamic_pointer_cast<OllamaConfig>(config)){// 处理本地模型}}2.5 ModelInfo 结构
structModelInfo{std::string _modelName;// 模型名称std::string _modelDesc;// 模型描述std::string _provider;// 模型提供方std::string _endpoint;// API endpoint 地址bool_isAvailable=false;// 模型是否可用ModelInfo(conststd::string&modelName="",conststd::string&modelDesc="",conststd::string&provider="",conststd::string&endpoint=""):_modelName(modelName),_modelDesc(modelDesc),_provider(provider),_endpoint(endpoint){}};设计要点:
- 用于展示模型的元信息,供前端或 API 返回使用
_isAvailable标记模型是否已成功初始化- 提供全参数构造函数,便于快速创建
使用场景:
- LLMManager 维护已注册模型的状态信息
- ChatServer 的
/api/models接口返回可用模型列表
2.6 Session 结构
structSession{std::string _sessionId;// 会话唯一标识std::string _modelName;// 会话使用的模型名称std::vector<Message>_messages;// 会话历史消息std::time_t _createAt;// 会话创建时间std::time_t _updateAt;// 会话最后更新时间Session(conststd::string&modelName=""):_modelName(modelName){}};设计要点:
_sessionId由SessionManager生成,全局唯一_messages存储完整的对话历史,用于上下文管理_createAt和_updateAt用于会话管理(如清理长时间未使用的会话)
使用场景:
- SessionManager 管理活跃会话
- DataManager 将会话持久化到 SQLite
- ChatServer 通过
/api/session/{id}/history返回会话历史
2.7 数据结构关系图
┌─────────────────────────────────────────────────────────────┐ │ common.h 数据结构 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────┐ │ │ │ Config │ (基类,虚析构) │ │ │ ────────────────── │ │ │ │ _modelName │ │ │ │ _temperature │ │ │ │ _maxTokens │ │ │ └──────────┬──────────┘ │ │ │ │ │ ┌──────┴──────┐ │ │ ▼ ▼ │ │ ┌──────────┐ ┌──────────────┐ │ │ │ApiConfig │ │OllamaConfig │ │ │ │ ─────── │ │ ─────────── │ │ │ │ _apiKey │ │ _modelDesc │ │ │ └──────────┘ │ _endpoint │ │ │ └──────────────┘ │ │ │ │ ┌─────────────────────┐ │ │ │ Message │ (消息单元) │ │ │ ────────────────── │ │ │ │ _messageId │ │ │ │ _role │ │ │ │ _content │ │ │ │ _timestamp │ │ │ └─────────────────────┘ │ │ │ │ ┌─────────────────────┐ │ │ │ ModelInfo │ (模型元信息) │ │ │ ────────────────── │ │ │ │ _modelName │ │ │ │ _modelDesc │ │ │ │ _provider │ │ │ │ _endpoint │ │ │ │ _isAvailable │ │ │ └─────────────────────┘ │ │ │ │ ┌─────────────────────┐ │ │ │ Session │ (会话上下文) │ │ │ ────────────────── │ │ │ │ _sessionId │ │ │ │ _modelName │ │ │ │ _messages[] │ ◀─── 包含多个 Message │ │ │ _createAt │ │ │ │ _updateAt │ │ │ └─────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘三、模块间依赖关系
3.1 myLog 的依赖关系
┌──────────────┐ │ myLog.h │ │ ─────────── │ │ Logger 类 │ │ 日志宏定义 │ └──────┬───────┘ │ ┌─────────────────┼─────────────────┐ │ │ │ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ChatSDK │ │LLMManager│ │所有Provider│ └──────────┘ └──────────┘ └──────────┘ │ │ │ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │Session- │ │Data- │ │ChatServer │ │Manager │ │Manager │ └──────────┘ └──────────┘ └──────────┘依赖特性:
- myLog 不依赖任何项目内部模块,仅依赖 spdlog 第三方库
- 项目中所有模块均依赖 myLog(日志输出)
- myLog 通过全局宏提供无侵入的日志记录能力
3.2 common.h 的依赖关系
┌──────────────┐ │ common.h │ │ ─────────── │ │ 数据结构定义 │ └──────┬───────┘ │ ┌─────────────────┼─────────────────┐ │ │ │ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │LLMProvider│ │ChatSDK │ │Session- │ │(抽象接口) │ └──────────┘ │Manager │ └────┬─────┘ └────┬─────┘ │ │ ▼ ▼ ┌─────────────────┐ ┌──────────┐ │ 所有 Provider │ │Data- │ │ (4个实现类) │ │Manager │ └─────────────────┘ └──────────┘依赖特性:
- common.h 不依赖任何项目内部模块,仅依赖标准库
- LLMProvider、ChatSDK、SessionManager、DataManager 均依赖 common.h
- 是整个 SDK 的数据契约,定义了模块间交互的数据格式
四、设计模式应用
4.1 myLog - 单例模式
| 要素 | 实现 |
|---|---|
| 私有构造 | Logger()声明为 private |
| 静态实例 | static std::shared_ptr<spdlog::logger> _logger |
| 全局访问 | static getLogger()方法 |
| 线程安全 | 双重检查锁 +std::lock_guard |
| 禁止拷贝 | Logger(const Logger&) = delete |
4.2 common.h - 模板方法模式(隐式)
通过配置基类Config和子类ApiConfig、OllamaConfig,实现了配置的多态处理:
// ChatSDK 中统一处理配置(模板方法模式的应用)voidinitProviders(conststd::vector<std::shared_ptr<Config>>&configs){for(constauto&config:configs){if(autoapiConfig=std::dynamic_pointer_cast<ApiConfig>(config)){initAPIModelProvider(apiConfig->_modelName,apiConfig);}elseif(autoollamaConfig=std::dynamic_pointer_cast<OllamaConfig>(config)){initOllamaModelProvider(ollamaConfig->_modelName,ollamaConfig);}}}五、总结
| 模块 | 核心职责 | 设计模式 | 依赖关系 |
|---|---|---|---|
| myLog | 全局日志管理 | 单例模式 | 仅依赖 spdlog |
| common.h | 公共数据结构定义 | 继承/多态 | 仅依赖标准库 |
这两个模块是项目的基础设施:
- myLog提供统一的日志能力,是调试和运维的基础
- common.h定义了模块间的数据契约,是 SDK 设计的核心
- 两者都不依赖项目内部其他模块,体现了良好的模块化设计原则