欢迎光临
我们一直在努力

【从零实现C++AI大模型接入SDK】(系列二)《用 C++ 写一个多厂商大模型统一接入SDK:ChatSDK 上手实现记录》

一个模型一个接口?不存在的。

这篇文章带你认识 ChatSDK —— 一款用 C++ 实现的大模型接入库:一套接口,同时接入 DeepSeek、ChatGPT、Gemini,还能本地跑 Ollama;自带多轮对话、流式输出、会话管理和 SQLite 持久化。

从它是什么到5 分钟跑通第一个对话,然后再到接入自己的项目。


一、ChatSDK 是什么?

ChatSDK 是一个用 C++ 封装的大模型统一接入库,把不同厂商的 API 差异藏在一个抽象接口后面。

目前它支持的能力:

能力说明
云端模型 已接入 deepseek-chat、gpt-4o-mini、gemini-2.0-flash
本地模型 支持 Ollama 本地接入 deepseek-r1:1.5b
对话形式 多轮聊天,支持全量返回和流式返回两种模式
会话管理 获取历史会话、获取历史会话消息
数据持久化 用 SQLite 存储会话数据,重启不丢

它解决的核心痛点

如果你直接调各家大模型的 API,会遇到这样的问题:

  • DeepSeek 一套 JSON 格式、ChatGPT 一套、Gemini 又一套;
  • 换模型就要改一堆业务代码;
  • 多轮对话的上下文要自己拼;
  • 会话历史要自己存。

ChatSDK 的设计目标是:让上层只认模型名和消息,具体怎么调各家 API,交给底层实现。


二、核心设计:面向接口 + 策略模式

先看它的类关系(架构骨架):

