1. 项目概述
在开发基于大模型的智能对话系统时,我们经常需要管理多个不同的大模型实例,并处理复杂的会话状态。直接调用各个大模型的API会导致代码臃肿且难以维护。本文将详细介绍如何实现一个高效的大模型管理系统(LLMManager),包括模型管理、会话管理和数据持久化三个核心模块。
这个系统的核心价值在于:
- 统一管理多个大模型实例,简化调用流程
- 维护完整的会话历史,支持多轮对话
- 通过SQLite实现数据持久化,保证会话不丢失
- 线程安全设计,支持高并发场景
2. LLMManager设计与实现
2.1 核心架构设计
LLMManager采用工厂模式设计,主要包含以下组件:
- LLMProvider:抽象基类,定义大模型的统一接口
- 具体模型实现:继承LLMProvider,实现特定模型的调用逻辑
- LLMManager:管理所有注册的模型提供者
- SessionManager:管理用户会话状态
- DataManager:处理数据持久化
2.2 头文件实现
cpp复制#pragma once
#include <map>
#include <memory>
#include <string>
#include <functional>
#include "LLMProvider.h"
namespace ai_chat_sdk {
class LLMManager {
public:
// 注册LLM提供者
bool registerProvider(const std::string& name, std::unique_ptr<LLMProvider> provider);
// 初始化指定模型
bool initModel(const std::string& modelName, const std::map<std::string,std::string>& modelParam);
// 获取可用模型列表
std::vector<ModelInfo> getAvailableModels() const;
// 检查模型是否可用
bool isModelAvailable(const std::string& modelName) const;
// 发送消息到指定模型(同步)
std::string sendMessage(const std::string& modelName,
const std::vector<Message>& messages,
const std::map<std::string, std::string>& requestParam);
// 发送消息到指定模型(流式)
std::string sendMessageStream(const std::string& modelName,
const std::vector<Message>& messages,
const std::map<std::string, std::string>& requestParam,
std::function<void(const std::string&, bool)> callback);
private:
std::map<std::string, std::unique_ptr<LLMProvider>> _providers;
std::map<std::string, ModelInfo> _modelInfos;
};
}
2.3 核心功能实现
2.3.1 模型注册与初始化
cpp复制bool LLMManager::registerProvider(const std::string& modelName,
std::unique_ptr<LLMProvider> provider) {
if(!provider) {
ERR("Cannot register null provider,modelName={}",modelName);
return false;
}
_providers[modelName] = std::move(provider);
_modelInfos[modelName] = ModelInfo(modelName);
INFO("Register LLM Provider,modelName : {}", modelName);
return true;
}
bool LLMManager::initModel(const std::string& modelName,
const std::map<std::string, std::string>& modelParam) {
auto it = _providers.find(modelName);
if(it == _providers.end()) {
ERR("Model {} is not registered!!!", modelName);
return false;
}
bool isSuccess = it->second->initModel(modelParam);
if(isSuccess) {
INFO("Model {} init success!!!", modelName);
_modelInfos[modelName]._modelDesc = it->second->getModelDesc();
_modelInfos[modelName]._isAvailable = true;
} else {
INFO("Model {} init Failed!!!", modelName);
_modelInfos[modelName]._isAvailable = false;
}
return isSuccess;
}
2.3.2 消息发送实现
cpp复制std::string LLMManager::sendMessage(const std::string& modelName,
const std::vector<Message>& messages,
const std::map<std::string, std::string>& requestParam) {
auto it = _providers.find(modelName);
if(it == _providers.end()) {
ERR("Model {} is not registered!!!", modelName);
return "";
}
if(!isModelAvailable(modelName)) {
ERR("Model {} is not available!!!", modelName);
return "";
}
return it->second->sendMessage(messages,requestParam);
}
关键点说明:
- 使用std::unique_ptr管理模型提供者,确保资源安全
- 通过move语义转移资源所有权,避免不必要的拷贝
- 线程安全由上层调用者保证
3. 会话管理系统实现
3.1 会话数据结构设计
会话管理需要维护以下核心信息:
- 会话ID
- 关联的模型名称
- 消息历史
- 创建和更新时间戳
cpp复制struct SessionInfo {
std::string _sessionid;
std::string _modelName;
std::vector<Message> _messages;
std::time_t _createdAt;
std::time_t _updatedAt;
SessionInfo(const std::string& modelName)
: _modelName(modelName),
_createdAt(std::time(nullptr)),
_updatedAt(_createdAt) {}
};
3.2 会话管理器实现
3.2.1 核心接口设计
cpp复制class SessionManager {
public:
// 创建会话
std::string createSession(const std::string& modelName);
// 获取会话信息
std::shared_ptr<SessionInfo> getSession(const std::string& sessionId) const;
// 添加消息
void addMessage(const std::string& sessionId, const Message& message);
// 获取历史消息
std::vector<Message> getHistoryMessages(const std::string& sessionId) const;
// 更新会话时间戳
void updateSessionTimestamp(const std::string& sessionId);
// 获取会话列表
std::vector<std::string> getSessionLists() const;
// 删除会话
bool deleteSession(const std::string& sessionId);
// 清空所有会话
void clearAllSessions();
// 获取会话总数
size_t getSessionCount() const;
private:
std::unordered_map<std::string, std::shared_ptr<SessionInfo>> _sessions;
mutable std::mutex _mutex;
std::atomic<int64_t> _sessionCounter{0};
};
3.2.2 关键功能实现
cpp复制std::string SessionManager::createSession(const std::string& modelName) {
std::lock_guard<std::mutex> lock(_mutex);
std::string sessionId = generateSessionId();
auto session = std::make_shared<SessionInfo>(modelName);
session->_sessionid = sessionId;
_sessions[sessionId] = session;
return sessionId;
}
bool SessionManager::addMessage(const std::string& sessionId, const Message& message) {
std::lock_guard<std::mutex> lock(_mutex);
auto it = _sessions.find(sessionId);
if(it == _sessions.end()) {
return false;
}
Message msg(message._role, message._content);
msg._messageid = generateMessageId(it->second->_messages.size());
it->second->_messages.push_back(msg);
it->second->_updatedAt = std::time(nullptr);
INFO("add message success, session id: {}, message.content: {}", sessionId, msg._content);
return true;
}
线程安全设计要点:
- 使用std::mutex保护共享数据
- 使用std::lock_guard实现RAII风格的锁管理
- 对会话计数使用std::atomic保证原子操作
4. 数据持久化实现
4.1 SQLite数据库设计
我们使用两个表来存储会话数据:
-
sessions表:
- session_id TEXT PRIMARY KEY
- model_name TEXT
- created_at INTEGER
- updated_at INTEGER
-
messages表:
- message_id TEXT PRIMARY KEY
- session_id TEXT
- role TEXT
- content TEXT
- FOREIGN KEY(session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
4.2 DataManager实现
4.2.1 数据库初始化
cpp复制bool DataManager::initDataBase() {
// 创建sessions表
const char* createSessionTableSQL =
"CREATE TABLE IF NOT EXISTS sessions ("
"session_id TEXT PRIMARY KEY,"
"model_name TEXT,"
"created_at INTEGER,"
"updated_at INTEGER);";
// 创建messages表
const char* createMessageTableSQL =
"CREATE TABLE IF NOT EXISTS messages ("
"message_id TEXT PRIMARY KEY,"
"session_id TEXT,"
"role TEXT,"
"content TEXT,"
"FOREIGN KEY(session_id) REFERENCES sessions(session_id) ON DELETE CASCADE);";
return execSQL(createSessionTableSQL) && execSQL(createMessageTableSQL);
}
4.2.2 会话持久化操作
cpp复制bool DataManager::insertSession(const SessionInfo& session) {
std::lock_guard<std::mutex> lock(_mutex);
std::string sql = "INSERT INTO sessions VALUES(?, ?, ?, ?);";
sqlite3_stmt* stmt;
if(sqlite3_prepare_v2(_db, sql.c_str(), -1, &stmt, nullptr) != SQLITE_OK) {
ERR("Failed to prepare statement: {}", sqlite3_errmsg(_db));
return false;
}
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, session._createdAt);
sqlite3_bind_int64(stmt, 4, session._updatedAt);
bool result = sqlite3_step(stmt) == SQLITE_DONE;
sqlite3_finalize(stmt);
return result;
}
5. 系统集成与优化
5.1 会话管理与数据管理集成
为了确保内存中的会话状态与数据库保持同步,我们需要修改SessionManager:
cpp复制class SessionManager {
public:
SessionManager(const std::string& dbName) : _dbManager(dbName) {
auto sessions = _dbManager.getAllSessions();
for(auto& session : sessions) {
_sessions[session->_sessionid] = session;
}
}
std::string createSession(const std::string& modelName) {
_mutex.lock();
std::string sessionId = generateSessionId();
auto session = std::make_shared<SessionInfo>(modelName);
session->_sessionid = sessionId;
session->_createdAt = session->_updatedAt = std::time(nullptr);
_sessions[sessionId] = session;
_mutex.unlock();
_dbManager.insertSession(*session);
return sessionId;
}
// 其他方法也需要类似地集成数据管理
};
5.2 性能优化建议
- 批量操作:对于大量消息插入,实现批量插入接口
- 缓存策略:对频繁访问的会话实现LRU缓存
- 连接池:管理数据库连接,避免频繁创建销毁
- 异步写入:对非关键数据采用异步写入策略
6. 常见问题与解决方案
6.1 线程安全问题排查
问题现象:在多线程环境下偶尔出现数据不一致
解决方案:
- 检查所有共享数据的访问是否都有锁保护
- 使用线程安全分析工具(如TSAN)检测数据竞争
- 确保锁的粒度适当,避免死锁
6.2 数据库性能优化
问题现象:随着数据量增加,操作变慢
优化方案:
- 为常用查询字段添加索引
sql复制CREATE INDEX idx_session_id ON messages(session_id);
- 定期执行VACUUM命令整理数据库
- 调整SQLite的页面大小和缓存大小
cpp复制sqlite3_exec(_db, "PRAGMA page_size = 4096;", nullptr, nullptr, nullptr);
sqlite3_exec(_db, "PRAGMA cache_size = 10000;", nullptr, nullptr, nullptr);
6.3 内存管理技巧
- 使用智能指针管理资源
- 对大块数据使用移动语义
- 实现对象池重用频繁创建销毁的对象
- 定期检查内存泄漏
7. 扩展与演进
这个系统可以进一步扩展:
- 支持更多模型:通过实现新的LLMProvider子类
- 分布式会话:将会话数据存储在Redis等分布式存储中
- 性能监控:添加指标收集和性能分析功能
- 插件系统:支持通过插件扩展功能
实际使用中发现,良好的日志记录对排查问题非常有帮助。建议在关键操作点添加详细的日志,并实现日志分级管理。例如,在模型调用失败时记录完整的请求和响应信息,而在正常操作时只记录摘要信息。