【AI大模型接入SDK】LLM会话管理模块设计

🎬 个人主页艾莉丝努力练剑
专栏传送门 :《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录
Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享

⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平


🎬 艾莉丝的简介:


文章目录

  • [1 ~> LLM 会话管理模块整体介绍](#1 ~> LLM 会话管理模块整体介绍)
    • [1.1 会话的核心概念](#1.1 会话的核心概念)
    • [1.2 会话管理需要实现全部功能集合](#1.2 会话管理需要实现全部功能集合)
    • [1.3 线程安全约束](#1.3 线程安全约束)
  • [2 ~> 核心数据结构定义](#2 ~> 核心数据结构定义)
    • [2.1 Message 消息结构体(common.h)](#2.1 Message 消息结构体(common.h))
    • [2.2 Session 会话结构体(common.h)](#2.2 Session 会话结构体(common.h))
  • [3 ~> SessionManager 会话管理类头文件设计(SessionManager.h)](#3 ~> SessionManager 会话管理类头文件设计(SessionManager.h))
    • [3.1 存储容器选型分析](#3.1 存储容器选型分析)
    • [3.2 完整头文件代码](#3.2 完整头文件代码)
  • [4 ~> SessionManager 源文件实现(SessionManager.cpp)](#4 ~> SessionManager 源文件实现(SessionManager.cpp))
  • [5 ~> 关键技术要点与面试考点](#5 ~> 关键技术要点与面试考点)
    • [5.1 ID 生成逻辑](#5.1 ID 生成逻辑)
    • [5.2 并发控制方案](#5.2 并发控制方案)
    • [5.3 getSessionLists 会话列表排序逻辑](#5.3 getSessionLists 会话列表排序逻辑)
    • [5.4 业务调用流程](#5.4 业务调用流程)
    • [5.5 内存会话管理的局限与扩展方向](#5.5 内存会话管理的局限与扩展方向)
    • [5.6 容器选型对比](#5.6 容器选型对比)
  • [6 ~> 易错坑点总结](#6 ~> 易错坑点总结)
  • 结尾


1 ~> LLM 会话管理模块整体介绍

1.1 会话的核心概念

  • 会话定义:会话是用户与大语言模型之间一组连续交互集合,维护对话上下文、状态信息,保障对话连贯性与一致性。
  • 模型侧特性 :大模型本身不会保存会话状态与历史上下文,会话数据必须由业务代码手动维护
  • 业务背景问题
    • 多轮对话消息如何持久存储;
    • 模型无记忆,调用模型时必须携带历史消息上下文;
    • 区分多条消息归属哪一次对话;
    • 在全部消息集合中快速提取某一次会话的全部历史。

1.2 会话管理需要实现全部功能集合

  1. 会话数据存储:维护session_id <-> Session会话对象映射关系,用于快速查询会话。
  2. 创建会话:对话开始前新建会话,后续聊天消息全部归属该会话。
  3. 根据会话 ID 获取会话对象。
  4. 向指定会话追加消息(用户提问、模型回复)。
  5. 获取会话内全部历史消息,用于界面渲染、模型请求上下文组装。
  6. 获取全部会话 ID 列表,用于前端历史会话列表展示。
  7. 删除指定会话。
  8. 更新会话时间戳(创建时间、最后更新时间)。
  9. 清空全部会话。
  10. 获取当前会话总数量。
  11. 生成全局唯一会话 ID。
  12. 生成全局唯一消息 ID。

1.3 线程安全约束

  • 触发并发风险场景:多用户同时访问服务,多线程同时执行创建会话、更新会话、删除会话操作。
  • 风险现象:会话 ID 生成冲突、容器数据错乱、内存数据损坏。
  • 解决方案:互斥锁保护共享容器;原子变量做计数;const 成员函数使用mutable修饰锁变量。

2 ~> 核心数据结构定义

2.1 Message 消息结构体(common.h)

单条聊天消息,保存一条对话的完整信息。

C++ 复制代码
// common.h
#pragma once
#include <string>

namespace ai_chat_sdk {
struct Message {
    std::string _messageId;   // 消息唯一ID
    std::string _role;        // 角色 user / assistant / system
    std::string _content;     // 消息文本内容

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

2.2 Session 会话结构体(common.h)

一次完整对话会话,包含会话元信息 + 该会话全部消息。

C++ 复制代码
// common.h
#pragma once
#include <string>
#include <vector>
#include <ctime>
#include <memory>

namespace ai_chat_sdk {
struct Session {
    std::string _sessionId;             // 会话唯一ID
    std::string _modelName;             // 当前会话使用的大模型名称
    std::vector<Message> _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))
    {}
};
}

3 ~> SessionManager 会话管理类头文件设计(SessionManager.h)

3.1 存储容器选型分析

  • 选型:std::unordered_map<std::string, std::shared_ptr<Session>>
  • key:std::string会话 ID;value:会话对象智能指针
  • 选择理由:底层哈希表,平均 O (1) 查询效率;不需要会话有序;使用shared_ptr避免对象拷贝,堆分配减少容器内存膨胀。
  • 不选用普通 map:map 底层红黑树,查询效率低于哈希表,会话 ID 有序性业务无需求。

3.2 完整头文件代码

C++ 复制代码
// sdk/include/SessionManager.h
#pragma once
#include <atomic>
#include <unordered_map>
#include <mutex>
#include <memory>
#include <string>
#include <vector>
#include "common.h"

namespace ai_chat_sdk {

class SessionManager {
public:
    /// @brief 创建会话
    /// @param modelName 会话使用模型名称
    /// @return 返回新生成会话ID
    std::string createSession(const std::string& modelName);

    /// @brief 根据会话ID获取会话对象
    /// @param sessionId 会话ID
    /// @return 会话智能指针;不存在返回nullptr
    std::shared_ptr<Session> getSession(const std::string& sessionId) const;

    /// @brief 向指定会话追加消息
    /// @param sessionId 目标会话ID
    /// @param message 待添加消息
    /// @return true成功;false会话不存在失败
    bool addMessage(const std::string& sessionId, const Message& message);

    /// @brief 获取会话全部历史消息
    /// @param sessionId 会话ID
    /// @return 消息vector,不存在返回空vector
    std::vector<Message> getHistoryMessages(const std::string& sessionId) const;

    /// @brief 更新会话最后更新时间戳
    /// @param sessionId 会话ID
    void updateSessionTimestamp(const std::string& sessionId);

    /// @brief 获取全部会话ID列表,按会话更新时间戳降序排序(最新会话靠前)
    /// @return 会话ID字符串数组
    std::vector<std::string> getSessionLists() const;

    /// @brief 删除指定会话
    /// @param sessionId 待删除会话ID
    /// @return true删除成功;false会话不存在
    bool deleteSession(const std::string& sessionId);

    /// @brief 清空内存中全部会话
    void clearAllSessions();

    /// @brief 获取当前会话总数
    /// @return 会话数量
    size_t getSessionCount() const;

private:
    // 私有工具函数:生成会话唯一ID
    std::string generateSessionId();
    // 私有工具函数:生成消息唯一ID
    std::string generateMessageId(size_t messageCounter);

private:
    /// 全部会话存储容器:key=sessionId,value=会话智能指针
    std::unordered_map<std::string, std::shared_ptr<Session>> _sessions;

    /// 互斥锁;mutable修饰允许const成员函数内部修改锁状态
    mutable std::mutex _mutex;

    /// 会话计数器,原子类型保证多线程计数安全,初始值0
    std::atomic<int64_t> _sessionCounter{0};
};

} // namespace ai_chat_sdk

关键知识点注解

  1. mutable std::mutex _mutex:const 修饰成员函数不允许修改类成员变量;但锁在只读查询场景必须加锁保护,mutable 允许 const 函数修改该成员。
  2. std::atomic<int64_t> _sessionCounter:原子变量,多线程自增无锁安全,用于 ID 生成计数。
  3. 返回会话列表只返回会话 ID 字符串集合:
    1. 避免大量 Session 对象拷贝,降低内存开销;
    2. 外部模块通过 ID 调用getSession()获取对象,降低模块耦合;
    3. 适配持久层 SQLite 数据库 CURD,数据库主键就是 sessionId。

4 ~> SessionManager 源文件实现(SessionManager.cpp)

C++ 复制代码
// sdk/src/SessionManager.cpp
#include "SessionManager.h"
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <ctime>

namespace ai_chat_sdk {

// 生成会话ID,格式 session_时间戳_8位0填充计数
std::string SessionManager::generateSessionId()
{
    // 原子变量线程安全自增
    int64_t curCnt = _sessionCounter.fetch_add(1, std::memory_order_relaxed);
    std::time_t now = std::time(nullptr);

    std::ostringstream oss;
    oss << "session_"
        << now
        << "_"
        << std::setw(8)
        << std::setfill('0')
        << curCnt;
    return oss.str();
}

// 生成消息ID,格式 msg_时间戳_8位0填充计数
std::string SessionManager::generateMessageId(size_t messageCounter)
{
    size_t curMsgCnt = messageCounter + 1;
    std::time_t now = std::time(nullptr);

    std::ostringstream oss;
    oss << "msg_"
        << now
        << "_"
        << std::setw(8)
        << std::setfill('0')
        << curMsgCnt;
    return oss.str();
}

std::string SessionManager::createSession(const std::string& modelName)
{
    // lock_guard自动加解锁,离开作用域释放锁
    std::lock_guard<std::mutex> lock(_mutex);

    std::string sessionId = generateSessionId();
    // 在堆上创建会话对象智能指针
    auto sessionPtr = std::make_shared<Session>(modelName);
    sessionPtr->_sessionId = sessionId;

    // 存入哈希映射
    _sessions[sessionId] = sessionPtr;
    return sessionId;
}

std::shared_ptr<Session> SessionManager::getSession(const std::string& sessionId) const
{
    std::lock_guard<std::mutex> 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<std::mutex> lock(_mutex);
    auto iter = _sessions.find(sessionId);
    if (iter == _sessions.end())
    {
        return false;
    }

    auto& sessionPtr = iter->second;
    // 根据当前会话消息数量生成消息ID
    std::string msgId = generateMessageId(sessionPtr->_messages.size());

    Message newMsg(message._role, message._content);
    newMsg._messageId = msgId;

    // 消息加入会话
    sessionPtr->_messages.push_back(newMsg);
    // 添加消息同时更新会话最后修改时间戳
    sessionPtr->_updatedAt = std::time(nullptr);
    return true;
}

std::vector<Message> SessionManager::getHistoryMessages(const std::string& sessionId) const
{
    std::lock_guard<std::mutex> 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<std::mutex> lock(_mutex);
    auto iter = _sessions.find(sessionId);
    if (iter == _sessions.end())
    {
        return;
    }
    iter->second->_updatedAt = std::time(nullptr);
}

std::vector<std::string> SessionManager::getSessionLists() const
{
    std::lock_guard<std::mutex> lock(_mutex);
    // 临时vector存储 <更新时间戳,会话智能指针对>
    std::vector<std::pair<std::time_t, std::shared_ptr<Session>>> tempList;
    tempList.reserve(_sessions.size());

    for (const auto& item : _sessions)
    {
        tempList.emplace_back(item.second->_updatedAt, item.second);
    }

    // lambda比较器,按更新时间戳降序,最新会话排在前面
    std::sort(tempList.begin(), tempList.end(),
        [](const std::pair<std::time_t, std::shared_ptr<Session>>& a,
           const std::pair<std::time_t, std::shared_ptr<Session>>& b)
        {
            return a.first > b.first;
        });

    // 提取排序完成后的会话ID
    std::vector<std::string> resultIds;
    resultIds.reserve(_sessions.size());
    for (const auto& item : tempList)
    {
        resultIds.push_back(item.second->_sessionId);
    }
    return resultIds;
}

bool SessionManager::deleteSession(const std::string& sessionId)
{
    std::lock_guard<std::mutex> lock(_mutex);
    auto iter = _sessions.find(sessionId);
    if (iter == _sessions.end())
    {
        return false;
    }
    _sessions.erase(iter);
    return true;
}

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

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

} // namespace ai_chat_sdk

5 ~> 关键技术要点与面试考点

5.1 ID 生成逻辑

  1. 会话 IDsession_时间戳_8位填充原子计数器;时间戳保证大范围区分,原子计数器解决同一秒内多个会话冲突。
  2. 消息 IDmsg_时间戳_8位填充会话内消息计数;基于会话已有消息数量做计数,每个会话内部消息唯一。
  3. 使用std::ostringstream + std::setw + std::setfill('0')做格式化字符串输出。

5.2 并发控制方案

  • 共享容器_sessions全部读写操作,使用std::lock_guard<std::mutex>自动管理锁生命周期,RAII 自动释放锁,避免异常场景死锁。
  • 计数使用std::atomic,简单计数操作不需要加互斥锁。
  • mutable关键字的使用场景:只读 const 成员函数,需要修改锁对象。

5.3 getSessionLists 会话列表排序逻辑

  • 不能直接操作原 unordered_map;先拷贝时间戳与会话指针到临时 vector;
  • 使用std::sort配合 lambda 表达式实现按会话最后更新时间降序
  • 提取排序完成的 sessionId 返回,前端展示历史会话列表时,最近交互会话放在列表顶部。

5.4 业务调用流程

  1. 用户新建对话:调用createSession(modelName)获取 sessionId。
  2. 用户发送消息:调用addMessage(sessionId, userMessage)保存用户消息。
  3. 获取该会话全部历史消息:调用getHistoryMessages(sessionId),组装成大模型请求上下文。
  4. 获取模型回复后,再次调用addMessage(sessionId, assistantMessage)保存模型回复,内部自动更新会话_updatedAt
  5. 前端加载历史会话列表:调用getSessionLists()获取有序会话 ID 数组,循环调用getSession()拿到会话元数据渲染 UI。
  6. 用户删除会话:调用deleteSession(sessionId)

5.5 内存会话管理的局限与扩展方向

当前实现全部会话保存在内存中,进程退出全部丢失; 工程化项目需要结合 SQLite 持久化:

  1. 创建会话、新增消息、删除会话同步写数据库;
  2. 程序启动时从数据库加载历史会话到内存_sessions容器。

5.6 容器选型对比

容器 底层结构 查询复杂度 适用场景
std::unordered_map 哈希表 平均 O (1) 会话管理,key 随机字符串,不需要有序,优先选用
std::map 红黑树 O(logN) 需要 key 有序排序场景,会话管理场景性能较差

6 ~> 易错坑点总结

  1. const 成员函数内部修改互斥锁,忘记加mutable修饰_mutex,编译报错。
  2. ID 生成忘记原子变量,多线程并发创建会话出现 ID 重复冲突。
  3. addMessage 添加消息之后忘记更新会话_updatedAt时间戳,会话列表排序错乱。
  4. 直接返回 Session 对象拷贝,造成巨大内存开销,模块耦合度上升;应当返回 sessionId + 提供 getSession 获取对象。
  5. 操作 unordered_map 迭代器失效风险:erase 操作不要使用失效迭代器;代码中使用 find 拿到迭代器之后,仅在锁保护范围内部操作。
  6. 排序时没有做临时拷贝,直接修改原 unordered_map,破坏哈希容器结构。
  7. std::setfill('0')只对下一次输出生效,不能忘记搭配std::setw,否则不会补零。

结尾

uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!

|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ### 艾莉丝努力练剑 C/C++ & Linux 底层探索者 | 一个正在努力练剑的技术博主 *** ** * ** *** 👀 【关注】 跟随我一起深耕技术领域,见证每一次成长。 ❤️ 【点赞】 让优质内容被更多人看见,让知识传递更有力量。 ⭐ 【收藏】 把核心知识点存好,在需要时随时查、随时用。 💬 【评论】 分享你的经验或疑问,评论区一起交流避坑! 不要忘记给博主"一键四连"哦! "今日练剑达成!" "技术之路难免有困惑,但同行的人会让前进更有方向。" |

结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主"一键四连"哦!

往期回顾

【AI大模型接入SDK】LLMManager类架构与智能指针选型

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა

相关推荐
萧行之1 小时前
可视化技术复盘:从图形语法看 D3、Vega-Lite、ECharts、Observable Plot
前端·学习·数据可视化
牛哥带你学代码1 小时前
远程打游戏远控测评
人工智能·智能手机
小此方1 小时前
「C++AI大模型接入SDK」(一) API接入与本地两种方式对比、API Key获取、API报文详解与简单API的构建
开发语言·c++·人工智能
geneculture1 小时前
三体虚拟竞赛的极致推演:AI for Dao
人工智能·ai for science·信息科学·哲学与科学统一性·序位逻辑·ai for maths·ai for logic
Raas1006 小时前
MAI Gateway(魔芋企业级AI网关)技术揭秘:AI网关支持哪些模型?从原理到落地
大数据·人工智能·网关·gateway·mai gateway·企业级产品
豪气的程序猿8 小时前
电商图片工作流怎么选?Lingko AI 对比折叠键盘主图与详情页
人工智能
小刘快学习8 小时前
把 AI 账单拆到部门:企业 AI 网关的精准分账思路
人工智能
米小虾8 小时前
你让监控模型读的思维链,可能是攻击者写好的剧本
人工智能
deepseek239 小时前
Iris 开源搜索智能体拆解:35B 与 397B 中文仅差 0.3 分,上下文管理胜过堆参数
人工智能·ai agent·开源模型