欢迎光临
我们一直在努力

C++大模型统一接入引擎(第三篇):模型管理、会话持久化与SDK门面封装的完整实现

文章目录

  • 一、准备工作
    • 1. 头文件common.h (Message、Session 和 Config等类的定义)
  • 二、模型管理(LLMManager类 管理所有 模型Provider类)
  • 三、会话管理
    • 1. 为什么需要会话管理模块
    • 2. 会话管理模块介绍
    • 3. 会话管理模块的代码实现
  • 四、数据管理
    • 1. 数据管理模块 的作用
    • 2. 数据管理模块介绍(使用SQLite数据库)
    • 3. 数据管理模块的代码实现
  • 五、在会话管理模块中 加入数据管理模块
    • 1. 融合了数据管理模块的 会话管理模块的代码实现
    • 2. 复盘:将数据管理模块 融合进 会话管理模块的思路
  • 六、ChatSDK封装(将模型管理 和 Session管理结合起来)
    • 1. 什么是SDK(比较API 和 SDK)
    • 2. ChatSDK代码实现
    • 3. ChatSDK的 实现思路
  • 七、ChatSDK使用手册
    • – 概述
    • – 项目结构
    • – 类关系图
    • – 获取 SDK
    • – 数据结构
    • – 快速入门
    • – API 参考
    • – 完整示例
    • – 编译集成
    • – 注意事项

一、准备工作

1. 头文件common.h (Message、Session 和 Config等类的定义)

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

namespace ai_chat_sdk {
// 消息结构
struct Message {
std::string _id; // 消息ID(唯一标识, 可选)(流式返回时使用)
std::string _role; // "user" or "assistant"
std::string _content; // 消息内容
std::time_t _create_at; // 消息生成时间

Message(const std::string& role = "", const std::string& content = "")
: _role(role), _content(content), _create_at(std::time(nullptr))
{}
// std::time(nullptr) 的作用是获取当前系统的时间戳
};

// 会话结构
struct Session {
std::string _id; // 会话ID(唯一标识)
std::string _model_name; // 模型名称
std::vector<Message> _messages; // 会话中的消息列表
std::time_t _created_at; // 会话创建时间
std::time_t _updated_at; // 会话更新时间

Session(const std::string& model_name = "")
: _model_name(model_name), _created_at(std::time(nullptr)), _updated_at(std::time(nullptr))
{}
// std::time(nullptr) 的作用是获取当前系统的时间戳
};

// 模型信息
struct ModelInfo {
std::string _name; // 模型名称
std::string _desc; // 模型描述信息
bool _available = false; // 模型是否可用

ModelInfo(const std::string& name = "", const std::string& desc = "")
: _name(name), _desc(desc)
{}
// 构造函数,初始化模型名称、描述信息
};

// 调用模型时的配置信息
struct Config {
std::string _model_name; // 模型名称
double _temperature; // 温度参数,默认值为0.7;
int _max_tokens; // 最大生成token数,默认值为2048;

Config(const std::string& model_name, double temperature = 0.7, int max_tokens = 2048)
: _model_name(model_name), _temperature(temperature), _max_tokens(max_tokens)
{}
// 构造函数,初始化模型名称和配置参数

virtual ~Config() = default; // 虚析构函数,确保子类可以调用析构函数用于释放资源
// dynamic_pointer_cast使用要求:基类有虚函数
};

// API配置结构(API接入云端模型时的配置信息)
struct ApiConfig : public Config {
std::string _api_key; // API密钥(接入云端模型时的认证信息)
std::string _api_url; // API基础URL(例如:"https://api.deepseek.cn/v1")

// 构造函数,初始化模型名称、温度参数、最大生成token数参数 以及 API密钥参数、API基础URL参数
ApiConfig(const std::string& model_name, double temperature = 0.7, int max_tokens = 2048,
const std::string& key = getenv("deepseek_apikey"), const std::string& url = "")
: Config(model_name, temperature, max_tokens),
_api_key(key),
_api_url(url)
{}
};

// Ollama本地接入大模型时的配置信息
struct OllamaConfig : public Config {
std::string _ollama_url; // Ollama本地模型地址(默认值为"http://localhost:11434")
std::string _model_desc; // 模型描述信息(默认值为空字符串)

// 构造函数,初始化模型名称、温度参数、最大生成token数参数 以及 Ollama本地模型地址、模型描述信息
OllamaConfig(const std::string& model_name, double temperature = 0.7, int max_tokens = 2048,
const std::string& url = "http://localhost:11434", const std::string& desc = "")
: Config(model_name, temperature, max_tokens),
_ollama_url(url),
_model_desc(desc)
{}
};
} // namespace ai_chat_sdk

二、模型管理(LLMManager类 管理所有 模型Provider类)

现在deepseek-chat、gpt-4o-mini、gemini-2.0-flash 以及 本地deepseek模型已经接入成功,每个模型都对应单独的Provider类,为了后续使用简单,再封装⼀个LLMManager类将每个Provider类对象管理起来,后续通过多态的方式实现模型路由。

  • LLMManager.h

LLMManager类 的核心成员变量:std::map<std::string, std::unique_ptr< LLMProvider > > _providers;

  • key: model_name
  • value: LLMProvider 指针(指向 模型提供者)

后续通过模型名称 与 LLMProvider 指针的对应关系,输入不同模型名称 就可以得到不同 LLMProvider 指针

#pragma once
#include "common.h"
#include "LLMProvider.h"
#include <map>
#include <memory>

namespace ai_chat_sdk{
// LLM 管理
class LLMManager{
public:
// 注册 LLM提供者
bool register_Provider(const std::string& name, std::unique_ptr<LLMProvider> provider);
// 初始化指定模型
bool init_Model(const std::string& model_name, const std::map<std::string, std::string>& model_config);
// 检测模型是否有效
bool is_Model_Available(const std::string& model_name);
// 获取可用模型列表
std::vector<ModelInfo> get_Available_Models();
// 发送消息给模型-非流式返回
std::string sendMessage(const std::string& model_name, const std::vector<Message>& messages,
const std::map<std::string,std::string>& request_param);
// 发送消息给模型-流式返回
std::string sendMessageStream(const std::string& model_name, const std::vector<Message>& messages,
const std::map<std::string,std::string>& request_param,
std::function<void(const std::string&, bool)> callback);

private:
// key: model_name value: LLMProvider 指针(指向 模型提供者)
std::map<std::string, std::unique_ptr<LLMProvider>> _providers;
// key: model_name value: ModelInfo 结构体(保存模型的信息)
std::map<std::string, ModelInfo> _models;
};
} // namespace ai_chat_sdk

  • LLMManager.cpp

#include "../include/util/mylog.h"
#include "../include/LLMManager.h"

namespace ai_chat_sdk{
// 注册 LLM提供者
bool LLMManager::register_Provider(const std::string& name, std::unique_ptr<LLMProvider> provider)
{
// 检查提供者是否为空
if(!provider){
return false;
}
// 检查提供者是否已注册
if(_providers.find(name) != _providers.end()){
return false;
}

// 注意,unique_ptr是防拷贝的,此处只能通过move的方式将资源转移给当前对象
_providers[name] = std::move(provider);
// 初始化模型信息
_models[name] = ModelInfo(name);
return true;
}

// 初始化指定模型
bool LLMManager::init_Model(const std::string& model_name, const std::map<std::string, std::string>& model_config)
{
// 检查模型是否注册
auto it = _providers.find(model_name);
if(it == _providers.end()){
ERR("Model %s not registered", model_name.c_str());
return false;
}

// 检查模型是否已初始化
if(_models[model_name]._available){
WARN("Model %s is already initialized", model_name.c_str());
return true;
}

// 初始化模型
bool isSuccess = it->second->init_Model(model_config);
if(isSuccess){
INFO("Model {} init success!!!", model_name);
_models[model_name]._desc = it->second->getModelDesc();
_models[model_name]._available = true; // _available为true, 代表模型已注册 且 初始化成功,可以直接使用!
}
else{
ERR("Model {} init failed!!!", model_name);
_models[model_name]._available = false;
}
return isSuccess;
}

// 检测模型是否有效
bool LLMManager::is_Model_Available(const std::string& model_name)
{
// 检查模型是否注册
if(_models.find(model_name) == _models.end()){
ERR("Model %s not registered", model_name.c_str());
return false;
}

return _models[model_name]._available;
}

// 获取可用模型列表
std::vector<ModelInfo> LLMManager::get_Available_Models()
{
std::vector<ModelInfo> available_models;
for(auto& model : _models){
if(model.second._available)
{
available_models.push_back(model.second);
}
}

return available_models;
}

// 发送消息给模型-非流式返回
std::string LLMManager::sendMessage(const std::string& model_name, const std::vector<Message>& messages,
const std::map<std::string,std::string>& request_param)
{
// 检查模型是否注册
auto it = _providers.find(model_name);
if(it == _providers.end()){
ERR("Model %s not registered", model_name.c_str());
return "";
}

// 检查模型是否可用
if(!_models[model_name]._available){
ERR("Model %s is not available", model_name.c_str());
return "";
}

// 调用模型
return it->second->sendMessage(messages, request_param);
}

// 发送消息给模型-流式返回
std::string LLMManager::sendMessageStream(const std::string& model_name, const std::vector<Message>& messages,
const std::map<std::string,std::string>& request_param,
std::function<void(const std::string&, bool)> callback)
{
// 检查模型是否注册
auto it = _providers.find(model_name);
if(it == _providers.end()){
ERR("Model %s not registered", model_name.c_str());
return "";
}

// 检查模型是否可用
if(!_models[model_name]._available){
ERR("Model %s is not available", model_name.c_str());
return "";
}

// 调用模型
return it->second->sendMessageStream(messages, request_param, callback);
}
} // namespace ai_chat_sdk

三、会话管理

1. 为什么需要会话管理模块

假设现在借助LLMManager搭建一个大模型后端服务,用户 和 模型间进行了多轮会话,每个会话中都包含了好多条消息。 在某个会话中,和模型聊天的多轮消息该如何管理?多个会话该如何管理?

在这里插入图片描述

为了解决以上问题,就需要引入 会话管理模块,会话管理模块 可以对多个会话进行管理,每个会话 管理会话中的多轮消息; 会话记录了多轮消息,下次用户在该轮会话继续进行提问的时候,就可以把该轮会话记录的多轮消息一起打包传入大语言模型进行提问,大语言模型就可以参考 会话上下文 来对用户的问题作出回答。