几个关键点:

  • LLMProvider 是统一接口,各家模型都实现它。想加一个新厂商,只要再写一个 XxxProvider : LLMProvider,上层代码一行不用改——这就是策略模式带来的「对扩展开放」。
  • Config 也做了抽象,ApiConfig(云端 API)和 OllamaConfig(本地 Ollama)分别实现,接入方式差异被隔离在配置层。
  • DataManager 负责 SQLite 持久化,SessionManage 管理会话,Session 记录单个会话,Message 记录每条消息。

  • 三、代码结构与编译安装

    1. 目录结构

    .
    ├── sdk
    │ ├── CMakeLists.txt
    │ ├── include
    │ │ ├── ChatGPTProvider.h
    │ │ ├── chat_sdk.h
    │ │ ├── common.h
    │ │ ├── dataManager.h
    │ │ ├── DeepSeekProvider.h
    │ │ ├── GeminiProvider.h
    │ │ ├── ILLMProvider.h
    │ │ ├── LLMManager.h
    │ │ ├── OllamaDeepSeekProvider.h
    │ │ ├── session_manager.h
    │ │ └── util
    │ │ └── my_logger.h
    │ ├── src
    │ │ ├── ChatGPTProvider.cpp
    │ │ ├── chat_sdk.cpp
    │ │ ├── DataManager.cpp
    │ │ ├── DeepSeekProvider.cpp
    │ │ ├── GeminiProvider.cpp
    │ │ ├── LLMManager.cpp
    │ │ ├── OllamaDeepSeekProvider.cpp
    │ │ ├── session_manager.cpp
    │ │ └── util
    │ │ └── my_logger.cpp
    └── build

    2. 编译安装

    进入 sdk 所在目录,依次执行:

    mkdir build && cd build # 创建 build 目录并进入
    cmake .. # 生成构建文件,最终生成 libai_chat_sdk.a 静态库
    sudo make install # 安装

    安装后:

    • 静态库:/usr/local/lib
    • 头文件:/usr/local/include/ai_chat_sdk

    四、核心 API:ChatSDK 类

    使用时,你几乎只需要跟 ChatSDK 这一个类打交道。头文件:

    #include <ai_chat_sdk/ChatSDK.h>

    ChatSDK 的公开函数一览:

    函数作用返回值
    bool initModels(const std::vector<std::shared_ptr<Config>>& configs) 初始化所支持的模型 成功返回 true
    std::string createSession(const std::string& modelName) 为指定模型创建会话 返回会话 ID
    std::shared_ptr<Session> getSession(const std::string& sessionId) 获取指定会话信息 会话对象
    std::vector<std::string> getSessionList() 获取所有会话 会话列表
    std::vector<ModelInfo> getAvailableModels() 获取支持的所有模型 模型列表
    bool deleteSession(const std::string& sessionId) 删除指定会话 成功返回 true
    std::string sendMessage(const std::string sessionId, const std::string& message) 发消息,模型一次性返回完整回复 完整回复
    std::string sendMessageStream(const std::string sessionId, const std::string& message, std::function<void(const std::string&, bool)> callback) 发消息,模型生成一点返回一点(流式) 完整回复

    相关数据结构(如 Message、ModelInfo 等)在 sdk/include/common.h 里定义。

    两种发消息方式的区别

    • sendMessage(全量):等模型全部生成完,一次性返回。简单,但不适合长回答。
    • sendMessageStream(流式):生成一段就回调一段,适合做「打字机效果」。回调函数的签名是 (const std::string& response, bool done):
      • response:本次返回的片段
      • done:是否已结束

    五、快速上手:5 分钟跑通第一个对话

    完整示例代码

    #include <iostream>
    #include <ai_chat_sdk/chat_sdk.h>
    #include <ai_chat_sdk/util/my_logger.h>

    // 发送一条消息(流式)
    void sendMessageStream(ai_chat_sdk::ChatSDK& chatSDK, const std::string& sessionId)
    {
    std::cout << "————–发送消息————–" << std::endl;
    std::cout << "user消息 > ";
    std::string message;
    std::getline(std::cin, message);
    std::cout << "————–发送消息完成————–" << std::endl;

    chatSDK.sendMessageStream(sessionId, message,
    [](const std::string& response, bool done) {
    std::cout << "assistant消息: " << response << std::endl;
    if (done) {
    std::cout << "————–接收消息完成————–" << std::endl;
    }
    });
    }

    int main(int argc, char* argv[])
    {
    // 1. 初始化日志
    bite::Logger::init_logger("aiChatServer", "stdout", spdlog::level::info);

    ai_chat_sdk::ChatSDK chatSDK;

    // 2. 配置 deepseek 模型信息
    ai_chat_sdk::ApiConfig deepseekConfig;
    deepseekConfig.api_key = std::getenv("deepseek_apikey");
    deepseekConfig.temperature = 0.7;
    deepseekConfig.max_tokens = 2048;
    deepseekConfig.model_name = "deepseek-chat";

    // 3. 组装配置列表并初始化模型
    std::vector<std::shared_ptr<ai_chat_sdk::Config>> configs;
    configs.push_back(std::make_shared<ai_chat_sdk::ApiConfig>(deepseekConfig));
    chatSDK.initModels(configs);

    // 4. 创建会话
    std::cout << "————–创建会话————–" << std::endl;
    std::string sessionId = chatSDK.createSession("deepseek-chat");
    std::cout << "创建会话成功, 会话ID: " << sessionId << std::endl;

    // 5. 循环对话
    int userOP = 1;
    while (true) {
    std::cout << "——-1. send message 0. exit—————–" << std::endl;
    std::cin >> userOp;
    if (userOP == 0) break;
    getchar();
    sendMessageStream(chatSDK, sessionId);
    }

    std::cout << "————–程序退出————–" << std::endl;
    return 0;
    }

    关键步骤解析:

  • 初始化日志:bite::Logger::init_logger(…),SDK 内部用 spdlog 打日志,建议放在最前面。
  • 配置模型:ApiConfig 里填 api_key、temperature、max_tokens、model_name。
  • 初始化模型:把配置装进 configs 向量,调用 initModels(configs)。
  • 创建会话:createSession("deepseek-chat") 返回一个 sessionId。
  • 循环发消息:用 sendMessageStream 做流式对话,回调里实时打印。
  • 这里有一个坑:getenv 可能返回空指针

    deepseekConfig.api_key = std::getenv("deepseek_apikey");

    如果环境变量 deepseek_apikey 没有设置,std::getenv 会返回 nullptr,把它赋给 std::string 是未定义行为,程序直接崩溃。

    稳妥写法:

    const char* key = std::getenv("deepseek_apikey");
    if (key == nullptr) {
    std::cerr << "请先设置环境变量 deepseek_apikey" << std::endl;
    return -1;
    }
    deepseekConfig.api_key = key;


    六、接入自己的项目:CMake 配置

    可以在自己的项目里写 CMakeLists.txt,参考如下:

    # 项目名称
    project(AIChatDemo)

    # 使用 C++17,不允许降级
    set(CMAKE_CXX_STANDARD 17)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)

    # 构建类型
    set(CMAKE_BUILD_TYPE Debug)

    # 可执行文件
    add_executable(AIChatDemo cmdChatDemo.cpp)

    # 查找 OpenSSL
    find_package(OpenSSL REQUIRED)
    include_directories(${OPENSSL_INCLUDE_DIR})

    # 添加库搜索目录(ai_chat_sdk 装在这里)
    link_directories(/usr/local/lib)

    # 链接 SDK 及依赖
    target_link_libraries(AIChatDemo PRIVATE
    ai_chat_sdk
    fmt
    jsoncpp
    OpenSSL::SSL
    OpenSSL::Crypto
    gflags
    spdlog
    sqlite3
    )

    编译运行:

    mkdir build && cd build
    cmake ..
    make


    七、注意事项

  • 先 initModels 再 createSession:必选顺序。createSession 的 modelName 要和你配置里的 model_name 对上(如 "deepseek-chat")。
  • 模型名别写错:deepseek-chat、gpt-4o-mini、gemini-2.0-flash 是三个不同的模型名,用哪个模型就传哪个。
  • 流式回调里别做重活:sendMessageStream 的回调会被频繁调用,里面尽量只做显示这类轻量操作。
  • 会话持久化在 SQLite:程序重启后,getSessionList() 依然能拿到历史会话——这正是 DataManager 的功劳。
  • 本地 Ollama:想零成本试玩,用 OllamaConfig + deepseek-r1:1.5b 起本地模型,不花 API 费。

  • 八、总结

    ChatSDK 这套设计最值得学的,不是某个具体 API,而是它的分层思想:

    • 门面(ChatSDK) 收口所有对外能力;
    • 抽象接口(LLMProvider / Config) 隔离变化;
    • 具体实现(各家 Provider) 各自为战;
    • 数据层(DataManager) 负责持久化。

    上层只认抽象,下层随便换厂商——这就是多模型统一接入的正确姿势。

    赞(0)
    未经允许不得转载:171主机测评 » 【从零实现C++AI大模型接入SDK】(系列二)《用 C++ 写一个多厂商大模型统一接入SDK:ChatSDK 上手实现记录》
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址