news 2026/5/1 6:27:00

C++大模型SDK开发实录(二):DeepSeek模型接入、HTTP通信实现与GTest单元测试

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++大模型SDK开发实录(二):DeepSeek模型接入、HTTP通信实现与GTest单元测试

这里写目录标题

    • 前言
    • 第一章:DeepSeek Provider类的派生与初始化
      • 1.1 头文件定义
      • 1.2 初始化逻辑实现
    • 第二章:HTTP通信与JSON处理核心实现
      • 2.1 依赖库安装:cpp-httplib
      • 2.2 全量消息发送逻辑详解
    • 第三章:基于Google Test的单元测试
      • 3.1 GTest环境搭建
      • 3.2 编写测试用例
      • 3.3 构建配置与编译
      • 3.4 编译与运行

前言

在完成了SDK的底层架构设计与抽象基类LLMProvider的定义后,接下来的核心任务是实现具体的模型接入逻辑。本文将以DeepSeek模型为例,详细阐述如何通过C++代码构建HTTP请求、处理API鉴权、解析JSON响应,并通过严格的单元测试验证模块功能的正确性。

如果文章中的代码存在问题,可以直接去仓库拉取最新代码
Ai_Model_SDK

https://gitee.com/caijiuuyk/ai_-model_-sdk

第一章:DeepSeek Provider类的派生与初始化

根据策略模式的设计,我们需要创建一个继承自LLMProvider的具体实现类DeepSeekProvider。该类负责管理DeepSeek模型的生命周期、配置加载以及消息发送逻辑。

1.1 头文件定义

include目录下创建DeepSeekProvider.h。该文件声明了DeepSeek提供者类,重写了基类中的纯虚函数。可以看到,除了基础的初始化和元数据获取接口外,重点在于sendMessage(全量返回)和sendMessageStream(流式返回)的声明。

SDK/include/DeepSeekProvider.h代码如下:

#include"LLMProvider.h"#include<string.h>#include<map>#include<vector>#include"common.h"namespacekk{// DeepSeekProvider类,继承自抽象基类LLMProviderclassDeepSeekProvider:publicLLMProvider{public:// 初始化模型// modelConfig: 包含apiKey, endpoint等配置信息的映射virtualvoidinitModel(conststd::map<std::string,std::string>&modelConfig);// 检查模型是否可用virtualboolisAvailable()const;// 获取模型名称virtualstd::stringgetModelName()const;// 获取模型描述virtualstd::stringgetModelDesc()const;// 发送消息 ---全量返回 非流式响应// messages: 上下文消息列表// requestParam: 运行时参数(如temperature, maxTokens)std::stringsendMessage(conststd::vector<Message>&messages,conststd::map<std::string,std::string>&requestParam);// 发送消息 ---全量返回 流式响应// callback: 回调函数,用于处理增量返回的Tokenvirtualstd::stringsendMessageStream(conststd::vector<Message>&messages,conststd::map<std::string,std::string>&requestParam,std::function<void(conststd::string&,bool)>callback);};}

1.2 初始化逻辑实现

src目录下创建DeepSeekProvider.cpp。初始化的核心逻辑在于解析传入的配置参数(modelConfig),提取API Key和Endpoint(API接入点),并设置模型的可用状态。这里使用了我们之前封装的myLog.h进行日志记录,以便在初始化失败时快速定位问题。

SDK/src/DeepSeekProvider.cpp初始化部分代码:

#include"../include/DeepSeekProvider.h"#include"../include/util/myLog.h"namespacekk{// 初始化模型实现boolDeepSeekProvider::initModel(conststd::map<std::string,std::string>&modelConfig){// 1. 初始化API keyautoit=modelConfig.find("apiKey");if(it==modelConfig.end())// 没找到apikey的话{ERR("DeepSeekProvider initModel:apiKey not found");// 打印错误日志returnfalse;}_apiKey=it->second;// 找到了赋值// 2. 初始化endpointit=modelConfig.find("endpoint");if(it==modelConfig.end())// 没找到endpoint的话{ERR("DeepSeekProvider initModel:endpoint not found");// 打印错误日志returnfalse;}_endpoint=it->second;// 找到了赋值_isAvailable=true;// 标记模型可用// 初始化成功,打印关键信息(注意生产环境不要打印完整的Key)INFO("DeepSeekProvider initModel:success,apiKey:%s,endpoint:%s",_apiKey.c_str(),_endpoint.c_str());returntrue;}// 检测模型是否可用boolDeepSeekProvider::isAvailable()const{return_isAvailable;}// 获取模型名称std::stringDeepSeekProvider::getModelName()const{return"deepseek-chat";}// 获取模型的描述信息std::stringDeepSeekProvider::getModelDesc()const{return"deepseek-chat模型是一个基于Transformer架构的对话模型,由DeepSeek公司开发。它可以用于生成自然语言对话,支持多轮对话。";}// sendMessage 和 sendMessageStream 的具体实现将在下文详述}