2. 会话管理模块介绍

会话是用户 与 大语言模型之间的一系列连续交互,它通过维护上下文和状态信息,确保对话的连贯性 和 一致性。由于大模型不会为用户管理会话信息,因此需要程序员手动完成会话管理。

会话管理涉及以下内容:

(1)保存会话数据。 由于存在多组会话,每组会话可能有多条聊天消息,在保存会话数据时可以将会话id和具体的会话建立映射关系,方便后续查询。

std::unordered_map< std::string, std::shared_ptr< Session > > _sessions;

(2)创建会话。 在和大模型建立连接前,需要先创建好会话,将后续每次和大模型聊天的消息信息要存在该会话中。

(3)通过会话id获取指定会话。

(4)向某会话中添加消息。 在和大模型聊天时,需要将用户提问 和 模型回复的消息保存到会话中。

(5)获某会话中所有消息。 当用户点击某个会话时,需要将该会话中的历史消息显示到界面。

(6)更新会话时间戳。 每次向会话中添加消息时,需要更新会话的时间戳。

(7)获取所有会话列表。 获取所有的会话列表,显示在界面中。

(8)获取会话总数。

(9)删除会话。 用户可以在页面中删除具体的会话。

(10)清空所有会话。

(11)生成会话id。 需要生成唯一的会话id来标记具体某条会话。

(12)生成消息id。 需要使用唯一的消息id来标记具体某条消息。


  • common.h 中定义的Message类 和 Session类

namespace ai_chat_sdk {
// 消息结构
struct Message {
std::string _id; // 消息ID(唯一标识, 可选)(流式返回时使用)
std::string _role; // "user" or "assistant"
std::string _content; // 消息内容
std::time_t _create_at; // 消息生成时间

Message(const std::string& role = "", const std::string& content = "")
: _role(role), _content(content), _create_at(std::time(nullptr))
{}
// std::time(nullptr) 的作用是获取当前系统的时间戳
};

// 会话结构
struct Session {
std::string _id; // 会话ID(唯一标识)
std::string _model_name; // 模型名称
std::vector<Message> _messages; // 会话中的消息列表
std::time_t _created_at; // 会话创建时间
std::time_t _updated_at; // 会话更新时间

Session(const std::string& model_name = "")
: _model_name(model_name), _created_at(std::time(nullptr)), _updated_at(std::time(nullptr))
{}
// std::time(nullptr) 的作用是获取当前系统的时间戳
};
}

3. 会话管理模块的代码实现

  • SessionManager.h

#pragma once
#include "common.h"
#include "DataManager.h"
#include <atomic>
#include <mutex>
#include <unordered_map>
#include <memory>

namespace ai_chat_sdk{
class SessionManager{
private:
// 生成会话ID,返回会话ID
std::string generateSessionID();

// 生成消息ID,返回消息ID
std::string generateMessageID(size_t messageCounter);

public:
// 创建新会话,返回会话ID (将会话 写入到会话列表中)
std::string createSession(const std::string& model_name);

// 添加消息到会话. session_id : 会话id, message : 消息 返回是否添加成功
bool addMessage(const std::string& session_id, const Message& message);

// 获取具体会话,通过会话id获取. session_id : 会话id 返回会话指针
std::shared_ptr<Session> getSession(const std::string& session_id);

// 获取会话历史,即获取具体某次会话的所有消息. session_id : 会话id 返回消息列表
std::vector<Message> getSessionHistory(const std::string& session_id);

// 获取会话列表 返回会话列表
std::vector<std::string> getSessionList();

// 获取会话总数 返回会话总数
size_t getSessionCount();

// 更新会话时间戳. session_id : 会话id
void updateSessionTimestamp(const std::string& session_id);

// 删除会话. session_id : 会话id 返回是否删除成功
bool deleteSession(const std::string& session_id);

// 清空所有会话
void clearAllSessions();

private:
// 管理所有会话,key为session_id, value为会话指针
std::unordered_map<std::string, std::shared_ptr<Session>> _sessions;
std::mutex _mutex;
// 原子类型: 进行原子操作,确保线程安全
std::atomic<int64_t> _session_counter = 0; // 记录会话总数
};
} // namespace ai_chat_sdk

  • SessionManager.cpp

#include "../include/SessionManager.h"
#include "../include/DataManager.h"
#include "../include/util/mylog.h"
#include <ctime>
#include <iomanip>
#include <sstream>

namespace ai_chat_sdk{
// 生成会话ID,返回会话ID
std::string SessionManager::generateSessionID()
{
// 会话计数自增(原子操作)
_session_counter.fetch_add(1);
std::time_t time = std::time(nullptr);

// 生成会话id
std::ostringstream os;
// 例如:session_1234567890_00000001
os<<"session_"<<time<<"_"<<std::setw(8)<<std::setfill('0')<<_session_counter;
return os.str();
}

// 生成消息ID,返回消息ID
std::string SessionManager::generateMessageID(size_t messageCounter)
{
messageCounter++;
std::time_t time = std::time(nullptr);

// 生成消息id
std::ostringstream os;
// 例如:message_1234567890_00000001
os<<"message_"<<time<<"_"<<std::setw(8)<<std::setfill('0')<<messageCounter;
return os.str();
}

// 创建新会话,返回会话ID (将会话 写入到会话列表中)
std::string SessionManager::createSession(const std::string& model_name)
{
std::lock_guard<std::mutex> lock(_mutex);

// 生成会话id (generateSessionID函数会将 会话计数自增1)
std::string session_id = generateSessionID();

// 创建会话, 并设置会话id
auto session = std::make_shared<Session>(model_name);
session->_id = session_id;

// 将会话 写入到会话列表中
_sessions[session_id] = session;

return session_id;
}

// 添加消息到会话. session_id : 会话id, message : 消息 返回是否添加成功
bool SessionManager::addMessage(const std::string& session_id, const Message& message)
{
std::lock_guard<std::mutex> lock(_mutex);

// 检查会话是否存在
auto it = _sessions.find(session_id);
if(it == _sessions.end())
{
ERR("会话不存在, session_id: {}", session_id);
// 会话不存在, 直接返回false
return false;
}

// 生成消息id
std::string message_id = generateMessageID(it->second->_messages.size());

// 创建消息, 并设置消息id
Message msg(message._role, message._content);
msg._id = message_id;

// 将消息 写入到会话列表中
_sessions[session_id]->_messages.push_back(msg);

// 更新会话时间戳
_sessions[session_id]->_updated_at = msg._create_at;

return true;
}

// 获取具体会话,通过会话id获取. session_id : 会话id 返回会话指针
std::shared_ptr<Session> SessionManager::getSession(const std::string& session_id)
{
std::lock_guard<std::mutex> lock(_mutex);

// 检查会话是否存在
auto it = _sessions.find(session_id);
if(it == _sessions.end())
{
ERR("会话不存在, session_id: {}", session_id);
// 会话不存在, 直接返回nullptr
return nullptr;
}

return it->second;
}

// 获取会话历史,即获取具体某次会话的所有消息. session_id : 会话id 返回消息列表
std::vector<Message> SessionManager::getSessionHistory(const std::string& session_id)
{
std::lock_guard<std::mutex> lock(_mutex);

// 检查会话是否存在
auto it = _sessions.find(session_id);
if(it == _sessions.end())
{
ERR("会话不存在, session_id: {}", session_id);
// 会话不存在, 直接返回空的消息列表
return std::vector<Message>();
}

return it->second->_messages;
}

struct compare
{
bool operator()(const std::pair<std::string, std::time_t>& it1,
const std::pair<std::string, std::time_t>& it2)
{
return it1.second > it2.second;
}
};

// 获取会话列表 (会话id列表),返回会话列表 (按更新时间排序,最新更新的在最前面)
std::vector<std::string> SessionManager::getSessionList()
{
std::lock_guard<std::mutex> lock(_mutex);

std::vector< std::pair<std::string, std::time_t> > vec;
for(auto it : _sessions)
{
vec.push_back({it.first, (it.second)->_updated_at});
}
// 按照更新时间降序排序
std::sort(vec.begin(), vec.end(), compare());

std::vector<std::string> session_ids;
for(auto& it : vec)
{
session_ids.push_back(it.first);
}

return session_ids;
}

// 获取会话总数 返回会话总数
size_t SessionManager::getSessionCount()
{
std::lock_guard<std::mutex> lock(_mutex);
return _session_counter;
}

// 更新会话时间戳. session_id : 会话id
void SessionManager::updateSessionTimestamp(const std::string& session_id)
{
std::lock_guard<std::mutex> lock(_mutex);

// 检查会话是否存在
auto it = _sessions.find(session_id);
if(it == _sessions.end())
{
ERR("会话不存在, session_id: {}", session_id);
// 会话不存在, 直接返回
return;
}

// 更新会话时间戳
it->second->_updated_at = std::time(nullptr);
}

// 删除会话. session_id : 会话id 返回是否删除成功
bool SessionManager::deleteSession(const std::string& session_id)
{
std::lock_guard<std::mutex> lock(_mutex);

// 检查会话是否存在
auto it = _sessions.find(session_id);
if(it == _sessions.end())
{
ERR("会话不存在, session_id: {}", session_id);
// 会话不存在, 直接返回false
return false;
}

// 删除会话
_sessions.erase(it);

// 会话计数自减1
_session_counter.fetch_sub(1);

return true;
}

// 清空所有会话
void SessionManager::clearAllSessions()
{
std::lock_guard<std::mutex> lock(_mutex);

// 清空所有会话
_sessions.clear();

// 会话计数重置为0
_session_counter.store(0);
}
} // namespace ai_chat_sdk

四、数据管理

1. 数据管理模块 的作用

目前在程序中创建的会话以及和模型交互的历史数据都存储在内存中,而内存是一种带电存储设备,一旦断电或者程序重启之后数据就丢失了,想要拿到之前的会话数据就回天乏术了。Deepseek、ChatGPT提供的网页聊天机器人都是支持会话存储的,方便用户查看以往的会话记录。因此,ChatSDK中也需要添加数据持久化存储的功能,即将数据永久的保存下来,不会因为断电或关闭程序而引起数据丢失。

持久化存储最常见的方案就是将数据存储到数据库,由于ChatSDK实现好之后,需要共享给其他需要的用户使用,为了减少用户使用成本(使用时不需要安装过多依赖 或 三方工具),本项目采用 SQLite数据库 实现数据存储。

2. 数据管理模块介绍(使用SQLite数据库)

