C++AI大模型接入SDK---deepseek接入封装
文章目录
项目地址: 橘子师兄/ai-model-acess-tech - Gitee.com
博客专栏:C++AI大模型接入SDK_橘子师兄的博客-CSDN博客
博主首页:橘子师兄-CSDN博客
1、DeepSeek提供API
DeepSeek的Chat Completion的参数说明:https://api-docs.deepseek.com/zh-cn/api/create-chatcompletion
Base URL:https://api.deepseek.com
模型名称:deepseek-chat,实际指向DeepSeek-V3.1模型
DeepSeek的API兼容OPenAI,因此请求和响应参数基本与ChatGPT相同:
deepseek-chat模型的聊天补全接口设置如下:
请求URL POST /v1/chat/completions
请求头参数:
| 字段名称 | 字段类型 | 字段说明 |
|---|---|---|
| Content-Type | string | application/json |
| Authorization | string | "Bearer" + _ api_key |
请求体参数:
| 字段名称 | 字段类型 | 字段说明 |
|---|---|---|
| model | string | 模型名称 |
| messages | array | 历史对话,内部为每个对话的object,包含role和content两个字段 |
| temperature | string | 采样温度 |
| max_tokens | integer | 最大tokens数 |
【温度 - temperature】
调整AI生成内容随机性的参数,温度值越高,AI回答越天马行空;温度越低,回答越保守靠谱。
DeepSeek官网建议:
| 温度 | 场景 |
|---|---|
| 0.0 | 代码生成/数学解题 |
| 1.0 | 数据抽取/分析 |
| 1.3 | 通用对话 |
| 1.3 | 翻译 |
| 1.5 | 创意类写作/诗歌创作 |
响应参数:
python
{
"id":"8b1ca715-9270-429a-b40d-2a644f6e1d3f",
"object":"chat.completion",
"created":1754880537,
"model":"deepseek-chat",
"choices":[
{
"index":0,
"message":
{
"role":"assistant",
"content":"我是DeepSeek Chat,由深度求索公司(DeepSeek)开发的智能
AI助手!✨ 我可以帮你解答问题、提供建议、整理信息,甚至陪你聊天。无论是学习、工作,还是日
常生活中的小困惑,都可以来找我聊聊!😊 \n\n有什么我可以帮你的吗?"},
"logprobs":null,
"finish_reason":"stop"
}
],
"usage":{
"prompt_tokens":5,
"completion_tokens":63,
"total_tokens":68,
"prompt_tokens_details":{
"cached_tokens":0
},
"prompt_cache_hit_tokens":0,
"prompt_cache_miss_tokens":5
},
"system_fingerprint":"fp_8802369eaa_prod0623_fp8_kvcache"
}
注意:
- 无状态服务原则 :DeepSeek的API基于无状态设计,每次请求视为独立会话。若需维护对话连续性,必须由客户端主动管理并传递完整上下文。这与HTTP协议的无状态特性一致。
- 系统提示:若需保持角色设定,如始终以专家身份回答,每次请求必须包含系统级指令
- 对话历史:模型仅处理当前请求中的上下文,无法关联前序对话
2、大模型初始化
在使用deepseek前,需要先配置好deepseek需要的一些参数信息,比如api-key、model、temperature、max_tokes等信息,否则无法正常使用deepseek的api。
c++
////////////////////////////////// DeepSeekProvider.h
////////////////////////////////////
#include "ILLMProvider.h"
namespace ai_chat_sdk {
class DeepSeekProvider : public ILLMProvider{
public:
// 初始化模型 key: api_key, value: api_key
virtual bool initModel(const std::map<std::string, std::string>&
model_config) override;
// 检测模型是否有效
virtual bool isAvailable() override;
// 获取模型名称
virtual std::string getModelName()const override;
// 获取模型描述信息,主要在网页中展示时使用
virtual std::string getModelDesc()const override;
};
} // end ai_chat_sdk
c++
//////////////////////////////////// DeepSeekProvider.cpp
//////////////////////////////
#include "DeepSeekProvider.h"
#include "../../util/my_logger.h"
#include <jsoncpp/json/json.h>
#include <jsoncpp/json/reader.h>
#include <jsoncpp/json/value.h>
#include <httplib.h>
#include <sstream>
namespace ai_chat_sdk {
// 初始化模型
bool DeepSeekProvider::initModel(const std::map<std::string, std::string>&
model_config){
// 初始化API Key
auto it = model_config.find("api_key");
if(it == model_config.end()){
ERR("DeepSeekProvider: api_key is empty!");
return false;
}else{
_api_key = it->second;
}
// 初始化endpoint
it = model_config.find("base_url");
if(it != model_config.end()){
_endpoint = it->second;
}else{
_endpoint = "https://api.deepseek.com";
}
// 设置初始化成功标记
_isAvailable = true;
INFO("init success!!!!");
INFO("DeepSeek provider init successs with endpoint : {}", _endpoint);
return true;
}
// 检测模型是否有效
bool DeepSeekProvider::isAvailable(){
return _isAvailable;
}
// 获取模型名称
std::string DeepSeekProvider::getModelName()const{
return "deepseek-chat";
}
// 获取模型描述信息-主要在网页中展示时使用
std::string DeepSeekProvider::getModelDesc()const{
return "一款实用性强、中文优化的通用对话助手,适合日常问答与创作";
}
} // end ai_chat_sdk
| 字段说明 | 字段类型 | 字段说明 |
|---|---|---|
| model | string | 是否成功 |
| messages | string | 结果描述 |
| temperature | double | 响应数据 |
| max_tokens | int | 会话id |
响应格式:
python
{
"id": "d41df5f7-046d-45a3-818c-512b990fff73",
"object": "chat.completion",
"created": 1756726494,
"model": "deepseek-chat",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "你的名字是你在注册时使用的称呼,或者你可以告诉我你希望我怎么称呼你?😊"
},
"logprobs": null,
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 9,
"completion_tokens": 20,
"total_tokens": 29,
"prompt_tokens_details": {
"cached_tokens": 0
},
"prompt_cache_hit_tokens": 0,
"prompt_cache_miss_tokens": 9
},
"system_fingerprint": "fp_feb633d1f5_prod0820_fp8_kvcache"
}
c++
////////////////////////////////////DeepSeekProvider.h
class DeepSeekProvider : public ILLMProvider {
// ...
// 发送消息给模型
virtual std::string sendMessage(const std::vector<Message>& messages,
const std::map<std::string, std::string>& request_param) override;
};
// 发送消息给模型
std::string DeepSeekProvider::sendMessage(const std::vector<Message>& messages,
const std::map<std::string, std::string>& request_param) {
// 检查模型是否有效
if (!_isAvailable) {
ERR("DeepSeekProvider: model is not init!");
return "";
}
// 获取采样温度 和 max_tokens
double temperature = 0.7;
int max_tokens = 2048;
if (request_param.find("temperature") != request_param.end()) {
temperature = std::stof(request_param.at("temperature"));
}
if (request_param.find("max_tokens") != request_param.end()) {
max_tokens = std::stoi(request_param.at("max_tokens"));
}
// 构建历史消息
Json::Value messages_array(Json::arrayValue);
for (const auto& message : messages) {
Json::Value msg;
msg["role"] = message.role;
msg["content"] = message.content;
messages_array.append(msg);
}
// 构建请求体
Json::Value request_body;
request_body["model"] = "deepseek-chat";
request_body["messages"] = messages_array;
request_body["temperature"] = temperature;
request_body["max_tokens"] = max_tokens;
// 序列化
Json::StreamWriterBuilder writer;
std::string json_string = Json::writeString(writer, request_body);
DBG("DeepSeekProvider: request_body: {}", json_string);
// 创建HTTP Client
httplib::Client client(_endpoint);
client.set_connection_timeout(30, 0); // 30秒超时
client.set_read_timeout(60, 0); // 60秒读取超时
// 设置请求头
httplib::Headers headers = {
{"Authorization", "Bearer " + _api_key},
{"Content-Type", "application/json"}
};
// 发送POST请求
auto response = client.Post("/v1/chat/completions", headers, json_string, "application/json");
if (!response) {
ERR("Failed to connect to DeepSeek API - check network and SSL");
return "";
}
DBG("DeepSeek API response status: {}", response->status);
DBG("DeepSeek API response body: {}", response->body);
// 检查响应是否成功
if (response->status != 200) {
ERR("DeepSeek API returned non-200 status: {} - {}", response->status, response->body);
return "";
}
// 解析响应体
Json::Value response_json;
Json::CharReaderBuilder reader_builder;
std::string parse_errors;
std::istringstream response_stream(response->body);
if (!Json::parseFromStream(reader_builder, response_stream, &response_json, &parse_errors)) {
ERR("Failed to parse DeepSeek API response: {}", parse_errors);
return "";
}
// 解析大模型回复内容
// 大模型回复包含在choices json数组中
if (response_json.isMember("choices") && response_json["choices"].isArray() &&
!response_json["choices"].empty()) {
auto& choice = response_json["choices"][0];
if (choice.isMember("message") && choice["message"].isMember("content")) {
std::string reply_content = choice["message"]["content"].asString();
INFO("Received DeepSeek response: {}", reply_content);
return reply_content;
}
}
// 解析失败,返回错误信息
ERR("Invalid response format from DeepSeek API");
return "Invalid response format from DeepSeek API";
}
4、发送消息--全量返回测试
c++
//////////////////////////////// testLLM.cpp ////////////////////////////////
#include <gtest/gtest.h>
#include "../sdk/include/util/my_logger.h"
#include "../sdk/include/DeepSeekProvider.h"
TEST(DeepSeekProviderTest, sendMessageDeepSeek) {
std::map<std::string, std::string> param_map;
param_map["api_key"] = std::getenv("deepseek_apikey");
param_map["temperature"] = "0.7";
auto deepseekProvider = std::make_shared<ai_chat_sdk::DeepSeekProvider>();
ASSERT_TRUE(deepseekProvider != nullptr);
deepseekProvider->initModel(param_map);
ASSERT_TRUE(deepseekProvider->isAvailable());
std::vector<ai_chat_sdk::Message> messages;
messages.push_back({"user", "你好"});
std::string response = deepseekProvider->sendMessage(messages, param_map);
ASSERT_FALSE(response.empty());
INFO("fulldata : {}", response);
}
int main(int argc, char* argv[]) {
// 初始化日志库
bite::Logger::init_logger("aiChatServer", "stdout", spdlog::level::debug);
// 初始化gtest库
testing::InitGoogleTest(&argc, argv);
// 运行所有测试
return RUN_ALL_TESTS();
}
c++
#///////////////////////////// CMakeLists.txt //////////////////////////////
# CMake 最低版本要求
cmake_minimum_required(VERSION 3.10)
# 项目名称
project(LLMTest)
# 设置C++标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 设置构建类型为Debug
set(CMAKE_BUILD_TYPE Debug)
# 添加可执行文件
add_executable(LLMTest
testLLMProvider.cpp
../sdk/src/DeepSeekProvider.cpp
)
# 设置输出目录
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/build)
# 添加头文件搜索路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 查找OpenSSL包
find_package(OpenSSL REQUIRED)
# 包含OpenSSL头文件路径
include_directories(${OPENSSL_INCLUDE_DIR})
# 添加编译定义
target_compile_definitions(LLMTest PUBLIC
CPPHTTPLIB_OPENSSL_SUPPORT
)
# 设置库的目录
link_directories(/usr/local/lib)
# 链接库
target_link_libraries(LLMTest
fmt
jsoncpp
OpenSSL::SSL
OpenSSL::Crypto
spdlog
gtest
)
注意:httplib库默认使用http协议,而deepseek的官网链接使用https协议,因此在编译时需要链接OpenSSL开发库以支持SSL/TLS。否则在使用httplib创建http客户端时报错:
c++
terminate called after throwing an instance of 'std::invalid_argument'
what(): 'https' scheme is not supported.
ubuntu下安装OpenSSL开发库命令
bash
sudo apt-get install libssl-dev
将OpenSSL库配置到CMakeList.txt文件,否则编译时不会链接OpenSSL库,具体参考CMakeList.txt红色标记部分。
编译运行程序,就能看到Deepseek的响应:
bash
DeepSeek API response_body: {"id":"eabeea4d-71f5-4084-8cc6-
bbd36292f947","object":"chat.completion","created":1756369697,"model":"deepseek
-chat","choices":[{"index":0,"message":{"role":"assistant","content":"你好!很高
兴见到你,有什么可以帮你的吗?
😊"},"logprobs":null,"finish_reason":"stop"}],"usage":
{"prompt_tokens":5,"completion_tokens":14,"total_tokens":19,"prompt_tokens_deta
ils":
{"cached_tokens":0},"prompt_cache_hit_tokens":0,"prompt_cache_miss_tokens":5},"
system_fingerprint":"fp_feb633d1f5_prod0820_fp8_kvcache"}
全量返回比较适合生成文档、数据报表之类,用户一次性拿到完整的数据文件。但对于聊天场景不是很友好,如果大模型一次回复内容较多,会让用户等待时间过长,体验不是很好。因此,一般聊天场景中,基本使用流式响应。
5、流式响应
HTTP协议是严格的**"请求-响应"模型**,永远是客户端发起请求,服务器才能响应,服务器就像个"哑巴",它知道更多内容,但是它无法主动告诉你。这种一问一答的模式对于大部分网页浏览器、数据提交等场景已经足够了。

但是有些场景下,服务器需要主动向客户端推送一些实时数据,比如,在看体育直播时,服务器要及时将比赛分数、金球球员等信息推送给客户端;在多人在线游戏中,服务器需要实时同步玩家的操作和游戏状态;在使用导航类应用时,服务器需要实时推动导航信息等。大佬们也发现这个问题了,在2004年的时候Ian Hickson就提出了SSE概念,Opera浏览器是第一个支持SSE的,2011年开始,一些主流浏览器(Chrome、Firefox、Safari)开始逐步支持SSE,2015年时SSE规范才正式成为W3C的标准。
SSE协议
SSE是Server Send Event的缩写,即服务器发送事件,是建立在HTTP协议之上的开发标准,允许服务
器主动向客户端(如浏览器)推送实时数据。

SSE通过单一的持久连接实现数据的实时传输,客户端无需频繁发起请求。
SSE协议特点
- 单向通信:服务器可以主动推送数据到客户端,但客户端无法直接通过SSE向服务器发送数据
- 基于HTTP协议:SSE使用标准的HTTP协议,无需额外的协议或端口配置,兼容性好易于实现
- 轻量级:SSE的实现更简单,代码量少,适合简单的实时数据推送场景
- 自动重连:如果连接断开,浏览器会自动尝试重新连接,无需开发者手动处理重连逻辑
- 支持事件类型:服务器可以发送不同类型事件,客户端可以根据事件类型执行不同的操作
- 支持消息ID:每条消息可以包含一个唯一的ID,用于断线重连后恢复消息流
数据格式:
每个事件可以包含以下字段:
-
data:消息内容(必须)
-
event:事件类型(可选)
-
id:消息ID(可选)
-
retry:重连时间(可选,单位:毫秒)
data: Hello, world!
event: message
id: 123
retry: 10000
data: Another message
每条消息以两个换行符(\n\n) 结束,消息流传输完毕后会有专门的结束标记,不同实现结束标记不同,比如data: [DONE]。
前面我们演示向DeepSeek、ChatGPT、Gemini等大模型提问时,这些大模型并不是一次性将完整回答丢给用户,而是服务器边思考,边主动将思考结果吐(推送)给用户的,就和打字一样一点点输出,用户不需要长时间的等待,能及时看到服务器响应的结果,体验比较好,这种方式称为流式响应。SSE推出后实际不温不火,大模型爆火后,正式大模型场景的需要,SSE协议就爆火了。
WebSocket协议
SSE协议有一个缺陷就是单向传输,即数据只能由服务器给客户端推送,在新闻推送、股票行情、体育比分等场景是比较合适的,因为这些场景客户端无需给服务器发数据。
但有些场景SSE就束手无策了。比如:你在你们宿舍的微信群里发了一个消息"谁去食堂帮我捎个饭",服务器收到后需要"谁去食堂帮我捎个个饭"这条消息主动推送给群中其他人,其他人收到消息后,就需要发消息回应你而不是不闻不问。此处由舍友回复"滚犊子",那服务器收到后又要推送给其他人...该场景中,不仅需要服务器主动给客户端推送消息,也需要客户端给服务器发送消息。这种场景下WebSocket协议就派上用场了。

SSE与WebSocket区别
| 特性 | SSE | WebSocket |
|---|---|---|
| 通信方向 | 单向通道:服务器 → 客户端 | 双向通道:服务器 <=> 客户端 |
| 设计目的 | 服务器主动推送数据(如新闻、状态更 新) | 双向实时对话(如聊天、游戏操作同步) |
| 协议 | HTTP | 独立的TCP协议,咱HTTP捂手后升级协议(ws/wss) |
| 数据格式 | 纯文本 | 二进制或文本 |
| 自动重连 | 内置 | 较需要手动实现 |
| 使用场景 | 实时通知、日志流、LLM响应等单向场 景 | 实时聊天、多人在线游戏、实时交易等双向交互场景 |
为什么DeepSeek的助手消息使用SSE,不使用websocket?
答:大模型的回复是服务器向客户端推送数据的单项数据流,在此期间客户端不需要给大模型服务器发送消息,而SSE刚好是服务器主动单项给客户端推送数据,并且实现简单高效,因此大模型回复通常都使用SSE协议。
HTTP普通响应体和流式响应体

普通HTTP响应体中,一个响应包含一个响应头和一个响应体,在HTTP流式返回响应体中,一个响应包含一个响应头和多个响应块。在流式返回时,会先返回响应头,然后在逐个返回各个响应体,因此在发送流式响应时,需要在请求参数中告知HTTP服务器,响应头和chunk该如何处理。在Httplib中,请求参数的定义(部分参数)如下:
c++
struct Request {
// 通用参数
std::string method; // 请求方法,GET、POST等
std::string path; // 资源路径,URL中域名之后的部分,比如:/api/users
Headers headers; // HTTP请求头,类型为 multimap<string, string>
std::string body; // HTTP请求体
// 查询参数
Params params; // 查询参数,类型为 multimap<string, string>
// 路径参数或路由参数
std::unordered_map<std::string, std::string> path_params; // 类型为 unordered_map<string, string>
// for client
ResponseHandler response_handler;
ContentReceiverWithProgress content_receiver;
// ...
};
response_handler 为响应处理回调函数,实际类型为``std::function<void(const Response&)> ,如果发起请求时设置该函数,当客户端收到完整的HTTP响应头和一些体(如果存在)
后,会调用该函数,并传入构造好的Response对象。content_recevier 内容接收回调函数,是处理流式处理响应的关键,类型为: function<bool(const char* data, size_t len, uint64_t offset, uint64_t total)>
◦ data:指向当前接收到的数据块的指针
◦ len: 当前数据块的长度
◦ offset: 当前数据块在请求体中的偏移量
◦ total: 请求体的总长度
返回值:true表示继续接收数据,false表示停止接收数据
设置该回调函数后,客户端不会等待整个响应体传输完再存到response.body中,而是每收到一小块数据就立刻调用该回调函数,处理实时数据,
6、发送消息-流式返回
URL: /v1/chat/completions
参数:
| 字段名称 | 字段类型 | 字段说明 |
|---|---|---|
| model | string | 是否成功 |
| messages | string | 结果描述 |
| temperature | double | 响应数据 |
| max_tokens | int | 会话id |
| stream | boolean | 是否开启流式响应 |
响应格式:
python
data: {
"id": "chatcmpl-1234567890",
"object": "chat.completion.chunk",
"created": 1700000000,
"model": "deepseek-chat",
"choices": [
{
"index": 0,
"delta": {
"role": "assistant",
"content": ""
},
"finish_reason": null
}
]
}
data: {
"id": "chatcmpl-1234567890",
"object": "chat.completion.chunk",
"created": 1700000000,
"model": "deepseek-chat",
"choices": [
{
"index": 0,
"delta": {
"content": "以下"
},
"finish_reason": null
}
]
}
...
data: {
"id": "chatcmpl-1234567890",
"object": "chat.completion.chunk",
"created": 1700000000,
"model": "deepseek-chat",
"choices": [
{
"index": 0,
"delta": {},
"finish_reason": "stop"
}
]
}
data: [DONE] // 最后一行,表示流式传输彻底结束
c++
////////////////////////////// DeepSeekProvider.h
///////////////////////////////////////
// ...
class DeepSeekProvider : public ILLMProvider {
public:
// ...
// 发送消息给模型 - 全量返回
virtual std::string sendMessage(
const std::vector<Message>& messages,
const std::map<std::string, std::string>& request_param
) override;
// 发送消息给模型 - 流式响应
virtual std::string sendMessageStream(
const std::vector<Message>& messages,
const std::map<std::string, std::string>& request_param,
std::function<void(const std::string&, bool)> callback
) override;
};
c++
////////////////////////////// DeepSeekProvider.cpp
///////////////////////////////////////
// ...
std::string DeepSeekProvider::sendMessageStream(
const std::vector<Message>& messages,
const std::map<std::string, std::string>& request_param,
std::function<void(const std::string&, bool)> callback
) {
INFO("DeepSeekProvider sendMessageStream");
if (!_isAvailable) {
ERR("DeepSeekProvider is not available");
return "";
}
// 获取采样温度 和 max_tokens
double temperature = 0.7;
int max_tokens = 2048;
if (request_param.find("temperature") != request_param.end()) {
temperature = std::stof(request_param.at("temperature"));
}
if (request_param.find("max_tokens") != request_param.end()) {
max_tokens = std::stoi(request_param.at("max_tokens"));
}
// 构建历史消息
Json::Value messages_array(Json::arrayValue);
for (const auto& message : messages) {
Json::Value msg;
msg["role"] = message.role;
msg["content"] = message.content;
messages_array.append(msg);
}
// 构建请求体
Json::Value request_body;
request_body["model"] = "deepseek-chat";
request_body["messages"] = messages_array;
request_body["temperature"] = temperature;
request_body["max_tokens"] = max_tokens;
request_body["stream"] = true; // 开启流式响应
// 序列化
Json::StreamWriterBuilder writer;
std::string json_string = Json::writeString(writer, request_body);
DBG("DeepSeekProvider: Send stream request to deepseek Server, request_body: {}", json_string);
// 创建HTTP Client
httplib::Client client(_endpoint);
client.set_connection_timeout(30, 0); // 30秒超时
client.set_read_timeout(300, 0); // 流式响应需要更长的时间
// 设置请求头
httplib::Headers headers = {
{"Authorization", "Bearer " + _api_key},
{"Content-Type", "application/json"},
{"Accept", "text/event-stream"}
};
// 流式处理变量
std::string buffer; // 接收流式响应的数据块
bool gotError = false; // 响应是否成功
std::string errorMsg; // 错误描述符
int statusCode = 0; // 状态码
bool streamFinish = false; // 标记流式返回数据是否结束
std::string fullResponse; // 累积完整的响应
// 创建请求对象
httplib::Request req;
req.method = "POST";
req.path = "/v1/chat/completions";
req.headers = headers;
req.body = json_string;
// HTTP协议响应的格式:
// 一个状态行:HTTP/1.1 200 OK
// 一组响应头:Content-Type,Content-Length
// 空行:\r\n\r\n
// 响应体
// 普通的HTTP响应是 一头一体
// 流式响应:只有一个响应头,但响应体被拆分成多个块(chunks)陆续发送,即一头多块
// 响应头处理
req.response_handler = [&](const httplib::Response& response) {
statusCode = response.status;
if (200 != statusCode) {
gotError = true;
errorMsg = "HTTP Error:" + std::to_string(statusCode);
return false; // 终止请求
}
return true; // 继续接收数据
};
// 响应体处理(流式回调)
req.content_receiver = [&](const char* data, size_t len, uint64_t offset,
uint64_t totalLength) {
// 如果http请求头出错,就不需要再继续接收了
if (gotError) {
return false;
}
// 追加新数据到缓冲区
buffer.append(data, len);
std::cout << "buffer: " << buffer << std::endl;
// 处理所有完整的事件,事件和事件之间以\n\n分隔
size_t pos = 0;
while ((pos = buffer.find("\n\n")) != std::string::npos) {
std::string event = buffer.substr(0, pos);
buffer.erase(0, pos + 2); // 移除已经处理的事件
// 处理空行和注释,以:开头的是注释行
if (event.empty() || event[0] == ':') {
continue;
}
// 检查事件类型
if (event.compare(0, 6, "data: ") == 0) {
std::string jsonStr = event.substr(6);
// 处理结束标记
if (jsonStr == "[DONE]") {
callback("", true);
streamFinish = true;
return true;
}
// 解析json数据
Json::Value chunk;
Json::CharReaderBuilder readerBuilder;
std::string errs;
std::istringstream jsonStream(jsonStr);
if (Json::parseFromStream(readerBuilder, jsonStream, &chunk, &errs)) {
// 提取增量内容
if (chunk.isMember("choices") &&
chunk["choices"].isArray() &&
!chunk["choices"].empty() &&
chunk["choices"][0].isMember("delta") &&
chunk["choices"][0]["delta"].isMember("content")) {
std::string content = chunk["choices"][0]["delta"]["content"].asString();
// 累积到完整响应
fullResponse += content;
callback(content, false);
}
} else {
// WARN("DeepSeek SSE JSON parse error : {}", errs);
}
}
}
return true; // 继续接收数据
};
// 发送请求并处理结果
auto res = client.send(req);
// send的返回值类型是Result类型,Result类型内部实现了operator bool(),
// 允许将Result类型的实例隐式转换为bool类型
if (!res) {
// 请求连接失败,网络问题、DNS解析失败等
auto err = res.error();
ERR("Network error : {}", std::to_string(static_cast<int>(err)));
return "";
}
// 确保流正常结束
if (!streamFinish) {
WARN("Stream ended without [DONE] marker");
callback("", true);
}
return fullResponse;
}
7、发送消息-流式返回测试
c++
/////////////////////////////// testLLM.cpp //////////////////////////////////
// ...
TEST(DeepSeekProviderTest, sendMessageStreamDeepSeek) {
std::map<std::string, std::string> param_map;
param_map["api_key"] = std::getenv("deepseek_apikey");
param_map["temperature"] = "0.7";
auto deepseekProvider = std::make_shared<ai_chat_sdk::DeepSeekProvider>();
ASSERT_TRUE(deepseekProvider != nullptr);
deepseekProvider->initModel(param_map);
ASSERT_TRUE(deepseekProvider->isAvailable());
std::vector<ai_chat_sdk::Message> messages;
messages.push_back({"user", "你好"});
messages.push_back({"assistant", "你好! 很高兴见到你, 有什么可以帮你的吗?"});
messages.push_back({"user", "请问什么是SSE?"});
auto write_chunk = [&](const std::string& chunk, bool last) {
INFO("chunk : {}", chunk);
if (last) {
INFO("[DONE]");
}
};
std::string fulldata = deepseekProvider->sendMessageStream(messages, param_map, write_chunk);
ASSERT_FALSE(fulldata.empty());
INFO("fulldata : {}", fulldata);
}
运行结果部分截图:
...
buffer: data: {"id":"ca195b58-4bf1-4ebd-903f-
25db748ff989","object":"chat.completion.chunk","created":1757563343,"model":"de
epseekchat","
system_fingerprint":"fp_08f168e49b_prod0820_fp8_kvcache","choices":
[{"index":0,"delta":{"content":"如果"},"logprobs":null,"finish_reason":null}]}
data: {"id":"ca195b58-4bf1-4ebd-903f-
25db748ff989","object":"chat.completion.chunk","created":1757563343,"model":"de
epseekchat","
system_fingerprint":"fp_08f168e49b_prod0820_fp8_kvcache","choices":
[{"index":0,"delta":{"content":"讨论"},"logprobs":null,"finish_reason":null}]}
buffer: data: {"id":"ca195b58-4bf1-4ebd-903f-
25db748ff989","object":"chat.completion.chunk","created":1757563343,"model":"de
epseekchat","
system_fingerprint":"fp_08f168e49b_prod0820_fp8_kvcache","choices":
[{"index":0,"delta":{"content":"**"},"logprobs":null,"finish_reason":null}]}
data: {"id":"ca195b58-4bf1-4ebd-903f-
25db748ff989","object":"chat.completion.chunk","created":1757563343,"model":"de
epseekchat","
system_fingerprint":"fp_08f168e49b_prod0820_fp8_kvcache","choices":
[{"index":0,"delta":{"content":"网页"},"logprobs":null,"finish_reason":null}]}
buffer: data: {"id":"ca195b58-4bf1-4ebd-903f-
25db748ff989","object":"chat.completion.chunk","created":1757563343,"model":"de
epseekchat","
system_fingerprint":"fp_08f168e49b_prod0820_fp8_kvcache","choices":
[{"index":0,"delta":{"content":"实时"},"logprobs":null,"finish_reason":null}]}
buffer: data: {"id":"ca195b58-4bf1-4ebd-903f-
25db748ff989","object":"chat.completion.chunk","created":1757563343,"model":"de
epseekchat","
system_fingerprint":"fp_08f168e49b_prod0820_fp8_kvcache","choices":
[{"index":0,"delta":{"content":"聊天"},"logprobs":null,"finish_reason":null}]}
buffer: data: {"id":"ca195b58-4bf1-4ebd-903f-
25db748ff989","object":"chat.completion.chunk","created":1757563343,"model":"de
epseekchat","
system_fingerprint":"fp_08f168e49b_prod0820_fp8_kvcache","choices":
[{"index":0,"delta":{"content":"、"},"logprobs":null,"finish_reason":null}]}
data: {"id":"ca195b58-4bf1-4ebd-903f-
25db748ff989","object":"chat.completion.chunk","created":1757563343,"model":"de
epseekchat","
system_fingerprint":"fp_08f168e49b_prod0820_fp8_kvcache","choices":
[{"index":0,"delta":{"content":"推送"},"logprobs":null,"finish_reason":null}]}
...