第二章:HTTP通信与JSON处理核心实现

为了实现sendMessage功能,我们需要引入HTTP客户端库来发送网络请求,并引入JSON库来处理数据交互。

2.1 依赖库安装:cpp-httplib

本项目选用cpp-httplib作为HTTP客户端,它是一个单头文件(header-only)的跨平台C++库,使用非常便捷。

安装方式一:Git克隆
可以直接从GitHub克隆源码,并将头文件复制到系统目录:

dev@dev-host:~/workspace$gitclone https://github.com/yhirose/cpp-httplib.git# 注意:cpp-httplib是header-only库,只需要一个头文件即可# 将httplib.h拷贝到系统目录下,这样在程序中#include <httplib.h>时能直接找到bit@bit08:~/cpp-httplib/cpp-httplib$sudocphttplib.h /usr/include

安装方式二:压缩包解压
如果在服务器端Git网络受限,可以通过上传压缩包的方式进行安装。

unzipcpp-httplib-master.zip

解压后的文件结构如下,只需关注核心的httplib.h文件。

2.2 全量消息发送逻辑详解

sendMessage函数的实现是SDK的核心之一。其工作流程如下:

  1. 状态校验:确认模型已初始化且可用。
  2. 参数构造:解析用户传入的requestParam(如温度、MaxTokens),设置默认值。
  3. JSON序列化:使用jsoncpp库,将消息历史(vector<Message>)和配置参数组装成符合DeepSeek API规范的JSON对象,并序列化为字符串。
  4. HTTP请求构建:实例化httplib::Client,设置超时时间,配置Header(包含Bearer Token认证)。
  5. 发送与接收:发送POST请求,等待服务器的全量响应。
  6. 错误处理:检查HTTP状态码(期望为200)和网络错误。
  7. JSON反序列化:解析API返回的JSON数据,提取choices[0].message.content字段作为最终回复。

以下是DeepSeekProvider.cpp中关于sendMessage的完整实现代码:

#include"../include/DeepSeekProvider.h"#include"../include/util/myLog.h"#include<jsoncpp/json/json.h>#include<httplib.h>namespacekk{// ... 前文的initModel等代码 ...// 发送消息 ---全量返回 非流式响应std::stringDeepSeekProvider::sendMessage(conststd::vector<Message>&messages,conststd::map<std::string,std::string>&requestParam){// 1、检测模型是否可用if(!isAvailable()){ERR("DeepSeekProvider sendMessage:model not available");return"";}// 2、构造请求参数doubletemperature=0.7;// 默认温度intmaxTokens=2048;// 默认最大tokenif(requestParam.find("temperature")!=requestParam.end()){temperature=std::stod(requestParam.at("temperature"));}if(requestParam.find("maxTokens")!=requestParam.end()){maxTokens=std::stoi(requestParam.at("maxTokens"));}// 3、构造历史消息 JSON数组Json::ValuemessageArray(Json::arrayValue);for(constauto&message:messages){Json::ValuemessageJson(Json::objectValue);messageJson["role"]=message._role;messageJson["content"]=message._content;messageArray.append(messageJson);}// 4、构造整个请求体 JSON对象Json::ValuerequestBody(Json::objectValue);requestBody["model"]=getModelName();// 设置模型名称requestBody["messages"]=messageArray;// 设置上下文requestBody["temperature"]=temperature;requestBody["max_tokens"]=maxTokens;// 5、序列化:将JSON对象转为字符串Json::StreamWriterBuilder writerBuilder;writerBuilder["indentation"]="";// 设置缩进为空,压缩数据传输量std::string requestBodyStr=Json::writeString(writerBuilder,requestBody);INFO("DeepSeekProvider sendMessage:requestBody:%s",requestBodyStr.c_str());// 6、使用cpp-httplib库构造http客户端// _endpoint 需为 base url,例如 https://api.deepseek.comhttplib::Clientclient(_endpoint.c_str());client.set_connection_timeout(30);// 连接超时30秒client.set_read_timeout(60,0);// 读取超时60秒,防止大模型生成时间过长导致断连// 7、设置请求头httplib::Headers headers={{"Authorization","Bearer "+_apiKey},// 鉴权的核心{"Content-Type","application/json"}};// 8、发送POST请求autoresponse=client.Post("/v1/chat/completions",headers,requestBodyStr,"application/json");if(!response)// 网络层面的请求失败{ERR("DeepSeekProvider sendMessage:request failed,error message:{}",httplib::to_string(response.error()));return"";}INFO("DeepSeekProvider sendMessage:POST request success ,status code:{}",response->status);INFO("DeepSeekProvider sendMessage:response body:{}",response->body);// 9、检测HTTP响应状态if(response->status!=200)return"";// 10、解析响应体Json::Value responseBody;Json::CharReaderBuilder readerBuilder;std::string parseError;std::istringstreamresponseStream(response->body);// 解析JSON字符串if(Json::parseFromStream(readerBuilder,responseStream,&responseBody,&parseError)){// 导航至 message 内容:responseBody["choices"][0]["message"]["content"]if(responseBody.isMember("choices")&&responseBody["choices"].isArray()&&!responseBody["choices"].empty()){autochoice=responseBody["choices"][0];if(choice.isMember("message")&&choice["message"].isMember("content")){std::string replyContent=choice["message"]["content"].asString();INFO("DeepSeekProvider sendMessage:replyContent:{}",replyContent);returnreplyContent;}}// JSON结构不符合预期ERR("DeepSeekProvider sendMessage:JSON parse failed,error message:{}",parseError);return"json parse failed";}return"json parse failed";}// 占位实现,流式响应将在后续章节详细讲解std::stringDeepSeekProvider::sendMessageStream(conststd::vector<Message>&messages,conststd::map<std::string,std::string>&requestParam,std::function<void(conststd::string&,bool)>callback){return"stream not implemented yet";}}

第三章:基于Google Test的单元测试

代码编写完成后,必须进行严格的测试。本项目引入C++领域最权威的单元测试框架Google Test (gtest)

3.1 GTest环境搭建

在Ubuntu环境下,可以通过apt快速安装GTest开发库:

sudoapt-getinstalllibgtest-dev

3.2 编写测试用例

在项目根目录下创建一个test文件夹,并编写testLLM.cpp。测试用例的设计涵盖了从对象创建、初始化到发送消息的完整流程。为了安全起见,API Key通过环境变量获取,支持回退到硬编码的Key(仅用于本地调试)。

test/testLLM.cpp代码:

#include<gtest/gtest.h>#include"../SDK/include/DeepSeekProvider.h"#include"../SDK/include/util/myLog.h"// 测试DeepSeekProvider的核心流程TEST(DeepSeekProviderTest,sendMessage){// 1. 创建Provider实例autoProvider=std::make_shared<kk::DeepSeekProvider>();ASSERT_TRUE(Provider!=nullptr);// 断言指针有效// 2. 准备初始化参数std::map<std::string,std::string>modelParam;constchar*env_apikey=std::getenv("deepseek_apikey");// 优先读取环境变量,否则使用备用KeymodelParam["apiKey"]=env_apikey?env_apikey:"sk-xxxxxxxxxxxxxxxxxxxxxxxx";// 支持配置自定义Endpoint,例如中转站地址modelParam["endpoint"]="https://api.deepseek.com";// 3. 初始化并检查可用性Provider->initModel(modelParam);ASSERT_TRUE(Provider->isAvailable());// 4. 准备请求运行时参数std::map<std::string,std::string>requestParam={{"temperature","0.5"},{"max_tokens","2048"}};// 5. 构造测试消息std::vector<kk::Message>messages;messages.push_back({"user","你是谁?"});// 6. 调用核心接口并验证std::string response=Provider->sendMessage(messages,requestParam);ASSERT_TRUE(!response.empty());// 断言回复不为空}intmain(intargc,char**argv){// 初始化日志库,开启DEBUG级别以便查看详细通信过程kk::Logger::initLogger("testLLM","stdout",spdlog::level::debug);INFO("Test start...");// 初始化GTest框架testing::InitGoogleTest(&argc,argv);// 运行所有注册的TEST宏returnRUN_ALL_TESTS();}

3.3 构建配置与编译