C++ 接入 SQLite 数据库:环境搭建、API 详解 与 两种执行方式对比

数据管理是通过数据库实现的,将数据存储到数据库中 实现持久化存储。 而且数据一旦被存储到数据库之后,通过SQL语句查询起来非常方便。

数据管理涉及以下内容:

(1)插入新会话。 创建一个新会话后,将新会话相关信息插入sessions表

(2)获取指定会话。 从sessions表中获取指定sessionId的会话信息

(3)更新指定会话的时间戳。

(4)删除指定会话。 删除指定sessionId的会话信息,该会话下管理的历史消息也会从messages表中同步删除(外键约束,级联删除)

(5)获取所有会话id。 通过SQL查询语句从sessions表中获取

(6)获取所有会话信息。 通过SQL查询语句从sessions表中获取

(7)删除所有会话。 删除sessions表中的所有会话信息,会话下管理的历史消息也会从messages表中同步删除

(8)获取所有会话个数。 通过SQL内置函数count(*)计算sessions表的列数

(9)删除会话。 用户可以在页面中删除具体的会话。

(10)向指定会话添加新消息。 通过SQL插入语句将消息信息添加进messages表

(11)获取指定会话的历史消息。 通过SQL查询语句从messages表中获取

(12)删除指定会话的历史消息。 通过SQL删除语句从messages表中删除

在这里插入图片描述

3. 数据管理模块的代码实现

  • DataManager.h

#pragma once
#include <sqlite3.h>
#include <mutex>
#include <memory>
#include "common.h"

namespace ai_chat_sdk{
class DataManager{
private:
// 初始化数据库(创建会话表 和 消息表,并建立两表之间的关联关系,如果表已存在则不创建)
bool initDatabase();

public:
// 创建或打开 数据库连接,然后调用 initDatabase()初始化数据库
DataManager(const std::string& dbName);
~DataManager();

// ***************** Session相关操作 *****************
// 插入新会话
bool insertSession(const Session& session);
// 获取指定会话
std::shared_ptr<Session> getSession(const std::string& sessionId)const;
// 更新指定会话的时间戳
void 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();
// 获取所有会话个数
int getSessionCount()const;

// ***************** Message相关操作 *****************
// 向指定会话添加新消息
bool insertMessage(const std::string& sessionId, const Message& message);
// 获取指定会话的历史消息
std::vector<Message> getMessages_BySessionId(const std::string& sessionId)const;
// 删除指定会话的历史消息
bool deleteMessages_BySessionId(const std::string& sessionId);

private:
sqlite3* _db;
std::string _db_path; // 数据库路径
mutable std::mutex _mutex; // mutable关键字允许在const成员函数中修改这个互斥锁
};
} // namespace ai_chat_sdk

  • DataManager.cpp

#include "../include/DataManager.h"
#include "../include/util/mylog.h"

namespace ai_chat_sdk{
// ***************** DataManager类中专用函数(内部使用) *****************
// 初始化数据库(创建会话表 和 消息表,并建立两表之间的关联关系,如果表已存在则不创建)
bool DataManager::initDatabase()
{
std::lock_guard<std::mutex> lock(_mutex);

// 创建sessions表
const std::string createSessionsTable =
"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"
");";
char *errmsg = nullptr;
if(SQLITE_OK != sqlite3_exec(_db, createSessionsTable.c_str(), nullptr, nullptr, &errmsg))
{
ERR("创建sessions表失败, errmsg: {}", errmsg);
sqlite3_free(errmsg); // 必须⽤sqlite3_free释放错误信息的指针
errmsg = nullptr;
return false;
}
INFO("创建sessions表成功");

// 创建messages表
const std::string createMessagesTable =
"CREATE TABLE IF NOT EXISTS messages ("
"message_id TEXT PRIMARY KEY, "
"session_id TEXT NOT NULL, "
"role TEXT NOT NULL, "
"content TEXT NOT NULL, "
"create_time INTEGER NOT NULL,"
"FOREIGN KEY (session_id) REFERENCES sessions (session_id) ON DELETE CASCADE"
");";
// FOREIGN KEY (session_id) REFERENCES sessions (session_id) ON DELETE CASCADE
// 关联关系:sessions表中的session_id字段 是 messages表中的session_id字段的外键
// ON DELETE CASCADE : 表示当会话被删除时,该会话下管理的历史消息也要同步删除

errmsg = nullptr;
if(SQLITE_OK != sqlite3_exec(_db, createMessagesTable.c_str(), nullptr, nullptr, &errmsg))
{
ERR("创建messages表失败, errmsg: {}", errmsg);
sqlite3_free(errmsg); // 必须⽤sqlite3_free释放错误信息的指针
errmsg = nullptr;
return false;
}
INFO("创建messages表成功");

// 创建索引以加速查询(为messages表添加session_id索引)
const std::string createMessageIndex =
"CREATE INDEX IF NOT EXISTS idx_messages_session_id ON messages(session_id);";

if(SQLITE_OK != sqlite3_exec(_db, createMessageIndex.c_str(), nullptr, nullptr, nullptr))
{
ERR("创建索引失败, errmsg: {}", errmsg);
}

// 数据库初始化成功
INFO("Database initialized successfully");
return true;
}

// *********************** 构造 和 析构函数 **********************
DataManager::DataManager(const std::string& dbName)
: _db(nullptr),
_db_path(dbName)
{
// 创建或打开 数据库连接
if(SQLITE_OK != sqlite3_open(_db_path.c_str(), &_db))
{
ERR("打开数据库失败, db_name: {}", sqlite3_errmsg(_db));
return;
}
INFO("数据库连接成功, db_name: {}", _db_path);

// 初始化数据库(创建表)
if(!initDatabase())
{
sqlite3_close(_db); // 关闭数据库连接
ERR("初始化数据库失败, db_name: {}", _db_path);
return;
}
INFO("数据库初始化成功, db_name: {}", _db_path);
}

DataManager::~DataManager()
{
if(_db)
{
sqlite3_close(_db); // 关闭数据库连接
_db = nullptr;
}
INFO("数据库连接已关闭, db_name: {}", _db_path);
}

// ***************** Session相关操作 *****************
// 插入新会话
bool DataManager::insertSession(const Session& session)
{
std::lock_guard<std::mutex> lock(_mutex);
const std::string sql ="INSERT INTO sessions (session_id, model_name, create_time, update_time) VALUES (?, ?, ?, ?);";

// 准备SQL语句
sqlite3_stmt *stmt = nullptr;
if(SQLITE_OK != sqlite3_prepare_v2(_db, sql.c_str(), 1, &stmt, nullptr))
{
ERR("准备SQL语句失败, errmsg: {}", sqlite3_errmsg(_db));
return false;
}

// 绑定参数
sqlite3_bind_text(stmt, 1, session._id.c_str(), 1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, session._model_name.c_str(), 1, SQLITE_TRANSIENT);
sqlite3_bind_int64(stmt, 3, static_cast<int64_t>(session._created_at));
sqlite3_bind_int64(stmt, 4, static_cast<int64_t>(session._updated_at));

if(SQLITE_DONE != sqlite3_step(stmt))
{
ERR("执行SQL语句 (插入会话) 失败, errmsg: {}", sqlite3_errmsg(_db));
sqlite3_finalize(stmt);
return false;
}

sqlite3_finalize(stmt); // 执⾏完成后, 释放stmt资源
INFO("Inserted session: {}", session._id);
return true;
}

// 获取指定会话信息 (不包含会话中的消息列表, 需要历史消息可以通过会话id单独获取)
std::shared_ptr<Session> DataManager::getSession(const std::string& sessionId)const
{
std::lock_guard<std::mutex> lock(_mutex);

// 准备SQL语句
const std::string sql = "SELECT model_name, create_time, update_time FROM sessions WHERE session_id = ?;";

// 准备SQL语句
sqlite3_stmt *stmt = nullptr;
if(SQLITE_OK != sqlite3_prepare_v2(_db, sql.c_str(), 1, &stmt, nullptr))
{
ERR("准备SQL语句失败, errmsg: {}", sqlite3_errmsg(_db));
return nullptr;
}

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

// 执行查询语句
if(SQLITE_ROW != sqlite3_step(stmt))
{
ERR("执行SQL语句 (查询会话) 失败, errmsg: {}", sqlite3_errmsg(_db));
sqlite3_finalize(stmt);
return nullptr;
}

// 从结果集中获取会话信息
std::shared_ptr<Session> session = std::make_shared<Session>();
session->_id = sessionId;
session->_model_name = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
session->_created_at = static_cast<std::time_t>(sqlite3_column_int64(stmt, 1));
session->_updated_at = static_cast<std::time_t>(sqlite3_column_int64(stmt, 2));

// 释放stmt资源
sqlite3_finalize(stmt);
return session;
}

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

const std::string sql = "UPDATE sessions SET update_time = ? WHERE session_id = ?;";

// 准备SQL语句
sqlite3_stmt* stmt = nullptr;
if(SQLITE_OK != sqlite3_prepare_v2(_db, sql.c_str(), 1, &stmt, nullptr))
{
ERR("准备SQL语句失败, errmsg: {}", sqlite3_errmsg(_db));
return;
}

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

// 执行更新语句
if (SQLITE_DONE != sqlite3_step(stmt)) {
ERR("Failed to update session timestamp: {}", sqlite3_errmsg(_db));
return;
}

sqlite3_finalize(stmt);
}

// 删除指定会话。注意:会话删除后,该会话下管理的历史消息会被自动删除(外键约束)
bool DataManager::deleteSession(const std::string& sessionId)
{
std::lock_guard<std::mutex> lock(_mutex);

const std::string sql = "DELETE FROM sessions WHERE session_id = ?;";

// 准备SQL语句
sqlite3_stmt* stmt = nullptr;
if(SQLITE_OK != sqlite3_prepare_v2(_db, sql.c_str(), 1, &stmt, nullptr))
{
ERR("准备SQL语句失败, errmsg: {}", sqlite3_errmsg(_db));
return false;
}

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

// 执行删除语句
if(SQLITE_DONE != sqlite3_step(stmt))
{
ERR("执行SQL语句 (删除会话) 失败, errmsg: {}", sqlite3_errmsg(_db));
sqlite3_finalize(stmt);
return false;
}
INFO("Deleted session: {}", sessionId);

// 释放stmt资源
sqlite3_finalize(stmt);
return true;
}

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

std::vector<std::string> session_ids;

