AI大模型接入SDK—管理器设计
三大管理器详解:DataManager、SessionManager、LLMManager
一、概述
项目通过三个核心管理类实现数据持久化、会话管理和模型路由三大职责。三者呈分层依赖关系:
┌─────────────────────────────────────────────────────────────┐
│ ChatSDK(SDK入口) │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ LLMManager │ │ SessionManager │ │
│ │ (模型管理) │ │ (会话管理) │ │
│ │ │ │ │ │
│ │ _providers │ │ _sessions │ │
│ │ _modelInfos │ │ _dataManager ───┼──────┐ │
│ └──────────────────┘ └──────────────────┘ │ │
│ ▼ │
│ ┌──────────┐ │
│ │DataManager│ │
│ │(数据持久化)│ │
│ │ _db │ │
│ └──────────┘ │
└─────────────────────────────────────────────────────────────┘
| 管理器 | 职责 | 核心数据结构 | 依赖 |
|---|---|---|---|
| DataManager | SQLite 数据库操作,持久化会话和消息 | sqlite3* _db |
sqlite3 |
| SessionManager | 会话生命周期管理,内存缓存 + 持久化 | unordered_map<string, shared_ptr<Session>> _sessions |
DataManager |
| LLMManager | 模型提供者注册、初始化、消息路由 | map<string, unique_ptr<LLMProvider>> _providers |
LLMProvider |
二、DataManager:数据持久化层
2.1 设计思想
职责分离原则:将所有与数据库交互的逻辑封装到独立的 DataManager,上层(SessionManager)不直接操作 SQL。
线程安全设计:所有公开方法都使用 std::lock_guard<std::mutex> 保护,确保多线程环境下的数据库操作安全。
RAII 资源管理:构造函数打开数据库,析构函数关闭数据库,确保资源不泄漏。
2.2 数据库 Schema 设计
-- 会话表
CREATE TABLE IF NOT EXISTS Sessions (
_sessionId TEXT PRIMARY KEY, -- 会话唯一标识
_modelName TEXT NOT NULL, -- 模型名称
_createTime INTEGER NOT NULL, -- 创建时间(Unix 时间戳)
_updateTime INTEGER NOT NULL -- 最后更新时间(Unix 时间戳)
);
-- 消息表
CREATE TABLE IF NOT EXISTS Messages (
_messageId TEXT PRIMARY KEY, -- 消息唯一标识
_sessionId TEXT NOT NULL, -- 所属会话(外键)
_role TEXT NOT NULL, -- 角色(user/assistant/system)
_content TEXT NOT NULL, -- 消息内容
_timestamp INTEGER NOT NULL, -- 时间戳
FOREIGN KEY (_sessionId) REFERENCES Sessions(_sessionId)
);
设计要点:
_sessionId和_messageId使用TEXT而非自增 ID,支持业务生成的唯一标识- 时间戳使用
INTEGER存储 Unix 时间戳,便于排序和比较 - Messages 表通过外键关联 Sessions 表,保证数据一致性
2.3 核心方法详解
构造函数与初始化
DataManager::DataManager(const std::string& dbName)
: _dbName(dbName), _db(nullptr)
{
// 1. 打开/创建数据库
int rc = sqlite3_open(_dbName.c_str(), &_db);
// 2. 创建表结构(如果不存在)
initDataBase();
}
关键设计:
sqlite3_open:如果数据库文件不存在会自动创建initDataBase:使用CREATE TABLE IF NOT EXISTS保证幂等性,多次调用不会报错
参数化 SQL 操作模式
DataManager 中所有 CRUD 操作遵循统一的三段式模式:
// 1. 准备 SQL 语句(使用 ? 占位符)
std::string insertSQL = R"(
INSERT INTO Sessions (_sessionId, _modelName, _createTime, _updateTime)
VALUES (?, ?, ?, ?);
)";
sqlite3_stmt* stmt;
int rc = sqlite3_prepare_v2(_db, insertSQL.c_str(), -1, &stmt, nullptr);
// 2. 绑定参数(防止 SQL 注入)
sqlite3_bind_text(stmt, 1, sessionId.c_str(), -1, SQLITE_TRANSIENT);
sqlite3_bind_int64(stmt, 2, timestamp);
// 3. 执行并清理
rc = sqlite3_step(stmt);
sqlite3_finalize(stmt);
为什么使用参数化查询:
- 防止 SQL 注入:用户输入的内容不会直接拼接到 SQL 字符串中
- 性能优化:SQL 语句可复用,SQLite 可缓存执行计划
插入会话
bool DataManager::insertSession(const Session& session)
{
std::lock_guard<std::mutex> lock(_mutex); // 线程安全
// 准备 INSERT 语句
sqlite3_stmt* stmt;
sqlite3_prepare_v2(_db,
"INSERT INTO Sessions (_sessionId, _modelName, _createTime, _updateTime) VALUES (?, ?, ?, ?);",
-1, &stmt, nullptr);
// 绑定参数
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._createAt));
sqlite3_bind_int64(stmt, 4, static_cast<int64_t>(session._updateAt));
// 执行
sqlite3_step(stmt);
sqlite3_finalize(stmt);
}
查询会话(附带消息)
std::shared_ptr<Session> DataManager::getSession(const std::string& sessionId) const
{
// 查询 Sessions 表
auto session = std::make_shared<Session>(modelName);
session->_sessionId = sessionId;
// ... 填充字段
// 级联查询 Messages 表
session->_messages = getMessages(sessionId);
return session;
}
设计特点:查询会话时自动级联加载该会话的所有消息,上层无需两次调用。
插入消息的级联更新
bool DataManager::insertMessage(const std::string& sessionId, const Message& message)
{
// 1. 插入消息到 Messages 表
// ... prepare/bind/step
// 2. 更新对应会话的 _updateTime
// ... UPDATE Sessions SET _updateTime = ? WHERE _sessionId = ?
}
设计意图:插入消息时自动更新会话的更新时间戳,保证 getAllSessionIds 的排序正确性。
2.4 API 对照表
| 方法 | SQL 操作 | 说明 |
|---|---|---|
insertSession |
INSERT INTO Sessions |
插入新会话 |
getSession |
SELECT ... FROM Sessions + getMessages |
查询会话(级联消息) |
updateSessionTimestamp |
UPDATE Sessions SET _updateTime |
更新时间戳 |
deleteSession |
DELETE FROM Sessions WHERE _sessionId |
删除单一会话 |
getAllSessionIds |
SELECT _sessionId ORDER BY _updateTime DESC |
按更新时间降序查询 |
getAllSessions |
SELECT * FROM Sessions ORDER BY _updateTime DESC |
查询所有会话 |
deleteAllSessions |
DELETE FROM Sessions |
清空所有会话 |
getSessionCount |
SELECT COUNT(*) |
统计数量 |
insertMessage |
INSERT INTO Messages + UPDATE Sessions |
插入消息并更新时间戳 |
getMessages |
SELECT * FROM Messages WHERE _sessionId |
查询会话消息 |
deleteMessages |
DELETE FROM Messages WHERE _sessionId |
删除会话消息 |
三、SessionManager:会话管理层
3.1 设计思想
双层存储策略:
- 内存层(
_sessions):unordered_map提供 O(1) 的快速访问 - 持久层(
DataManager):SQLite 保证数据不丢失
读写分离策略:
- 写操作:先更新内存,再同步到数据库
- 读操作:优先从内存读取,未命中时从数据库加载
线程安全:使用 std::mutex 保护内存中的 _sessions。
3.2 核心数据结构
class SessionManager
{
private:
// 内存缓存:会话id → 会话对象
std::unordered_map<std::string, std::shared_ptr<Session>> _sessions;
// 互斥锁,保护 _sessions 的并发访问
mutable std::mutex _mutex;
// 会话 ID 生成计数器(线程安全)
std::atomic<int64_t> _sessionCounter = {0};
// 数据持久化管理器
DataManager _dataManager;
};
为什么用 shared_ptr<Session>:
Session对象在内存和数据库之间共享- 避免拷贝大对象(包含消息历史)
- 支持安全的多处引用
3.3 构造函数:数据恢复
SessionManager::SessionManager(const std::string& dbName)
: _dataManager(dbName)
{
// 启动时从数据库恢复所有会话到内存
auto sessions = _dataManager.getAllSessions();
for(const auto& session : sessions) {
_sessions[session->_sessionId] = session;
}
}
设计意图:程序重启后,会话数据从数据库自动恢复到内存,用户无需重新创建会话。
3.4 ID 生成策略
// 会话 ID 格式:session_时间戳_计数器
std::string SessionManager::generateSessionId()
{
_sessionCounter.fetch_add(1); // 原子自增
std::time_t time = std::time(nullptr);
std::ostringstream os;
os << "session_" << time << "_" << std::setw(8) << std::setfill('0') << _sessionCounter.load();
return os.str();
}
// 示例:session_1703317784_00000001
// 消息 ID 格式:msg_时间戳_计数器
std::string SessionManager::generateMessageId(size_t messageCounter)
{
messageCounter++;
std::time_t time = std::time(nullptr);
std::ostringstream os;
os << "msg_" << time << "_" << std::setw(8) << std::setfill('0') << messageCounter;
return os.str();
}
// 示例:msg_1703317784_00000001
设计特点:
- 使用时间戳 + 计数器保证全局唯一性
_sessionCounter是std::atomic,确保多线程下 ID 不重复- 8 位前导零格式化,便于排序和阅读
3.5 创建会话
std::string SessionManager::createSession(const std::string& modelName)
{
_mutex.lock();
// 1. 生成唯一会话 ID
std::string sessionId = generateSessionId();
// 2. 创建 Session 对象
auto session = std::make_shared<Session>(modelName);
session->_sessionId = sessionId;
session->_createAt = std::time(nullptr);
session->_updateAt = session->_createAt;
// 3. 加入内存缓存
_sessions[sessionId] = session;
_mutex.unlock();
// 4. 持久化到数据库(在锁外执行,避免长时间占用锁)
_dataManager.insertSession(*session);
return sessionId;
}
锁策略分析:
- 先加锁 → 内存操作 → 解锁 → 数据库操作
- 数据库操作在锁外执行,减少锁持有时间,提高并发性能
3.6 获取会话(内存优先 + 降级查询)
std::shared_ptr<Session> SessionManager::getSession(const std::string& sessionId)
{
// 第一层:内存缓存查找
_mutex.lock();
auto it = _sessions.find(sessionId);
if (it != _sessions.end()) {
// 内存命中,从数据库加载最新消息(其他进程可能已添加消息)
it->second->_messages = _dataManager.getMessages(sessionId);
_mutex.unlock();
return it->second;
}
_mutex.unlock();
// 第二层:数据库查找
auto session = _dataManager.getSession(sessionId);
if (session) {
_mutex.lock();
// 双重检查(防止其他线程已加载)
auto it = _sessions.find(sessionId);
if (it == _sessions.end()) {
_sessions[sessionId] = session;
}
it->second->_messages = _dataManager.getMessages(sessionId);
_mutex.unlock();
return it->second;
}
// 未找到
return nullptr;
}
缓存策略:
- 内存命中:直接从内存返回,但重新加载消息(保证消息最新)
- 内存未命中:从数据库加载并回填到内存缓存
- 双重检查:防止多线程下重复加载同一会话
3.7 添加消息
bool SessionManager::addMessage(const std::string& sessionId, const Message& message)
{
_mutex.lock();
// 1. 查找会话
auto it = _sessions.find(sessionId);
if (it == _sessions.end()) {
_mutex.unlock();
return false;
}
// 2. 构造消息(生成 ID)
Message msg(message._role, message._content);
msg._messageId = generateMessageId(it->second->_messages.size());
// 3. 添加到内存
it->second->_messages.push_back(msg);
it->second->_updateAt = std::time(nullptr);
_mutex.unlock();
// 4. 持久化到数据库
_dataManager.insertMessage(sessionId, msg);
return true;
}
3.8 获取会话列表(内存 + 数据库合并)
std::vector<std::string> SessionManager::getSessionIdLists() const
{
auto sessions = _dataManager.getAllSessions();
std::lock_guard<std::mutex> lock(_mutex);
// 构建临时列表,合并内存和数据库中的会话
std::vector<std::pair<std::time_t, std::shared_ptr<Session>>> temp;
// 添加内存中的会话
for (const auto& pair : _sessions) {
temp.emplace_back(pair.second->_updateAt, pair.second);
}
// 添加数据库中有但内存中没有的会话
for (const auto& session : sessions) {
if(_sessions.find(session->_sessionId) == _sessions.end()) {
temp.emplace_back(session->_updateAt, session);
}
}
// 按更新时间降序排序(最新的在前面)
std::sort(temp.begin(), temp.end(),
[](const auto& a, const auto& b) { return a.first > b.first; });
// 提取会话 ID
std::vector<std::string> sessionIdLists;
for (const auto& pair : temp) {
sessionIdLists.push_back(pair.second->_sessionId);
}
return sessionIdLists;
}
设计意图:
- 合并内存和数据库数据,确保不遗漏任何会话
- 按更新时间降序排列,用户看到的会话列表最新在前
- 适用于多进程/多实例场景(其他实例创建的会话也能被列出)
3.9 删除会话
bool SessionManager::deleteSession(const std::string& sessionId)
{
{
// 使用代码块缩小锁范围
std::lock_guard<std::mutex> lock(_mutex);
auto it = _sessions.find(sessionId);
if (it == _sessions.end()) {
return false;
}
_sessions.erase(it);
} // 锁在这里释放
// 数据库操作在锁外执行
_dataManager.deleteSession(sessionId);
return true;
}
锁范围优化:使用代码块 {} 限制 lock_guard 的作用域,减少锁持有时间。
四、LLMManager:模型管理器
4.1 设计思想
策略模式的分发器:LLMManager 是策略模式中的**上下文(Context)**角色,负责:
- 注册具体的策略(Provider)
- 根据模型名称分发请求到对应的策略
- 维护模型的可用性状态
单一职责:只负责模型的注册、初始化和路由,不处理具体的 HTTP 请求逻辑。
4.2 核心数据结构
class LLMManager
{
private:
// 模型名称 → Provider 实例(独占所有权)
std::map<std::string, std::unique_ptr<LLMProvider>> _providers;
// 模型名称 → 模型元信息
std::map<std::string, ModelInfo> _modelInfos;
};
为什么用 unique_ptr<LLMProvider>:
unique_ptr表示独占所有权,一个 Provider 实例只能属于一个 LLMManager- 禁止拷贝,通过
std::move转移所有权 - 自动管理内存,析构时自动释放 Provider
为什么用 map 而非 unordered_map:
- 当前规模小,性能差异可忽略
map按键排序,便于调试时查看模型列表
4.3 注册 Provider
bool LLMManager::registerProvider(const std::string& modelName,
std::unique_ptr<LLMProvider> provider)
{
// 参数检查
if(!provider) {
ERR("provider is null");
return false;
}
// 转移所有权到 _providers
_providers[modelName] = std::move(provider);
// 初始化模型信息(此时 _isAvailable = false)
_modelInfos[modelName] = ModelInfo(modelName);
return true;
}
关键设计:
- 注册时不初始化 Provider,只保存实例
- 初始化由单独的
initModel()方法完成,支持延迟加载
4.4 初始化模型
bool LLMManager::initModel(const std::string& modelName,
const std::map<std::string, std::string>& modelParams)
{
// 1. 检查是否注册
auto it = _providers.find(modelName);
if(it == _providers.end()) {
ERR("model {} not registered", modelName);
return false;
}
// 2. 调用 Provider 的 initModel
bool initSuccess = it->second->initModel(modelParams);
if(!initSuccess) {
ERR("model {} init failed", modelName);
return false;
}
// 3. 更新模型信息
_modelInfos[modelName]._modelDesc = it->second->getProviderDesc();
_modelInfos[modelName]._isAvailable = true;
return true;
}
两阶段初始化:
- 注册阶段(
registerProvider):创建 Provider 实例,此时不可用 - 初始化阶段(
initModel):设置 API Key 等配置,成功后标记为可用
好处:支持延迟加载,可以在程序启动时注册所有 Provider,用户按需初始化。
4.5 发送消息(路由分发)
std::string LLMManager::sendMessage(const std::string& modelName,
const std::vector<Message>& messages,
const std::map<std::string, std::string>& requestParam)
{
// 1. 查找 Provider
auto it = _providers.find(modelName);
if(it == _providers.end()) {
ERR("model {} not registered", modelName);
return "";
}
// 2. 检查可用性
if(!it->second->isProviderAvailable()) {
ERR("model {} not available", modelName);
return "";
}
// 3. 调用具体 Provider 的方法
return it->second->sendMessage(messages, requestParam);
}
路由逻辑:
用户请求: sendMessage("deepseek-chat", messages, params)
│
▼
LLMManager::sendMessage
│
├──▶ _providers.find("deepseek-chat")
│ │
│ ▼
│ DeepSeekProvider::sendMessage(messages, params)
│ │
│ ▼
│ HTTP POST → API 服务端
│ │
│ ▼
│ 返回响应内容
│
└──▶ 返回给调用方
流式发送(sendMessageStream)逻辑相同,只是多传递一个 callback 参数。
4.6 获取可用模型
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;
}
过滤机制:只返回 _isAvailable == true 的模型,未初始化或初始化失败的模型对用户不可见。
五、三者协作关系
5.1 调用链路
┌─────────────────────────────────────────────────────────────────────┐
│ 消息发送完整链路 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. ChatServer 接收 HTTP 请求 │
│ POST /api/message │
│ │ │
│ ▼ │
│ 2. ChatSDK::sendMessage(sessionId, message) │
│ │ │
│ ├──▶ SessionManager::addMessage(sessionId, userMessage) │
│ │ │ │
│ │ ├──▶ 内存:_sessions[sessionId]._messages.push_back │
│ │ └──▶ 数据库:DataManager::insertMessage │
│ │ │
│ ├──▶ SessionManager::getHistoryMessages(sessionId) │
│ │ └──▶ 返回完整消息历史 │
│ │ │
│ ├──▶ LLMManager::sendMessage(modelName, messages, params) │
│ │ │ │
│ │ └──▶ DeepSeekProvider::sendMessage(...) │
│ │ └──▶ HTTP POST → API 返回 │
│ │ │
│ ├──▶ SessionManager::addMessage(sessionId, assistantMessage)│
│ │ │ │
│ │ ├──▶ 内存:添加到 _messages │
│ │ └──▶ 数据库:DataManager::insertMessage │
│ │ │
│ └──▶ SessionManager::updateSessionTimestamp(sessionId) │
│ │ │
│ ├──▶ 内存:更新 _updateAt │
│ └──▶ 数据库:DataManager::updateSessionTimestamp │
│ │
└─────────────────────────────────────────────────────────────────────┘
5.2 数据流向
用户输入消息
│
▼
┌──────────────┐
│ SessionManager│ 管理会话状态(内存 + 持久化)
│ _sessions │
└──────┬───────┘
│ 获取历史消息
▼
┌──────────────┐
│ LLMManager │ 路由到对应模型
│ _providers │
└──────┬───────┘
│ 调用 Provider
▼
┌──────────────┐
│ Provider │ HTTP 请求 AI API
└──────┬───────┘
│ 返回响应
▼
┌──────────────┐
│ SessionManager│ 保存助手回复
│ _sessions │
└──────┬───────┘
│ 写入数据库
▼
┌──────────────┐
│ DataManager │ 持久化到 SQLite
│ _db │
└───────────────┘
六、设计模式分析
6.1 DataManager —— DAO 模式
| 模式要素 | 实现 |
|---|---|
| 数据访问对象 | DataManager 封装所有 SQL 操作 |
| 实体对象 | Session、Message |
| 职责分离 | 上层不直接操作数据库 |
6.2 SessionManager —— 缓存 + 代理模式
| 模式要素 | 实现 |
|---|---|
| 内存缓存 | _sessions 作为一级缓存 |
| 持久化代理 | DataManager 作为后端存储 |
| 缓存策略 | 内存优先,未命中降级到数据库 |
6.3 LLMManager —— 策略模式 + 外观模式
| 模式要素 | 实现 |
|---|---|
| 策略接口 | LLMProvider |
| 具体策略 | DeepSeekProvider、ChatGPTProvider 等 |
| 上下文 | LLMManager 负责分发 |
| 外观 | 对外提供统一的 sendMessage 接口,隐藏内部 Provider 差异 |
七、总结
| 管理器 | 核心职责 | 设计亮点 |
|---|---|---|
| DataManager | SQLite 数据持久化 | 参数化 SQL、线程安全、RAII |
| SessionManager | 会话生命周期 + 缓存 | 双层存储、内存优先、数据恢复 |
| LLMManager | 模型注册与路由 | 策略模式分发、两阶段初始化 |
三者关系:
- SessionManager 依赖 DataManager 实现数据持久化
- ChatSDK 组合 SessionManager 和 LLMManager,协调会话管理与模型调用
- LLMManager 独立管理 Provider,不依赖其他管理器
协作核心:ChatSDK 是协调者,通过 SessionManager 管理状态,通过 LLMManager 调用模型,实现完整的对话闭环。
更多推荐




所有评论(0)