C++AI

文章目录

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

bash 复制代码
# 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 库)

bash 复制代码
# 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

bash 复制代码
# 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中链接

makefile 复制代码
g++ main.cpp -o my_program -lsqlite3

CMakeLists.txt中链接

cmake 复制代码
target_link_libraries(${SDK_NAME} sqlite3)

三、 项目实现

1. 目录结构与开发顺序

最终目录结构如下图所示

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

目录中的内容

sdk开发顺序

  1. common.hpp
  2. util/myLog.hpp myLog.cc
  3. LLMProvider.hpp DeepSeekProvider.cc
  4. 去test目录下写个测试,看看写好的DeepSeek能不能正常调用
  5. LLMManager.hpp LLMManager.cc
  6. 写不接入DataManager的SessionManager.hpp SessionManager.cc
  7. DataManager.hpp DataManager.cc
  8. 完善SessionManager.hpp SessionManager.cc 接入DataManager类
  9. ChatSDK.hpp ChatSDK.cc
  10. 最后test目录下写个测试,测一下ChatSDK能不能正常使用就行
  11. sdk目录整体工作完成

ChatServer开发顺序

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

2. common.hpp

这里面就定义一些基础的数据结构,以备后面需要。

Message定义消息结构体,用来表示每条消息记录

Config用来表示模型的配置信息 析构函数记得声明虚函数要不然可能会有问题

ModelInfo用来描述模型信息

Session描述每个会话的信息

这里的会话是指这个:

cpp 复制代码
#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

cpp 复制代码
#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);

cpp 复制代码
//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中读取配置参数,没有传参则使用默认值,不影响协议和格式,只影响模型生成策略:

cpp 复制代码
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数组格式:

cpp 复制代码
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结构示例:

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

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

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

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

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

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

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

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

cpp 复制代码
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");

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

  1. 大模型返回的 res->body 是一个 JSON格式的字符串,格式是固定的(接口文档约定)
  2. 使用 JsonCpp 把「JSON字符串」解析成「C++的Json::Value对象」
  3. 按照 JSON的层级结构,一层一层「精准取值」,最终拿到AI回复的纯文本内容
  4. 所有取值步骤都做了合法性判断,防止JSON字段缺失导致程序崩溃

响应会有如下字段,我们只需要把choices->message->content提取到就行,也就是说我们只要这个content字段,根据json的语法去获取之后再校验一下就行。

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}
}
cpp 复制代码
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结构完全一一对应)

cpp 复制代码
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; // 返回结果,结束函数
        }
    }
}

完整代码:

cpp 复制代码
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";

}
cpp 复制代码
//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=truetext/event-stream (简称SSE) 协议,这是大模型流式输出的事实标准协议(OpenAI/DeepSeek/Anthropic 全用它),不是普通的HTTP JSON响应,这是理解流式的前提:

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

cpp-httplib 实现流式响应的核心:两个回调函数

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

cpp 复制代码
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;   // 继续接收后续数据
};

作用&执行时机

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

回调2:req.content_receiver ------ 流式数据体回调(核心!执行N次)

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

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

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

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

回到我们的代码中:

先要设置http头部字段,要设置流式字段

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

后面就是设置了两个关键的回调函数:

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

200,是则继续接收,反之停止。

cpp 复制代码
// 设置响应处理器
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用来流式发给前端的。

cpp 复制代码
// 设置数据接收处理器--解析流式响应的每个块的数据
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;
};

完整代码:

cpp 复制代码
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官网弄一下,比较简单不多说了。

终端执行:

cpp 复制代码
export deepseek_apikey="你的deepseek apikey"

cmake:

cpp 复制代码
# 设置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:

cpp 复制代码
#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对象,要调用什么模型直接通过这个对象调用就行。

以下是所有业务:

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