const std::string sql = "SELECT session_id FROM sessions;";
// 准备SQL语句
sqlite3_stmt *stmt = nullptr;
if(SQLITE_OK != sqlite3_prepare_v2(_db, sql.c_str(), 1, &stmt, nullptr))
{
ERR("准备SQL语句失败, errmsg: {}", sqlite3_errmsg(_db));
return session_ids;
}

// 执行查询语句
while(SQLITE_ROW == sqlite3_step(stmt))
{
session_ids.push_back(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0)));
}

// 释放stmt资源
sqlite3_finalize(stmt);
return session_ids;
}

// 获取所有会话信息 (按照更新时间降序排序)
// 不包含每个会话中的消息列表, 需要历史消息可以通过会话id单独获取
std::vector<std::shared_ptr<Session>> DataManager::getAllSessions()const
{
std::lock_guard<std::mutex> lock(_mutex);

std::vector<std::shared_ptr<Session>> sessions;

const std::string sql =
"SELECT session_id, model_name, create_time, update_time FROM sessions ORDER BY update_time DESC;";
// 按更新时间降序排序,确保会话按时间顺序返回

// 准备SQL语句
sqlite3_stmt *stmt = nullptr;
if(SQLITE_OK != sqlite3_prepare_v2(_db, sql.c_str(), 1, &stmt, nullptr))
{
ERR("准备SQL语句失败, errmsg: {}", sqlite3_errmsg(_db));
return sessions;
}

// 执行查询语句
while(SQLITE_ROW == sqlite3_step(stmt))
{
std::shared_ptr<Session> session = std::make_shared<Session>();
session->_id = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
session->_model_name = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1));
session->_created_at = static_cast<std::time_t>(sqlite3_column_int64(stmt, 2));
session->_updated_at = static_cast<std::time_t>(sqlite3_column_int64(stmt, 3));
sessions.push_back(session);
}

// 释放stmt资源
sqlite3_finalize(stmt);
return sessions;
}

// 删除所有会话,注意:会话删除后,该会话下管理的历史消息会被自动删除(外键约束)
bool DataManager::clearAllSessions()
{
std::lock_guard<std::mutex> lock(_mutex);

// 清空所有会话
if(SQLITE_OK != sqlite3_exec(_db, "DELETE FROM sessions;", nullptr, nullptr, nullptr))
{
ERR("执行SQL语句 (清空所有会话) 失败, errmsg: {}", sqlite3_errmsg(_db));
return false;
}
INFO("Cleared all sessions.");

return true;
}

// 获取所有会话个数
int DataManager::getSessionCount()const
{
std::lock_guard<std::mutex> lock(_mutex);

int count = 0;
const std::string sql = "SELECT COUNT(*) FROM sessions;";

// 准备SQL语句
sqlite3_stmt *stmt = nullptr;
if(SQLITE_OK != sqlite3_prepare_v2(_db, sql.c_str(), 1, &stmt, nullptr))
{
ERR("准备SQL语句失败, errmsg: {}", sqlite3_errmsg(_db));
return 1;
}

// 执行查询语句
if(SQLITE_ROW == sqlite3_step(stmt))
{
count = sqlite3_column_int(stmt, 0);
}

// 释放stmt资源
sqlite3_finalize(stmt);
return count;
}

// ***************** Message相关操作 *****************
// 向指定会话添加新消息
bool DataManager::insertMessage(const std::string& sessionId, const Message& message)
{
std::lock_guard<std::mutex> lock(_mutex);

const std::string sql =
"INSERT INTO messages (message_id, session_id, role, content, create_time) VALUES (?, ?, ?, ?, ?);";

// 准备SQL语句
sqlite3_stmt *stmt = nullptr;
if(SQLITE_OK != sqlite3_prepare_v2(_db, sql.c_str(), 1, &stmt, nullptr))
{
ERR("准备SQL语句失败, errmsg: {}", sqlite3_errmsg(_db));
return false;
}

// 绑定参数
sqlite3_bind_text(stmt, 1, message._id.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._create_at));

// 执行插入语句
if(SQLITE_DONE != sqlite3_step(stmt))
{
ERR("执行SQL语句 (插入新消息) 失败, errmsg: {}", sqlite3_errmsg(_db));
sqlite3_finalize(stmt);
return false;
}
INFO("Inserted new message for session: {}, message: {}", sessionId, message._id);

// 释放stmt资源
sqlite3_finalize(stmt);
return true;
}

// 获取指定会话的历史消息 (按创建时间升序排序)
std::vector<Message> DataManager::getMessages_BySessionId(const std::string& sessionId)const
{
std::lock_guard<std::mutex> lock(_mutex);

std::vector<Message> message_lists;

const std::string sql =
"SELECT message_id, role, content, create_time FROM messages WHERE session_id = ? ORDER BY create_time ASC;";
// 按创建时间升序排序,确保消息按时间顺序返回

// 准备SQL语句
sqlite3_stmt *stmt = nullptr;
if(SQLITE_OK != sqlite3_prepare_v2(_db, sql.c_str(), 1, &stmt, nullptr))
{
ERR("准备SQL语句失败, errmsg: {}", sqlite3_errmsg(_db));
return message_lists;
}

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

// 执行查询语句
while(SQLITE_ROW == sqlite3_step(stmt))
{
Message message;
message._id = 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._create_at = static_cast<std::time_t>(sqlite3_column_int64(stmt, 3));
message_lists.push_back(message);
}

// 释放stmt资源
sqlite3_finalize(stmt);
return message_lists;
}

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

const std::string sql = "DELETE FROM messages WHERE session_id = ?;";

// 准备SQL语句
sqlite3_stmt *stmt = nullptr;
if(SQLITE_OK != sqlite3_prepare_v2(_db, sql.c_str(), 1, &stmt, nullptr))
{
ERR("准备SQL语句失败, errmsg: {}", sqlite3_errmsg(_db));
return false;
}

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

// 执行删除语句
if(SQLITE_DONE != sqlite3_step(stmt))
{
ERR("执行SQL语句 (删除指定会话的历史消息) 失败, errmsg: {}", sqlite3_errmsg(_db));
sqlite3_finalize(stmt);
return false;
}
INFO("Deleted all messages for session: {}", sessionId);

sqlite3_finalize(stmt);
return true;
}
}

五、在会话管理模块中 加入数据管理模块

1. 融合了数据管理模块的 会话管理模块的代码实现

  • SessionManager.h

#pragma once
#include "common.h"
#include "DataManager.h"
#include <atomic>
#include <mutex>
#include <unordered_map>
#include <memory>

namespace ai_chat_sdk{
class SessionManager{
private:
// 生成会话ID,返回会话ID
std::string generateSessionID();

// 生成消息ID,返回消息ID
std::string generateMessageID(size_t messageCounter);

public:
// 构造函数:
// 1. 打开数据库连接, 将数据库中的session表数据读取到会话列表中
// 2. 从数据库中读取每个会话的所有消息, 并将消息导入到会话列表中
// 3. 初始化会话总数
SessionManager(const std::string& dbName = "ai_chat.db");

// 创建新会话,返回会话ID (将会话 写入到会话列表中 以及 数据库中的session表中)
std::string createSession(const std::string& model_name);

// 添加消息到会话. session_id : 会话id, message : 消息 返回是否添加成功
bool addMessage(const std::string& session_id, const Message& message);

// 获取具体会话,通过会话id获取. session_id : 会话id 返回会话指针
std::shared_ptr<Session> getSession(const std::string& session_id);

// 获取会话历史,即获取具体某次会话的所有消息. session_id : 会话id 返回消息列表
std::vector<Message> getSessionHistory(const std::string& session_id);

// 获取会话列表 返回会话列表
std::vector<std::string> getSessionList();

// 获取会话总数 返回会话总数
size_t getSessionCount();

// 更新会话时间戳. session_id : 会话id
void updateSessionTimestamp(const std::string& session_id);

// 删除会话. session_id : 会话id 返回是否删除成功
bool deleteSession(const std::string& session_id);

// 清空所有会话
void clearAllSessions();

private:
// 管理所有会话,key为session_id, value为会话指针
std::unordered_map<std::string, std::shared_ptr<Session>> _sessions;
std::mutex _mutex;
// 原子类型: 进行原子操作,确保线程安全
std::atomic<int64_t> _session_counter = 0; // 记录会话总数
// 数据管理器
DataManager _data_manager;
};
} // namespace ai_chat_sdk

  • SessionManager.cpp

#include "../include/SessionManager.h"
#include "../include/DataManager.h"
#include "../include/util/mylog.h"
#include <ctime>
#include <iomanip>
#include <sstream>

