欢迎光临
我们一直在努力

C++AI

文章目录

  • 一、前言
    • 1. 项目介绍
    • 2. 最终效果
    • 3. gitee链接
  • 二、 环境配置
    • 1. 依赖库安装命令
    • 2. cpp-httplib 配置(header-only 库)
    • 3. 安装SQLite
  • 三、 项目实现
    • 1. 目录结构与开发顺序
      • sdk开发顺序
      • ChatServer开发顺序
    • 2. common.hpp
    • 3. LLMProvider.hpp
    • 4. DeepSeekProvider类
      • 全量响应
      • 流式响应
    • 5. 测试DeepSeek接口是否成功
    • 6. LLMManager类
    • 7. 不接入DataManager的SessionManager
    • 8. DataManager类
    • 9. 接入DataManager的SessionManager
    • 10. ChatSDK
    • 11. ChatSDK测试
    • 12. ChatServer类与前端

一、前言

1. 项目介绍

本项目带领大家从零实现C++AI大语言模型接入SDK,SDK核心特性如下: ✅统一的多模型接入:支持DeepSeek-V3.2-Exp、gemini-2.0-flash、gpt-4o-mini等主流模型;支持通过Ollama无缝接入本地模型(deepseek-r1:1.5b),保障数据隐私与低延迟 ✅高效流式响应与解析:完整支持 Server-Sent Events 流式传输,实时解析模型返回的数据流,实现类似“逐字打印”效果 ✅完整的会话管理:自动维护多轮对话上下文;支持创建、删除、获取会话列表,历史会话信息查看;支持应用重启后会话状态恢复,实现"断点续聊" ✅开箱即用的SDK设计:清晰简洁的API接口,方便在项目中集成以及扩展本项目不仅提供了一个功能完备的SDK,还包含一个基于该SDK实现的智能聊天机器人,演示如何将SDK快速接入到自己项目中。

目前博主只是接入了一个deepseek,自己感兴趣还可以接入chatgpt、Gemini等等大模型,和deepseek的接入过程几乎一摸一样,大家感兴趣可以自己试试

2. 最终效果

在这里插入图片描述 在这里插入图片描述

在这里插入图片描述

3. gitee链接

gitee链接:https://gitee.com/qi-haozhe/ai-model-acess-tech 超链接 大家可以点击提交记录,就可以看我从零开始,先写的什么,后写的什么了。 在这里插入图片描述

二、 环境配置

博主是在云服务器上写的,建议大家如果要配置ubuntu环境的话,选择新一点的版本,这样安装各种依赖方便点。此处我的云服务器版本是ubuntu24.04 在这里插入图片描述

1. 依赖库安装命令

# 1. gflags 安装
sudo apt-get install libgflags-dev

# 2. spdlog 安装
sudo apt-get install libspdlog-dev

# 3. fmt 安装
sudo apt-get install fmt

# 4. jsoncpp 安装
sudo apt-get install libjsoncpp-dev

# 5. gtest 安装
sudo apt-get install libgtest-dev

# 6. ssl 安装
sudo apt-get install libssl-dev

# 7. cmake 安装
sudo apt-get install cmake

# 8. pkg-config 安装(编译时查找库文件工具)
sudo apt install pkg-config

# 9. curl 工具安装
sudo apt install curl

2. cpp-httplib 配置(header-only 库)

# 1. 下载库
git clone https://github.com/yhirose/cpp-httplib.git

# 2. 拷贝头文件到系统目录(全局可用)
sudo cp cpp-httplib/httplib.h /usr/include/

说明:cpp-httplib是单头文件库,代码中直接#include <httplib.h>即可使用

3. 安装SQLite

# 1. 安装sqlite3命令行工具(用于终端操作数据库)
sudo apt install sqlite3

# 2. 安装sqlite开发库(用于C/C++程序编译)
sudo apt install libsqlite3-dev

# 3. 查看开发库包含的文件及路径(可选)
dpkg -L libsqlite3-dev

  • 头文件路径:/usr/include/sqlite3.h、/usr/include/sqlite3ext.h
  • 静态库文件:/usr/lib/x86_64-linux-gnu/libsqlite3.a
  • 动态库文件:/usr/lib/x86_64-linux-gnu/libsqlite3.so
  • 配置文件:/usr/lib/x86_64-linux-gnu/pkgconfig/sqlite3.pc

编译时链接sqlite3库 Makefile中链接

g++ main.cpp -o my_program -lsqlite3

CMakeLists.txt中链接

target_link_libraries(${SDK_NAME} sqlite3)

三、 项目实现

1. 目录结构与开发顺序

最终目录结构如下图所示 在这里插入图片描述

编写顺序建议先写sdk目录下的所有内容,sdk的完成了再去写ChatServer 目录中的内容

