C++大模型SDK会话管理模块实现

大语言模型本身不会保存会话上下文,多轮对话中的会话、消息和时间状态必须由业务侧手动维护;同时,多线程并发访问会话管理器时,必须做线程安全控制。C++大模型接入SDK中的SessionManager,正是围绕这两个约束设计的会话管理模块。

来源说明:以下实现与接口来自一篇CSDN社区文章的示例代码,属于社区作者的个人实践总结,未经官方核验。文中所有代码、接口命名和设计取舍均以该社区文章为参照,不代表任何官方SDK的规范或承诺。

会话基础概念

该社区文章将会话定义为用户与大模型之间一组连续交互的集合,用于维护聊天上下文与会话状态,保障多轮对话语义连贯。其核心存储模型是 session_id 到 Session 的映射,通过会话ID快速定位会话。

完整的业务需求包括:创建会话并分配唯一 session_id、绑定模型名称;按 session_id 查询会话;向指定会话追加用户或模型消息;获取指定会话全部历史消息,用于组装给大模型的上下文请求;获取全部会话ID列表供 UI 展示;删除指定会话;更新最后更新时间戳;清空全部会话;获取当前会话总数;生成全局唯一 session_id 和会话内唯一的 message_id;以及线程安全。

公共数据结构

在数据结构上,common.h 定义了 Message 和 Session。Message 包含 _messageId、_role、_content,其中 _role 取 user、assistant 或 system;构造函数只接收 role 和 content,_messageId 后续生成。Session 包含 _sessionId、_modelName、_messages、_createdAt、_updatedAt,_messages 是 vector;构造函数初始化 modelName,并把创建时间和更新时间设为当前时间。

// common.h
#pragma once
#include 
#include 
#include 

struct Message {
    std::string _messageId;
    std::string _role;
    std::string _content;

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

struct Session {
    std::string _sessionId;
    std::string _modelName;
    std::vector _messages;
    std::time_t _createdAt;
    std::time_t _updatedAt;

    explicit Session(const std::string& modelName = "")
        : _modelName(modelName),
          _createdAt(std::time(nullptr)),
          _updatedAt(std::time(nullptr)) {}
};

SessionManager 接口设计

SessionManager.h 的接口覆盖上述业务:createSession、getSession、addMessage、getHistoryMessages、updateSessionTimestamp、getSessionLists、deleteSession、clearAllSessions、getSessionCount。私有工具包括 generateSessionId 和 generateMessageId。成员变量有三个关键点:一是 unordered_map> _sessions;二是 mutable std::mutex _mutex;三是 std::atomic _sessionCounter{0}。

// SessionManager.h
#pragma once
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "common.h"

namespace ai_chat_sdk {

class SessionManager {
public:
    std::string createSession(const std::string& modelName);
    std::shared_ptr getSession(const std::string& sessionId) const;
    bool addMessage(const std::string& sessionId, const Message& message);
    std::vector getHistoryMessages(const std::string& sessionId) const;
    void updateSessionTimestamp(const std::string& sessionId);
    std::vector getSessionLists() const;
    bool deleteSession(const std::string& sessionId);
    void clearAllSessions();
    size_t getSessionCount() const;

private:
    std::string generateSessionId();
    std::string generateMessageId(size_t messageCounter);