namespace ai_chat_sdk{
// 生成会话ID,返回会话ID
std::string SessionManager::generateSessionID()
{
// 会话计数自增(原子操作)
_session_counter.fetch_add(1);
std::time_t time = std::time(nullptr);

// 生成会话id
std::ostringstream os;
// 例如:session_1234567890_00000001
os<<"session_"<<time<<"_"<<std::setw(8)<<std::setfill('0')<<_session_counter;
return os.str();
}

// 生成消息ID,返回消息ID
std::string SessionManager::generateMessageID(size_t messageCounter)
{
messageCounter++;
std::time_t time = std::time(nullptr);

// 生成消息id
std::ostringstream os;
// 例如:message_1234567890_00000001
os<<"message_"<<time<<"_"<<std::setw(8)<<std::setfill('0')<<messageCounter;
return os.str();
}

// 构造函数:
// 1. 打开数据库连接, 将数据库中的session表数据读取到会话列表中
// 2. 从数据库中读取每个会话的所有消息, 并将消息导入到会话列表中
// 3. 初始化会话总数
SessionManager::SessionManager(const std::string& dbName)
: _data_manager(dbName)
{
// 从数据库中读取所有会话, 并将message数据读取到会话列表中
auto session_lists = _data_manager.getAllSessions();
for(auto& session : session_lists)
{
_sessions[session->_id] = session;
// 从数据库中读取指定会话的所有消息, 并将消息数据读取到会话列表中
_sessions[session->_id]->_messages = _data_manager.getMessages_BySessionId(session->_id);
}
// 初始化会话总数
_session_counter = session_lists.size();
INFO("SessionManager initialized,session count: {}", _session_counter);
}

// 创建新会话,返回会话ID (将会话 写入到会话列表中 以及 数据库中的session表中)
std::string SessionManager::createSession(const std::string& model_name)
{
std::lock_guard<std::mutex> lock(_mutex);

// 生成会话id (generateSessionID函数会将 会话计数自增1)
std::string session_id = generateSessionID();

// 创建会话, 并设置会话id
auto session = std::make_shared<Session>(model_name);
session->_id = session_id;

// 将会话 写入到会话列表中
_sessions[session_id] = session;

// 插入会话到数据库中的session表中
_data_manager.insertSession(*session);
return session_id;
}

// 添加消息到会话. session_id : 会话id, message : 消息 返回是否添加成功
bool SessionManager::addMessage(const std::string& session_id, const Message& message)
{
std::lock_guard<std::mutex> lock(_mutex);

// 检查会话是否存在
auto it = _sessions.find(session_id);
if(it == _sessions.end())
{
ERR("会话不存在, session_id: {}", session_id);
// 会话不存在, 直接返回false
return false;
}

// 生成消息id
std::string message_id = generateMessageID(it->second->_messages.size());

// 创建消息, 并设置消息id
Message msg(message._role, message._content);
msg._id = message_id;

// 将消息 写入到会话列表中
_sessions[session_id]->_messages.push_back(msg);

// 更新会话时间戳
_sessions[session_id]->_updated_at = msg._create_at;

// 插入消息到数据库中的message表中, 并更新会话时间戳
_data_manager.insertMessage(session_id, msg);
_data_manager.updateSessionTimestamp(session_id, _sessions[session_id]->_updated_at);

return true;
}

// 获取具体会话,通过会话id获取. session_id : 会话id 返回会话指针
std::shared_ptr<Session> SessionManager::getSession(const std::string& session_id)
{
std::lock_guard<std::mutex> lock(_mutex);

// 检查会话是否存在
auto it = _sessions.find(session_id);
if(it == _sessions.end())
{
ERR("会话不存在, session_id: {}", session_id);
// 会话不存在, 直接返回nullptr
return nullptr;
}

return it->second;
}

// 获取会话历史,即获取具体某次会话的所有消息. session_id : 会话id 返回消息列表
std::vector<Message> SessionManager::getSessionHistory(const std::string& session_id)
{
std::lock_guard<std::mutex> lock(_mutex);

// 检查会话是否存在
auto it = _sessions.find(session_id);
if(it == _sessions.end())
{
ERR("会话不存在, session_id: {}", session_id);
// 会话不存在, 直接返回空的消息列表
return std::vector<Message>();
}

return it->second->_messages;
}

// 获取会话列表 (会话id列表),返回会话列表 (按更新时间排序,最新更新的在最前面)
std::vector<std::string> SessionManager::getSessionList()
{
std::lock_guard<std::mutex> lock(_mutex);

// 从数据库中读取所有会话 (按照更新时间降序排序)
auto session_lists = _data_manager.getAllSessions();

std::vector<std::string> session_ids;
for(auto& session : session_lists)
{
session_ids.push_back(session->_id);
}

return session_ids;
}

// 获取会话总数 返回会话总数
size_t SessionManager::getSessionCount()
{
std::lock_guard<std::mutex> lock(_mutex);
return _session_counter;
}

// 更新会话时间戳. session_id : 会话id
void SessionManager::updateSessionTimestamp(const std::string& session_id)
{
std::lock_guard<std::mutex> lock(_mutex);

// 检查会话是否存在
auto it = _sessions.find(session_id);
if(it == _sessions.end())
{
ERR("会话不存在, session_id: {}", session_id);
// 会话不存在, 直接返回
return;
}

// 更新会话时间戳
it->second->_updated_at = std::time(nullptr);

// 更新数据库中的会话时间戳
_data_manager.updateSessionTimestamp(session_id, it->second->_updated_at);
}

// 删除会话. session_id : 会话id 返回是否删除成功
bool SessionManager::deleteSession(const std::string& session_id)
{
std::lock_guard<std::mutex> lock(_mutex);

// 检查会话是否存在
auto it = _sessions.find(session_id);
if(it == _sessions.end())
{
ERR("会话不存在, session_id: {}", session_id);
// 会话不存在, 直接返回false
return false;
}

// 删除会话
_sessions.erase(it);

// 会话计数自减1
_session_counter.fetch_sub(1);

// 从数据库中删除会话
_data_manager.deleteSession(session_id);

return true;
}

// 清空所有会话
void SessionManager::clearAllSessions()
{
std::lock_guard<std::mutex> lock(_mutex);

// 清空所有会话
_sessions.clear();

// 会话计数重置为0
_session_counter.store(0);

// 从数据库中删除所有会话
_data_manager.clearAllSessions();
}
} // namespace ai_chat_sdk

2. 复盘:将数据管理模块 融合进 会话管理模块的思路

对比两份代码后,整合方式非常清晰——将 DataManager 作为 SessionManager 的组合成员,在每次内存操作后同步写库,并在构造函数中完成数据恢复。以下是逐点的融合细节:


融合方式总览:内存缓存 + 数据库双写

(1) 类结构层面

头文件 SessionManager.h 新增成员:

  • #include "DataManager.h" 引入数据管理模块
  • DataManager _data_manager; 作为 SessionManager 的成员变量,通过组合方式融合
  • 构造函数签名改为 SessionManager(const std::string& dbName = "ai_chat.db"),将 dbName(数据库名称) 传递给 DataManager

(2) 每个方法的具体融合点

方法纯内存版本融合后版本
构造函数 无(或默认构造) : _data_manager(dbName) 初始化 DataManager,然后调用 _data_manager.getAllSessions() 从数据库加载所有会话到 _sessions 缓存,再遍历每个会话调用 _data_manager.getMessages_BySessionId() 加载所有历史消息,最后用 session_lists.size() 初始化 _session_counter
createSession 仅 _sessions[session_id] = session + _data_manager.insertSession(*session)
addMessage 仅写缓存、更新内存时间戳 + _data_manager.insertMessage(session_id, msg) + _data_manager.updateSessionTimestamp(…)
getSessionList 手动遍历 _sessions 用 std::sort 按时间降序排列 直接 _data_manager.getAllSessions(),由 SQL 的 ORDER BY update_time DESC 完成排序
updateSessionTimestamp 仅更新 it->second->_updated_at + _data_manager.updateSessionTimestamp(session_id, …)
deleteSession 仅 _sessions.erase(it) 和计数器自减 + _data_manager.deleteSession(session_id)
clearAllSessions 仅 _sessions.clear() 和计数器归零 + _data_manager.clearAllSessions()

(3) 融合的设计模式

核心模式:写时双写 + 启动恢复

创建会话 ──→ _sessions[sid] = session (内存缓存)
──→ _data_manager.insertSession() (SQLite 持久化)

启动时 ──→ _data_manager.getAllSessions() (从 SQLite 全部加载到内存)
──→ _data_manager.getMessages_BySessionId() (加载每条会话的历史消息)
──→ _session_counter = 恢复的总数

  • 读操作(getSession、getSessionHistory)直接读内存 _sessions 缓存,不查库,性能最优
  • 写操作(增删改)内存和数据库双写,保证数据持久化
  • 启动时从 SQLite 全量恢复到内存,服务重启后会话和消息不丢失

getSessionList 是唯一一个读操作也从 DataManager 走的方法,因为直接利用 SQL 的 ORDER BY 避免了在内存中手动排序的开销。

(4) 一句话总结

DataManager 作为 SessionManager 的组合成员,每个写操作都在完成内存缓存写入后立即同步写入 SQLite;构造函数则从 SQLite 全量回填内存缓存,实现了会话数据的持久化与恢复。

六、ChatSDK封装(将模型管理 和 Session管理结合起来)

1. 什么是SDK(比较API 和 SDK)

假设你的电脑在运行时风扇声音特别大,需要清理灰尘,或者玩游戏时比较卡,考虑添加一个内存条。如果要进行上述操作,就需要借助一些专业工具,比如特定螺丝刀、撬棒、镊子、吸盘等,将电脑拆开才能进行后续操作。这些工具一般会放在一个箱子中,该箱子称为工具箱,里面存放各种各样以供使用的工具。

请添加图片描述

SDK就是程序员的工具箱,Software Develop Kit的缩写,一套帮开发者方便开发特定功能的工具包。

比如:

  • 微信支付SDK:你只要接入SDK,就能在程序中支持微信支付,不用自己研究支付安全、加密、银行对接等。
  • 相机SDK:使用厂商提供的SDK,就能让APP调用收集摄像头拍照、录像

SDK中通常包含:

(1) 库文件:被人已经写好的功能,需要实现什么功能直接调用即可。 (2) API接口:告诉你该怎么和平台打交道。 (3) 文档:说明书,告诉你怎么用 (4) 示例代码:相当于菜谱,照这些就能跑通 (5) 调试工具:出错时能帮你查问题

也就是说:SDK就是厂商打包好的一套 “现成工具+说明书”,让开发者可以更快、更安全、更省心地在自己的软件中实现特定功能。

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

你想做一道菜(开发应用):

API就像是菜谱,告诉你需要什么食材和步骤。但需要自己准备锅、灶台、刀(相当于你自己要写底层代码)。 SDK则是⼀个食材配送盒+菜谱,里面不仅有意面、酱料(库文件)、菜谱(API文档),甚至可能附赠一把锅铲和一个小锅(开发工具 和 示例代码)

2. ChatSDK代码实现

将模型管理和Session管理结合起来,为用户提供统一的使用接口。

  • ChatSDK.h

#pragma once
#include "common.h"
#include "LLMManager.h"
#include "SessionManager.h"
#include <memory>
#include <string>
#include <vector>
#include <unordered_map>

namespace ai_chat_sdk
{
class ChatSDK
{
private:
// 初始化模型提供者-通过api调⽤
bool apiProviderInit(const std::string& modelName, const std::shared_ptr<ApiConfig>& apiConfig);
// 初始化模型提供者-通过ollama
bool ollamaProviderInit(const std::string& modelName, const std::shared_ptr<OllamaConfig>& ollamaConfig);

public:
// 构造函数
// 1. 初始化SessionManager对象
ChatSDK(const std::string& dbName = "ai_chat.db");

// 注册并初始化所有模型提供者
bool registerAndInit_AllProviders(const std::vector<std::shared_ptr<Config>>& configs);

// 创建session
std::string createSession(const std::string& modelName);
// 获取会话
std::shared_ptr<Session> getSession(const std::string& sessionId);
// 获取所有会话列表
std::vector<std::string> getSessionList();
// 删除会话
bool deleteSession(const std::string& sessionId);

// 获取指定会话的所有历史消息
std::vector<Message> getSessionHistory(const std::string& sessionId);

// 获取可用模型列表
std::vector<ModelInfo> getAvailableModels();
// 发送消息 – 全量返回
std::string sendMessage(const std::string sessionId, const std::string& message);
// 发送消息 – 流式响应
std::string sendMessageStream(const std::string sessionId, const std::string& message,
std::function<void(const std::string&, bool)> callback);

private:
bool _initialized = false;
// 第一个参数:模型名称; 第二个参数:模型配置
std::unordered_map<std::string, std::shared_ptr<Config>> _configs;
LLMManager _LLM_Manager;
SessionManager _session_Manager;
};
} // namespace ai_chat_sdk

  • ChatSDK.cpp