sdk开发顺序

  • common.hpp
  • util/myLog.hpp myLog.cc
  • LLMProvider.hpp DeepSeekProvider.cc
  • 去test目录下写个测试,看看写好的DeepSeek能不能正常调用
  • LLMManager.hpp LLMManager.cc
  • 写不接入DataManager的SessionManager.hpp SessionManager.cc
  • DataManager.hpp DataManager.cc
  • 完善SessionManager.hpp SessionManager.cc 接入DataManager类
  • ChatSDK.hpp ChatSDK.cc
  • 最后test目录下写个测试,测一下ChatSDK能不能正常使用就行
  • sdk目录整体工作完成
  • ChatServer开发顺序

  • ChatServer.hpp ChatServer.cc 都写完
  • 直接写个main.cc启动服务器即可
  • 至此本项目的服务端部分就完成了
  • 最后写个前端界面和配置文件就行了,前端界面要不让ai写一个,要不直接把我build目录下的前端代码拷贝到你项目中即可。
  • 2. common.hpp

    这里面就定义一些基础的数据结构,以备后面需要。 Message定义消息结构体,用来表示每条消息记录 Config用来表示模型的配置信息 析构函数记得声明虚函数要不然可能会有问题 ModelInfo用来描述模型信息 Session描述每个会话的信息 这里的会话是指这个: 在这里插入图片描述

    #pragma once
    #include <vector>
    #include <string>
    #include <ctime>

    namespace ai_chat_sdk{

    struct Message{
    std::string _messageId;
    std::string _role; //表示这条消息是用户发的还是ai回复的
    std::string _content;
    std::time_t _timestamp;

    Message(const std::string &role = "", const std::string &content = "")
    : _role(role), _content(content), _timestamp(0)
    {}
    };

    struct Config{
    std::string _modelName;
    double _temperature; //回答发散程度
    int _maxTokens; //模型最大tokens,这个太小可能会导致模型回答不完一个问题就结束了

    virtual ~Config() = default;
    };

    struct ApiConfig:public Config
    {
    std::string _apiKey;
    };

    struct OllamaConfig : public Config
    {
    std::string _modelName; // 模型名称
    std::string _modelDesc; // 模型描述
    std::string _endpoint; // 模型API endpoint base url
    };

    struct ModelInfo{
    std::string _modelName;
    std::string _modelDesc;
    std::string _provider;
    std::string _endPoint;
    bool _isAvailable = false;

    ModelInfo(const std::string& modelName = "", const std::string& modelDesc = "", const std::string& provider = "", const std::string& endpoint = "")
    : _modelName(modelName), _modelDesc(modelDesc), _provider(provider), _endPoint(endpoint){}
    };
    struct Session{
    std::string _sessionId;
    std::string _modelName; //会话用的哪一个模型
    std::vector<Message> _messages; //会话内的所有消息,包含用户问的和ai回复的
    std::time_t _createTime;
    std::time_t _updateTime;
    Session(const std::string &modelName = "")
    : _modelName(modelName)
    {}
    };
    }// end ai_chat_sdk

    3. LLMProvider.hpp

    这个类就是一个抽象类,里面定义了一堆虚函数。后面所有要接入的模型都需要继承这一个类然后重写这些虚函数。也就是说你后面要接入DeepSeek、ChatGPT、Gemini的话都创建一个类,继承LLMProvider实现虚函数就行。算是一个策略类,以后把LLMProvider这个父类指针当作成员变量,想要什么模型就把什么子类指针赋值给父类指针就行,这里是利用了一下多态。 成员变量设置为protected,三个成员变量分别表示是否就绪、apikey和请求地址,比如deepseek的请求地址就是https://api.deepseek.com

    #pragma once
    #include <string>
    #include <map>
    #include <vector>
    #include <functional>
    #include "common.hpp"
    namespace ai_chat_sdk {
    class LLMProvider{
    public:
    // 初始化模型
    virtual bool initModel(const std::map<std::string, std::string>& modelConfig) = 0;
    //模型是否有效
    virtual bool isAvailable() = 0;
    // 获取模型名称
    virtual std::string getModelName() const = 0;
    // 获取模型描述
    virtual std::string getModelDesc() const = 0;
    // 发送消息并获取响应
    virtual std::string sendMessage(const std::vector<Message>& messages,const std::map<std::string, std::string>& requestParams) = 0;
    // 发送消息并获取流式响应
    virtual std::string sendMessageStream(const std::vector<Message>& messages,
    const std::map<std::string, std::string>& requestParams,
    std::function<void(const std::string&, bool)> callback) = 0; // callback: 对模型返回的增量数据如何处理,第一个参数为增量数据,第二个参数为是否为最后一个增量数据)

    protected:
    bool _isAvailable = false;
    std::string _apiKey;
    std::string _endPoint;
    };
    }

    4. DeepSeekProvider类

    DeepSeekProvider类就是继承LLMProvider抽象类重写虚函数而已。 前四个都比较简单,我们重点说一下最后两个虚函数具体如何实现:

    // 发送消息并获取响应 std::string sendMessage(const std::vector&messages,const std::map<std::string, std::string>& requestParams); //发送消息并获取流式响应 std::string sendMessageStream(const std::vector&messages, const std::map<std::string, std::string>& requestParams, std::function<void(const std::string&, bool)> callback);

    //DeepSeekProvider.hpp
    #pragma once
    #include "LLMProvider.hpp"

    namespace ai_chat_sdk {
    class DeepSeekProvider : public LLMProvider {
    public:
    // 初始化模型
    bool initModel(const std::map<std::string, std::string>& modelConfig);
    //模型是否有效
    bool isAvailable();
    // 获取模型名称
    std::string getModelName() const;
    // 获取模型描述
    std::string getModelDesc() const;
    // 发送消息并获取响应
    std::string sendMessage(const std::vector<Message>& messages,const std::map<std::string, std::string>& requestParams);
    // 发送消息并获取流式响应
    std::string sendMessageStream(const std::vector<Message>& messages,
    const std::map<std::string, std::string>& requestParams,
    std::function<void(const std::string&, bool)> callback);

    };

    } // end ai_chat_sdk

    全量响应

    这个函数的主要功能就是拼凑json格式的请求消息,然后通过httplib构造http请求,去deepseek官网根据你设置的deepseek_apikey鉴权,然后获取响应消息,解析响应消息然后返回字符串就行,这个返回的字符串未来就是前端显示的字符串。

    拼装请求JSON → 发起HTTP POST请求 → 接收HTTP响应 → 校验响应有效性 → 解析响应JSON → 精准提取AI回复内容 → 返回纯文本结果

    这个函数就主要是全量响应,意思是你发送请求后,只有当获取到全部的响应之后才会返回到前端,给人的感觉就是你前端问了一个问题,然后等了几秒钟,然后突然所有结果全都展现出来了,与之对应的就是流式响应,现在的ai都是流式响应。

    先从传入的requestParams中读取配置参数,没有传参则使用默认值,不影响协议和格式,只影响模型生成策略:

    double temperature = 0.7; // 默认温度值,控制回复随机性
    int maxTokens = 2048; // 默认最大生成长度
    // 解析外部传入的参数覆盖默认值
    if(requestParams.find("temperature") != requestParams.end()) temperature = std::stod(requestParams.at("temperature"));
    if(requestParams.find("max_tokens") != requestParams.end()) maxTokens = std::stoi(requestParams.at("max_tokens"));


    然后是构造请求消息,请求的http body字段就是下面这个表格,这几个字段是必填项,我们按照格式拼接就行。 在这里插入图片描述

    大模型需要上下文,所以每次请求的时候我们需要把传入的std::vector<Message>历史消息列表,转成JSON数组格式:

    Json::Value messageArray(Json::arrayValue); // 创建JSON数组对象
    for(const auto &msg : messages){
    Json::Value messageJson; // 单个消息的JSON对象
    messageJson["role"] = msg._role; // 填充角色:user/assistant/system
    messageJson["content"] = msg._content; // 填充消息内容:用户提问/AI回复
    messageArray.append(messageJson); // 单个消息对象加入数组
    }

    拼装后的messageArray JSON结构示例:

    [
    {"role":"user","content":"你好"},
    {"role":"assistant","content":"您好,有什么可以帮您?"},
    {"role":"user","content":"给我整理一份C++库清单"}
    ]

    组装完整的请求JSON根对象按 DeepSeek 官方接口规范,拼接顶层必填字段,缺一不可:

    Json::Value requestJson; // JSON根对象
    requestJson["model"] = getModelName(); // 必填:模型名称 deepseek-chat
    requestJson["messages"] = messageArray; // 必填:历史消息数组
    requestJson["temperature"] = temperature; // 可选:随机性参数
    requestJson["max_tokens"] = maxTokens; // 可选:最大生成token数

    JSON对象 → 无缩进的纯字符串(请求体最终格式)

    Json::StreamWriterBuilder writerBuilder;
    writerBuilder["indentation"] = ""; // 关键:设置空缩进,生成紧凑的JSON字符串,减少传输体积
    std::string requestBodyStr = Json::writeString(writerBuilder, requestJson);

    最终拼接完成的requestBodyStr 标准请求体(纯字符串),直接作为HTTP POST的请求体发送:

    {"model":"deepseek-chat","messages":[{"role":"user","content":"你好"},{"role":"assistant","content":"您好"},{"role":"user","content":"整理C++库清单"}],"temperature":0.7,"max_tokens":2048}


    发起HTTP POST请求(cpp-httplib实现)

    httplib::Client cli(_endPoint.c_str()); // 创建HTTP客户端,_endPoint是DeepSeek的接口域名/地址
    cli.set_connection_timeout(30); // 连接超时30秒
    cli.set_read_timeout(60); // 读取响应超时60秒

    // 设置HTTP请求头(重中之重,2个必填请求头,缺一不可)
    httplib::Headers headers = {
    {"Authorization", "Bearer " + _apiKey}, // 身份认证:接口鉴权,格式固定 Bearer+空格+APIKey
    {"Content-Type", "application/json"} // 告诉服务端:本次请求体是JSON格式
    };

    // 发起POST请求:【接口路径 + 请求头 + 请求体字符串 + 请求体格式】
    auto res = cli.Post("/v1/chat/completions", headers, requestBodyStr, "application/json");


    然后就是获取响应解析响应了:

  • 大模型返回的 res->body 是一个 JSON格式的字符串,格式是固定的(接口文档约定)
  • 使用 JsonCpp 把「JSON字符串」解析成「C++的Json::Value对象」
  • 按照 JSON的层级结构,一层一层「精准取值」,最终拿到AI回复的纯文本内容
  • 所有取值步骤都做了合法性判断,防止JSON字段缺失导致程序崩溃
  • 响应会有如下字段,我们只需要把choices->message->content提取到就行,也就是说我们只要这个content字段,根据json的语法去获取之后再校验一下就行。

    {
    "id": "chatcmpl-xxx",
    "object": "chat.completion",
    "created": 1735620000,
    "model": "deepseek-chat",
    "choices": [ // 核心数组,固定只有1个元素
    {
    "index": 0,
    "message": { // 核心对象
    "role": "assistant",
    "content": "这是AI给你的最终回复内容,纯文本格式" // ✅ 我们要的最终数据
    },
    "finish_reason": "stop"
    }
    ],
    "usage": {"prompt_tokens": 10, "completion_tokens": 20, "total_tokens": 30}
    }

    在这里插入图片描述

    Json::Value responseBody; // 存储解析后的JSON对象
    Json::CharReaderBuilder readerBuilder; // JsonCpp解析器
    std::string parseError; // 解析失败的错误信息
    std::istringstream responseStream(res->body); // 把响应字符串转成输入流

    // 核心解析API:把响应流解析到responseBody对象中
    bool parseOk = Json::parseFromStream(readerBuilder, responseStream, &responseBody, &parseError);

    对应的提取代码(和上面的JSON结构完全一一对应)

    if(parseOk){ // 第一步:判断JSON解析是否成功
    // 第二步:判断是否有choices字段 + 是数组 + 数组非空
    if(responseBody.isMember("choices") && responseBody["choices"].isArray() && !responseBody["choices"].empty()){
    auto choice = responseBody["choices"][0]; // 取choices数组的第一个元素(固定只有1个)
    // 第三步:判断choice里有message字段 + message里有content字段
    if(choice.isMember("message") && choice["message"].isMember("content")){
    // ✅ 最终提取:拿到AI回复的纯文本内容
    std::string replyContent = choice["message"]["content"].asString();
    INFO("DeepSeekProvider response text: {}", replyContent);
    return replyContent; // 返回结果,结束函数
    }
    }
    }

    完整代码:

    std::string DeepSeekProvider::sendMessage(const std::vector<Message> &messages, const std::map<std::string, std::string> &requestParams)
    {
    if(!isAvailable()){
    ERROR("DeepSeekProvider sendMessage model not available");
    return "";
    }

    double temperature = 0.7;
    int maxTokens = 2048;

    if(requestParams.find("temperature") != requestParams.end()){
    temperature = std::stod(requestParams.at("temperature"));
    }
    if(requestParams.find("max_tokens") != requestParams.end()){
    maxTokens = std::stoi(requestParams.at("max_tokens"));
    }

    //根据以前的messages构建历史消息记录
    Json::Value messageArray(Json::arrayValue);
    for(const auto &msg : messages){
    //INFO("Role: {}, Content: {}", msg._role, msg._content);
    Json::Value messageJson;
    messageJson["role"] = msg._role;
    messageJson["content"] = msg._content;
    messageArray.append(messageJson);
    }

    Json::Value requestJson;
    requestJson["model"] = getModelName();
    requestJson["messages"] = messageArray;
    requestJson["temperature"] = temperature;
    requestJson["max_tokens"] = maxTokens;

    Json::StreamWriterBuilder writerBuilder;
    writerBuilder["indentation"] = "";
    std::string requestBodyStr = Json::writeString(writerBuilder, requestJson);
    INFO("DeepSeekProvider sendMessage requestBody: {}", requestBodyStr);

    httplib::Client cli(_endPoint.c_str());
    cli.set_connection_timeout(30); // 连接超时30秒
    cli.set_read_timeout(60); // 读取超时60秒

    // 设置请求头
    httplib::Headers headers = {
    {"Authorization", "Bearer " + _apiKey},
    {"Content-Type", "application/json"}
    };
    auto res = cli.Post("/v1/chat/completions", headers, requestBodyStr, "application/json");
    if(!res){
    ERROR("DeepSeekProvider sendMessage request failed, error: {}", httplib::to_string(res.error()));
    return "";
    }
    INFO("DeepSeekProvider sendMessage POST request success, status : {}", res->status);
    INFO("DeepSeekProvider sendMessage POST request success, body : {}", res->body);

    // 检测响应是否成功
    if(res->status != 200){
    return "";
    }

    Json::Value responseBody;
    Json::CharReaderBuilder readerBuilder;
    std::string parseError;
    std::istringstream responseStream(res->body);
    if(Json::parseFromStream(readerBuilder, responseStream, &responseBody, &parseError)){
    // 获取message数组
    if(responseBody.isMember("choices") && responseBody["choices"].isArray() && !responseBody["choices"].empty()){
    auto choice = responseBody["choices"][0];
    if(choice.isMember("message") && choice["message"].isMember("content")){
    std::string replyContent = choice["message"]["content"].asString();
    INFO("DeepSeekProvider response text: {}", replyContent);
    return replyContent;
    }
    }
    }

    // 8. json解析失败
    ERROR("DeepSeekProvider sendMessage POST response body parse failed, error");
    return "deepseek response json parse failed";

    }

    //DeepSeekProvider.cc
    #include "../include/DeepSeekProvider.hpp"
    #include "../include/util/myLog.hpp"
    #include "../include/DeepSeekProvider.hpp"
    #include <jsoncpp/json/json.h>
    #include "../include/httplib.h"
    #include <jsoncpp/json/reader.h>
    namespace ai_chat_sdk
    {
    bool DeepSeekProvider::initModel(const std::map<std::string, std::string> &modelConfig){
    auto it = modelConfig.find("api_key");
    if(it != modelConfig.end()){
    _apiKey = it->second;
    } else {
    ERROR("DeepSeekProvider initModel api_key not found");
    return false;
    }

    it = modelConfig.find("end_point");
    if(it != modelConfig.end()){
    _endPoint = it->second;
    } else {
    _endPoint = "https://api.deepseek.com";
    }
    _isAvailable = true;
    INFO("DeepSeekProvider initModel success, endpoint: {}",_endPoint);
    return true;
    }
    bool DeepSeekProvider::isAvailable()
    {
    return _isAvailable;
    }
    std::string DeepSeekProvider::getModelName() const
    {
    return "deepseek-chat";
    }
    std::string DeepSeekProvider::getModelDesc() const{
    return "一款实用性强、中文优化的通用对话助手,适合日常问答与创作";
    }

    std::string DeepSeekProvider::sendMessage(const std::vector<Message> &messages, const std::map<std::string, std::string> &requestParams)
    {
    if(!isAvailable()){
    ERROR("DeepSeekProvider sendMessage model not available");
    return "";
    }

    double temperature = 0.7;
    int maxTokens = 2048;

    if(requestParams.find("temperature") != requestParams.end()){
    temperature = std::stod(requestParams.at("temperature"));
    }
    if(requestParams.find("max_tokens") != requestParams.end()){
    maxTokens = std::stoi(requestParams.at("max_tokens"));
    }

    //根据以前的messages构建历史消息记录
    Json::Value messageArray(Json::arrayValue);
    for(const auto &msg : messages){
    //INFO("Role: {}, Content: {}", msg._role, msg._content);
    Json::Value messageJson;
    messageJson["role"] = msg._role;
    messageJson["content"] = msg._content;
    messageArray.append(messageJson);
    }

    Json::Value requestJson;
    requestJson["model"] = getModelName();
    requestJson["messages"] = messageArray;
    requestJson["temperature"] = temperature;
    requestJson["max_tokens"] = maxTokens;

    Json::StreamWriterBuilder writerBuilder;
    writerBuilder["indentation"] = "";
    std::string requestBodyStr = Json::writeString(writerBuilder, requestJson);
    INFO("DeepSeekProvider sendMessage requestBody: {}", requestBodyStr);

    httplib::Client cli(_endPoint.c_str());
    cli.set_connection_timeout(30); // 连接超时30秒
    cli.set_read_timeout(60); // 读取超时60秒

    // 设置请求头
    httplib::Headers headers = {
    {"Authorization", "Bearer " + _apiKey},
    {"Content-Type", "application/json"}
    };
    auto res = cli.Post("/v1/chat/completions", headers, requestBodyStr, "application/json");
    if(!res){
    ERROR("DeepSeekProvider sendMessage request failed, error: {}", httplib::to_string(res.error()));
    return "";
    }
    INFO("DeepSeekProvider sendMessage POST request success, status : {}", res->status);
    INFO("DeepSeekProvider sendMessage POST request success, body : {}", res->body);

    // 检测响应是否成功
    if(res->status != 200){
    return "";
    }

    Json::Value responseBody;
    Json::CharReaderBuilder readerBuilder;
    std::string parseError;
    std::istringstream responseStream(res->body);
    if(Json::parseFromStream(readerBuilder, responseStream, &responseBody, &parseError)){
    // 获取message数组
    if(responseBody.isMember("choices") && responseBody["choices"].isArray() && !responseBody["choices"].empty()){
    auto choice = responseBody["choices"][0];
    if(choice.isMember("message") && choice["message"].isMember("content")){
    std::string replyContent = choice["message"]["content"].asString();
    INFO("DeepSeekProvider response text: {}", replyContent);
    return replyContent;
    }
    }
    }

    // 8. json解析失败
    ERROR("DeepSeekProvider sendMessage POST response body parse failed, error");
    return "deepseek response json parse failed";

    }
    std::string DeepSeekProvider::sendMessageStream(const std::vector<Message> &messages,
    const std::map<std::string, std::string> &requestParams,
    std::function<void(const std::string &, bool)> callback)
    {
    if(!isAvailable()){
    ERROR("DeepSeekProvider sendMessageStream model not available");
    return "";
    }

    // 2. 构造请求参数
    double temperature = 0.7;
    int maxTokens = 2048;
    if(requestParams.find("temperature") != requestParams.end()){
    temperature = std::stod(requestParams.at("temperature"));
    }
    if(requestParams.find("max_tokens") != requestParams.end()){
    maxTokens = std::stoi(requestParams.at("max_tokens"));
    }

    // 构造历史消息
    Json::Value messageArray(Json::arrayValue);
    for(const auto& message : messages){
    Json::Value messageObject;
    messageObject["role"] = message._role;
    messageObject["content"] = message._content;
    messageArray.append(messageObject);
    }

    // 3. 构造请求体
    Json::Value requestBody;
    requestBody["model"] = getModelName();
    requestBody["messages"] = messageArray;
    requestBody["temperature"] = temperature;
    requestBody["max_tokens"] = maxTokens;
    requestBody["stream"] = true;

    // 4. 序列化
    Json::StreamWriterBuilder writerBuilder;
    writerBuilder["indentation"] = "";
    std::string requestBodyStr = Json::writeString(writerBuilder, requestBody);
    INFO("DeepSeekProvider sendMessageStream requestBody: {}", requestBodyStr);

    // 5. 使用cpp-httplib库构造HTTP客户端
    httplib::Client client(_endPoint.c_str());
    client.set_connection_timeout(30, 0); // 连接超时时间为30秒
    client.set_read_timeout(300, 0); // 流式响应需要更长的时间,设置超时时间为300秒

    // 设置请求头
    httplib::Headers headers = {
    {"Authorization", "Bearer " + _apiKey},
    {"Content-Type", "application/json"},
    {"Accept", "text/event-stream"}
    };

    // 流式处理变量
    std::string buffer; // 接受流式响应的数据块
    bool gotError = false; // 标记响应是否成功
    std::string errorMsg; // 错误描述符
    int statusCode = 0; // 响应状态码
    bool streamFinish = false; // 标记流式响应是否完成
    std::string fullResponse; // 累积完整的响应

    // 创建请求对象
    httplib::Request req;
    req.method = "POST";
    req.path = "/v1/chat/completions";
    req.headers = headers;
    req.body = requestBodyStr;
    // 设置响应处理器
    req.response_handler = [&](const httplib::Response& res) {
    if(res.status != 200){
    gotError = true;
    errorMsg = "HTTP status code: " + std::to_string(res.status);
    return false; // 终止请求
    }
    return true; // 继续接收后续数据
    };

    // 设置数据接收处理器–解析流式响应的每个块的数据
    req.content_receiver = [&](const char* data, size_t len, size_t offset, size_t totalLength){
    // 验证响应头是否错误,如果出错就不需要再继续接收数据
    if(gotError){
    return false;
    }

    // 追加数据到buffer
    buffer.append(data, len);
    INFO("DeepSeekProvider sendMessageStream buffer: {}", buffer);

    // 处理所有的流式响应的数据块,注意:数据块之间是一个\\n\\n分隔
    size_t pos= 0;
    while((pos = buffer.find("\\n\\n")) != std::string::npos){
    // 截取当前找到的数据块
    std::string chunk = buffer.substr(0, pos);
    buffer.erase(0, pos + 2);

    // 解析该块响应数据的中模型返回的有效数据
    // 处理空行和注释,注意:以:开头的行是注释行,需要忽略
    if(chunk.empty() || chunk[0] == ':'){
    continue;
    }

    // 获取模型返回的有效数据
    if(chunk.compare(0, 6, "data: ") == 0){
    std::string modelData = chunk.substr(6);

    // 检测是否为结束标记
    if(modelData == "[DONE]"){
    callback("", true);
    streamFinish = true;
    return true;
    }

    // 反序列化
    Json::Value modelDataJson;
    Json::CharReaderBuilder reader;
    std::string errors;
    std::istringstream modelDataStream(modelData);
    if(Json::parseFromStream(reader, modelDataStream, &modelDataJson, &errors)){
    // 模型返回的json格式的数据现在就保存在modelDataJson
    if(modelDataJson.isMember("choices") &&
    modelDataJson["choices"].isArray() &&
    !modelDataJson["choices"].empty() &&
    modelDataJson["choices"][0].isMember("delta") &&
    modelDataJson["choices"][0]["delta"].isMember("content")){
    std::string content = modelDataJson["choices"][0]["delta"]["content"].asString();
    // 处理deltaContent,例如追加到fullResponse
    fullResponse += content;

    // 将本次解析出的模型返回的有效数据转给调用sendMessageStraem函数的用户使用—callback
    callback(content, false);
    }
    }else{
    WARN("DeepSeekProvider sendMessageStream parse modelDataJson error: {}", errors);
    }
    }
    }
    return true;
    };

    // 给模型发送请求
    auto result = client.send(req);
    if(!result){
    // 请求发送失败,出现网络问题,比如DNS解析失败、连接超时
    ERROR("Network error {}", to_string(result.error()));
    return "";
    }

    // 确保流式操作正确结束
    if(!streamFinish){
    WARN("stream ended without [DONE] marker");
    callback("", true);
    }

    return fullResponse;
    }
    } // end ai_chat_sdk

    流式响应

    关于流式响应我们这里用的是SSE协议,流式响应在这里的使用方法就是客户端一次请求,服务端分多次把完整的数据发过来,即所谓的流式,具体介绍如下:

    /v1/chat/completions + stream=true 是 text/event-stream (简称SSE) 协议,这是大模型流式输出的事实标准协议(OpenAI/DeepSeek/Anthropic 全用它),不是普通的HTTP JSON响应,这是理解流式的前提:

  • SSE 是单向的服务端推流、客户端流式接收的 HTTP 协议,基于 HTTP 长连接实现
  • 服务端不会一次性返回完整JSON,而是分批次、一小块一小块的返回数据
  • 数据格式约定:每个有效数据块用 \\n\\n 双换行符分隔,每个块的开头固定是 data: 前缀
  • 结束标记固定:服务端最后会返回 data: [DONE] 表示流推送完成
  • 空行/以:开头的行是注释行,协议层面要求客户端直接忽略,不做解析

  • cpp-httplib 实现流式响应的核心:两个回调函数 cpp-httplib 是一个轻量但功能完整的C++ HTTP客户端库,它对流式HTTP响应的支持,完全依赖两个核心回调, 回调1:req.response_handler —— 响应头回调(只执行1次)

    req.response_handler = [&](const httplib::Response& res) {
    if(res.status != 200){
    gotError = true;
    errorMsg = "HTTP status code: " + std::to_string(res.status);
    return false; // 终止请求
    }
    return true; // 继续接收后续数据
    };

    作用&执行时机

  • 执行时机:客户端接收到服务端返回的 HTTP响应头(Status+Headers)时,立刻执行,且只会执行一次
  • 核心作用:校验请求是否成功、提前拦截错误,不需要等到接收完整数据再判断
  • 返回值规则:return true 表示「响应头合法,继续接收流式数据体」;return false 表示「响应非法,立刻终止整个请求,不再接收任何数据」
  • 此处是判断状态码是否为200,非200则标记错误、终止请求。
  • 回调2:req.content_receiver —— 流式数据体回调(核心!执行N次)

    req.content_receiver = [&](const char* data, size_t len, size_t offset, size_t totalLength){
    // 流式数据解析逻辑
    };

    服务端返回的流式数据体会被拆分成多个二进制数据块,每收到一小块数据,这个回调就会被执行一次,直到:

    • 服务端推送完所有数据、主动关闭连接;
    • 回调内部返回 false 主动终止;
    • 超时/网络异常中断。

    比如大模型返回100个字,会拆成10次推送,这个回调就执行10次,每次拿到10个字左右的二进制数据。

    回到我们的代码中: 先要设置http头部字段,要设置流式字段

    // 设置请求头
    httplib::Headers headers = {
    {"Authorization", "Bearer " + _apiKey},
    {"Content-Type", "application/json"},
    {"Accept", "text/event-stream"} //设置流式响应
    };

    后面就是设置了两个关键的回调函数: 这个是首次返回的时候执行一次的会回调函数,用于决定是否继续接收后面的信息,返回true表示继续接收反之直接停止,不再接收。下面的函数就是判断返回的htto状态码是不是 200,是则继续接收,反之停止。

    // 设置响应处理器
    req.response_handler = [&](const httplib::Response& res) {
    if(res.status != 200){
    gotError = true;
    errorMsg = "HTTP status code: " + std::to_string(res.status);
    return false; // 终止请求
    }
    return true; // 继续接收后续数据
    };

    下面这个是第二个重要回调,用于每次收到服务端发来的消息片段的处理。 这个回调函数就是把全量返回获取响应消息中的message的流程干了一边,然后每次提取到消息之后执行一下函数形参设置的回调函数而已,这个回调函数其实就是ChatServer用来流式发给前端的。

    // 设置数据接收处理器–解析流式响应的每个块的数据
    req.content_receiver = [&](const char* data, size_t len, size_t offset, size_t totalLength){
    // 验证响应头是否错误,如果出错就不需要再继续接收数据
    if(gotError){
    return false;
    }

    // 追加数据到buffer
    buffer.append(data, len);
    INFO("DeepSeekProvider sendMessageStream buffer: {}", buffer);

    // 处理所有的流式响应的数据块,注意:数据块之间是一个\\n\\n分隔
    size_t pos= 0;
    while((pos = buffer.find("\\n\\n")) != std::string::npos){
    // 截取当前找到的数据块
    std::string chunk = buffer.substr(0, pos);
    buffer.erase(0, pos + 2);

    // 解析该块响应数据的中模型返回的有效数据
    // 处理空行和注释,注意:以:开头的行是注释行,需要忽略
    if(chunk.empty() || chunk[0] == ':'){
    continue;
    }

    // 获取模型返回的有效数据
    if(chunk.compare(0, 6, "data: ") == 0){
    std::string modelData = chunk.substr(6);

    // 检测是否为结束标记
    if(modelData == "[DONE]"){
    callback("", true);
    streamFinish = true;
    return true;
    }

    // 反序列化
    Json::Value modelDataJson;
    Json::CharReaderBuilder reader;
    std::string errors;
    std::istringstream modelDataStream(modelData);
    if(Json::parseFromStream(reader, modelDataStream, &modelDataJson, &errors)){
    // 模型返回的json格式的数据现在就保存在modelDataJson
    if(modelDataJson.isMember("choices") &&
    modelDataJson["choices"].isArray() &&
    !modelDataJson["choices"].empty() &&
    modelDataJson["choices"][0].isMember("delta") &&
    modelDataJson["choices"][0]["delta"].isMember("content")){
    std::string content = modelDataJson["choices"][0]["delta"]["content"].asString();
    // 处理deltaContent,例如追加到fullResponse
    fullResponse += content;

    // 将本次解析出的模型返回的有效数据转给调用sendMessageStraem函数的用户使用—callback
    callback(content, false);
    }
    }else{
    WARN("DeepSeekProvider sendMessageStream parse modelDataJson error: {}", errors);
    }
    }
    }
    return true;
    };


    完整代码:

    std::string DeepSeekProvider::sendMessageStream(const std::vector<Message> &messages,
    const std::map<std::string, std::string> &requestParams,
    std::function<void(const std::string &, bool)> callback)
    {
    if(!isAvailable()){
    ERROR("DeepSeekProvider sendMessageStream model not available");
    return "";
    }

    // 2. 构造请求参数
    double temperature = 0.7;
    int maxTokens = 2048;
    if(requestParams.find("temperature") != requestParams.end()){
    temperature = std::stod(requestParams.at("temperature"));
    }
    if(requestParams.find("max_tokens") != requestParams.end()){
    maxTokens = std::stoi(requestParams.at("max_tokens"));
    }

    // 构造历史消息
    Json::Value messageArray(Json::arrayValue);
    for(const auto& message : messages){
    Json::Value messageObject;
    messageObject["role"] = message._role;
    messageObject["content"] = message._content;
    messageArray.append(messageObject);
    }

    // 3. 构造请求体
    Json::Value requestBody;
    requestBody["model"] = getModelName();
    requestBody["messages"] = messageArray;
    requestBody["temperature"] = temperature;
    requestBody["max_tokens"] = maxTokens;
    requestBody["stream"] = true;

    // 4. 序列化
    Json::StreamWriterBuilder writerBuilder;
    writerBuilder["indentation"] = "";
    std::string requestBodyStr = Json::writeString(writerBuilder, requestBody);
    INFO("DeepSeekProvider sendMessageStream requestBody: {}", requestBodyStr);

    // 5. 使用cpp-httplib库构造HTTP客户端
    httplib::Client client(_endPoint.c_str());
    client.set_connection_timeout(30, 0); // 连接超时时间为30秒
    client.set_read_timeout(300, 0); // 流式响应需要更长的时间,设置超时时间为300秒

    // 设置请求头
    httplib::Headers headers = {
    {"Authorization", "Bearer " + _apiKey},
    {"Content-Type", "application/json"},
    {"Accept", "text/event-stream"}
    };

    // 流式处理变量
    std::string buffer; // 接受流式响应的数据块
    bool gotError = false; // 标记响应是否成功
    std::string errorMsg; // 错误描述符
    int statusCode = 0; // 响应状态码
    bool streamFinish = false; // 标记流式响应是否完成
    std::string fullResponse; // 累积完整的响应

    // 创建请求对象
    httplib::Request req;
    req.method = "POST";
    req.path = "/v1/chat/completions";
    req.headers = headers;
    req.body = requestBodyStr;
    // 设置响应处理器
    req.response_handler = [&](const httplib::Response& res) {
    if(res.status != 200){
    gotError = true;
    errorMsg = "HTTP status code: " + std::to_string(res.status);
    return false; // 终止请求
    }
    return true; // 继续接收后续数据
    };

    // 设置数据接收处理器–解析流式响应的每个块的数据
    req.content_receiver = [&](const char* data, size_t len, size_t offset, size_t totalLength){
    // 验证响应头是否错误,如果出错就不需要再继续接收数据
    if(gotError){
    return false;
    }

    // 追加数据到buffer
    buffer.append(data, len);
    INFO("DeepSeekProvider sendMessageStream buffer: {}", buffer);

    // 处理所有的流式响应的数据块,注意:数据块之间是一个\\n\\n分隔
    size_t pos= 0;
    while((pos = buffer.find("\\n\\n")) != std::string::npos){
    // 截取当前找到的数据块
    std::string chunk = buffer.substr(0, pos);
    buffer.erase(0, pos + 2);

    // 解析该块响应数据的中模型返回的有效数据
    // 处理空行和注释,注意:以:开头的行是注释行,需要忽略
    if(chunk.empty() || chunk[0] == ':'){
    continue;
    }

    // 获取模型返回的有效数据
    if(chunk.compare(0, 6, "data: ") == 0){
    std::string modelData = chunk.substr(6);

    // 检测是否为结束标记
    if(modelData == "[DONE]"){
    callback("", true);
    streamFinish = true;
    return true;
    }

    // 反序列化
    Json::Value modelDataJson;
    Json::CharReaderBuilder reader;
    std::string errors;
    std::istringstream modelDataStream(modelData);
    if(Json::parseFromStream(reader, modelDataStream, &modelDataJson, &errors)){
    // 模型返回的json格式的数据现在就保存在modelDataJson
    if(modelDataJson.isMember("choices") &&
    modelDataJson["choices"].isArray() &&
    !modelDataJson["choices"].empty() &&
    modelDataJson["choices"][0].isMember("delta") &&
    modelDataJson["choices"][0]["delta"].isMember("content")){
    std::string content = modelDataJson["choices"][0]["delta"]["content"].asString();
    // 处理deltaContent,例如追加到fullResponse
    fullResponse += content;

    // 将本次解析出的模型返回的有效数据转给调用sendMessageStraem函数的用户使用—callback
    callback(content, false);
    }
    }else{
    WARN("DeepSeekProvider sendMessageStream parse modelDataJson error: {}", errors);
    }
    }
    }
    return true;
    };

    // 给模型发送请求
    auto result = client.send(req);
    if(!result){
    // 请求发送失败,出现网络问题,比如DNS解析失败、连接超时
    ERROR("Network error {}", to_string(result.error()));
    return "";
    }

    // 确保流式操作正确结束
    if(!streamFinish){
    WARN("stream ended without [DONE] marker");
    callback("", true);
    }

    return fullResponse;
    }

    5. 测试DeepSeek接口是否成功

    这是当时测试的cmake和main.cc以及运行结果截图,不多赘述,大家可以自己测一测: 运行前记得设置自己的deepseek apikey环境变量,各位可以去deepseek官网弄一下,比较简单不多说了。

    终端执行:

    export deepseek_apikey="你的deepseek apikey"

    cmake:

    # 设置Cmake的最小版本
    cmake_minimum_required(VERSION 3.10)

    # 项目名称
    project(testLLM)

    # 设置C++标准
    set(CMAKE_CXX_STANDARD 17)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)

    # 设置构建类型Debug
    set(CMAKE_BUILD_TYPE Debug)

    # 添加可执行文件
    add_executable(testLLM testLLM.cc
    ../sdk/src/util/myLog.cc
    ../sdk/src/DeepSeekProvider.cc
    )

    # 设置输出目录
    set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})

    # 添加头文件搜索路劲
    include_directories(${CMAKE_SOURCE_DIR}/../sdk/include)

    find_package(OpenSSL REQUIRED)
    include_directories(${OPENSSL_INCLUDE_DIR})

    # 添加CPPHTTPLIB_OPENSSL_SUPPORT定义
    target_compile_definitions(testLLM PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT)

    # 链接库
    target_link_libraries(testLLM pthread jsoncpp fmt spdlog gtest OpenSSL::SSL OpenSSL::Crypto)


    testLLM.cc:

    #include "../sdk/include/DeepSeekProvider.hpp"
    #include <iostream>
    #include <gtest/gtest.h>
    #include "../sdk/include/util/myLog.hpp"
    TEST(DeepSeekProviderTest, sendMessage){
    auto provider = std::make_shared<ai_chat_sdk::DeepSeekProvider>();
    ASSERT_TRUE(provider != nullptr);

    std::map<std::string, std::string> modelParam;
    modelParam["api_key"] = std::getenv("deepseek_apikey");
    modelParam["endpoint"] = "https://api.deepseek.com";

    provider->initModel(modelParam);
    ASSERT_TRUE(provider->isAvailable());

    std::map<std::string, std::string> requestParam = {
    {"temperature", "0.7"},
    {"max_tokens", "2048"}
    };
    std::vector<ai_chat_sdk::Message> messages;
    messages.push_back({"user", "你是谁?"});

    // 实例化DeepSeekProvider的对象
    // 调用sendMessage方法
    std::string response = provider->sendMessage(messages, requestParam);
    ASSERT_FALSE(response.empty());
    INFO("fulldata : {}", response);
    // auto writeChunk = [&](const std::string& chunk, bool last){
    // INFO("chunk : {}", chunk);
    // if(last){
    // INFO("[DONE]");
    // }
    // };
    // std::string fullData = provider->sendMessageStream(messages, requestParam, writeChunk);
    // ASSERT_FALSE(fullData.empty());
    // INFO("response : {}", fullData);
    }
    int main(int argc, char **argv) {
    lg::Logger::initLogger("testLLM", "stdout", spdlog::level::debug);
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
    }


    运行结果: 在这里插入图片描述

    6. LLMManager类

    LLMManager 是AI聊天SDK的核心管理类、统一入口类,采用管理者设计模式,对底层各类大模型提供商(如DeepSeekProvider)做统一封装与调度,向上层提供标准化的大模型调用接口,屏蔽不同大模型的底层实现差异,是整个SDK的调度中枢。

    核心作用:统一管理所有大模型的注册、初始化、可用性校验,以及统一转发全量/流式消息请求,提供极简、统一的调用入口,该类主要是放到ChatSDK类里面使用的,ChatSDK里面会有一个 LLMManager对象,要调用什么模型直接通过这个对象调用就行。

    以下是所有业务:

  • 保存会话数据:建立<session_id, session>映射关系,实现多会话的关联存储与查询。
  • 创建会话:在连接大模型前创建会话,后续聊天消息统一存入该会话。
  • 按ID获取会话:通过会话ID查询指定会话。
  • 向会话添加消息:将用户提问、模型回复保存至对应会话。
  • 获取会话历史消息:根据会话ID提取所有消息,用于界面展示。
  • 获取所有会话列表:查询全量会话列表,用于界面展示。
  • 删除会话:支持删除指定会话。
  • 更新会话时间戳:用户再次使用会话时,更新其时间戳。
  • 清空所有会话:删除所有会话数据。
  • 统计会话总数:获取当前会话的数量。
  • 生成会话ID:生成唯一ID标识会话。
  • 生成消息ID:生成唯一ID标识单条消息。
  • 以下是简要介绍,不是很难,不多赘述,

  • 私有成员(核心存储)

    • _providers:键值对映射,存储已注册的「模型名称-大模型提供商实例」,通过智能指针管理不同厂商的模型实现类,解耦底层不同大模型的差异;
    • _modelInfos:存储所有模型的基础信息与可用状态,维护模型启用/禁用标识。
  • 核心对外接口功能

    • registerProvider:注册大模型提供商,将具体的模型实现类挂载到管理器中,完成模型接入;
    • initModel:初始化指定模型,传入模型配置参数完成鉴权、连接等前置操作,初始化成功后标记模型为可用状态;
    • getAvailableModels:查询所有已初始化、可正常调用的模型列表;
    • isModelAvailable:校验指定模型是否完成初始化、处于可用状态;
    • sendMessage:统一转发全量消息请求,调用对应模型的全量响应接口,返回完整AI回复文本;
    • sendMessageStream:统一转发流式消息请求,调用对应模型的流式响应接口,透传流式回调函数,实现实时文本推送。
  • 这样设计的优势 该类是上层业务与底层大模型的中间层,上层业务无需关注具体大模型的对接细节,只需通过该类的统一接口即可完成所有操作,做到一次调用、适配所有模型,极大降低上层接入成本。

    调用逻辑 业务层调用流程:注册模型 → 初始化模型 → 校验可用性 → 发起全量/流式请求,所有操作均通过该类完成,逻辑闭环且职责单一。

    完整代码:

    #pragma once
    #include <map>
    #include <memory>
    #include "LLMProvider.hpp"

    namespace ai_chat_sdk {
    class LLMManager {
    public:
    bool registerProvider(const std::string& providerName,std::unique_ptr<LLMProvider> provider);

    bool initModel(const std::string& providerName, const std::map<std::string, std::string>& modelParam);

    std::vector<ModelInfo> getAvailableModels()const;

    bool isModelAvailable(const std::string& modelName)const;

    std::string sendMessage(const std::string& modelName, const std::vector<Message>& messages, const std::map<std::string, std::string>& requestParam);

    std::string sendMessageStream(const std::string& modelName, const std::vector<Message>& messages, const std::map<std::string, std::string>& requestParam, std::function<void(const std::string&, bool)>& callback);

    LLMManager() = default;

    private:
    std::map<std::string, std::unique_ptr<LLMProvider>> _providers;
    std::map<std::string, ModelInfo> _modelInfos;
    };
    } // end ai_chat_sdk

    #include "../include/LLMManager.hpp"
    #include "../include/util/myLog.hpp"
    #include "../include/common.hpp"

    namespace ai_chat_sdk {
    // 注册LLM提供者
    bool LLMManager::registerProvider(const std::string& modelName, std::unique_ptr<LLMProvider> provider){
    // 参数检测
    if(!provider){
    ERROR("cannot register nullptr provider, modelName = {}", modelName);
    return false;
    }

    // 注意:unique_ptr是防拷贝的,此处只能通过move的方式将资源转移给当前对象
    _providers[modelName] = std::move(provider);

    // 添加模型信息
    _modelInfos[modelName] = ModelInfo(modelName);

    // 模型初始化成功
    INFO("register provider success, modelName = {}", modelName);
    return true;
    }

    // 初始化指定模型
    bool LLMManager::initModel(const std::string& modelName, const std::map<std::string, std::string>& modelParam){
    // 检测模型是否注册
    auto it = _providers.find(modelName);
    if(it == _providers.end()){
    ERROR("model provider not found, modelName = {}", modelName);
    return false;
    }

    // 模型已经注册成功,可以初始化该模型
    bool isSuccess = it->second->initModel(modelParam);
    if(!isSuccess){
    ERROR("init model failed, modelName = {}", modelName);
    }else{
    INFO("init model success, modelName = {}", modelName);
    _modelInfos[modelName]._modelDesc = it->second->getModelDesc();
    _modelInfos[modelName]._isAvailable = true;
    }

    return isSuccess;
    }

    // 获取可用模型
    std::vector<ModelInfo> LLMManager::getAvailableModels()const{
    std::vector<ModelInfo> models;
    for(const auto& pair : _modelInfos){
    if(pair.second._isAvailable){
    models.push_back(pair.second);
    }
    }
    return models;
    }

    // 检查模型是否可用
    bool LLMManager::isModelAvailable(const std::string& modelName)const{
    auto it = _modelInfos.find(modelName);
    if(it == _modelInfos.end()){
    return false;
    }

    return it->second._isAvailable;
    }

    // 发送消息给指定模型
    std::string LLMManager::sendMessage(const std::string& modelName, const std::vector<Message>& messages, const std::map<std::string, std::string>& requestParam){
    // 检测模型是否注册
    auto it = _providers.find(modelName);
    if(it == _providers.end()){
    ERROR("model provider not found, modelName = {}", modelName);
    return "";
    }

    // 检测模型是否可用
    if(!it->second->isAvailable()){
    ERROR("model not available, modelName = {}", modelName);
    return "";
    }

    // 模型已经注册,并且已经初始化成功
    return it->second->sendMessage(messages, requestParam);
    }

    // 发送消息流给指定模型
    std::string LLMManager::sendMessageStream(const std::string& modelName, const std::vector<Message>& messages, const std::map<std::string, std::string>& requestParam, std::function<void(const std::string&, bool)>& callback){
    // 检测模型是否注册
    auto it = _providers.find(modelName);
    if(it == _providers.end()){
    ERROR("model provider not found, modelName = {}", modelName);
    return "";
    }

    // 检测模型是否可用
    if(!it->second->isAvailable()){
    ERROR("model not available, modelName = {}", modelName);
    return "";
    }

    // 模型已经注册,并且已经初始化成功
    return it->second->sendMessageStream(messages, requestParam, callback);
    }
    }

    7. 不接入DataManager的SessionManager

    这里的SessionManager即会话管理,这里的会话就是大家每次用ai的时候这个对话用完想要新建一个对话的对话(这里表述好像有点小绕),看图片一目了然如图:就这个东西。 在这里插入图片描述 所有的一切都是围绕这个展开的,具体包括会话的获取、创建、删除、更新时间戳、清空以及增加消息。

    所谓不接入DataManager的SessionManager就是没接入数据库,所有操作都是在内存中进行的,后续我们写完DataManager之后再完善这个类。 具体实现就不多介绍了,都很简单,也没啥陌生的语法。

    全部代码:

    #pragma once
    #include <atomic>
    #include <unordered_map>
    #include <mutex>
    #include <memory>
    #include <map>
    #include "DataManager.hpp"
    #include "common.hpp"
    namespace ai_chat_sdk {
    class SessionManager {
    public:
    static SessionManager& getInstance() {
    static SessionManager instance;
    return instance;
    }
    std::string createSession(const std::string& modelName);
    std::shared_ptr<Session> getSession(const std::string& sessionId);
    bool addMessage(const std::string& sessionId, const Message& message);
    std::vector<Message> getHistroyMessages(const std::string& sessionId) const;
    std::vector<std::string> getSessionLists() const;
    bool deleteSession(const std::string& sessionId);
    void updateSessionTimestamp(const std::string& sessionId);
    void clearAllSessions();
    size_t getSessionCount() const;

    private:
    SessionManager() = default;
    SessionManager(const SessionManager&) = delete;
    SessionManager &operator=(const SessionManager &) = delete;
    // ⽣成唯⼀会话id 返回会话id
    std::string generateSessionId();
    // ⽣成唯⼀消息id 返回消息id
    std::string generateMessageId();

    private:
    std::unordered_map<std::string, std::shared_ptr<Session>> _sessions;
    mutable std::mutex _mutex; // 在const成员函数中,可能会修改所的状态,因此需要
    static std::atomic<int64_t> _message_counter; // 记录所有会话中消息总数
    static std::atomic<int64_t> _session_counter; // 记录会话总数
    };
    } // end ai_chat_sdk

    #include "../include/SessionManager.hpp"
    #include "../include/util/myLog.hpp"
    #include <iomanip>
    #include <sstream>
    namespace ai_chat_sdk
    {
    std::atomic<int64_t> SessionManager::_message_counter{0};
    std::atomic<int64_t> SessionManager::_session_counter{0};
    std::string SessionManager::createSession(const std::string &modelName){
    std::unique_lock<std::mutex> lock(_mutex);
    std::string sessionId = generateSessionId();
    auto session = std::make_shared<Session>(modelName);
    session->_sessionId = sessionId;
    _sessions[sessionId] = session;
    INFO("create session, session_id: {}, model_name: {}", sessionId,modelName);
    return sessionId;
    }
    std::shared_ptr<Session> SessionManager::getSession(const std::string &sessionId){
    std::unique_lock<std::mutex> lock(_mutex);
    auto it = _sessions.find(sessionId);
    if (it != _sessions.end()) {
    return it->second;
    }
    return nullptr;
    }
    bool SessionManager::addMessage(const std::string &sessionId, const Message &message){
    std::unique_lock<std::mutex> lock(_mutex);
    auto it = _sessions.find(sessionId);
    if (it != _sessions.end()) {
    Message msg = message;
    msg._messageId = generateMessageId();
    msg._timestamp = std::time(nullptr);
    it->second->_messages.push_back(msg);
    it->second->_updateTime = std::time(nullptr);
    INFO("add message, session_id: {}, message_id: {}", sessionId, msg._messageId);
    return true;
    }
    return false;
    }
    std::vector<Message> SessionManager::getHistroyMessages(const std::string &sessionId) const{
    std::unique_lock<std::mutex> lock(_mutex);
    auto it = _sessions.find(sessionId);
    if (it != _sessions.end()) {
    return it->second->_messages;
    }
    return {};
    }
    std::vector<std::string> SessionManager::getSessionLists() const
    {
    std::unique_lock<std::mutex> lock(_mutex);
    std::vector<std::pair<std::time_t, std::shared_ptr<const Session>>> temp;
    temp.reserve(_sessions.size());
    // 填充临时会话
    for (const auto &pair : _sessions)
    {
    const auto &sessionPtr = pair.second;
    temp.emplace_back(sessionPtr->_updateTime, sessionPtr);
    }
    // 按更新时间排序, > 排降序
    std::sort(temp.begin(), temp.end(), [](const auto &a, const auto &b)
    { return a.first > b.first; });
    // 构造返回列表
    std::vector<std::string> session_ids;
    session_ids.reserve(temp.size());
    for (const auto &item : temp)
    {
    session_ids.push_back(item.second->_sessionId);
    }
    return session_ids;
    }
    bool SessionManager::deleteSession(const std::string &sessionId)
    {
    std::unique_lock<std::mutex> lock(_mutex);
    auto it = _sessions.find(sessionId);
    if (it != _sessions.end()) {
    _sessions.erase(it);
    INFO("delete session, session_id: {}", sessionId);
    return true;
    }
    INFO("session {} not found for delete", sessionId);
    return false;
    }
    void SessionManager::updateSessionTimestamp(const std::string &sessionId){
    std::unique_lock<std::mutex> lock(_mutex);
    auto it = _sessions.find(sessionId);
    if (it != _sessions.end()) {
    it->second->_updateTime = std::time(nullptr);
    INFO("update session timestamp, session_id: {}", sessionId);
    }
    }
    void SessionManager::clearAllSessions(){
    std::unique_lock<std::mutex> lock(_mutex);
    _sessions.clear();
    INFO("clear all sessions");
    }
    size_t SessionManager::getSessionCount() const{
    std::unique_lock<std::mutex> lock(_mutex);
    return _sessions.size();
    }

    // ⽣成唯⼀会话id 返回会话id
    std::string SessionManager::generateSessionId()
    {
    // 会话计数⾃增
    _session_counter.fetch_add(1);
    std::time_t time = std::time(nullptr);
    // 会话id格式:session_时间戳_会话计数
    std::ostringstream os;
    os << "session_" << time << "_" << std::setfill('0') << std::setw(8) << _session_counter;
    return os.str();
    }
    // ⽣成唯⼀消息id 返回消息id
    std::string SessionManager::generateMessageId()
    {
    // 消息计数⾃增
    _message_counter.fetch_add(1);
    std::time_t time = std::time(nullptr);
    // 消息id格式:msg_时间戳_消息计数
    std::ostringstream os;
    os << "msg_" << time << "_" << std::setfill('0') << std::setw(8) << _message_counter;
    return os.str();
    }
    } // end ai_chat_sdk

    8. DataManager类

    DataManager类是基于sqlite3原生C接口实现的 会话+消息 数据库持久化管理类,是AI聊天SDK的本地数据层,负责会话、聊天消息的增删改查。

    成员变量:

    • 私有成员:sqlite3* _db(数据库句柄)、std::string _dbName(数据库文件路径)、mutable std::mutex _mutex(线程安全锁,所有操作加锁,支持多线程调用)
    • 构造函数:打开指定sqlite数据库文件,调用initDataBase()初始化表结构;析构函数安全关闭数据库句柄。

    数据库表设计:

    • sessions 会话表:主键session_id,存储会话ID、模型名、创建时间、更新时间;
    • messages 消息表:主键message_id,外键session_id关联会话表,存储消息ID、角色(role)、消息内容、时间戳;
    • 外键特性:ON DELETE CASCADE 级联删除 → 删除会话时,关联的所有消息自动删除,无需手动删消息。

    工具函数:

    • initDataBase():创建会话/消息两张表(CREATE TABLE IF NOT EXISTS 幂等创建,避免重复建表报错);
    • executeSQL():执行无返回值的原生SQL(建表/清空表等),封装sqlite3_exec,包含错误日志与内存释放。

    封装会话全量CRUD:插入会话、查询单个会话(自动加载关联消息)、更新会话时间戳、删除会话、查询所有会话ID/会话列表、统计会话数、清空所有会话;

    封装消息全量CRUD:插入消息(插入时自动更新会话最新时间戳)、查询指定会话的所有消息、删除指定会话的消息;

    再细的具体实现大家就直接看代码吧,也不难。

    全部代码:

    #pragma once
    #include <string>
    #include <vector>
    #include <memory>
    #include <mutex>
    #include <sqlite3.h>
    #include "common.hpp"
    namespace ai_chat_sdk
    {
    class DataManager
    {
    public:
    DataManager(const std::string &dbName);
    ~DataManager();

    // Session相关操作
    // 插入新会话
    bool insertSession(const Session &session);
    // 获取指定会话信息
    std::shared_ptr<Session> getSession(const std::string &sessionId) const;
    // 更新指定会话的时间戳
    bool updateSessionTimestamp(const std::string &sessionId, std::time_t timestamp);
    // 删除指定会话–注意:删除会话时,也需要删除该会话中管理的所有的消息
    bool deleteSession(const std::string &sessionId);
    // 获取所有会话id
    std::vector<std::string> getAllSessionIds() const;
    // 获取所有会话信息
    std::vector<std::shared_ptr<Session>> getAllSessions() const;
    // 删除所有会话
    bool clearAllSessions();
    // 获取会话总数
    size_t getSessionCount() const;
    ////////////////////////////////////////////////////////////////////
    // Message相关操作
    // 插入新消息–注意:插入消息时,需要更新会话的时间戳
    bool insertMessage(const std::string &sessionId, const Message &message);
    // 获取指定会话的历史消息
    std::vector<Message> getSessionMessages(const std::string &sessionId) const;
    // 删除指定会话的所有消息
    bool deleteSessionMessages(const std::string &sessionId);

    private:
    // 初始化数据库 — 创建数据库表
    bool initDataBase();
    // 执行SQL语句的工具函数
    bool executeSQL(const std::string &sql);

    private:
    sqlite3 *_db;
    std::string _dbName;
    mutable std::mutex _mutex;
    };
    } // end ai_chat_sdk

    #include "../include/DataManager.hpp"
    #include "../include/util/myLog.hpp"
    namespace ai_chat_sdk
    {
    DataManager::DataManager(const std::string &dbName)
    : _dbName(dbName), _db(nullptr)
    {
    // 创建并打开数据库
    int rc = sqlite3_open(dbName.c_str(), &_db);
    if (rc != SQLITE_OK)
    {
    ERROR("打开数据库失败:{}", sqlite3_errmsg(_db));
    }
    INFO("打开数据库成功:{}", dbName);

    // 初始化数据库表 – 创建会话表和消息表
    if (!initDataBase())
    {
    sqlite3_close(_db);
    _db = nullptr;
    ERROR("初始化数据库表失败");
    }
    }

    DataManager::~DataManager()
    {
    if (_db)
    {
    sqlite3_close(_db);
    }
    }

    // 初始化数据库
    bool DataManager::initDataBase()
    {
    // 创建会话表
    std::string createSessionTable = R"(
    CREATE TABLE IF NOT EXISTS sessions (
    session_id TEXT PRIMARY KEY,
    model_name TEXT NOT NULL,
    create_time INTEGER NOT NULL,
    update_time INTEGER NOT NULL
    );
    )"
    ;

    // 执行创建Sessions表的SQL语句
    if (!executeSQL(createSessionTable))
    {
    return false;
    }

    // 创建消息表
    std::string createMessageTable = R"(
    CREATE TABLE IF NOT EXISTS messages (
    message_id TEXT PRIMARY KEY,
    session_id TEXT NOT NULL,
    role TEXT NOT NULL,
    content TEXT NOT NULL,
    timestamp INTEGER NOT NULL,
    FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
    );
    )"
    ;

    // 执行创建Messages表的SQL语句
    if (!executeSQL(createMessageTable))
    {
    return false;
    }

    return true;
    }

    bool DataManager::executeSQL(const std::string &sql)
    {
    if (!_db)
    {
    ERROR("数据库未初始化");
    return false;
    }

    char *errMsg = nullptr;
    int rc = sqlite3_exec(_db, sql.c_str(), nullptr, nullptr, &errMsg);
    if (rc != SQLITE_OK)
    {
    ERROR("执行SQL语句失败: {}", errMsg);
    sqlite3_free(errMsg);
    return false;
    }

    return true;
    }

    // 插入会话
    bool DataManager::insertSession(const Session &session)
    {
    std::lock_guard<std::mutex> lock(_mutex);

    // 构建SQL语句
    std::string insertSQL = R"(
    INSERT INTO sessions (session_id, model_name, create_time, update_time)
    VALUES (?, ?, ?, ?);
    )"
    ;

    // 准备SQL语句
    sqlite3_stmt *stmt;
    int rc = sqlite3_prepare_v2(_db, insertSQL.c_str(), 1, &stmt, nullptr);
    if (rc != SQLITE_OK)
    {
    ERROR("insertSession – 准备语句失败:{}", sqlite3_errmsg(_db));
    return false;
    }

    // 绑定参数
    sqlite3_bind_text(stmt, 1, session._sessionId.c_str(), 1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 2, session._modelName.c_str(), 1, SQLITE_TRANSIENT);
    sqlite3_bind_int64(stmt, 3, static_cast<int64_t>(session._createTime));
    sqlite3_bind_int64(stmt, 4, static_cast<int64_t>(session._updateTime));

    // 执行SQL语句
    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE)
    {
    ERROR("insertSession – 执行语句失败:{}", sqlite3_errmsg(_db));
    sqlite3_finalize(stmt);
    return false;
    }
    // 释放语句
    sqlite3_finalize(stmt);
    INFO("insertSession – 插入会话成功:{}", session._sessionId);
    return true;
    }

    // 获取指定sessionId的会话信息
    std::shared_ptr<Session> DataManager::getSession(const std::string &sessionId) const
    {

    std::lock_guard<std::mutex> lock(_mutex);

    // 构建SQL语句
    std::string selectSQL = R"(
    SELECT model_name, create_time, update_time FROM sessions WHERE session_id = ?;
    )"
    ;

    // 准备SQL语句
    sqlite3_stmt *stmt;
    int rc = sqlite3_prepare_v2(_db, selectSQL.c_str(), 1, &stmt, nullptr);
    if (rc != SQLITE_OK)
    {
    ERROR("getSession – 准备语句失败:{}", sqlite3_errmsg(_db));
    return nullptr;
    }

    // 绑定参数
    sqlite3_bind_text(stmt, 1, sessionId.c_str(), 1, SQLITE_TRANSIENT);

    // 执行SQL语句
    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE)
    {
    ERROR("getSession – 执行语句失败:{}", sqlite3_errmsg(_db));
    sqlite3_finalize(stmt);
    return nullptr;
    }

    // 从结果集中提取数据
    std::string modelName = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0));
    int64_t createTime = sqlite3_column_int64(stmt, 1);
    int64_t updateTime = sqlite3_column_int64(stmt, 2);

    // 创建会话对象
    auto session = std::make_shared<Session>(modelName);
    session->_sessionId = sessionId;
    session->_createTime = static_cast<std::time_t>(createTime);
    session->_updateTime = static_cast<std::time_t>(updateTime);

    // 释放语句
    sqlite3_finalize(stmt);
    INFO("getSession – 获取会话成功:{}", sessionId);

    // 获取该会话的所有消息
    session->_messages = getSessionMessages(sessionId);

    return session;
    }

    // 更新指定会话的时间戳
    bool DataManager::updateSessionTimestamp(const std::string &sessionId, std::time_t timestamp)
    {
    std::lock_guard<std::mutex> lock(_mutex);

    // 构建SQL语句
    std::string updateSQL = R"(
    UPDATE sessions SET update_time = ? WHERE session_id = ?;
    )"
    ;

    // 准备SQL语句
    sqlite3_stmt *stmt;
    int rc = sqlite3_prepare_v2(_db, updateSQL.c_str(), 1, &stmt, nullptr);
    if (rc != SQLITE_OK)
    {
    ERROR("updateSessionTimestamp – 准备语句失败:{}", sqlite3_errmsg(_db));
    return false;
    }

    // 绑定参数
    sqlite3_bind_int64(stmt, 1, static_cast<int64_t>(timestamp));
    sqlite3_bind_text(stmt, 2, sessionId.c_str(), 1, SQLITE_TRANSIENT);

    // 执行SQL语句
    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE)
    {
    ERROR("updateSessionTimestamp – 执行语句失败:{}", sqlite3_errmsg(_db));
    sqlite3_finalize(stmt);
    return false;
    }

    // 释放语句
    sqlite3_finalize(stmt);
    INFO("updateSessionTimestamp – 更新会话时间戳成功:{}", sessionId);
    return true;
    }

    // 删除指定会话
    bool DataManager::deleteSession(const std::string &sessionId)
    {
    std::lock_guard<std::mutex> lock(_mutex);

    // 构建SQL语句
    std::string deleteSQL = R"(
    DELETE FROM sessions WHERE session_id = ?;
    )"
    ;

    // 准备SQL语句
    sqlite3_stmt *stmt;
    int rc = sqlite3_prepare_v2(_db, deleteSQL.c_str(), 1, &stmt, nullptr);
    if (rc != SQLITE_OK)
    {
    ERROR("deleteSession – 准备语句失败:{}", sqlite3_errmsg(_db));
    return false;
    }

    // 绑定参数
    sqlite3_bind_text(stmt, 1, sessionId.c_str(), 1, SQLITE_TRANSIENT);

    // 执行SQL语句
    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE)
    {
    ERROR("deleteSession – 执行语句失败:{}", sqlite3_errmsg(_db));
    sqlite3_finalize(stmt);
    return false;
    }

    // 释放语句
    sqlite3_finalize(stmt);
    INFO("deleteSession – 删除会话成功:{}", sessionId);
    return true;
    }

    // 获取所有会话id
    std::vector<std::string> DataManager::getAllSessionIds() const
    {
    std::lock_guard<std::mutex> lock(_mutex);

    // 构建SQL语句
    std::string selectSQL = R"(
    SELECT session_id FROM sessions ORDER BY update_time DESC;
    )"
    ;

    // 准备SQL语句
    sqlite3_stmt *stmt;
    int rc = sqlite3_prepare_v2(_db, selectSQL.c_str(), 1, &stmt, nullptr);
    if (rc != SQLITE_OK)
    {
    ERROR("getAllSessionIds – 准备语句失败:{}", sqlite3_errmsg(_db));
    return {};
    }

    std::vector<std::string> sessionIds;
    while (sqlite3_step(stmt) == SQLITE_ROW)
    {
    std::string sessionId = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0));
    sessionIds.push_back(sessionId);
    }

    // 释放语句
    sqlite3_finalize(stmt);
    INFO("getAllSessionIds – 获取所有会话id成功, 会话总数:{}", sessionIds.size());
    return sessionIds;
    }

    // 获取所有session信息,并按照更新时间降序排列
    std::vector<std::shared_ptr<Session>> DataManager::getAllSessions() const
    {
    std::lock_guard<std::mutex> lock(_mutex);

    // 构建SQL语句
    std::string selectSQL = R"(
    SELECT session_id, model_name, create_time, update_time FROM sessions ORDER BY update_time DESC;
    )"
    ;

    // 准备SQL语句
    sqlite3_stmt *stmt;
    int rc = sqlite3_prepare_v2(_db, selectSQL.c_str(), 1, &stmt, nullptr);
    if (rc != SQLITE_OK)
    {
    ERROR("getAllSessionIds – 准备语句失败:{}", sqlite3_errmsg(_db));
    return {};
    }

    std::vector<std::shared_ptr<Session>> sessions;
    while (sqlite3_step(stmt) == SQLITE_ROW)
    {
    std::string sessionId = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0));
    std::string modelName = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 1));
    int64_t createTime = sqlite3_column_int64(stmt, 2);
    int64_t updateTime = sqlite3_column_int64(stmt, 3);

    auto session = std::make_shared<Session>(modelName);
    session->_sessionId = sessionId;
    session->_createTime = static_cast<std::time_t>(createTime);
    session->_updateTime = static_cast<std::time_t>(updateTime);
    sessions.push_back(session);

    // 历史消息暂时不获取,需要时再通过会话id来进行获取
    }

    // 释放语句
    sqlite3_finalize(stmt);
    INFO("getAllSessions – 获取所有会话信息成功, 会话总数:{}", sessions.size());
    return sessions;
    }

    // 获取会话总数
    size_t DataManager::getSessionCount() const
    {
    std::lock_guard<std::mutex> lock(_mutex);

    // 准备SQL语句
    std::string selectSQL = R"(
    SELECT COUNT(*) FROM sessions;
    )"
    ;

    // 准备SQL语句
    sqlite3_stmt *stmt;
    int rc = sqlite3_prepare_v2(_db, selectSQL.c_str(), 1, &stmt, nullptr);
    if (rc != SQLITE_OK)
    {
    ERROR("getSessionCount – 准备语句失败:{}", sqlite3_errmsg(_db));
    return 0;
    }

    // 执行SQL语句
    rc = sqlite3_step(stmt);
    if (rc != SQLITE_ROW)
    {
    ERROR("getSessionCount – 执行语句失败:{}", sqlite3_errmsg(_db));
    sqlite3_finalize(stmt);
    return 0;
    }

    // 获取会话总数
    size_t count = sqlite3_column_int64(stmt, 0);

    // 释放语句
    sqlite3_finalize(stmt);
    INFO("getSessionCount – 获取会话总数成功:{}", count);
    return count;
    }

    // 删除所有会话
    bool DataManager::clearAllSessions()
    {
    std::lock_guard<std::mutex> lock(_mutex);

    // 构建SQL语句
    std::string deleteSQL = R"(
    DELETE FROM sessions;
    )"
    ;

    // 准备SQL语句
    sqlite3_stmt *stmt;
    int rc = sqlite3_prepare_v2(_db, deleteSQL.c_str(), 1, &stmt, nullptr);
    if (rc != SQLITE_OK)
    {
    ERROR("clearAllSessions – 准备语句失败:{}", sqlite3_errmsg(_db));
    return false;
    }

    // 执行SQL语句
    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE)
    {
    ERROR("clearAllSessions – 执行语句失败:{}", sqlite3_errmsg(_db));
    sqlite3_finalize(stmt);
    return false;
    }

    // 释放语句
    sqlite3_finalize(stmt);
    INFO("clearAllSessions – 删除所有会话成功");
    return true;
    }

    /////////////////////////////////////////////////////////////Messages///////////////////////////////////////
    // 插入新消息–注意:插入消息时,需要更新会话的时间戳
    bool DataManager::insertMessage(const std::string &sessionId, const Message &message)
    {
    std::lock_guard<std::mutex> lock(_mutex);

    // 构建SQL语句
    std::string insertSQL = R"(
    INSERT INTO messages (message_id, session_id, role, content, timestamp)
    VALUES (?, ?, ?, ?, ?);
    )"
    ;

    // 准备SQL语句
    sqlite3_stmt *stmt;
    int rc = sqlite3_prepare_v2(_db, insertSQL.c_str(), 1, &stmt, nullptr);
    if (rc != SQLITE_OK)
    {
    ERROR("insertMessage – 准备语句失败:{}", sqlite3_errmsg(_db));
    return false;
    }

    // 绑定参数
    sqlite3_bind_text(stmt, 1, message._messageId.c_str(), 1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 2, sessionId.c_str(), 1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 3, message._role.c_str(), 1, SQLITE_TRANSIENT);
    sqlite3_bind_text(stmt, 4, message._content.c_str(), 1, SQLITE_TRANSIENT);
    sqlite3_bind_int64(stmt, 5, static_cast<int64_t>(message._timestamp));

    // 执行SQL语句
    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE)
    {
    ERROR("insertMessage – 执行语句失败:{}", sqlite3_errmsg(_db));
    sqlite3_finalize(stmt);
    return false;
    }

    // 同时更新session的update_time
    std::string updateSQL = R"(
    UPDATE sessions SET update_time = ? WHERE session_id = ?;
    )"
    ;

    // 准备SQL语句
    sqlite3_stmt *updateStmt;
    rc = sqlite3_prepare_v2(_db, updateSQL.c_str(), 1, &updateStmt, nullptr);
    if (rc != SQLITE_OK)
    {
    ERROR("insertMessage – 准备语句失败:{}", sqlite3_errmsg(_db));
    sqlite3_finalize(stmt);
    return false;
    }

    // 绑定参数
    sqlite3_bind_int64(updateStmt, 1, static_cast<int64_t>(message._timestamp));
    sqlite3_bind_text(updateStmt, 2, sessionId.c_str(), 1, SQLITE_TRANSIENT);

    // 执行SQL语句
    rc = sqlite3_step(updateStmt);
    if (rc != SQLITE_DONE)
    {
    ERROR("insertMessage – 执行语句失败:{}", sqlite3_errmsg(_db));
    sqlite3_finalize(updateStmt);
    return false;
    }

    // 释放语句
    sqlite3_finalize(stmt);
    sqlite3_finalize(updateStmt);
    INFO("insertMessage – 插入消息成功:{}", message._messageId);
    return true;
    }

    // 获取会话中的所有消息
    std::vector<Message> DataManager::getSessionMessages(const std::string &sessionId) const
    {
    std::lock_guard<std::mutex> lock(_mutex);

    // 准备SQL语句
    std::string selectSQL = R"(
    SELECT message_id, role, content, timestamp FROM messages WHERE session_id = ?;
    )"
    ;

    // 准备SQL语句
    sqlite3_stmt *stmt;
    int rc = sqlite3_prepare_v2(_db, selectSQL.c_str(), 1, &stmt, nullptr);
    if (rc != SQLITE_OK)
    {
    ERROR("getSessionMessages – 准备语句失败:{}", sqlite3_errmsg(_db));
    return {};
    }

    // 绑定参数
    sqlite3_bind_text(stmt, 1, sessionId.c_str(), 1, SQLITE_TRANSIENT);

    // 执行SQL语句
    std::vector<Message> messages;
    while ((rc = sqlite3_step(stmt)) == SQLITE_ROW)
    {
    Message message;
    message._messageId = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0));
    message._role = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 1));
    message._content = reinterpret_cast<const char *>(sqlite3_column_text(stmt, 2));
    message._timestamp = static_cast<std::time_t>(sqlite3_column_int64(stmt, 3));
    messages.push_back(message);
    }

    if (rc != SQLITE_DONE)
    {
    ERROR("getSessionMessages – 执行语句失败:{}", sqlite3_errmsg(_db));
    sqlite3_finalize(stmt);
    return {};
    }

    // 释放语句
    sqlite3_finalize(stmt);
    return messages;
    }

    // 删除制定会话的历史消息
    bool DataManager::deleteSessionMessages(const std::string &sessionId)
    {
    std::lock_guard<std::mutex> lock(_mutex);

    // 构建SQL语句
    std::string deleteSQL = R"(
    DELETE FROM messages WHERE session_id = ?;
    )"
    ;

    // 准备SQL语句
    sqlite3_stmt *stmt;
    int rc = sqlite3_prepare_v2(_db, deleteSQL.c_str(), 1, &stmt, nullptr);
    if (rc != SQLITE_OK)
    {
    ERROR("deleteSessionMessages – 准备语句失败:{}", sqlite3_errmsg(_db));
    return false;
    }

    // 绑定参数
    sqlite3_bind_text(stmt, 1, sessionId.c_str(), 1, SQLITE_TRANSIENT);

    // 执行SQL语句
    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE)
    {
    ERROR("deleteSessionMessages – 执行语句失败:{}", sqlite3_errmsg(_db));
    sqlite3_finalize(stmt);
    return false;
    }

    // 释放语句
    sqlite3_finalize(stmt);
    INFO("deleteSessionMessages – 删除会话消息成功:{}", sessionId);
    return true;
    }
    } // namespace ai_chat_sdk


    9. 接入DataManager的SessionManager

    就是SessionManager加一个DataManager类型的成员变量,在执行完会话的相关操作之后,再继续执行封装好的数据库操作进行持久化即可。其实就是每个成员函数后面再添几行代码就行。 如下: SessionManager.cpp – 构造函数

    SessionManager::SessionManager()
    : _dataManager("chatDB.db")
    {
    // 获取所有会话
    auto sessions = _dataManager.getAllSessions();
    for(auto& session : sessions){
    _sessions[session->session_id] = session;
    }
    INFO("SessionManager init, session count: {}", _sessions.size());
    }

    SessionManager.cpp – 创建会话 代码块

    // 创建会话
    std::string SessionManager::createSession(const std::string& model_name){
    std::string session_id;
    std::shared_ptr<Session> session;
    _mutex.lock();
    // ⽣成会话id
    //…

    _mutex.unlock();
    // 插⼊会话到数据库
    _dataManager.insertSession(*session);
    return session_id;
    }

    SessionManager.cpp – 获取会话 代码块

    // 获取会话
    std::shared_ptr<Session> SessionManager::getSession(const std::string& session_id){
    // 先快速检查内存中是否存在
    _mutex.lock();
    auto it = _sessions.find(session_id);
    if(it != _sessions.end()){
    _mutex.unlock();
    // 获取该会话的消息列表
    it->second->messages = _dataManager.getMessagesBySessionId(session_id);
    return it->second;
    }
    _mutex.unlock();
    // 内存中没有, 从数据库中查,顺便内容中也存储⼀份
    auto sessionPtr = _dataManager.getSession(session_id);
    if(sessionPtr){
    // 在更新内存前再次加锁,防⽌其他线程已经添加
    std::lock_guard<std::mutex> lock(_mutex);
    // 再次检查,因为可能在获取锁的过程中,其他线程已经添加了这个session
    auto it = _sessions.find(session_id);
    if(it == _sessions.end()){
    _sessions[session_id] = sessionPtr;
    }
    // 获取该会话的消息列表
    sessionPtr->messages = _dataManager.getMessagesBySessionId(session_id);
    return sessionPtr;
    }
    WARN("session {} not found", session_id);
    return nullptr;
    }

    SessionManager.cpp – 添加消息 代码块

    // 添加消息
    bool SessionManager::addMessage(const std::string& session_id, const Message& message){
    _mutex.lock();
    // 获取sessionId对应的会话
    // …
    _mutex.unlock();
    // 插⼊消息到数据库
    _dataManager.insertMessage(session_id,msg);
    return true;
    }

    SessionManager.cpp – 获取会话历史 代码块

    // 获取会话历史
    std::vector<Message> SessionManager::getSessionHistory(const std::string& session_id){
    _mutex.lock();
    // 先从内存中获取,如果内存中获取不到,再到数据库获取
    // …
    _mutex.unlock();
    // 从数据库中获取消息
    return _dataManager.getMessagesBySessionId(session_id);
    }

    SessionManager.cpp – 获取会话列表 代码块

    // 获取会话列表, 包含sessionId和modelName
    std::vector<std::string> SessionManager::getSessionList()const{
    // 先从数据库获取
    auto sessions = _dataManager.getAllSessions();
    std::lock_guard<std::mutex> lock(_mutex);
    std::vector<std::pair<std::time_t, std::shared_ptr<const Session>>> temp;
    temp.reserve(_sessions.size());
    // 填充临时会话
    for(const auto& pair : _sessions){
    const auto& session_ptr = pair.second;
    temp.emplace_back(session_ptr->update_time, session_ptr);
    }
    // 合并数据库会话和内存会话
    for(const auto& session : sessions){
    if(_sessions.find(session->session_id) == _sessions.end()){
    temp.emplace_back(session->update_time, session);
    }
    }
    // 按更新时间排序, > 排降序
    // …
    }

    SessionManager.cpp – 删除会话 代码块

    // 删除会话
    bool SessionManager::deleteSession(const std::string& session_id){
    _mutex.lock();
    // 先删除内存中会话信息,然后删除数据库
    // …
    _mutex.unlock();
    // 从数据库中删除会话以及该会话对应的消息列表
    _dataManager.deleteSession(session_id);
    return true;
    }

    SessionManager.cpp – 更新会话时间戳 代码块

    void SessionManager::updateSessionTimestamp(const std::string& session_id){
    _mutex.lock();
    // 先更新内容中指定会话的时间戳,然后同步到数据库
    // …
    _mutex.unlock();
    // 更新数据库中的会话时间戳
    _dataManager.updateSessionTimestamp(session_id, it->second->update_time);
    }

    SessionManager.cpp – 获取会话总数 代码块

    // 获取会话总数
    size_t SessionManager::getSessionCount()const{
    return _dataManager.getSessionCount();
    }

    完整代码:

    #include "../include/SessionManager.hpp"
    #include "../include/util/myLog.hpp"
    #include <iomanip>
    #include <sstream>
    namespace ai_chat_sdk
    {
    std::atomic<int64_t> SessionManager::_message_counter{0};
    std::atomic<int64_t> SessionManager::_session_counter{0};

    SessionManager::SessionManager() : _dataManager("chatDB.db")
    {
    auto sessions = _dataManager.getAllSessions();
    for (auto &session : sessions)
    {
    _sessions[session->_sessionId] = session;
    }
    INFO("SessionManager init, session count: {}", _sessions.size());
    };
    std::string SessionManager::createSession(const std::string &modelName){
    std::unique_lock<std::mutex> lock(_mutex);
    std::string sessionId = generateSessionId();
    auto session = std::make_shared<Session>(modelName);
    session->_sessionId = sessionId;
    _sessions[sessionId] = session;
    INFO("create session, session_id: {}, model_name: {}", sessionId,modelName);
    _dataManager.insertSession(*session);
    return sessionId;
    }
    std::shared_ptr<Session> SessionManager::getSession(const std::string &sessionId)
    {
    {
    std::unique_lock<std::mutex> lock(_mutex);
    auto it = _sessions.find(sessionId);
    if (it != _sessions.end())
    {
    it->second->_messages = _dataManager.getSessionMessages(sessionId);
    return it->second;
    }
    }
    auto sessionPtr = _dataManager.getSession(sessionId);
    if (sessionPtr) {
    std::unique_lock<std::mutex> lock(_mutex);
    auto it = _sessions.find(sessionId);
    if (it == _sessions.end()) {
    _sessions[sessionId] = sessionPtr;
    }
    sessionPtr->_messages = _dataManager.getSessionMessages(sessionId);
    return sessionPtr;
    }
    WARN("session {} not found", sessionId);
    return nullptr;
    }
    bool SessionManager::addMessage(const std::string &sessionId, const Message &message){
    std::unique_lock<std::mutex> lock(_mutex);
    auto it = _sessions.find(sessionId);
    if (it != _sessions.end()) {
    Message msg = message;
    msg._messageId = generateMessageId();
    msg._timestamp = std::time(nullptr);
    it->second->_messages.push_back(msg);
    it->second->_updateTime = std::time(nullptr);
    _dataManager.insertMessage(sessionId, msg);
    INFO("add message, session_id: {}, message_id: {}", sessionId, msg._messageId);
    return true;
    }
    return false;
    }
    std::vector<Message> SessionManager::getHistroyMessages(const std::string &sessionId) const{
    {
    std::unique_lock<std::mutex> lock(_mutex);
    auto it = _sessions.find(sessionId);
    if (it != _sessions.end())
    {
    return it->second->_messages;
    }
    }
    return _dataManager.getSessionMessages(sessionId);
    }
    // 获取所有会话列表
    std::vector<std::string> SessionManager::getSessionLists() const{
    auto sessions = _dataManager.getAllSessions();

    std::unique_lock<std::mutex> lock(_mutex);
    // 构建一个临时对话列表,将其内容的会话按照会话更新的时间戳降序排列
    std::vector<std::pair<std::time_t, std::shared_ptr<Session>>> temp;
    temp.reserve(_sessions.size());

    // 将会话添加到临时列表中
    for (const auto &pair : _sessions)
    {
    temp.emplace_back(pair.second->_updateTime, pair.second);
    }

    // 将数据库中的会话添加到临时列表中
    for (const auto &session : sessions)
    {
    if (_sessions.find(session->_sessionId) == _sessions.end())
    {
    temp.emplace_back(session->_updateTime, session);
    }
    }

    // 按照会话更新时间戳降序排序
    std::sort(temp.begin(), temp.end(), [](const auto &a, const auto &b)
    { return a.first > b.first; });

    std::vector<std::string> sessionIds;
    sessionIds.reserve(_sessions.size());
    // 从临时列表中提取会话id
    for (const auto &pair : temp)
    {
    sessionIds.push_back(pair.second->_sessionId);
    }
    return sessionIds;
    }
    bool SessionManager::deleteSession(const std::string &sessionId)
    {
    std::unique_lock<std::mutex> lock(_mutex);
    auto it = _sessions.find(sessionId);
    if (it != _sessions.end()) {
    _sessions.erase(it);
    _dataManager.deleteSession(sessionId);
    INFO("delete session, session_id: {}", sessionId);
    return true;
    }
    INFO("session {} not found for delete", sessionId);
    return false;
    }
    void SessionManager::updateSessionTimestamp(const std::string &sessionId){
    std::unique_lock<std::mutex> lock(_mutex);
    auto it = _sessions.find(sessionId);
    if (it != _sessions.end()) {
    it->second->_updateTime = std::time(nullptr);
    _dataManager.updateSessionTimestamp(sessionId, it->second->_updateTime);
    INFO("update session timestamp, session_id: {}", sessionId);
    }
    }
    void SessionManager::clearAllSessions(){
    std::unique_lock<std::mutex> lock(_mutex);
    _sessions.clear();
    _dataManager.clearAllSessions();
    INFO("clear all sessions");
    }
    size_t SessionManager::getSessionCount() const{
    std::unique_lock<std::mutex> lock(_mutex);
    return _sessions.size();
    }

    // ⽣成唯⼀会话id 返回会话id
    std::string SessionManager::generateSessionId()
    {
    // 会话计数⾃增
    _session_counter.fetch_add(1);
    std::time_t time = std::time(nullptr);
    // 会话id格式:session_时间戳_会话计数
    std::ostringstream os;
    os << "session_" << time << "_" << std::setfill('0') << std::setw(8) << _session_counter;
    return os.str();
    }
    // ⽣成唯⼀消息id 返回消息id
    std::string SessionManager::generateMessageId()
    {
    // 消息计数⾃增
    _message_counter.fetch_add(1);
    std::time_t time = std::time(nullptr);
    // 消息id格式:msg_时间戳_消息计数
    std::ostringstream os;
    os << "msg_" << time << "_" << std::setfill('0') << std::setw(8) << _message_counter;
    return os.str();
    }
    } // end ai_chat_sdk

    10. ChatSDK

    到这里其实基本功能就已经做完了,但我们要给它封装成SDK。

    那么问题来了,何为SDK?

    SDK 核心定义 SDK 是 Software Develop Kit(软件开发工具包) 的缩写,是程序员的「工具箱」,是一套帮开发者方便开发特定功能的工具包。 类比:电脑清灰、加装内存条需要螺丝刀、撬棒等专业工具组成的工具箱;开发者实现特定功能,就需要对应的 SDK 工具包。

    举例

    • 微信支付SDK:接入后即可在程序中实现微信支付,无需自研支付安全、加密、银行对接等复杂逻辑。
    • 相机SDK:接入后APP可直接调用手机摄像头完成拍照、录像功能。

    SDK 组成内容

  • 库文件:已封装好的现成功能,按需直接调用即可。
  • API接口:标准化交互规则,指导开发者与平台/功能进行对接。
  • 文档:使用说明书,说明各类功能、接口的使用方式。
  • 示例代码:实操参考,参照即可快速跑通功能。
  • 调试工具:排查开发、调用过程中的问题。
  • SDK 核心价值 SDK 是厂商打包好的一套「现成工具+说明书」,让开发者可以更快、更安全、更省心地在自己的软件中实现特定功能,无需从零开发底层逻辑。

    SDK 和 API 的区别

    特性APISDK
    概念 一组规则和定义,用于标准化不同软件的交互方式 包含API、库、文档、示例代码等的一个全面的工具包
    优势 提供与特定服务或系统交互的接口 提供了完整的开发环境和工具
    形式 接口文档,比如:RESTful API 一个可本地下载的完整工具库包
    关系 API 是 SDK 的一部分 SDK 包含 API
    选择 如果需要与某个服务进行交互,但不需要开发完整的应用程序 需要开发一个完整的应用程序,且需要一系列工具和资源来支持开发过程

    通俗类比 做一道菜(开发应用):

    • API 就像是菜谱,只告诉你食材和步骤,锅碗瓢盆、刀具等需要自己准备(自研底层代码)。
    • SDK 则是「食材配送盒+全套厨具+菜谱」,包含食材(库文件)、菜谱(API文档)、厨具(开发工具)、教程(示例代码),开箱即用。

    具体功能与实现: ChatSDK 是AI聊天能力的统一顶层入口类,封装模型管理、会话管理所有能力,对外提供极简调用接口,屏蔽底层实现细节。

    包含以下功能:

  • 模型初始化:注册+初始化云端/本地Ollama多类大模型(DeepSeek、GPT、Gemini、Ollama),校验密钥/地址等配置;
  • 会话管理:创建会话、查询会话、获取会话列表、删除会话,所有会话操作委托给SessionManager;
  • 消息交互:提供同步全量返回、流式增量返回两种发消息接口,自动拼接会话历史上下文,消息自动落库持久化;
  • 基础能力:获取当前可用模型列表。
  • 核心实现逻辑

  • 初始化流程:initModels为入口,先registerAllProvider注册模型提供者,再initProviders按配置初始化云端/Ollama模型,校验参数合法性后完成模型就绪;
  • 核心依赖:内部持有LLMManager(模型调度/调用)、SessionManager(会话增删查/消息管理)两个核心管理类,解耦模型与会话能力;
  • 消息发送流程:校验SDK初始化状态 → 获取会话+历史消息 → 拼接请求参数 → 调用模型生成回复 → 自动保存用户/模型消息、更新会话时间戳;
  • 双重消息能力:同步接口一次性返回完整回复,流式接口通过回调函数增量返回内容,满足不同业务场景;
  • 全链路校验:所有接口前置判断SDK初始化状态,参数合法性校验完善,异常日志清晰,保证调用安全。
  • 其实就是对SessionManager又封装了一层,大体逻辑几乎一摸一样,不多赘述。


    11. ChatSDK测试

    以下是测试的cmake、main.cc代码和测试结果,经供参考:

    cmake:

    # 设置Cmake的最小版本
    cmake_minimum_required(VERSION 3.10)

    # 项目名称
    project(testLLM)

    # 设置C++标准
    set(CMAKE_CXX_STANDARD 17)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)

    # 设置构建类型Debug
    set(CMAKE_BUILD_TYPE Debug)

    # 添加可执行文件
    add_executable(testLLM testLLM.cc
    ../sdk/src/util/myLog.cc
    ../sdk/src/ChatSDK.cc
    ../sdk/src/SessionManager.cc
    ../sdk/src/DataManager.cc
    ../sdk/src/DeepSeekProvider.cc
    ../sdk/src/LLMManager.cc
    )

    # 设置输出目录
    set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})

    # 添加头文件搜索路劲
    include_directories(${CMAKE_SOURCE_DIR}/../sdk/include)

    find_package(OpenSSL REQUIRED)
    include_directories(${OPENSSL_INCLUDE_DIR})

    # 添加CPPHTTPLIB_OPENSSL_SUPPORT定义
    target_compile_definitions(testLLM PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT)

    # 链接库
    target_link_libraries(testLLM pthread jsoncpp fmt spdlog gtest OpenSSL::SSL OpenSSL::Crypto sqlite3)

    testLLM.cc:

    #include <gtest/gtest.h>
    #include <istream>
    #include <memory>
    #include <spdlog/common.h>
    #include "../sdk/include/DeepSeekProvider.hpp"
    #include "../sdk/include/util/myLog.hpp"
    #include "../sdk/include/ChatSDK.hpp"
    #include <iostream>
    #include <string>
    #include <vector>
    // TEST(DeepSeekProviderTest, sendMessage){
    // auto provider = std::make_shared<ai_chat_sdk::DeepSeekProvider>();
    // ASSERT_TRUE(provider != nullptr);

    // std::map<std::string, std::string> modelParam;
    // modelParam["api_key"] = std::getenv("deepseek_apikey");
    // modelParam["endpoint"] = "https://api.deepseek.com";

    // provider->initModel(modelParam);
    // ASSERT_TRUE(provider->isAvailable());

    // std::map<std::string, std::string> requestParam = {
    // {"temperature", "0.7"},
    // {"max_tokens", "2048"}
    // };
    // std::vector<ai_chat_sdk::Message> messages;
    // messages.push_back({"user", "你是谁?"});

    // // 实例化DeepSeekProvider的对象
    // // 调用sendMessage方法
    // std::string response = provider->sendMessage(messages, requestParam);
    // ASSERT_FALSE(response.empty());
    // INFO("fulldata : {}", response);
    // // auto writeChunk = [&](const std::string& chunk, bool last){
    // // INFO("chunk : {}", chunk);
    // // if(last){
    // // INFO("[DONE]");
    // // }
    // // };
    // // std::string fullData = provider->sendMessageStream(messages, requestParam, writeChunk);
    // // ASSERT_FALSE(fullData.empty());
    // // INFO("response : {}", fullData);
    // }
    TEST(ChatSDKTest, sendMessage){
    auto sdk = std::make_shared<ai_chat_sdk::ChatSDK>();
    ASSERT_TRUE(sdk != nullptr);

    // 配置支持的模型参数:云模型-deepseek-chat gpt-4o-mini gemini-2.0-flash Ollama本地接入deepseek-r1:1.5b
    // deepseek-chat
    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;

    std::vector<std::shared_ptr<ai_chat_sdk::Config>> modelConfigs = {
    deepseekConfig
    };

    sdk->initModels(modelConfigs);

    // 创建会话
    auto 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);
    sdk->sendMessage(sessionId, message);
    ASSERT_FALSE(response.empty());

    // 获取会话历史消息
    auto messages = sdk->_sessionManager.getHistroyMessages(sessionId);
    for(const auto& msg : messages){
    std::cout<<msg._role<<": "<<msg._content<<std::endl;
    }
    ASSERT_FALSE(messages.empty());
    }
    int main(int argc, char **argv) {
    lg::Logger::initLogger("testLLM", "stdout", spdlog::level::debug);
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
    }

    测试结果: 在这里插入图片描述 至此服务端全部结束,想要接入其他模型的可以继续写一下Provider类就行。

    12. ChatServer类与前端

    ChatServer类就是充当前后端交互的媒介,在这个类中直接写好对应的http请求与响应格式,然后注册对应回调函数即可。

    聊天Demo核心功能

  • 获取会话列表
  • 获取支持的模型
  • 新建会话
  • 发送消息(默认流式返回)
  • 获取会话历史
  • 删除会话
  • 接口列表(按功能分类)

    1. 获取会话列表

    • 请求URL:GET /api/sessions
    • 响应(200 OK):
      字段名类型说明
      success bool 是否成功
      message string 结果描述
      data array 会话数据列表
      data.id string 会话id
      data.model string 模型名称
      data.created_at int64_t 创建时间戳
      data.updated_at int64_t 更新时间戳
      data.message_count int 对话次数
      data.first_user_message string 第一条用户消息

    2. 获取可用模型

    • 请求URL:GET /api/models
    • 响应(200 OK):
      字段名类型说明
      success bool 是否成功
      message string 结果描述
      data array 模型列表
      data.name string 模型名称
      data.desc string 模型描述

    3. 创建新会话

    • 请求URL:POST /api/session
    • 请求参数:
      字段名类型说明
      model string 模型名称
    • 响应(200 OK):
      字段名类型说明
      success bool 是否成功
      message string 结果描述
      data object 会话数据
      data.session_id string 会话id
      data.model string 模型名称

    4. 获取会话历史

    • 请求URL:GET /api/session/${session_id}/history
    • 响应(200 OK):
      字段名类型说明
      success bool 是否成功
      message string 结果描述
      data array 消息列表
      data.id string 消息id
      data.role string 消息类型
      data.content string 消息正文
      data.timestamp int64_t 消息时间戳

    5. 发送消息-全量返回

    • 请求URL:POST /api/message
    • 请求参数:
      字段名类型说明
      session_id string 会话id
      message string 消息内容
    • 响应(200 OK):
      字段名类型说明
      success bool 是否成功
      message string 结果描述
      data object 响应数据
      data.session_id string 会话id
      data.response string 模型返回内容

    6. 发送消息-流式响应

    • 请求URL:POST /api/message/async
    • 请求参数:
      字段名类型说明
      session_id string 会话id
      message string 消息内容
    • 响应(200 OK): 流式返回格式(SSE):data: 正文
      data: 正文

      data: [DONE]

    7. 删除会话

    • 请求URL:DELETE /api/session/${session_id}
    • 响应(200 OK):
      字段名类型说明
      success bool 是否成功
      message string 结果描述

    具体实现就不多赘述了,就是按着协议格式去拼凑请求和解析响应返回给前端即可。

    完整代码:

    #pragma once
    #include <httplib.h>
    #include <memory>
    #include <ai_chat_sdk/ChatSDK.hpp>

    namespace ai_chat_server{

    // 服务器配置信息
    struct ServerConfig{
    std::string host = "0.0.0.0"; // 服务器绑定ip
    int port = 8080; // 服务器绑定端口
    std::string logLevel = "INFO"; // 日志级别

    // 模型需要的配置信息
    double temperature = 0.7; // 温度参数
    int maxTokens = 1024; // 最大token数

    // API Key
    std::string deepseekAPIKey; // deepseek API Key
    std::string geminiAPIKey; // gemini API Key
    std::string chatGPTAPIKey; // chatGPT API Key

    // Ollama
    std::string ollamaModelName; // Ollama模型名称
    std::string ollamaModelDesc; // Ollama模型描述
    std::string ollamaEndpoint; // Ollama API 地址
    };

    class ChatServer{
    public:
    ChatServer(const ServerConfig& config);

    bool start(); // 启动服务器
    void stop(); // 停止服务器
    bool isRunning()const; // 是否正在运行

    private:
    // 构造响应
    std::string buildResponse(const std::string& message, bool success = false);
    // 处理创建会话请求
    void handleCreateSessionRequest(const httplib::Request& request, httplib::Response& response);
    // 处理获取会话列表请求
    void handleGetSessionListsRequest(const httplib::Request& request, httplib::Response& response);
    // 处理获取模型列表请求
    void handleGetModelListsRequest(const httplib::Request& request, httplib::Response& response);
    // 处理删除会话请求
    void handleDeleteSessionRequest(const httplib::Request& request, httplib::Response& response);
    // 处理获取历史消息请求
    void handleGetHistoryMessagesRequest(const httplib::Request& request, httplib::Response& response);
    // 处理发送消息请求-全量返回
    void handleSendMessageRequest(const httplib::Request& request, httplib::Response& response);
    // 处理发送消息请求-增量返回
    void handleSendMessageStreamRequest(const httplib::Request& request, httplib::Response& response);

    // 设置HTTP路由规则
    void setHttpRoutes();

    private:
    ServerConfig _config; // 服务器配置信息
    std::unique_ptr<httplib::Server> _chatServer = nullptr; // HTTP服务器
    std::shared_ptr<ai_chat_sdk::ChatSDK> _chatSDK = nullptr; // 聊天SDK
    std::atomic<bool> _isRunning = {false}; // 是否正在运行
    };

    } // end ai_chat_server

    #include "ChatServer.hpp"
    #include <ai_chat_sdk/util/myLog.hpp>
    #include <httplib.h>
    #include <jsoncpp/json/forwards.h>
    #include <jsoncpp/json/value.h>
    #include <jsoncpp/json/reader.h>
    #include <jsoncpp/json/writer.h>

    namespace ai_chat_server
    {

    ChatServer::ChatServer(const ServerConfig &config)
    {
    _chatSDK = std::make_shared<ai_chat_sdk::ChatSDK>();
    std::vector<std::shared_ptr<ai_chat_sdk::Config>> modelConfigs;

    if (!config.deepseekAPIKey.empty()) {
    auto deepseekConfig = std::make_shared<ai_chat_sdk::ApiConfig>();
    deepseekConfig->_modelName = "deepseek-chat";
    deepseekConfig->_apiKey = config.deepseekAPIKey;
    deepseekConfig->_temperature = config.temperature;
    deepseekConfig->_maxTokens = config.maxTokens;
    modelConfigs.push_back(deepseekConfig);
    }

    if (!config.ollamaModelName.empty()) {
    auto ollamaConfig = std::make_shared<ai_chat_sdk::OllamaConfig>();
    ollamaConfig->_modelName = config.ollamaModelName;
    ollamaConfig->Config::_modelName = config.ollamaModelName;
    ollamaConfig->_modelDesc = config.ollamaModelDesc;
    ollamaConfig->_endpoint = config.ollamaEndpoint;
    ollamaConfig->_temperature = config.temperature;
    ollamaConfig->_maxTokens = config.maxTokens;
    modelConfigs.push_back(ollamaConfig);
    }

    INFO("start init ChatSDK models…");
    if (!_chatSDK->initModels(modelConfigs))
    {
    ERROR("ChatSDK init Failed!!!");
    return;
    }
    INFO("ChatSDK models init success!!!");

    // 创建http服务器
    _chatServer = std::make_unique<httplib::Server>();
    if (!_chatServer)
    {
    ERROR("ChatServer init Failed!!!");
    return;
    }
    }

    bool ChatServer::start()
    {
    if (_isRunning.load())
    {
    ERROR("ChatServer is running!!!");
    return false;
    }

    // 设置路由规则
    setHttpRoutes();

    // 设置静态资源的路径
    // 前端页面相关的所有文件都放在www目录下 注意:将来前端页面名称命名为index.html
    // 当用户在浏览器中输入:http://ip:port/index.html http://ip:port也能访问index.html页面
    // 在httplib中,默认情况下,如果请求路径中只有ip和端口,httplib默认会使用index.html文件
    _chatServer->set_mount_point("/", "./www");

    // 为了不卡服务器云不卡主线程,服务器在单独的线程中运行
    std::thread serverThread([this]()
    {
    _chatServer->listen(_config.host, _config.port);
    INFO("ChatServer start on {} :{}", _config.host, _config.port); });

    serverThread.detach();
    _isRunning.store(true);
    INFO("ChatServer start success!!!");
    return true;
    }

    void ChatServer::stop()
    {
    if (!_isRunning.load())
    {
    ERROR("ChatServer is not running!!!");
    return;
    }

    if (_chatServer)
    {
    _chatServer->stop();
    }

    _isRunning.store(false);
    INFO("ChatServer stop success!!!");
    }

    bool ChatServer::isRunning() const
    {
    return _isRunning.load();
    }

    // 构造响应
    std::string ChatServer::buildResponse(const std::string &message, bool success)
    {
    Json::Value responseJson;
    responseJson["success"] = success;
    responseJson["message"] = message;

    // 序列化
    Json::StreamWriterBuilder writerBuilder;
    return Json::writeString(writerBuilder, responseJson);
    }

    // 处理创建会话请求
    void ChatServer::handleCreateSessionRequest(const httplib::Request &request, httplib::Response &response)
    {
    // 获取请求参数,请求参数在请求体
    // 通过反序列化拿到请求体的json格式
    Json::Value requestJson;
    Json::Reader reader;
    if (!reader.parse(request.body, requestJson))
    {
    std::string errorJsonStr = buildResponse("parse request body failed, json format error");
    response.status = 400; // 客户端发送的请求有语法错误,服务器无法理解或处理该请求
    response.set_content(errorJsonStr, "application/json");
    return;
    }

    // 获取请求参数
    std::string modelName = requestJson.get("model", "deepseek-chat").asString();

    // 创建会话
    std::string sessionID = _chatSDK->createSession(modelName);
    if (sessionID.empty())
    {
    std::string errorJsonStr = buildResponse("create session failed");
    response.status = 500; // 服务器内部错误,无法完成请求
    response.set_content(errorJsonStr, "application/json");
    return;
    }

    // 构建响应体
    Json::Value dataJson;
    dataJson["session_id"] = sessionID;
    dataJson["model"] = modelName;

    Json::Value responseJson;
    responseJson["success"] = true;
    responseJson["message"] = "create session success";
    responseJson["data"] = dataJson;

    // 序列化
    Json::StreamWriterBuilder writerBuilder;
    std::string responseJsonStr = Json::writeString(writerBuilder, responseJson);

    response.status = 200; // 成功
    response.set_content(responseJsonStr, "application/json");
    }

    // 处理获取会话列表请求
    void ChatServer::handleGetSessionListsRequest(const httplib::Request &request, httplib::Response &response)
    {
    // 获取会话列表
    std::vector<std::string> sessionIDs = _chatSDK->getSessionLists();

    // 构建session信息
    Json::Value dataArray(Json::arrayValue);
    for (const auto &sessionID : sessionIDs)
    {
    auto session = _chatSDK->getSession(sessionID);
    if (session)
    {
    Json::Value sessionJson;
    sessionJson["id"] = session->_sessionId;
    sessionJson["model"] = session->_modelName;
    sessionJson["created_at"] = static_cast<int64_t>(session->_createTime);
    sessionJson["updated_at"] = static_cast<int64_t>(session->_updateTime);
    sessionJson["message_count"] = session->_messages.size();
    if (!session->_messages.empty())
    {
    sessionJson["first_user_message"] = session->_messages.front()._content;
    }

    dataArray.append(sessionJson);
    }
    }

    // 构建响应体
    Json::Value responseJson;
    responseJson["success"] = true;
    responseJson["message"] = "get session lists success";
    responseJson["data"] = dataArray;

    // 序列化
    Json::StreamWriterBuilder writerBuilder;
    std::string responseJsonStr = Json::writeString(writerBuilder, responseJson);

    response.status = 200; // 成功
    response.set_content(responseJsonStr, "application/json");
    }

    // 处理获取模型列表请求
    void ChatServer::handleGetModelListsRequest(const httplib::Request &request, httplib::Response &response)
    {
    // 获取支持的模型列表
    auto modelLists = _chatSDK->getAvailableModels();

    // 构建响应体
    Json::Value dataArray(Json::arrayValue);
    for (const auto &modelInfo : modelLists)
    {
    Json::Value modelJson;
    modelJson["name"] = modelInfo._modelName;
    modelJson["desc"] = modelInfo._modelDesc;
    dataArray.append(modelJson);
    }

    // 构建响应体
    Json::Value responseJson;
    responseJson["success"] = true;
    responseJson["message"] = "get model lists success";
    responseJson["data"] = dataArray;

    // 序列化
    Json::StreamWriterBuilder writerBuilder;
    std::string responseJsonStr = Json::writeString(writerBuilder, responseJson);

    response.status = 200; // 成功
    response.set_content(responseJsonStr, "application/json");
    }
    // 处理删除会话请求
    void ChatServer::handleDeleteSessionRequest(const httplib::Request &request, httplib::Response &response)
    {
    // 获取会话id,注意:会话id是一个路径参数
    std::string sessionId = request.matches[1];

    // 删除会话
    bool ret = _chatSDK->deleteSession(sessionId);
    if (ret)
    {
    std::string errorJsonStr = buildResponse("delete session success", true);
    response.status = 200;
    response.set_content(errorJsonStr, "application/json");
    }
    else
    {
    std::string errorJsonStr = buildResponse("delete session failed, session not found");
    response.status = 404; // 会话不存在
    response.set_content(errorJsonStr, "application/json");
    }
    }

    // 处理获取历史消息请求
    void ChatServer::handleGetHistoryMessagesRequest(const httplib::Request &request, httplib::Response &response)
    {
    // 获取会话id
    std::string sessionId = request.matches[1];
    // 获取会话
    auto session = _chatSDK->getSession(sessionId);
    if (!session)
    {
    std::string errorJsonStr = buildResponse("session not found");
    response.status = 404; // 会话不存在
    response.set_content(errorJsonStr, "application/json");
    return;
    }

    // 构建历史消息列表
    Json::Value dataArray(Json::arrayValue);
    for (const auto &message : session->_messages)
    {
    Json::Value messageJson;
    messageJson["id"] = message._messageId;
    messageJson["role"] = message._role;
    messageJson["content"] = message._content;
    messageJson["timestamp"] = static_cast<int64_t>(message._timestamp);
    dataArray.append(messageJson);
    }

    // 构建响应体
    Json::Value responseJson;
    responseJson["success"] = true;
    responseJson["message"] = "get history messages success";
    responseJson["data"] = dataArray;

    // 序列化
    Json::StreamWriterBuilder writerBuilder;
    std::string responseJsonStr = Json::writeString(writerBuilder, responseJson);

    response.status = 200; // 成功
    response.set_content(responseJsonStr, "application/json");
    }

    // 处理发送消息请求-全量返回
    void ChatServer::handleSendMessageRequest(const httplib::Request &request, httplib::Response &response)
    {
    // 获取请求参数
    Json::Value requestJson;
    Json::Reader reader;
    if (!reader.parse(request.body, requestJson))
    {
    std::string errorJsonStr = buildResponse("parse request body failed, json format error");
    response.status = 400; // 解析请求参数失败
    response.set_content(errorJsonStr, "application/json");
    return;
    }

    // 解析请求参数
    std::string sessionId = requestJson["session_id"].asString();
    std::string message = requestJson["message"].asString();
    if (sessionId.empty() || message.empty())
    {
    std::string errorJsonStr = buildResponse("session_id or message is empty");
    response.status = 400; // 解析请求参数失败
    response.set_content(errorJsonStr, "application/json");
    return;
    }

    // 发送消息
    std::string assistantMessage = _chatSDK->sendMessage(sessionId, message);
    if (assistantMessage.empty())
    {
    std::string errorJsonStr = buildResponse("Failed to send AI response message");
    response.status = 500; // 发送消息失败
    response.set_content(errorJsonStr, "application/json");
    return;
    }

    // 构造响应参数
    Json::Value dataJson;
    dataJson["session_id"] = sessionId;
    dataJson["response"] = assistantMessage;
    dataJson["data"]["assistant_message"] = assistantMessage;

    // 构建响应体
    Json::Value responseJson;
    responseJson["success"] = true;
    responseJson["message"] = "send message success";
    responseJson["data"] = dataJson;

    // 序列化
    Json::StreamWriterBuilder writerBuilder;
    std::string responseJsonStr = Json::writeString(writerBuilder, responseJson);

    response.status = 200; // 成功
    response.set_content(responseJsonStr, "application/json");
    }

    // 处理发送消息请求-增量返回
    void ChatServer::handleSendMessageStreamRequest(const httplib::Request &request, httplib::Response &response)
    {
    // 获取请求参数
    Json::Value requestJson;
    Json::Reader reader;
    if (!reader.parse(request.body, requestJson))
    {
    std::string errorJsonStr = buildResponse("parse request body failed, json format error");
    response.status = 400; // 解析请求参数失败
    response.set_content(errorJsonStr, "application/json");
    return;
    }

    // 解析请求参数
    std::string sessionId = requestJson["session_id"].asString();
    std::string message = requestJson["message"].asString();
    if (sessionId.empty() || message.empty())
    {
    std::string errorJsonStr = buildResponse("session_id or message is empty");
    response.status = 400; // 解析请求参数失败
    response.set_content(errorJsonStr, "application/json");
    return;
    }

    // 准备流式响应
    response.status = 200; // 成功
    response.set_header("Cache-Control", "no-cache"); // 不使用缓存,服务器立即将数据发送到网络
    response.set_header("Connection", "keep-alive"); // 保持连接,服务器不会关闭连接
    response.set_header("Access-Control-Allow-Origin", "*"); // 允许跨域请求
    response.set_header("Access-Control-Allow-Headers", "*"); // 允许所有请求头

    // set_chunked_content_provider:告诉服务器,响应内从不是一次性发送的,而是分多次逐步发送给客户端,一般用在实时生成响应内容 或者 流式数据传输场景
    //
    response.set_chunked_content_provider("text/event-stream", [this, sessionId, message](size_t offset, httplib::DataSink &dataSink) -> bool
    {
    auto writeChunk = [&](const std::string &chunk, bool last)
    {
    // 将chunk转换为SSE数据格式
    // Json::valueToQuotedString: 对chunk进行Json转换,目的防止chunk中包含一些特殊字符来破坏数据格式,比如:在chunk中包含了两个连续的换行,就会影响SSE数据格式
    std::string sseData = "data: " + Json::valueToQuotedString(chunk.c_str()) + "\\n\\n";

    // 需要将模型返回的结果 chunk 发送给客户单
    dataSink.write(sseData.c_str(), sseData.size()); // 将数据写入响应流,即立即发送给客户单,该方法不会等待缓冲区满之后发送

    // 处理结束标记
    if (last)
    {
    // 流向响应结束
    std::string doneData = "data: [DONE]\\n\\n";
    dataSink.write(doneData.c_str(), doneData.size());
    dataSink.done(); // 表示流式响应结束
    return false; // 不再有后续数据
    }
    return true;
    };

    // 先给客户端发送一个空的数据块,避免客户端长时间的等待
    if (!writeChunk("", false))
    {
    return false;
    }

    // 发送消息流
    _chatSDK->sendMessageStream(sessionId, message, writeChunk);

    return false; // 不再有后续数据
    });
    }

    // 设置HTTP路由规则
    void ChatServer::setHttpRoutes()
    {
    // 处理创建会话请求
    _chatServer->Post("/api/session", [this](const httplib::Request &request, httplib::Response &response)
    { handleCreateSessionRequest(request, response); });

    // 处理获取会话列表请求
    _chatServer->Get("/api/sessions", [this](const httplib::Request &request, httplib::Response &response)
    { handleGetSessionListsRequest(request, response); });

    // 处理获取模型列表请求
    _chatServer->Get("/api/models", [this](const httplib::Request &request, httplib::Response &response)
    { handleGetModelListsRequest(request, response); });

    // 处理删除会话请求
    _chatServer->Delete("/api/session/(.*)", [this](const httplib::Request &request, httplib::Response &response)
    { handleDeleteSessionRequest(request, response); });

    // 处理获取历史消息请求
    _chatServer->Get("/api/session/(.*)/history", [this](const httplib::Request &request, httplib::Response &response)
    { handleGetHistoryMessagesRequest(request, response); });

    // 处理发送消息请求-全量返回
    _chatServer->Post("/api/message", [this](const httplib::Request &request, httplib::Response &response)
    { handleSendMessageRequest(request, response); });

    // 处理发送消息请求-增量返回
    _chatServer->Post("/api/message/async", [this](const httplib::Request &request, httplib::Response &response)
    { handleSendMessageStreamRequest(request, response); });
    }

    } // end ai_chat_server

    对应的main.cc:

    #include "ChatServer.hpp"
    #include <gflags/gflags.h>
    #include <iostream>
    #include <fstream>
    #include <cstdlib>
    #include <string>
    #include <stdexcept>
    #include <chrono>
    #include <thread>
    #include <spdlog/common.h>
    // 正确的日志头文件路径
    #include <ai_chat_sdk/util/myLog.hpp>

    // 定义gflags参数
    DEFINE_string(host, "0.0.0.0", "服务器绑定的地址");
    DEFINE_int32(port, 50090, "服务器绑定的端口号");
    DEFINE_string(log_level, "INFO", "日志级别");
    DEFINE_double(temperature, 0.7, "温度值,影响生成文本的随机性");
    DEFINE_int32(max_tokens, 2048, "最大token数");
    DEFINE_string(config_file, "./ChatServer.conf", "配置文件路径");
    // DEFINE_bool(version, false, "显示版本信息");
    // Ollama配置参数
    DEFINE_string(ollama_model_name, "", "Ollama模型名称");
    DEFINE_string(ollama_model_desc, "", "Ollama模型描述");
    DEFINE_string(ollama_endpoint, "", "Ollama API地址");

    // 版本号
    const std::string VERSION = "1.0.0";

    // 从环境变量获取API密钥
    std::string getEnvVar(const std::string& key) {
    char* value = std::getenv(key.c_str());
    return value ? std::string(value) : "";
    }

    // 注意:配置文件ChatServer.conf将由gflags库自动解析,不需要手动生成

    // 验证配置参数
    bool validateConfig(ai_chat_server::ServerConfig& config) {
    // 验证温度值
    if (config.temperature < 0.0 || config.temperature > 2.0) {
    ERROR("错误: 温度值必须在0.0到2.0之间,当前值: {}", config.temperature);
    return false;
    }

    // 验证最大token数
    if (config.maxTokens <= 0) {
    ERROR("错误: 最大token数必须为正数,当前值: {}", config.maxTokens);
    return false;
    }

    // 验证至少有一个API密钥不为空
    if (config.deepseekAPIKey.empty() && config.chatGPTAPIKey.empty() && config.geminiAPIKey.empty() && config.ollamaModelName.empty()) {
    ERROR("错误: 至少需要提供一个有效的API密钥或Ollama模型配置");
    return false;
    }

    // 验证Ollama配置参数
    if (!config.ollamaModelName.empty()) {
    if (config.ollamaModelDesc.empty() || config.ollamaEndpoint.empty()) {
    ERROR("错误: 如果提供了Ollama模型名称,则必须同时提供模型描述和端点");
    return false;
    }
    }

    return true;
    }

    // 显示接口说明
    void showAPIInfo() {
    std::cout << "\\nChatServer API接口说明:\\n";
    std::cout << " POST /api/session – 创建新会话\\n";
    std::cout << " GET /api/sessions – 获取所有会话列表\\n";
    std::cout << " GET /api/models – 获取可用模型列表\\n";
    std::cout << " DELETE /api/session/{session_id} – 删除指定会话\\n";
    std::cout << " GET /api/session/{session_id}/history – 获取会话历史消息\\n";
    std::cout << " POST /api/message – 发送消息(全量返回)\\n";
    std::cout << " POST /api/message/async – 发送消息(流式返回)\\n";
    std::cout << "\\n使用示例:\\n";
    std::cout << " # 基本启动\\n";
    std::cout << " ./AIChatServer\\n";
    std::cout << "\\n # 指定端口启动\\n";
    std::cout << " ./AIChatServer –port=9000\\n";
    std::cout << "\\n # 使用指定配置文件\\n";
    std::cout << " ./AIChatServer –config_file=my_config.conf\\n";
    std::cout << "\\n # 设置环境变量后启动\\n";
    std::cout << " export DEEPSEEK_API_KEY=your_api_key\\n";
    std::cout << " ./AIChatServer\\n";
    }

    int main(int argc, char** argv) {
    try {
    // 显示帮助信息(当使用-h或–help时,gflags会自动显示帮助信息并退出)
    // 这里我们额外显示API接口说明
    if (argc == 2 && (std::string(argv[1]) == "-h" || std::string(argv[1]) == "–help")) {
    showAPIInfo();
    return 0;
    }

    // 解析命令行参数
    gflags::SetUsageMessage("AIChatServer – AI聊天服务器\\n\\n使用方法: ./AIChatServer [options]");
    gflags::ParseCommandLineFlags(&argc, &argv, true);
    gflags::SetVersionString(VERSION);
    // 使用gflags库自动解析配置文件
    // 如果配置文件存在,gflags会自动加载其中的参数
    // 注意:gflags会按照以下顺序解析参数:默认值 -> 配置文件 -> 命令行参数
    std::ifstream file(FLAGS_config_file);
    if (file) {
    gflags::SetCommandLineOption("flagfile", FLAGS_config_file.c_str());
    }

    // 构建ServerConfig
    ai_chat_server::ServerConfig config;
    config.host = FLAGS_host;
    config.port = FLAGS_port;
    config.logLevel = FLAGS_log_level;
    config.temperature = FLAGS_temperature;
    config.maxTokens = FLAGS_max_tokens;

    // 从环境变量获取API密钥
    config.deepseekAPIKey = getEnvVar("deepseek_apikey");
    // config.chatGPTAPIKey = getEnvVar("chatgpt_apikey");
    // config.geminiAPIKey = getEnvVar("gemini_apikey_new");
    // 从命令行参数获取Ollama配置
    config.ollamaModelName = FLAGS_ollama_model_name;
    config.ollamaModelDesc = FLAGS_ollama_model_desc;
    config.ollamaEndpoint = FLAGS_ollama_endpoint;

    // 验证配置参数
    if (!validateConfig(config)) {
    ERROR("配置验证失败,请检查参数设置");
    return 1;
    }

    // 设置日志级别
    spdlog::level::level_enum logLevel = spdlog::level::info; // 默认INFO级别
    if (config.logLevel == "TRACE") logLevel = spdlog::level::trace;
    else if (config.logLevel == "DEBUG") logLevel = spdlog::level::debug;
    else if (config.logLevel == "INFO") logLevel = spdlog::level::info;
    else if (config.logLevel == "WARN" || config.logLevel == "WARNING") logLevel = spdlog::level::warn;
    else if (config.logLevel == "ERROR") logLevel = spdlog::level::err;
    else if (config.logLevel == "CRITICAL") logLevel = spdlog::level::critical;

    // 初始化日志组件
    lg::Logger::initLogger("ChatServer", "stdout", logLevel);

    // 显示当前配置
    INFO("AIChatServer 启动配置:");
    INFO(" 版本: {}", VERSION);
    INFO(" 主机: {}", config.host);
    INFO(" 端口: {}", config.port);
    INFO(" 日志级别: {}", config.logLevel);
    INFO(" 温度值: {}", config.temperature);
    INFO(" 最大Token: {}", config.maxTokens);
    INFO(" DeepSeek API Key: {}", (config.deepseekAPIKey.empty() ? "未设置" : "已设置"));
    INFO(" ChatGPT API Key: {}", (config.chatGPTAPIKey.empty() ? "未设置" : "已设置"));
    INFO(" Gemini API Key: {}", (config.geminiAPIKey.empty() ? "未设置" : "已设置"));
    INFO(" Ollama 模型: {}", (config.ollamaModelName.empty() ? "未设置" : config.ollamaModelName));
    INFO(" Ollama 模型描述: {}", (config.ollamaModelDesc.empty() ? "未设置" : config.ollamaModelDesc));
    INFO(" Ollama 端点: {}", (config.ollamaEndpoint.empty() ? "未设置" : config.ollamaEndpoint));

    // 创建并启动ChatServer
    ai_chat_server::ChatServer server(config);
    if (server.start()) {
    INFO("ChatServer 启动成功!");
    INFO("服务器地址: http://{}:{}", config.host, config.port);

    // 主线程等待,让服务器在单独线程中运行
    while (server.isRunning()) {
    std::this_thread::sleep_for(std::chrono::seconds(100));
    }
    } else {
    ERROR("ChatServer 启动失败!");
    return 1;
    }

    return 0;
    } catch (const std::exception& e) {
    ERROR("发生异常: {}", e.what());
    return 1;
    } catch (...) {
    ERROR("发生未知异常");
    return 1;
    }
    }

    前端的话,直接把上面的请求响应的http格式发给AI,让AI整一个就行,或者直接把我gitee中的build目录下的这几个文件复制过去就行。 在这里插入图片描述

    最后记得.js文件中修改成你的IP+PORT,然后就可以运行了。 在这里插入图片描述

    赞(0)
    未经允许不得转载:171主机测评 » C++AI
    分享到: 更多 (0)

    评论 抢沙发

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