三大管理器详解: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

设计特点

  • 使用时间戳 + 计数器保证全局唯一性
  • _sessionCounterstd::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)**角色,负责:

  1. 注册具体的策略(Provider)
  2. 根据模型名称分发请求到对应的策略
  3. 维护模型的可用性状态

单一职责:只负责模型的注册、初始化和路由,不处理具体的 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;
}

两阶段初始化

  1. 注册阶段registerProvider):创建 Provider 实例,此时不可用
  2. 初始化阶段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 操作
实体对象 SessionMessage
职责分离 上层不直接操作数据库

6.2 SessionManager —— 缓存 + 代理模式

模式要素 实现
内存缓存 _sessions 作为一级缓存
持久化代理 DataManager 作为后端存储
缓存策略 内存优先,未命中降级到数据库

6.3 LLMManager —— 策略模式 + 外观模式

模式要素 实现
策略接口 LLMProvider
具体策略 DeepSeekProviderChatGPTProvider
上下文 LLMManager 负责分发
外观 对外提供统一的 sendMessage 接口,隐藏内部 Provider 差异

七、总结

管理器 核心职责 设计亮点
DataManager SQLite 数据持久化 参数化 SQL、线程安全、RAII
SessionManager 会话生命周期 + 缓存 双层存储、内存优先、数据恢复
LLMManager 模型注册与路由 策略模式分发、两阶段初始化

三者关系

  • SessionManager 依赖 DataManager 实现数据持久化
  • ChatSDK 组合 SessionManagerLLMManager,协调会话管理与模型调用
  • LLMManager 独立管理 Provider,不依赖其他管理器

协作核心:ChatSDK 是协调者,通过 SessionManager 管理状态,通过 LLMManager 调用模型,实现完整的对话闭环。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