#include "../include/ChatSDK.h"
#include "../include/LLMManager.h"
#include "../include/SessionManager.h"
#include "../include/DeepSeekProvider.h"
#include "../include/GeminiProvider.h"
#include "../include/ChatgptProvider.h"
#include "../include/OllamaProvider.h"
#include "../include/util/mylog.h"

namespace ai_chat_sdk
{
// 初始化模型提供者-通过api调⽤
bool ChatSDK::apiProviderInit(const std::string& modelName, const std::shared_ptr<ApiConfig>& apiConfig)
{
std::map<std::string, std::string> model_config;
model_config["api_key"] = apiConfig->_api_key;
model_config["base_url"] = apiConfig->_api_url;

return _LLM_Manager.init_Model(modelName, model_config);
}

// 初始化模型提供者-通过ollama
bool ChatSDK::ollamaProviderInit(const std::string& modelName, const std::shared_ptr<OllamaConfig>& ollamaConfig)
{
std::map<std::string, std::string> model_config;
model_config["model_name"] = ollamaConfig->_model_name;
model_config["model_desc"] = ollamaConfig->_model_desc;
model_config["base_url"] = ollamaConfig->_ollama_url;

return _LLM_Manager.init_Model(modelName, model_config);
}

// 构造函数
// 1. 初始化SessionManager对象
ChatSDK::ChatSDK(const std::string& dbName)
: _session_Manager(dbName)
{}

// 注册并初始化 所有模型提供者
bool ChatSDK::registerAndInit_AllProviders(const std::vector<std::shared_ptr<Config>>& configs)
{
for(const auto& config : configs)
{
// dynamic_pointer_cast 用于智能指针的动态类型安全转换(多态对象的向下转型,返回子类指针,失败返回nullptr)
// dynamic_pointer_cast<ApiConfig> 将config指针向下转型为ApiConfig指针,成功返回ApiConfig指针,失败返回nullptr
auto apiConfig = std::dynamic_pointer_cast<ApiConfig>(config);
// dynamic_pointer_cast<OllamaConfig> 将config指针向下转型为OllamaConfig指针,成功返回OllamaConfig指针,失败返回nullptr
auto ollamaConfig = std::dynamic_pointer_cast<OllamaConfig>(config);

if(apiConfig)
{
if(apiConfig->_model_name == "deepseek-chat")
{
_LLM_Manager.register_Provider(apiConfig->_model_name, std::make_unique<DeepSeekProvider>());
if(apiProviderInit(apiConfig->_model_name, apiConfig))
{
INFO("注册并初始化DeepseekProvider成功");
if(_configs.find(apiConfig->_model_name) == _configs.end())
_configs[apiConfig->_model_name] = apiConfig;
}
else
{
ERR("注册并初始化DeepseekProvider失败");
return false;
}
}
else if(apiConfig->_model_name == "gemini-2.0-flash")
{
_LLM_Manager.register_Provider(apiConfig->_model_name, std::make_unique<GeminiProvider>());
if(apiProviderInit(apiConfig->_model_name, apiConfig))
{
INFO("注册并初始化GeminiProvider成功");
if(_configs.find(apiConfig->_model_name) == _configs.end())
_configs[apiConfig->_model_name] = apiConfig;
}
else
{
ERR("注册并初始化GeminiProvider失败");
return false;
}
}
else if(apiConfig->_model_name == "gpt-4o-mini")
{
_LLM_Manager.register_Provider(apiConfig->_model_name, std::make_unique<ChatgptProvider>());
if(apiProviderInit(apiConfig->_model_name, apiConfig))
{
INFO("注册并初始化ChatgptProvider成功");
if(_configs.find(apiConfig->_model_name) == _configs.end())
_configs[apiConfig->_model_name] = apiConfig;
}
else
{
ERR("注册并初始化ChatgptProvider失败");
return false;
}
}
else
{
ERR("未知的模型配置类型");
return false;
}
}
else if(ollamaConfig)
{
if(ollamaConfig->_model_name == "deepseek-r1:1.5b")
{
_LLM_Manager.register_Provider(ollamaConfig->_model_name, std::make_unique<OllamaProvider>());
if(ollamaProviderInit(ollamaConfig->_model_name, ollamaConfig))
{
INFO("注册并初始化OllamaProvider成功");
if(_configs.find(ollamaConfig->_model_name) == _configs.end())
_configs[ollamaConfig->_model_name] = ollamaConfig;
}
else
{
ERR("注册并初始化OllamaProvider失败");
return false;
}
}
else
{
ERR("未知的模型配置类型");
return false;
}
}
else
{
ERR("未知的模型配置类型");
return false;
}
}

INFO("注册及初始化所有模型提供者成功");
_initialized = true;
return true;
}

// 创建session (返回会话id)
std::string ChatSDK::createSession(const std::string& modelName)
{
if(!_initialized)
{
ERR("SDK未初始化, 无法创建会话");
return "";
}

// 检查模型是否注册并初始化成功
if(!_LLM_Manager.is_Model_Available(modelName))
{
ERR("模型 {} 未注册或未初始化", modelName);
return "";
}

std::string sessionId = _session_Manager.createSession(modelName);
INFO("create session {} success with model {}!", sessionId, modelName);
return sessionId;
}

// 获取会话
std::shared_ptr<Session> ChatSDK::getSession(const std::string& sessionId)
{
return _session_Manager.getSession(sessionId);
}

// 获取所有会话列表
std::vector<std::string> ChatSDK::getSessionList()
{
return _session_Manager.getSessionList();
}

// 删除会话
bool ChatSDK::deleteSession(const std::string& sessionId)
{
bool result = _session_Manager.deleteSession(sessionId);
if(result){
INFO("delete session {} success!!!", sessionId);
}

return result;
}

// 获取指定会话的所有历史消息
std::vector<Message> ChatSDK::getSessionHistory(const std::string& sessionId)
{
return _session_Manager.getSessionHistory(sessionId);
}

// 获取可用模型列表
std::vector<ModelInfo> ChatSDK::getAvailableModels()
{
return _LLM_Manager.get_Available_Models();
}

// 发送消息 – 全量返回
std::string ChatSDK::sendMessage(const std::string sessionId, const std::string& message)
{
if(!_initialized){
ERR("ChatSDK is not initialized!!!");
return "";
}

// 获取会话模型名称
if(!_session_Manager.getSession(sessionId))
{
ERR("会话 {} 不存在", sessionId);
return "";
}
std::string model_name = _session_Manager.getSession(sessionId)->_model_name;

// 构建消息列表
std::vector<Message> message_lists;
// 从数据库中获取会话历史消息
auto session_history = _session_Manager.getSessionHistory(sessionId);
for(auto& msg : session_history)
{
message_lists.push_back(msg);
}
// 添加用户消息
message_lists.push_back(Message("user", message));
// 添加用户消息到会话历史消息
_session_Manager.addMessage(sessionId, message_lists.back());

// 构建请求参数
std::map<std::string,std::string> request_param;
std::shared_ptr<Config> config = _configs[model_name];
request_param["temperature"] = std::to_string(config->_temperature);
request_param["max_tokens"] = std::to_string(config->_max_tokens);

// 发送消息
std::string response = _LLM_Manager.sendMessage(model_name, message_lists, request_param);

// 添加模型回复到会话历史消息
Message model_response("assistant", response);
_session_Manager.addMessage(sessionId, model_response);
return response;
}

// 发送消息 – 流式响应
std::string ChatSDK::sendMessageStream(const std::string sessionId, const std::string& message,
std::function<void(const std::string&, bool)> callback)
{
if(!_initialized)
{
ERR("ChatSDK is not initialized!!!");
return "";
}

// 获取会话模型名称
if(!_session_Manager.getSession(sessionId))
{
ERR("会话 {} 不存在", sessionId);
return "";
}
std::string model_name = _session_Manager.getSession(sessionId)->_model_name;

// 构建消息列表
std::vector<Message> message_lists;
// 从数据库中获取会话历史消息
auto session_history = _session_Manager.getSessionHistory(sessionId);
for(auto& msg : session_history)
{
message_lists.push_back(msg);
}
// 添加用户消息
message_lists.push_back(Message("user", message));
// 添加用户消息到会话历史消息
_session_Manager.addMessage(sessionId, message_lists.back());

// 构建请求参数
std::map<std::string,std::string> request_param;
std::shared_ptr<Config> config = _configs[model_name];
request_param["temperature"] = std::to_string(config->_temperature);
request_param["max_tokens"] = std::to_string(config->_max_tokens);

// 发送消息
std::string response = _LLM_Manager.sendMessageStream(model_name, message_lists, request_param, callback);

// 添加模型回复到会话历史消息
Message model_response("assistant", response);
_session_Manager.addMessage(sessionId, model_response);
return response;
}
} // namespace ai_chat_sdk

3. ChatSDK的 实现思路

ChatSDK 是典型的 门面模式(Facade) 实现,通过组合 LLMManager 和 SessionManager 两个子系统,对外暴露统一的高层 API。下面梳理融合思路。

  • 整体设计:组合 + 门面

class ChatSDK {
LLMManager _LLM_Manager; // 模型管理子系统
SessionManager _session_Manager; // 会话管理子系统
unordered_map<string, shared_ptr<Config>> _configs; // 模型配置缓存
bool _initialized = false;
};

三个核心成员各自承担不同职责,ChatSDK 负责编排它们的协作流程。


  • 融合链路详解

(1) 初始化阶段:模型注册与会话初始化

构造函数中初始化 _session_Manager,传入数据库名,触发 SessionManager 从 SQLite 恢复数据:

ChatSDK::ChatSDK(const string& dbName) : _session_Manager(dbName) {}

registerAndInit_AllProviders 通过 dynamic_pointer_cast 自动识别配置类型,逐个注册 Provider:

