跳到主要内容从零构建 C++ AI 大模型接入 SDK | 极客日志C++AI算法
从零构建 C++ AI 大模型接入 SDK
基于 C++ 构建统一的大模型接入 SDK,支持 DeepSeek、Gemini 等多模型切换。核心实现包括流式响应解析(SSE)、SQLite 会话持久化及 HTTP 服务封装。通过抽象 Provider 层屏蔽底层差异,提供同步与流式两种消息接口,并集成 ChatServer 演示前后端交互流程。
念念不忘2 浏览 从零构建 C++ AI 大模型接入 SDK
一、前言
本项目旨在从零实现一个 C++ 大语言模型接入 SDK。SDK 的核心特性包括统一的多模型接入、高效流式响应解析以及完整的会话管理。
核心特性
- 多模型支持:支持 DeepSeek-V3.2-Exp、gemini-2.0-flash、gpt-4o-mini 等主流云端模型;支持通过 Ollama 无缝接入本地模型,保障数据隐私与低延迟。
- 流式响应:完整支持 Server-Sent Events (SSE) 流式传输,实时解析模型返回的数据流,实现类似'逐字打印'的效果。
- 会话管理:自动维护多轮对话上下文;支持创建、删除、获取会话列表及历史查看;应用重启后支持会话状态恢复。
- 开箱即用:清晰简洁的 API 接口,方便在项目中集成及扩展。
除了已接入的 DeepSeek,其他模型如 ChatGPT、Gemini 的接入过程几乎一致,只需继承抽象类并实现相应接口即可。
二、环境配置
建议在 Ubuntu 24.04 或更新版本上开发,依赖安装更为便捷。
1. 依赖库安装命令
sudo apt-get install libgflags-dev libspdlog-dev fmt libjsoncpp-dev libgtest-dev libssl-dev cmake pkg-config curl
git clone https://github.com/yhirose/cpp-httplib.git
sudo cp cpp-httplib/httplib.h /usr/include/
说明:cpp-httplib 是单头文件库,代码中直接 #include <httplib.h> 即可使用。
3. 安装 SQLite
sudo apt install sqlite3
sudo apt install libsqlite3-dev
编译时链接方式:
- Makefile:
g++ main.cpp -o my_program -lsqlite3
- CMakeLists.txt:
target_link_libraries(${SDK_NAME} sqlite3)
三、项目实现
1. 目录结构与开发顺序
建议先完成 sdk 目录下的所有内容,再编写 ChatServer。
SDK 开发顺序
- 定义基础数据结构 (
common.hpp)。
- 实现抽象基类 (
LLMProvider.hpp)。
实现具体模型提供者 (DeepSeekProvider.cc)。测试接口连通性。实现模型管理类 (LLMManager)。实现内存会话管理 (SessionManager)。实现数据库持久化 (DataManager)。完善 SessionManager 接入数据库。封装顶层入口 (ChatSDK)。编写测试用例。ChatServer 开发顺序
- 编写
ChatServer.hpp 和 .cc。
- 编写
main.cc 启动服务器。
- 前端界面对接(可直接参考示例或使用 AI 生成)。
2. common.hpp
定义基础数据结构,包括消息、配置、会话信息等。注意析构函数需声明为虚函数。
#pragma once
#include <vector>
#include <string>
#include <ctime>
namespace ai_chat_sdk {
struct Message {
std::string _messageId;
std::string _role;
std::string _content;
std::time_t _timestamp;
Message(const std::string &role = "", const std::string &content = "")
: _role(role), _content(content), _timestamp(0) {}
};
struct Config {
std::string _modelName;
double _temperature;
int _maxTokens;
virtual ~Config() = default;
};
struct ApiConfig : public Config {
std::string _apiKey;
};
struct Session {
std::string _sessionId;
std::string _modelName;
std::vector<Message> _messages;
std::time_t _createTime;
std::time_t _updateTime;
Session(const std::string &modelName = "") : _modelName(modelName) {}
};
}
3. LLMProvider.hpp
这是一个抽象策略类,所有要接入的模型都需要继承它并重写虚函数。利用多态,上层只需持有父类指针即可切换不同模型。
#pragma once
#include <string>
#include <map>
#include <vector>
#include <functional>
#include "common.hpp"
namespace ai_chat_sdk {
class LLMProvider {
public:
virtual bool initModel(const std::map<std::string, std::string>& modelConfig) = 0;
virtual bool isAvailable() = 0;
virtual std::string getModelName() const = 0;
virtual std::string getModelDesc() const = 0;
virtual std::string sendMessage(const std::vector<Message>& messages,
const std::map<std::string, std::string>& requestParams) = 0;
virtual std::string sendMessageStream(const std::vector<Message>& messages,
const std::map<std::string, std::string>& requestParams,
std::function<void(const std::string&, bool)> callback) = 0;
protected:
bool _isAvailable = false;
std::string _apiKey;
std::string _endPoint;
};
}
4. DeepSeekProvider 类
继承 LLMProvider 实现具体逻辑。重点在于全量响应与流式响应的 HTTP 处理。
全量响应
构造 JSON 请求体,发起 POST 请求,解析返回的 JSON 提取内容。
std::string DeepSeekProvider::sendMessage(const std::vector<Message>& messages,
const std::map<std::string, std::string>& requestParams) {
if (!isAvailable()) return "";
double temperature = 0.7;
int maxTokens = 2048;
if (requestParams.find("temperature") != requestParams.end())
temperature = std::stod(requestParams.at("temperature"));
if (requestParams.find("max_tokens") != requestParams.end())
maxTokens = std::stoi(requestParams.at("max_tokens"));
Json::Value messageArray(Json::arrayValue);
for (const auto& msg : messages) {
Json::Value messageJson;
messageJson["role"] = msg._role;
messageJson["content"] = msg._content;
messageArray.append(messageJson);
}
Json::Value requestJson;
requestJson["model"] = getModelName();
requestJson["messages"] = messageArray;
requestJson["temperature"] = temperature;
requestJson["max_tokens"] = maxTokens;
Json::StreamWriterBuilder writerBuilder;
writerBuilder["indentation"] = "";
std::string requestBodyStr = Json::writeString(writerBuilder, requestJson);
httplib::Client cli(_endPoint.c_str());
cli.set_connection_timeout(30);
cli.set_read_timeout(60);
httplib::Headers headers = {{"Authorization", "Bearer " + _apiKey},
{"Content-Type", "application/json"}};
auto res = cli.Post("/v1/chat/completions", headers, requestBodyStr, "application/json");
if (!res || res->status != 200) return "";
Json::Value responseBody;
Json::CharReaderBuilder readerBuilder;
std::istringstream responseStream(res->body);
std::string parseError;
if (Json::parseFromStream(readerBuilder, responseStream, &responseBody, &parseError)) {
if (responseBody.isMember("choices") && responseBody["choices"].isArray() && !responseBody["choices"].empty()) {
auto choice = responseBody["choices"][0];
if (choice.isMember("message") && choice["message"].isMember("content")) {
return choice["message"]["content"].asString();
}
}
}
return "";
}
流式响应
使用 SSE 协议,设置 Accept: text/event-stream,并通过回调函数处理分块数据。
std::string DeepSeekProvider::sendMessageStream(const std::vector<Message>& messages,
const std::map<std::string, std::string>& requestParams,
std::function<void(const std::string &, bool)> callback) {
if (!isAvailable()) return "";
requestBody["stream"] = true;
httplib::Client client(_endPoint.c_str());
client.set_connection_timeout(30, 0);
client.set_read_timeout(300, 0);
httplib::Headers headers = {{"Authorization", "Bearer " + _apiKey},
{"Content-Type", "application/json"},
{"Accept", "text/event-stream"}};
httplib::Request req;
req.method = "POST";
req.path = "/v1/chat/completions";
req.headers = headers;
req.body = requestBodyStr;
std::string buffer;
std::string fullResponse;
bool streamFinish = false;
req.response_handler = [&](const httplib::Response& res) {
if (res.status != 200) return false;
return true;
};
req.content_receiver = [&](const char* data, size_t len, size_t offset, size_t totalLength) {
buffer.append(data, len);
size_t pos = 0;
while ((pos = buffer.find("\n\n")) != std::string::npos) {
std::string chunk = buffer.substr(0, pos);
buffer.erase(0, pos + 2);
if (chunk.empty() || chunk[0] == ':') continue;
if (chunk.compare(0, 6, "data: ") == 0) {
std::string modelData = chunk.substr(6);
if (modelData == "[DONE]") {
callback("", true);
streamFinish = true;
return true;
}
Json::Value modelDataJson;
Json::CharReaderBuilder reader;
std::istringstream modelDataStream(modelData);
if (Json::parseFromStream(reader, modelDataStream, &modelDataJson)) {
if (modelDataJson.isMember("choices") && modelDataJson["choices"].isArray() &&
!modelDataJson["choices"].empty() &&
modelDataJson["choices"][0].isMember("delta") &&
modelDataJson["choices"][0]["delta"].isMember("content")) {
std::string content = modelDataJson["choices"][0]["delta"]["content"].asString();
fullResponse += content;
callback(content, false);
}
}
}
}
return true;
};
auto result = client.send(req);
if (!result) return "";
if (!streamFinish) callback("", true);
return fullResponse;
}
5. 测试接口
运行前请设置环境变量 export deepseek_apikey="你的 key"。
add_executable(testLLM testLLM.cc ../sdk/src/util/myLog.cc ../sdk/src/DeepSeekProvider.cc)
target_link_libraries(testLLM pthread jsoncpp fmt spdlog gtest OpenSSL::SSL OpenSSL::Crypto)
6. LLMManager 类
作为核心管理类,采用管理者模式统一调度底层模型。提供注册、初始化、可用性校验及消息转发接口。
#pragma once
#include <map>
#include <memory>
#include "LLMProvider.hpp"
namespace ai_chat_sdk {
class LLMManager {
public:
bool registerProvider(const std::string& providerName, std::unique_ptr<LLMProvider> provider);
bool initModel(const std::string& providerName, 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;
};
}
7. SessionManager 类
负责会话的生命周期管理。初期基于内存实现,后续接入数据库。
#pragma once
#include <atomic>
#include <unordered_map>
#include <mutex>
#include <memory>
#include "common.hpp"
namespace ai_chat_sdk {
class SessionManager {
public:
static SessionManager& getInstance();
std::string createSession(const std::string& modelName);
std::shared_ptr<Session> getSession(const std::string& sessionId);
bool addMessage(const std::string& sessionId, const Message& message);
std::vector<Message> getHistroyMessages(const std::string& sessionId) const;
std::vector<std::string> getSessionLists() const;
bool deleteSession(const std::string& sessionId);
void updateSessionTimestamp(const std::string& sessionId);
void clearAllSessions();
size_t getSessionCount() const;
private:
SessionManager() = default;
SessionManager(const SessionManager&) = delete;
SessionManager& operator=(const SessionManager&) = delete;
std::string generateSessionId();
std::string generateMessageId();
std::unordered_map<std::string, std::shared_ptr<Session>> _sessions;
mutable std::mutex _mutex;
static std::atomic<int64_t> _message_counter;
static std::atomic<int64_t> _session_counter;
};
}
8. DataManager 类
基于 SQLite3 实现数据的持久化存储,包含会话表和消息表,支持外键级联删除。
#pragma once
#include <string>
#include <vector>
#include <memory>
#include <mutex>
#include <sqlite3.h>
#include "common.hpp"
namespace ai_chat_sdk {
class DataManager {
public:
DataManager(const std::string &dbName);
~DataManager();
bool insertSession(const Session &session);
std::shared_ptr<Session> getSession(const std::string &sessionId) const;
bool updateSessionTimestamp(const std::string &sessionId, std::time_t timestamp);
bool deleteSession(const std::string &sessionId);
std::vector<std::string> getAllSessionIds() const;
std::vector<std::shared_ptr<Session>> getAllSessions() const;
bool clearAllSessions();
size_t getSessionCount() const;
bool insertMessage(const std::string &sessionId, const Message &message);
std::vector<Message> getSessionMessages(const std::string &sessionId) const;
bool deleteSessionMessages(const std::string &sessionId);
private:
bool initDataBase();
bool executeSQL(const std::string &sql);
sqlite3 *_db;
std::string _dbName;
mutable std::mutex _mutex;
};
}
9. 接入 DataManager 的 SessionManager
将 DataManager 成员变量加入 SessionManager,在内存操作前后同步数据库,实现断点续聊。
SessionManager::SessionManager():_dataManager("chatDB.db"){
auto sessions = _dataManager.getAllSessions();
for(auto& session : sessions){ _sessions[session->_sessionId]= session;}
}
std::string SessionManager::createSession(const std::string &modelName){
std::string sessionId = generateSessionId();
auto session = std::make_shared<Session>(modelName);
session->_sessionId = sessionId;
_sessions[sessionId]= session;
_dataManager.insertSession(*session);
return sessionId;
}
10. ChatSDK 类
作为统一顶层入口,封装模型管理与会话管理,对外提供极简调用接口。
- 模型初始化:注册并初始化多类大模型。
- 会话管理:委托给
SessionManager。
- 消息交互:提供同步与流式两种发送接口。
- 基础能力:获取可用模型列表。
11. ChatServer 类与前端
ChatServer 充当前后端交互媒介,基于 httplib 实现 RESTful API。
GET /api/sessions: 获取会话列表
GET /api/models: 获取可用模型
POST /api/session: 新建会话
DELETE /api/session/{id}: 删除会话
GET /api/session/{id}/history: 获取历史消息
POST /api/message: 发送消息(全量)
POST /api/message/async: 发送消息(流式 SSE)
流式响应实现要点:
使用 set_chunked_content_provider 配合 SSE 格式输出。
response.set_header("Cache-Control","no-cache");
response.set_header("Connection","keep-alive");
response.set_header("Access-Control-Allow-Origin","*");
response.set_chunked_content_provider("text/event-stream", [this, sessionId, message](size_t offset, httplib::DataSink &dataSink)->bool{
auto writeChunk = [&](const std::string &chunk, bool last){
std::string sseData = "data: " + Json::valueToQuotedString(chunk.c_str()) + "\n\n";
dataSink.write(sseData.c_str(), sseData.size());
if(last){
dataSink.write("data: [DONE]\n\n");
dataSink.done();
return false;
}
return true;
};
_chatSDK->sendMessageStream(sessionId, message, writeChunk);
return false;
});
启动配置:
主程序支持通过命令行参数或配置文件指定端口、日志级别及 API Key。
DEFINE_string(host,"0.0.0.0","服务器绑定的地址");
DEFINE_int32(port,50090,"服务器绑定的端口号");
前端部分可直接参考示例代码,修改 JS 中的 IP 与端口配置即可运行。
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
- 随机西班牙地址生成器
随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online