【AI大模型接入SDK】ChatSDK 集成测试概述

🎬 个人主页 :艾莉丝努力练剑
❄专栏传送门 :《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录》
《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平


🎬 艾莉丝的简介:


文章目录

  • [1 ~> ChatSDK 整体测试概述](#1 ~> ChatSDK 整体测试概述)
    • [1.1 测试目标](#1.1 测试目标)
    • [1.2 测试前置条件](#1.2 测试前置条件)
  • [2 ~> ChatSDK 核心数据结构与接口](#2 ~> ChatSDK 核心数据结构与接口)
    • [2.1 模型配置基类与派生配置](#2.1 模型配置基类与派生配置)
      • [2.1.1 基类 Config](#2.1.1 基类 Config)
      • [2.1.2 云端 API 模型配置 APIConfig](#2.1.2 云端 API 模型配置 APIConfig)
      • [2.1.3 Ollama 本地模型配置 OllamaConfig](#2.1.3 Ollama 本地模型配置 OllamaConfig)
    • [2.2 ChatSDK 关键成员](#2.2 ChatSDK 关键成员)
    • [2.3 核心接口定义](#2.3 核心接口定义)
      • [2.3.1 模型初始化接口](#2.3.1 模型初始化接口)
      • [2.3.2 会话管理接口](#2.3.2 会话管理接口)
      • [2.3.3 消息发送接口](#2.3.3 消息发送接口)
    • [2.4 SessionManager 内部获取历史消息逻辑](#2.4 SessionManager 内部获取历史消息逻辑)
  • [3 ~> 测试用例实现(testLLM.cpp)](#3 ~> 测试用例实现(testLLM.cpp))
    • [3.1 测试整体流程](#3.1 测试整体流程)
    • [3.2 测试代码片段](#3.2 测试代码片段)
    • [3.3 CMakeLists.txt 配置要点](#3.3 CMakeLists.txt 配置要点)
  • [4 ~> 编译运行流程](#4 ~> 编译运行流程)
  • [5 ~> 常见问题分析](#5 ~> 常见问题分析)
    • [5.1 Gemini 返回 429 错误 "You exceeded your current quota"](#5.1 Gemini 返回 429 错误 "You exceeded your current quota")
    • [5.2 云端模型初始化失败](#5.2 云端模型初始化失败)
    • [5.3 Ollama 本地模型初始化失败](#5.3 Ollama 本地模型初始化失败)
  • [6 ~> 业务流程验证结果](#6 ~> 业务流程验证结果)
    • [6.1 云端模型(DeepSeek 示例)](#6.1 云端模型(DeepSeek 示例))
    • [6.2 Ollama 本地 deepseek‑r1:1.5b](#6.2 Ollama 本地 deepseek‑r1:1.5b)
  • [7 ~> 测试总结与后续工作](#7 ~> 测试总结与后续工作)
    • [7.1 测试覆盖](#7.1 测试覆盖)
    • [7.2 待完成工作](#7.2 待完成工作)
  • 结尾


1 ~> ChatSDK 整体测试概述

1.1 测试目标

  • 验证ChatSDK整体流程正确性,间接校验SessionManager会话管理模块、DataManager数据持久化模块(SQLite 存储)功能正常。
  • 完成云端 API 模型、本地 Ollama 模型的初始化、会话创建、消息发送、全量应答、历史消息读取完整链路测试。
  • 模块依赖关系:ChatSDK内部组合LLMManager、SessionManager、DataManager,模块之间强耦合。

1.2 测试前置条件

  • 编译依赖:gtest 测试框架、spdlog 日志库、openssl、cpp‑httplib、nlohmann‑json、sqlite3。
  • 环境变量配置:各类大模型 API‑Key 通过系统环境变量注入,不在代码硬编码密钥。
  • Ollama 环境:本地启动 Ollama 服务,默认监听http://localhost:11434,预先拉取对应本地模型。

2 ~> ChatSDK 核心数据结构与接口

2.1 模型配置基类与派生配置

2.1.1 基类 Config

cpp 复制代码
// 模型公共基础配置
struct Config{
    std::string _modelName;             // 模型名称
    double _temperature = 0.7;          // 生成随机性温度参数
    int _maxTokens = 2048;              // 最大输出token数量
    virtual ~Config() = default;         // 虚析构,保障dynamic_pointer_cast向下转型安全
};

2.1.2 云端 API 模型配置 APIConfig

cpp 复制代码
// 对接云端HTTP API的模型配置,继承Config
struct APIConfig : public Config{
    std::string _apikey;    // 云端服务API密钥
};

2.1.3 Ollama 本地模型配置 OllamaConfig

cpp 复制代码
// 对接本地Ollama服务模型配置,继承Config
struct OllamaConfig : public Config{
    std::string _modelDesc;     // 模型描述信息
    std::string _endpoint;      // Ollama服务地址,例:http://localhost:11434
};

2.2 ChatSDK 关键成员

cpp 复制代码
class ChatSDK{
private:
    bool _initialized = false;
    std::unordered_map<std::string, std::shared_ptr<Config>> _modelConfigs;
    LLMManager _llmManager;
    // 测试阶段临时改为public,生产环境必须恢复为private
public:
    SessionManager _sessionManager;
};

2.3 核心接口定义

2.3.1 模型初始化接口

cpp 复制代码
/**
 * @brief 批量初始化多个模型
 * @param configs 模型配置对象vector容器
 * @return 初始化成功返回true
 */
bool ChatSDK::initModel(const std::vector<std::shared_ptr<Config>>& configs)
{
    registerAllProvider(configs);   // 注册模型提供者
    initProviders(configs);         // 完成模型初始化
    _initialized = true;
    return true;
}
  • registerAllProvider:完成各个模型 Provider 注册,存入LLMManager。
  • initProviders:运行时 RTTI 识别配置实际类型,区分 API 模型 / Ollama 本地模型,分别调用对应初始化函数。

2.3.2 会话管理接口

  • std::string createSession(const std::string& modelName):基于指定模型创建会话,返回唯一 sessionId。
  • std::vector<Message> getHistoryMessages(const std::string& sessionId):获取会话全部历史消息。

2.3.3 消息发送接口

  1. 全量返回:std::string sendMessage(const std::string& sessionId, const std::string& userInput),阻塞等待完整模型应答返回。
  2. 流式返回:sendMessageStream(会话ID,输入消息,回调函数),分片通过回调返回模型输出,回调参数chunk为分片字符串,last标记是否为最后一片。

2.4 SessionManager 内部获取历史消息逻辑

cpp 复制代码
/**
 * @brief 获取会话历史消息,优先读取内存缓存,缓存缺失读取SQLite数据库
 * @param sessionId 会话唯一标识
 * @return 会话全部消息vector
 */
std::vector<Message> SessionManager::getHistoryMessages(const std::string& sessionId) const
{
    _mutex.lock();
    auto it = _sessions.find(sessionId);
    if(it != _sessions.end())
    {
        _mutex.unlock();
        return it->second->_messages;
    }
    _mutex.unlock();
    // 内存中不存在该会话,从sqlite数据库加载消息
    return _dataManager.getSessionMessages(sessionId);
}
  • 线程安全:访问内存会话哈希表必须加互斥锁保护。
  • 数据读取策略:内存优先,内存未命中回落到持久层 SQLite。

3 ~> 测试用例实现(testLLM.cpp)

3.1 测试整体流程

  1. 初始化 spdlog 日志库。
  2. 初始化 gtest 测试框架。
  3. 构造多个模型配置对象(DeepSeek、GPT‑4o‑mini、Gemini、Ollama‑deepseek‑r1:1.5b)。
  4. 将配置装入vector<std::shared_ptr<Config>>,调用sdk->initModels()完成批量初始化。
  5. 调用createSession创建会话,获取 sessionId。
  6. 循环读取控制台用户输入,调用sendMessage发送消息。
  7. 通过_sessionManager读取会话历史消息,打印角色 + 消息内容。
  8. 断言校验:SDK 实例非空、sessionId 非空、应答字符串非空、历史消息非空。

3.2 测试代码片段

cpp 复制代码
TEST(ChatSDKTest, sendMessage)
{
    auto sdk = std::make_shared<ai_chat_sdk::ChatSDK>();
    ASSERT_TRUE(sdk != nullptr);

    // ========== 1.构造云端DeepSeek配置 ==========
    auto deepseekConfig = std::make_shared<ai_chat_sdk::APIConfig>();
    ASSERT_TRUE(deepseekConfig != nullptr);
    deepseekConfig->_modelName = "deepseek‑chat";
    deepseekConfig->_apikey = std::getenv("deepseek_apikey");
    ASSERT_FALSE(deepseekConfig->_apikey.empty());
    deepseekConfig->_temperature = 0.7;
    deepseekConfig->_maxTokens = 2048;

    // ========== 2.构造GPT‑4o‑mini配置 ==========
    auto gpt4oConfig = std::make_shared<ai_chat_sdk::APIConfig>();
    ASSERT_TRUE(gpt4oConfig != nullptr);
    gpt4oConfig->_modelName = "gpt‑4o‑mini";
    gpt4oConfig->_apikey = std::getenv("chatgpt_apikey");
    ASSERT_FALSE(gpt4oConfig->_apikey.empty());
    gpt4oConfig->_temperature = 0.7;
    gpt4oConfig->_maxTokens = 2048;

    // ========== 3.构造Gemini配置 ==========
    auto geminiConfig = std::make_shared<ai_chat_sdk::APIConfig>();
    ASSERT_TRUE(geminiConfig != nullptr);
    geminiConfig->_modelName = "gemini‑2.0‑flash";
    geminiConfig->_apikey = std::getenv("gemini_apikey");
    ASSERT_FALSE(geminiConfig->_apikey.empty());
    geminiConfig->_temperature = 0.7;
    geminiConfig->_maxTokens = 2048;

    // ========== 4.构造Ollama本地模型配置 ==========
    auto ollamaConfig = std::make_shared<ai_chat_sdk::OllamaConfig>();
    ASSERT_TRUE(ollamaConfig != nullptr);
    ollamaConfig->_modelName = "deepseek‑r1:1.5b";
    ollamaConfig->_modelDesc = "本地部署deepseek‑r1:1.5b模型,采用专家混合架构,专注深度理解推理";
    ollamaConfig->_endpoint = "http://localhost:11434";
    ollamaConfig->_temperature = 0.7;
    ollamaConfig->_maxTokens = 2048;

    // 将全部配置装入vector,交给SDK初始化
    std::vector<std::shared_ptr<ai_chat_sdk::Config>> modelConfigs = {
        deepseekConfig,
        gpt4oConfig,
        geminiConfig,
        ollamaConfig
    };
    ASSERT_TRUE(sdk->initModel(modelConfigs));

    // 创建会话,可以切换为ollamaConfig->_modelName测试本地模型
    std::string sessionId = sdk->createSession(deepseekConfig->_modelName);
    ASSERT_FALSE(sessionId.empty());

    // 循环读取控制台输入,交互测试
    std::string message;
    std::cout << ">>>";
    std::getline(std::cin, message);
    auto response = sdk->sendMessage(sessionId, message);
    ASSERT_FALSE(response.empty());

    std::cout << ">>>";
    std::getline(std::cin, message);
    response = sdk->sendMessage(sessionId, message);
    ASSERT_FALSE(response.empty());

    // 获取会话历史消息
    auto messages = sdk->_sessionManager.getHistoryMessages(sessionId);
    ASSERT_FALSE(messages.empty());
    for(const auto& msg : messages)
    {
        std::cout << msg._role << ":" << msg._content << std::endl;
    }
}

int main(int argc, char **argv)
{
    // 初始化日志,输出到stdout,日志等级debug
    bite::Logger::initLogger("testLLM", "stdout", spdlog::level::debug);
    // gtest初始化
    testing::InitGoogleTest(&argc, argv);
    // 执行所有测试用例
    return RUN_ALL_TESTS();
}

3.3 CMakeLists.txt 配置要点

bash 复制代码
cmake_minimum_required(VERSION 3.10)
project(testLLM)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_BUILD_TYPE Debug)

# 编译生成测试可执行文件testLLM,汇总全部SDK源码
add_executable(testLLM testLLM.cpp
../sdk/src/util/myLog.cpp
../sdk/src/DeepSeekProvider.cpp
../sdk/src/ChatGPTProvider.cpp
../sdk/src/GeminiProvider.cpp
../sdk/src/OllamaLLMProvider.cpp
../sdk/src/DataManager.cpp
../sdk/src/SessionManager.cpp
../sdk/src/LLMManager.cpp
../sdk/src/ChatSDK.cpp
)

set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})

# 头文件搜索路径
include_directories(${CMAKE_PROJECT_INCLUDE_DIR}/../sdk/include)

# OpenSSL依赖
find_package(OpenSSL REQUIRED)
include_directories(${OPENSSL_INCLUDE_DIR})
# 开启cpp‑httplib的openssl支持
target_compile_definitions(testLLM PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT)

# 链接库:gtest、spdlog、openssl、sqlite3等
target_link_libraries(testLLM gtest gtest_main spdlog OpenSSL::SSL OpenSSL::Crypto sqlite3)
  • 必须链接sqlite3库,DataManager依赖 SQLite 数据库做会话持久化。
  • 定义宏CPPHTTPLIB_OPENSSL_SUPPORT开启 http‑lib 的 HTTPS 能力,云端模型 API 均使用 HTTPS 协议。

4 ~> 编译运行流程

  1. 创建 build 构建目录,进入 build 文件夹。
  2. cmake ..:解析 CMakeLists 生成 Makefile 构建脚本。
  3. make:编译全部源码生成testLLM可执行程序。
  4. 执行./testLLM运行 gtest 测试用例。
  5. 运行后会在当前目录生成chatDB.dbsqlite 数据库文件,存储会话、消息持久化数据。

注意 :编译成功仅代表语法无错误,必须运行测试验证业务逻辑正确性。


5 ~> 常见问题分析

5.1 Gemini 返回 429 错误 "You exceeded your current quota"

  • 根因:Google Gemini 免费额度耗尽。
  • 背景:Google 提供免费 token 配额,存在上限;耗尽后需要付费。Google Cloud 付费验证仅支持境外银行卡,国内实体卡无法完成身份校验;市面虚拟卡会触发风控校验失败(错误码OR_BACR2_44),极易封号。
  • 解决方案:
    • 使用新的有效付费 API‑Key;
    • 环境变量更新:vim ~/.bashrc设置新环境变量,执行source ~/.bashrc使环境变量生效;
    • 代码读取更新后的环境变量。
  • 风险提示:第三方倒卖 API‑Key 存在中间人转发劫持风险,用户对话明文会被中间人窃取,生产环境禁止使用非官方中转服务。

5.2 云端模型初始化失败

  • 排查方向:
    • API‑Key 环境变量是否正确导出,std::getenv读取是否为空;
    • 网络连通性:GPT/Gemini 需要代理网络;DeepSeek 国内网络可直连;
    • 端点 base_url 是否配置正确,不要硬编码,代码配置可兼容服务商地址变更。

5.3 Ollama 本地模型初始化失败

  • 确认 Ollama 服务启动,端口 11434 监听正常;
  • 确认本地已经 pull 对应模型,模型名称和配置中_modelName严格一致;
  • endpoint 填写http://localhost:11434。

6 ~> 业务流程验证结果

6.1 云端模型(DeepSeek 示例)

完整链路:

  1. SDK 初始化成功,Provider 注册;
  2. SQLite 数据库打开;
  3. 创建会话,会话写入数据库;
  4. 用户输入消息,消息存入内存Session对象,同时持久化写入 SQLite;
  5. SDK 组装包含完整上下文历史的 HTTP 请求发送云端 API;
  6. 获取 200 应答,模型返回结果;
  7. 助手回复存入内存会话,写入 SQLite;
  8. 读取历史消息,可以拿到多轮对话完整上下文;
  9. 下一轮请求自动携带全部历史消息给大模型,实现多轮对话记忆。

6.2 Ollama 本地 deepseek‑r1:1.5b

  • 整体流程和云端模型完全一致;
  • HTTP 请求发送至本地 Ollama 服务,不走公网;
  • Ollama 完成模型推理,返回应答给 SDK;
  • 会话、消息存储逻辑与云端模型完全复用同一套SessionManager+DataManager。

7 ~> 测试总结与后续工作

7.1 测试覆盖

  • 测试两类接入方式:云端 API 接入、本地 Ollama 接入;
  • 覆盖功能点:模型注册初始化、会话创建、多轮消息交互、内存缓存会话、SQLite 持久化、历史消息读取;
  • 验证模块协同:ChatSDK、LLMManager、SessionManager、DataManager协同工作正常。

7.2 待完成工作

  1. 封装 Web Demo,提供网页聊天界面,调用 ChatSDK 完成对话。
  2. 补充流式返回场景完整测试用例。
  3. 测试结束将SessionManager访问权限恢复为private,禁止外部直接访问内部成员。
  4. 补充异常用例:非法 sessionId、空 API‑Key、网络断开、模型不存在等边界场景测试。

结尾

uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!

|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ### 艾莉丝努力练剑 C/C++ & Linux 底层探索者 | 一个正在努力练剑的技术博主 *** ** * ** *** 👀 【关注】 跟随我一起深耕技术领域,见证每一次成长。 ❤️ 【点赞】 让优质内容被更多人看见,让知识传递更有力量。 ⭐ 【收藏】 把核心知识点存好,在需要时随时查、随时用。 💬 【评论】 分享你的经验或疑问,评论区一起交流避坑! 不要忘记给博主"一键四连"哦! "今日练剑达成!" "技术之路难免有困惑,但同行的人会让前进更有方向。" |

结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主"一键四连"哦!

往期回顾:

【AI大模型接入SDK】ChatSDK整体实现

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა

相关推荐
探客木木夕1 小时前
AI伦理体系核心价值观声明
大数据·人工智能·机器学习
明志数科1 小时前
具身智能训练数据分布设计:为什么分布比数量更关键
人工智能·深度学习·机器学习
Data-Miner1 小时前
能处理Excel表的AI怎么选?脚本能复用,一次买断后期不烧 token
大数据·人工智能·excel
艾莉丝努力练剑1 小时前
【AI大模型接入SDK】ChatSDK:CMake构建静态库完整实现
java·开发语言·网络·c++·人工智能·学习·sdk
YFJ_mily1 小时前
二轮截稿延期|BDAIA2026第三届大数据分析与人工智能应用|往届会后两月见刊 IEEE出版 EI&Scopus检索
人工智能·数据挖掘·数据分析·大数据分析·rdlink研发家·大语言模型与智能体·ai系统安全风险评估
易天ETU1 小时前
PCB与AI浪潮的关联性《补充篇》
网络·人工智能·光模块·光通信·易天光通信
GEO实战经验分享1 小时前
LLM-First Search可复现性:环境、任务、版本与假设缺一不可
大数据·人工智能·安全·chatgpt
Joe_Wang51 小时前
【从0到1学习JVM · 14】堆内存各区域分工与Xms和Xmx设为一样的真相
java·jvm·学习·垃圾回收
AI创界者1 小时前
Python 进阶:重构经典设计模式(五)—— 状态模式与观察者模式在 Python 3.10+ 中的类型驱动与事件解耦
人工智能·aigc