Config 列表
├── ApiConfig ──→ dynamic_cast 识别 ──→ register_Provider(name, XxxProvider)
├── OllamaConfig ──→ dynamic_cast 识别 ──→ register_Provider(name, OllamaProvider)
└── … ──→ init_Model(name, config_map)
──→ 缓存 config 到 _configs[name]

关键设计:dynamic_pointer_cast 实现运行时多态识别,使调用方只需传入统一的 Config 列表,不必关心具体是云端模型还是本地模型。

(2) 创建会话:模型有效性验证 + 会话创建

// 两步走
_LLM_Manager.is_Model_Available(modelName) // (1) 检查模型是否就绪
_session_Manager.createSession(modelName) // (2) 创建会话并持久化

(3) 发送消息(核心融合点)

这是两个子系统协作最紧密的地方,以 sendMessage 为例:

ChatSDK::sendMessage(sessionId, message)

┌───────────▼───────────┐
│ 1. 获取会话模型名 │
│ _session_Manager │
│ .getSession(sessionId) │
│ → 得到 model_name │
└───────────┬───────────┘

┌───────────▼───────────┐
│ 2. 构建消息上下文 │
│ _session_Manager │
│ .getSessionHistory() │ ← 历史消息
│ + new Message(user) │ ← 用户新消息
│ .addMessage() │ ← 持久化用户消息
└───────────┬───────────┘

┌───────────▼───────────┐
│ 3. 获取模型参数 │
│ _configs[model_name] │
│ → temperature/max_tokens│
└───────────┬───────────┘

┌───────────▼───────────┐
│ 4. 调用模型 │
│ _LLM_Manager │
│ .sendMessage(model, │
│ messages, params) │
│ → 得到 AI 回复 │
└───────────┬───────────┘

┌───────────▼───────────┐
│ 5. 持久化模型回复 │
│ _session_Manager │
│ .addMessage(assistant)│
└───────────────────────┘

返回响应

核心的数据流:会话历史 → 拼入用户消息 → 完整上下文喂给模型 → 模型回复 → 写入会话历史。SessionManager 负责消息的存取和持久化,LLMManager 负责模型路由和 API 调用,ChatSDK 串联两者。

(4) 查询方法:直接委派

getSession(id) → 委派 _session_Manager.getSession(id)
getSessionList() → 委派 _session_Manager.getSessionList()
deleteSession(id) → 委派 _session_Manager.deleteSession(id)
getSessionHistory(id) → 委派 _session_Manager.getSessionHistory(id)
getAvailableModels() → 委派 _LLM_Manager.get_Available_Models()


  • 总结

ChatSDK 通过组合持有 LLMManager(模型能力)和 SessionManager(会话能力),在发送消息的流程中将两者编排为 “从会话历史中拿上下文 → 交给模型推理 → 将推理结果写回会话历史” 的闭环,_configs 缓存作为参数桥梁,完成两个子系统的融合。

七、ChatSDK使用手册

– 概述

ChatSDK 是一个基于 C++17 的多模型接入 SDK,提供统一接口管理多个大语言模型(DeepSeek、ChatGPT、Gemini、Ollama 本地模型),支持会话管理、多轮对话上下文保留与数据持久化。

核心功能:

  • 多模型统一管理:通过 LLMManager 注册、初始化多个模型提供者,业务层无感切换
  • 会话生命周期管理:通过 SessionManager 创建、查询、删除会话,自动管理消息上下文
  • 数据持久化:基于 SQLite 自动保存会话和消息,服务重启后数据完整恢复
  • 流式与非流式调用:支持全量返回和 SSE 流式返回两种消息发送方式

– 项目结构

AI_mode-acess-SDK/
├── sdk/ # SDK 核心源码
│ ├── CMakeLists.txt # 构建配置(静态库 + 安装导出)
│ ├── include/ # 头文件
│ │ ├── chat_sdk.h # SDK 统一对外 API
│ │ ├── common.h # 基础数据结构(Message/Session/Config)
│ │ ├── ILLMProvider.h # 模型提供者抽象接口
│ │ ├── LLMManager.h # 模型管理器
│ │ ├── session_manager.h # 会话管理器
│ │ ├── dataManager.h # SQLite 数据持久化管理器
│ │ ├── DeepSeekProvider.h
│ │ ├── ChatGPTProvider.h
│ │ ├── GeminiProvider.h
│ │ ├── OllamaDeepSeekProvider.h
│ │ └── util/
│ │ └── my_logger.h # 日志工具
│ ├── src/ # 源文件
│ │ ├── chat_sdk.cpp
│ │ ├── LLMManager.cpp
│ │ ├── session_manager.cpp
│ │ ├── DataManager.cpp
│ │ ├── DeepSeekProvider.cpp
│ │ ├── ChatGPTProvider.cpp
│ │ ├── GeminiProvider.cpp
│ │ ├── OllamaDeepSeekProvider.cpp
│ │ └── util/
│ │ └── my_logger.cpp
│ └── build/ # 编译输出(libai_chat_sdk.a)


– 类关系图

在这里插入图片描述

– 获取 SDK

git clone https://gitee.com/bacht/ai_model-acess-sdk.git

SDK 源码位于仓库的 AI_mode-acess-SDK/sdk 目录下。


– 数据结构

使用 SDK 前需了解以下数据结构,均定义在 common.h 的 ai_chat_sdk 命名空间中。

(1) Message — 消息

struct Message {
std::string _id; // 消息ID(唯一标识)
std::string _role; // 角色:"user" 或 "assistant"
std::string _content; // 消息内容
std::time_t _create_at; // 消息生成时间戳

Message(const std::string& role = "", const std::string& content = "");
};

(2) Session — 会话

struct Session {
std::string _id; // 会话ID(唯一标识)
std::string _model_name; // 关联的模型名称
std::vector<Message> _messages; // 会话中的消息列表
std::time_t _created_at; // 会话创建时间
std::time_t _updated_at; // 会话更新时间

Session(const std::string& model_name = "");
};

(3) ModelInfo — 模型信息

struct ModelInfo {
std::string _name; // 模型名称(如 "deepseek-chat")
std::string _desc; // 模型描述
bool _available = false; // 模型是否已注册并初始化成功
};

(4) Config — 模型配置(基类)

struct Config {
std::string _model_name; // 模型名称
double _temperature; // 温度参数,默认 0.7
int _max_tokens; // 最大生成 token 数,默认 2048
virtual ~Config() = default;
};

  • ApiConfig — 云端模型配置

struct ApiConfig : public Config {
std::string _api_key; // API 密钥(默认从环境变量读取)
std::string _api_url; // API 基础 URL

ApiConfig(const std::string& model_name,
double temperature = 0.7,
int max_tokens = 2048,
const std::string& key = getenv("deepseek_apikey"),
const std::string& url = "");
};

环境变量默认值对照:

  • deepseek-chat → getenv("deepseek_apikey")
  • gpt-4o-mini → getenv("chatgpt_apikey")
  • gemini-2.0-flash → getenv("gemini_apikey")
  • OllamaConfig — 本地模型配置

struct OllamaConfig : public Config {
std::string _ollama_url; // Ollama 服务地址,默认 "http://localhost:11434"
std::string _model_desc; // 模型描述

OllamaConfig(const std::string& model_name,
double temperature = 0.7,
int max_tokens = 2048,
const std::string& url = "http://localhost:11434",
const std::string& desc = "");
};