test目录下创建独立的CMakeLists.txt,用于构建测试可执行文件。需要特别注意的是,这里必须正确链接OpenSSL、JsonCpp、Fmt、Spdlog以及GTest库。

test/CMakeLists.txt配置:

# 设置Cmake的最小版本为3.10 cmake_minimum_required(VERSION 3.10) # 项目名称 project(testLLM) # 设置C++标准为C++17 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_BUILD_TYPE Debug) # 添加可执行文件,包含测试代码和SDK源码 add_executable(testLLM testLLM.cpp ../SDK/src/DeepSeekProvider.cpp ../SDK/src/util/myLog.cpp) # 设置输出目录 set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}) # 添加头文件搜索路径 include_directories(${CMAKE_PROJECT_INCLUDE_DIR}/ ../SDK/include) # 查找并链接OpenSSL find_package(OpenSSL REQUIRED) include_directories(${OPENSSL_INCLUDE_DIR}) # 宏定义:告诉cpp-httplib支持HTTPS target_compile_definitions(testLLM PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT) # 链接所有依赖库 target_link_libraries(testLLM spdlog gtest jsoncpp fmt OpenSSL::Crypto OpenSSL::SSL)

目录结构确认
确保文件布局如下所示:

3.4 编译与运行

进入test目录,创建build文件夹并进行编译:

mkdirbuild&&cdbuild cmake..

CMake配置成功后,会生成Makefile:

执行make进行编译。在开发过程中,可能会遇到库链接顺序或头文件缺失导致的编译错误,需要根据报错信息仔细调整CMakeLists.txt

修复问题后编译成功,运行生成的可执行文件./testLLM。可以看到控制台输出了请求的JSON Body、服务器返回的状态码以及解析后的回复内容。这里演示了通过自定义Endpoint连接API中转站的场景。

至此,我们已经成功实现了DeepSeek模型的全量消息发送功能,并通过单元测试验证了从网络通信到JSON解析的完整链路。下一章将深入最具挑战性的部分——流式响应(Streaming Response)的实现。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/26 7:22:32

【大数据毕设全套源码+文档】基于Hadoop+python的租房数据分析系统的设计与实现(丰富项目+远程调试+讲解+定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/4/23 12:48:29

【大数据毕设源码分享】基于springboot+Hadoop的豆瓣电子图书推荐的设计与实现(程序+文档+代码讲解+一条龙定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/4/26 3:42:37

LangChain入门(九)- 从单元测试到行为轨迹追踪,让你的AI不再“乱拐弯”

前言最近在研究LangChain的测试模块&#xff0c;不得不说这玩意儿设计得挺有意思。传统的单元测试在AI智能体面前就像用尺子量水流量——完全不对路。想想看&#xff0c;一个简单的智能体就包含了LLM的随机性、Prompt的蝴蝶效应、工具调用的不确定性&#xff0c;这测试难度直接…

作者头像 李华
网站建设 2026/5/1 6:20:32

论文写作利器:6款AI驱动平台对比评测,智能润色让语言更专业

开头总结工具对比&#xff08;技能4&#xff09; &#xfffd;&#xfffd; 为帮助学生们快速选出最适合的AI论文工具&#xff0c;我从处理速度、降重效果和核心优势三个维度&#xff0c;对比了6款热门网站&#xff0c;数据基于实际使用案例&#xff1a; 工具名称 处理速度 降…

作者头像 李华
网站建设 2026/4/21 15:39:44

特征工程不该再靠人肉:聊聊 Feature Store 为什么是数据团队的分水岭

“特征工程不该再靠人肉&#xff1a;聊聊 Feature Store 为什么是数据团队的分水岭”说句掏心窝子的实话&#xff1a; 绝大多数模型效果不行&#xff0c;真不怪算法&#xff0c;怪特征。 而绝大多数特征问题&#xff0c;也不怪你不努力&#xff0c;是工程方式太原始了。 我见过…

作者头像 李华
网站建设 2026/5/1 6:09:34

AI论文助手Top8:详细解析平台写作能力及降重技术,智能化需求响应

AI论文生成工具排行榜&#xff1a;8个网站对比&#xff0c;论文降重写作功能全 工具对比总结 以下是8个AI论文工具的简要排名&#xff0c;基于核心功能、处理速度和适用性对比。排名侧重实用性与用户反馈&#xff0c;数据源于引用内容案例&#xff1a; 工具名称 主要功能 优…

作者头像 李华