    std::unordered_map> _sessions;
    mutable std::mutex _mutex;
    std::atomic _sessionCounter{0};
};

} // namespace ai_chat_sdk

关键设计要点

存储选择:使用 unordered_map>。该社区文章明确不直接存储 Session 对象值,以避免 map 容器存储大量大对象带来的拷贝开销;会话对象放在堆内存,由智能指针管理生命周期。key 是会话ID字符串,平均 O(1) 查找。

mutable 修饰互斥锁:const 成员函数承诺不修改对象业务数据,但锁的 lock/unlock 会修改 mutex 内部状态,普通 mutex 无法在 const 函数中使用,mutable 专门用于这种场景。

原子变量 _sessionCounter:用于无锁线程安全自增,多线程并发创建会话不会出现序号重复。

对外接口以 session_id 为契约:该社区文章的设计意图是让外部模块通过 session_id 操作会话,以降低模块耦合,适配持久化 SQLite 数据库 CURD,并避免跨进程传递裸内存指针。但需要指出,getSession 返回 shared_ptr,外部调用方可以持有该指针并直接修改 Session 内容,因此“不暴露内部 Session 对象”这一表述并不严格成立。作者分析:若严格遵循该设计意图,getSession 应改为返回只读视图或消息副本;若保留 shared_ptr 返回,则必须在文档中明确调用方不得在锁外修改 Session,否则会绕过互斥锁造成数据竞争。

源文件实现

源文件实现中,createSession 先用 lock_guard 加锁,调用 generateSessionId 生成唯一会话ID,再通过 make_shared 在堆上构造对象,设置 _sessionId 后存入哈希表,最后返回 session_id。getSession 加锁后查找,不存在返回 nullptr,存在则返回 shared_ptr。addMessage 加锁后查找会话,不存在返回 false;以当前 _messages.size() 作为消息序号入参,构造新 Message,设置 _messageId 为 generateMessageId(msgSeq),push_back 进消息列表,并把 _updatedAt 更新为当前时间,返回 true。getHistoryMessages 加锁查找,不存在返回空 vector,存在则返回 _messages。updateSessionTimestamp 加锁查找,不存在直接返回,存在则更新 _updatedAt。getSessionLists 同样加锁,返回全部会话ID列表,并按 _updatedAt 降序排列,使最新会话排在最前面。

```cpp
// SessionManager.cpp

include "SessionManager.h"

include

namespace ai_chat_sdk {

std::string SessionManager::createSession(const std::string& modelName)
{
std::lock_guard lock(_mutex);
std::string sessionId = generateSessionId();
auto session = std::make_shared(modelName);
session->_sessionId = sessionId;
_sessions[sessionId] = session;
return sessionId;
}

std::shared_ptr SessionManager::getSession(const std::string& sessionId) const
{
std::lock_guard lock(_mutex);
auto iter = _sessions.find(sessionId);
if (iter == _sessions.end()) {
return nullptr;
}
return iter->second;
}

bool SessionManager::addMessage(const std::string& sessionId, const Message& message)
{
std::lock_guard lock(_mutex);
auto iter = _sessions.find(sessionId);
if (iter == _sessions.end()) {
return false;
}
size_t msgSeq = iter->second->_messages.size();
Message newMsg(message._role, message._content);
newMsg._messageId = generateMessageId(msgSeq);
iter->second->_messages.push_back(newMsg);
iter->second->_updatedAt = std::time(nullptr);
return true;
}

std::vector SessionManager::getHistoryMessages(const std::string& sessionId) const
{
std::lock_guard lock(_mutex);
auto iter = _sessions.find(sessionId);
if (iter == _sessions.end()) {
return {};
}
return iter->second->_messages;
}

void SessionManager::updateSessionTimestamp(const std::string& sessionId)
{
std::lock_guard lock(_mutex);
auto iter = _sessions.find(sessionId);
if (iter == _sessions.end()) {
return;
}
iter->second->_updatedAt = std::time(nullptr);
}

std::vector SessionManager::getSessionLists() const
{
std::lock_guard lock(_mutex);
std::vector> tmp;
for (const auto& kv : _sessions) {
tmp.emplace_back(kv.second->_updatedAt, kv.first);
}
std::sort(tmp.begin(), tmp.end(),
{ return a.first > b.first; });
std::vector result;
result.reserve(tmp.size());
for (const auto& p : tmp) {
result.push_back(p.second);
}
return result;
}

bool SessionManager::deleteSession(const std::string& sessionId)
{
std::lock_guard lock(_mutex);
return _sessions.erase(sessionId) > 0;
}

void SessionManager::clearAllSessions()
{
std::lock_guard lock(_mutex);
_sessions.clear();
}

size_t SessionManager::getSessionCount() const
{
std::lock_guard lock(_mutex);
return _sessions.size();
}

std::string SessionManager::generateSessionId()
{
int64_t seq = _sessionCounter.fetch_add(1);
std::time_t now = std::time(nullptr);
std::ostringstream oss;
oss ,让会话对象生命周期由智能指针接管,规避裸指针风险。该社区文章还列出常见面试考点:unordered_map 与 map 的会话存储选型、mutable 关键字使用场景、std::atomic fetch_add、RAII 锁 std::lock_guard,以及会话管理器的扩展方向。

工程分析

从工程分析角度看,这套实现把模型无状态与业务侧有状态解耦,以 session_id 为外部契约,内部用哈希表、互斥锁、原子计数和智能指针支撑多轮对话上下文维护。作者建议:该结构可作为 C++ 大模型接入 SDK 会话管理层的参考起点,但生产环境仍需按实际 SDK 约束、持久化方案和并发模型进行验证与调整。