以下是简要介绍,不是很难,不多赘述,

  1. 私有成员(核心存储)

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

    • registerProvider:注册大模型提供商,将具体的模型实现类挂载到管理器中,完成模型接入;
    • initModel:初始化指定模型,传入模型配置参数完成鉴权、连接等前置操作,初始化成功后标记模型为可用状态;
    • getAvailableModels:查询所有已初始化、可正常调用的模型列表;
    • isModelAvailable:校验指定模型是否完成初始化、处于可用状态;
    • sendMessage:统一转发全量消息请求,调用对应模型的全量响应接口,返回完整AI回复文本;
    • sendMessageStream:统一转发流式消息请求,调用对应模型的流式响应接口,透传流式回调函数,实现实时文本推送。

这样设计的优势

该类是上层业务与底层大模型的中间层 ,上层业务无需关注具体大模型的对接细节,只需通过该类的统一接口即可完成所有操作,做到一次调用、适配所有模型,极大降低上层接入成本。

调用逻辑

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

完整代码:

cpp 复制代码
#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
cpp 复制代码
#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之后再完善这个类。

具体实现就不多介绍了,都很简单,也没啥陌生的语法。

全部代码:

cpp 复制代码
#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
cpp 复制代码
#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:插入消息(插入时自动更新会话最新时间戳)、查询指定会话的所有消息、删除指定会话的消息;

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

全部代码:

cpp 复制代码
#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
cpp 复制代码
#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 - 构造函数

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 - 创建会话 代码块

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 - 获取会话 代码块

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 - 添加消息 代码块

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 - 获取会话历史 代码块

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

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

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 - 删除会话 代码块

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

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

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

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

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

完整代码:

cpp 复制代码
#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 组成内容

  1. 库文件:已封装好的现成功能,按需直接调用即可。
  2. API接口:标准化交互规则,指导开发者与平台/功能进行对接。
  3. 文档:使用说明书,说明各类功能、接口的使用方式。
  4. 示例代码:实操参考,参照即可快速跑通功能。
  5. 调试工具:排查开发、调用过程中的问题。

SDK 核心价值

SDK 是厂商打包好的一套「现成工具+说明书」,让开发者可以更快、更安全、更省心地在自己的软件中实现特定功能,无需从零开发底层逻辑。

SDK 和 API 的区别

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

通俗类比

做一道菜(开发应用):

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

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

包含以下功能:

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

核心实现逻辑

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

其实就是对SessionManager又封装了一层,大体逻辑几乎一摸一样,不多赘述。


11. ChatSDK测试

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

cmake:

cpp 复制代码
# 设置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:

cpp 复制代码
#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. 获取会话列表
  2. 获取支持的模型
  3. 新建会话
  4. 发送消息(默认流式返回)
  5. 获取会话历史
  6. 删除会话

接口列表(按功能分类)

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 结果描述

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

完整代码:

cpp 复制代码
#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
cpp 复制代码
#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:

cpp 复制代码
#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,然后就可以运行了。

相关推荐
skywalk81632 小时前
小米大模型mimo-v2-flash简单接触
人工智能·小米
Rui_Freely2 小时前
Vins-Fusion之 TrackImage-Lukas-Kanade光流法(四)
人工智能
Hcoco_me2 小时前
大模型面试题26:Adam优化器小白版速懂
人工智能·rnn·自然语言处理·lstm·word2vec
kevin_kang2 小时前
25-客服工单系统实战(二):RAG检索与智能问答
人工智能
njsgcs2 小时前
基于vlm+ocr+yolo的一键ai从模之屋下载模型
人工智能·python·yolo·ocr·vlm
敲上瘾2 小时前
C++11线程库指南:线程、锁、原子操作与并发编程实战
开发语言·c++·多线程
夏幻灵2 小时前
JAVA基础-就近原则和this关键字
java·开发语言
DeepVis Research2 小时前
【Chaos/Neuro】2026年度混沌动力学仿真与机器遗忘算法基准索引 (Benchmark Index)
人工智能·算法·数据集·混沌工程·高性能计算
Stardep2 小时前
深度学习进阶:偏差方差分析与正则化策略全解析
人工智能·深度学习·dropout·正则化·过拟合·欠拟合·方差与偏差