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 success

1.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_guardRAII 自动加锁/解锁异常安全,防止死锁
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 三种角色
  • _messageIdSessionManager中生成,用于消息追踪和持久化
  • 提供默认参数的构造函数,便于快速创建用户消息

使用场景

  • 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
ApiConfigChatGPT、DeepSeek、Gemini_apiKey
OllamaConfigOllama 本地模型_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){}};

设计要点

  • _sessionIdSessionManager生成,全局唯一
  • _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和子类ApiConfigOllamaConfig,实现了配置的多态处理:

// 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 设计的核心
  • 两者都不依赖项目内部其他模块,体现了良好的模块化设计原则