– 快速入门

  • 引入头文件
  • #include "ChatSDK.h"
    using namespace ai_chat_sdk;

  • 创建 ChatSDK 实例
  • // 默认 SQLite 数据库名为 "ai_chat.db",也可自定义
    ChatSDK sdk("my_chat.db");

  • 配置模型并初始化
  • std::vector<std::shared_ptr<Config>> configs;

    // 接入 DeepSeek 云端模型
    configs.push_back(std::make_shared<ApiConfig>(
    /* model_name */ "deepseek-chat",
    /* temperature */ 0.7,
    /* max_tokens */ 2048,
    /* api_key */ "sk-your-key", // 不传则读取环境变量 deepseek_apikey
    /* api_url */ "https://api.deepseek.cn/v1"
    ));

    // 接入 Ollama 本地模型
    configs.push_back(std::make_shared<OllamaConfig>(
    /* model_name */ "deepseek-r1:1.5b",
    /* temperature */ 0.7,
    /* max_tokens */ 2048,
    /* ollama_url */ "http://127.0.0.1:11434",
    /* model_desc */ "本地部署 deepseek-r1:1.5b 模型"
    ));

    // 注册并初始化所有模型
    if (!sdk.registerAndInit_AllProviders(configs)) {
    // 初始化失败处理
    return 1;
    }

  • 创建会话
  • // 为指定模型创建新会话,返回会话 ID
    std::string sessionId = sdk.createSession("deepseek-chat");
    if (sessionId.empty()) {
    // 模型不可用或 SDK 未初始化
    return 1;
    }

  • 发送消息(非流式)
  • std::string response = sdk.sendMessage(sessionId, "你好,请介绍一下你自己。");
    // response 为模型返回的完整文本

  • 发送消息(流式)
  • std::string fullResponse = sdk.sendMessageStream(sessionId, "讲个故事",
    [](const std::string& chunk, bool isDone) {
    // chunk: 当前收到的文本片段
    // isDone: 是否结束
    std::cout << chunk << std::flush;
    }
    );

  • 查询与管理
  • // 获取所有可用模型
    std::vector<ModelInfo> models = sdk.getAvailableModels();

    // 获取所有会话
    std::vector<std::string> sessions = sdk.getSessionList();

    // 获取会话历史
    std::vector<Message> history = sdk.getSessionHistory(sessionId);

    // 删除会话
    sdk.deleteSession(sessionId);


    – API 参考

    (1)构造函数

    ChatSDK(const std::string& dbName = "ai_chat.db");

    参数类型说明
    dbName string SQLite 数据库文件名,用于持久化会话和消息

    构造时自动初始化 SessionManager,从指定数据库恢复历史会话数据。


    (2)registerAndInit_AllProviders

    bool registerAndInit_AllProviders(const std::vector<std::shared_ptr<Config>>& configs);

    注册并初始化所有模型提供者。内部通过 dynamic_pointer_cast 自动识别配置类型(ApiConfig / OllamaConfig),根据模型名称创建对应的 Provider 实例并初始化。

    参数类型说明
    configs vector<shared_ptr<Config>> 模型配置列表,可混合传入 ApiConfig 和 OllamaConfig
    返回值说明
    true 全部模型注册并初始化成功
    false 存在注册或初始化失败的模型

    流程:

  • 遍历配置列表
  • dynamic_pointer_cast 识别配置子类型
  • 根据模型名创建对应 Provider 子类(DeepSeekProvider / ChatgptProvider / GeminiProvider / OllamaProvider)
  • 调用 LLMManager::register_Provider 注册
  • 调用 LLMManager::init_Model 初始化
  • 将配置缓存到 _configs(后续发送消息时取出 temperature / max_tokens)

  • (3) createSession

    std::string createSession(const std::string& modelName);

    为指定模型创建新会话。

    参数类型说明
    modelName string 模型名称,需已在 registerAndInit_AllProviders 中注册
    返回值说明
    非空字符串 会话 ID,格式为 session_{时间戳}_{8位序号}
    空字符串 创建失败(SDK 未初始化 或 模型不可用)

    (4) sendMessage — 非流式

    std::string sendMessage(const std::string sessionId, const std::string& message);

    向指定会话发送消息,等待模型返回完整响应。

    参数类型说明
    sessionId string 会话 ID
    message string 用户消息内容
    返回值说明
    string 模型回复的完整文本

    内部流程:

  • 校验 SDK 是否初始化
  • 通过 sessionId 获取会话,提取关联的 model_name
  • 从会话历史加载所有历史消息,拼入用户新消息
  • 将用户消息持久化到 SQLite
  • 从 _configs 取出 temperature / max_tokens
  • 调用 LLMManager::sendMessage 将完整上下文发至模型
  • 将模型回复持久化到 SQLite
  • 返回模型回复文本

  • (5) sendMessageStream — 流式

    std::string sendMessageStream(const std::string sessionId, const std::string& message,
    std::function<void(const std::string&, bool)> callback);

    向指定会话发送消息,通过回调函数实时接收模型逐片段输出。

    参数类型说明
    sessionId string 会话 ID
    message string 用户消息内容
    callback function<void(const string&, bool)> 回调函数。第一参数为文本片段,第二参数 true 表示流结束
    返回值说明
    string 模型回复的完整文本(所有片段拼接结果)

    回调使用示例:

    sdk.sendMessageStream(sessionId, "Hello",
    [](const std::string& chunk, bool isDone) {
    if (isDone) {
    std::cout << "\\n— 流结束 —" << std::endl;
    } else {
    std::cout << chunk; // 实时输出
    }
    }
    );


    (6)查询方法

    // 获取会话(返回 nullptr 表示不存在)
    std::shared_ptr<Session> getSession(const std::string& sessionId);

    // 获取所有会话 ID 列表(按更新时间降序)
    std::vector<std::string> getSessionList();

    // 获取指定会话的历史消息
    std::vector<Message> getSessionHistory(const std::string& sessionId);

    // 获取所有可用模型列表
    std::vector<ModelInfo> getAvailableModels();


    (7)删除操作

    // 删除指定会话(关联消息自动级联删除)
    bool deleteSession(const std::string& sessionId);

    返回值说明
    true 删除成功
    false 会话不存在

    – 完整示例

    #include "ChatSDK.h"
    #include <iostream>

    using namespace ai_chat_sdk;

    int main() {
    // 1. 创建 SDK 实例
    ChatSDK sdk("chat_history.db");

    // 2. 配置模型
    std::vector<std::shared_ptr<Config>> configs;

    // DeepSeek 云端
    configs.push_back(std::make_shared<ApiConfig>(
    "deepseek-chat", 0.7, 2048,
    "sk-deepseek-key", "https://api.deepseek.cn/v1"
    ));

    // Ollama 本地
    configs.push_back(std::make_shared<OllamaConfig>(
    "deepseek-r1:1.5b", 0.7, 2048,
    "http://127.0.0.1:11434", "本地 DeepSeek-R1"
    ));

    if (!sdk.registerAndInit_AllProviders(configs)) {
    std::cerr << "模型初始化失败" << std::endl;
    return 1;
    }

    // 3. 查看可用模型
    auto models = sdk.getAvailableModels();
    for (const auto& m : models) {
    std::cout << "可用模型: " << m._name << " – " << m._desc << std::endl;
    }

    // 4. 创建会话
    std::string sid = sdk.createSession("deepseek-chat");
    if (sid.empty()) return 1;
    std::cout << "会话 ID: " << sid << std::endl;

    // 5. 发送消息
    std::string reply = sdk.sendMessage(sid, "C++17 有哪些新特性?");
    std::cout << "AI: " << reply << std::endl;

    // 6. 继续对话(上下文自动保留)
    reply = sdk.sendMessage(sid, "举一个具体代码例子");
    std::cout << "AI: " << reply << std::endl;

    // 7. 查询会话历史
    auto history = sdk.getSessionHistory(sid);
    for (const auto& msg : history) {
    std::cout << "[" << msg._role << "] " << msg._content.substr(0, 50) << "…" << std::endl;
    }

    // 8. 流式示例
    sdk.sendMessageStream(sid, "讲个科幻小故事",
    [](const std::string& chunk, bool done) {
    std::cout << chunk << std::flush;
    if (done) std::cout << std::endl;
    }
    );

    // 9. 删除会话
    sdk.deleteSession(sid);
    std::cout << "会话已删除" << std::endl;

    return 0;
    }


    – 编译集成

    • sdk/CMakeLists.txt

    # CMake 最低版本要求
    cmake_minimum_required(VERSION 3.10)

    # 项目名称
    project(ai_chat_sdk_project)

    #设置SDK的名称 ( 用于生成静态库文件的名称 )
    set(SDK_NAME "ai_chat_sdk")

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

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

    # 收集源文件
    # 递归收集所有源文件
    file(GLOB_RECURSE SDK_SOURCES "src/*.cpp")

    # 生成静态库目标
    add_library(${SDK_NAME} STATIC ${SDK_SOURCES})

    # 设置头文件搜索路径。用于编译静态库时,找到头文件
    target_include_directories(${SDK_NAME} PRIVATE
    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
    )

    include(GNUInstallDirs)
    # 设置库的使用要求,也就是下游消费者必须包含的头文件搜索路径
    target_include_directories(${SDK_NAME} INTERFACE
    "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${SDK_NAME}>" # /usr/local/include/ai_chat_sdk/
    )

    # 设置静态库目标 的输出目录
    set_target_properties(${SDK_NAME} PROPERTIES
    ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/build
    )

    # 添加编译定义
    target_compile_definitions(${SDK_NAME} PUBLIC
    CPPHTTPLIB_OPENSSL_SUPPORT
    )

    # 查找OpenSSL包
    find_package(OpenSSL REQUIRED)
    # 包含OpenSSL头文件路径
    target_include_directories(${SDK_NAME} PUBLIC ${OPENSSL_INCLUDE_DIR})

    # 链接库
    target_link_libraries(${SDK_NAME} PUBLIC fmt gtest spdlog jsoncpp OpenSSL::SSL OpenSSL::Crypto sqlite3)

    ############################## 安装阶段设置 ###############################
    # 安装我们的静态库
    install(TARGETS ${SDK_NAME}
    EXPORT ${SDK_NAME}Targets # 将目标 ${SDK_NAME}的导出信息 关联到名为 ${SDK_NAME}Targets的导出目标集合
    DESTINATION ${CMAKE_INSTALL_LIBDIR} # /usr/local/lib
    )

    # 安装头文件
    install(DIRECTORY include/
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${SDK_NAME} # /usr/local/include/ai_chat_sdk/
    FILES_MATCHING PATTERN "*.h"
    )

    # 安装导出目标集合 到 构建树
    export(EXPORT ${SDK_NAME}Targets
    FILE ${CMAKE_CURRENT_BINARY_DIR}/${SDK_NAME}Targets.cmake # 生成的导出文件放在构建目录
    )

    # 安装导出目标集合 到 安装树
    install(EXPORT ${SDK_NAME}Targets
    FILE ${SDK_NAME}Targets.cmake
    NAMESPACE ${SDK_NAME}:: # ${SDK_NAME}:${SDK_NAME}(命名空间)
    DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${SDK_NAME} # /usr/local/lib/cmake/${SDK_NAME}/${SDK_NAME}Targets.cmake
    )

    # ⽣成find_package 需要的 配置⽂件
    include(CMakePackageConfigHelpers) # 包含辅助函数(帮助用户将模板文件 转换成 配置文件)
    configure_package_config_file(
    ${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in # 输入 模板文件(自己创建)
    ${CMAKE_CURRENT_BINARY_DIR}/${SDK_NAME}Config.cmake # 生成的配置文件
    INSTALL_DESTINATION "lib/cmake/${SDK_NAME}" # 配置文件最终安装位置
    )

    # 安装配置⽂件到cmake 标准的安装路径
    install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${SDK_NAME}Config.cmake
    DESTINATION "lib/cmake/${SDK_NAME}"
    )

    (1) 手动编译

    cd AI_mode-acess-SDK/sdk
    mkdir -p build && cd build
    cmake ..
    make -j$(nproc)
    sudo make install

    编译后生成:

    • 静态库:libai_chat_sdk.a
    • 头文件安装到 include/ai_chat_sdk/
    • 库文件安装到 lib/

    (2)CMake 集成

    # 将 ChatSDK 作为子目录引入
    add_subdirectory(AI_mode-acess-SDK/sdk)
    target_link_libraries(your_target ai_chat_sdk)

    # 或使用 find_package(需先 sudo make install)
    find_package(ai_chat_sdk REQUIRED)
    target_link_libraries(your_target ai_chat_sdk::ai_chat_sdk)


    – 注意事项

  • 线程安全:ChatSDK 内部使用 std::mutex 保护共享数据,多线程调用是安全的
  • 模型命名:createSession 传入的模型名必须在 registerAndInit_AllProviders 的配置中注册过,否则创建失败
  • 环境变量:ApiConfig 的 api_key 默认从环境变量读取,建议通过环境变量注入密钥而非硬编码
  • 数据库文件:默认数据库文件 ai_chat.db 生成在工作目录下,可通过构造函数指定其他路径
  • Ollama 本地模型:使用前需确认 Ollama 服务已启动且目标模型已拉取(ollama pull deepseek-r1:1.5b)
  • 会话恢复:SDK 构造时自动从 SQLite 加载历史会话,重启后无需额外操作即可恢复上下文

  • 赞(0)
    未经允许不得转载:171主机测评 » C++大模型统一接入引擎(第三篇):模型管理、会话持久化与SDK门面封装的完整实现
    分享到: 更多 (0)

    评论 抢沙发

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