基于Deepseek(C++)的SSE协议流式响应实现方案

++参考代码在文末,中间记录了代码的重点与卡点(所以文中间不包含完整代码)++

++本次实现接入Deepseek(C++) 采用SSE协议(基于HTTP协议)++

一、流式响应设计

cpp 复制代码
// op1 :判断模型是否有效
// op2 :创建客户端
// op3 :构建请求 设计两个响应回调
// op4 :发送请求
// op5 :判断结果,打印全部响应信息

二、流式响应实现

1.实现时,为了方便。我们先写出json格式的请求和响应:

XML 复制代码
{ 
    "model": "deepseek-chat", 
    "messages": [ 
        { 
        "role": "user", 
        "content": "你是谁?" 
        }, 
    ], 
    "temperature": 0.7, 
    "max_tokens": 2048, 
    "stream" : true 
}
XML 复制代码
data: {
    "id": "chatcmpl-1234567890",
    "object": "chat.completion.chunk",
    "created": 1700000000,
    "model": "deepseek-chat",
    "choices": [
        {
            "index": 0,
            "delta": {
                "content": "以下"
            },
            "finish_reason": null
        }
    ]
}
...
data: [DONE]
# 注释行 :以 冒号为首,注意判断
# 终止行 :data: [DONE]
# 实际上传来的响应应当是序列化的数据,且全程只有一个响应头 ,n 个响应体

2.开始实现:

cpp 复制代码
        req.content_receiver = [&](const char *data, size_t data_length, size_t offset, size_t total_length)->bool{
            //op1:首先判断能否处理
            //op2:积攒数据
            //op3:解析数据 while
                // 1) 判断数据类型
                // 2) 相应处理
            if(gotError)
            {
                bear::ERROR("deepseek response error!");
                return false;
            }
            buffer.append(data,data_length);

        };
   // 卡点1 :不知道接下来应该做什么 ? 想通:buffer append data后,buffer每行数据一定有\n\n 而每行数据不一定都有效

其中难点

Request的参数介绍

cpp 复制代码
struct Request {
  std::string method;
  std::string path;
  std::string matched_route;
  Params params;
  Headers headers;
  Headers trailers;
  std::string body;

  std::string remote_addr;
  int remote_port = -1;
  std::string local_addr;
  int local_port = -1;

  // for server
  std::string version;
  std::string target;
  MultipartFormData form;
  Ranges ranges;
  Match matches;
  std::unordered_map<std::string, std::string> path_params;
  std::function<bool()> is_connection_closed = []() { return true; };

  // for client
  std::vector<std::string> accept_content_types;
  ResponseHandler response_handler;
  ContentReceiverWithProgress content_receiver;
  DownloadProgress download_progress;
  UploadProgress upload_progress;
   // 一些方法...
}

本次实现的函数接口

cpp 复制代码
using ResponseHandler = std::function<bool(const Response &response)>;
using ContentReceiverWithProgress = std::function<bool(
    const char *data, size_t data_length, size_t offset, size_t total_length)>;
// content_receiver返回值的含义:
//return true;  → 继续接收下一段数据(保持连接)
//return false; → 立即停止接收,断开连接,终止整个请求!

发送消息-流式返回测试:

cpp 复制代码
///////////////////testLLM.cpp//////////////////////////
TEST(testDeepseek,sendMessage)
{
    //op1:构建 
    auto deepseekProvider = std::make_shared<ai_model_access_tech::DeepseekProvider>();
    ASSERT_TRUE(deepseekProvider.get() != nullptr);
    //op2:初始化
    std::map<std::string,std::string> initModelParams;
    initModelParams.emplace("apiKey",std::getenv("DEEPSEEK_APIKEY"));
    initModelParams.emplace("endpoint","https://api.deepseek.com");
    auto ret = deepseekProvider->initModel(initModelParams);
    ASSERT_TRUE(deepseekProvider->isAvailable());
    //op3:发送消息
    std::vector<ai_model_access_tech::Message> messages;
    messages.push_back({"user","你是谁?"});
    // messages.push_back({"assistant","我是deepseek请问有什么需要帮助的?"});
    // messages.push_back({"user","请问SSE协议是什么?"});

    std::map<std::string, std::string> messageParams;
    messageParams.emplace("max_tokens","2048");
    messageParams.emplace("temprature","0.7");
    auto contentCallback = [&](const std::string & message, bool isLast){
        bear::INFO("current info flow:{}",message);
        if(isLast)
        {
            bear::INFO("over~");
        }
    };
    std::string ret_s = deepseekProvider->sendMessageStream(messages,messageParams,contentCallback);
    ASSERT_FALSE(ret_s.empty());
}
int main(int argc,char* argv[])
{
    bear::myLog::initLogger("testLLM","stdout",spdlog::level::info);
    testing::InitGoogleTest(&argc,argv);
    return RUN_ALL_TESTS();
}

参考代码:

cpp 复制代码
    std::string DeepseekProvider::sendMessageStream(const std::vector<Message> &messages,
                                       const std::map<std::string, std::string> &params,
                                       std::function<void(const std::string &, bool)>
                                           callback)
    {
        bear::DEBUG("deepseekProvider senMessageStream......");
        //op1:判断模型是否有效
        if(!_isAvailable)
        {
            bear::ERROR("DeepseekProvider sendMessage:model not available");
            return "";
        }
        //op2:创建客户端
        httplib::Client client(_endpoint.c_str());
        client.set_connection_timeout(10,0);
        client.set_read_timeout(300,0);
        //op3:构建请求
            //头
        httplib::Headers headers = {
            {"Content-Type", "application/json"},
            {"Authorization", "Bearer " + _apiKey},
            {"Accept","text/event-stream"}
        };
            //体
        int max_tokens = 2048;
        double temprature = 0.7;
        if (params.find("max_tokens") != params.end())
        {
            max_tokens = std::stoi(params.at("max_tokens"));
        }
        if (params.find("temprature") != params.end())
        {
            temprature = std::stod(params.at("temprature"));
        }

        Json::Value messagesArray(Json::arrayValue);
        for(auto &message:messages)
        {
            Json::Value msg;
            msg["role"] = message._role;
            msg["content"] = message._content;
            messagesArray.append(msg);
        }

        Json::Value requestBody;
        requestBody["model"] = getModelName();
        requestBody["messages"] = messagesArray;
        requestBody["max_tokens"] = max_tokens;
        requestBody["temprature"] = temprature;
        requestBody["stream"] = true;

        Json::StreamWriterBuilder swb;
        swb["indentation"] = "";
        auto body = Json::writeString(swb,requestBody);
            //真正构建req
        httplib::Request req;
        req.method = "POST";
        req.path = "/chat/completions";
        req.headers = headers;
        req.body = body;
                //回调:响应处理器
        std::string buffer;                     //积攒的序列化响应体
        int statusCode;                         //响应的响应码
        bool gotError = false;                  //
        std::string errorMsg;                   //
        bool streamFinish = false;                      //流式响应是否处理完毕
        std::string fullMsg = "";                    //解析完毕所有的响应内容
        req.response_handler = [&](const httplib::Response &response)->bool{
            statusCode = response.status;
            if(response.status != 200)
            {
                errorMsg = "HTTP ERROR: " + std::to_string(statusCode);
                bear::ERROR("deepseek response error! {}",errorMsg);
                gotError = true;
                return false;
            }
            return true;
        };
                //回调:响应内容处理器
        req.content_receiver = [&](const char *data, size_t data_length, size_t offset, size_t total_length)->bool{
            //op1:首先判断能否处理
            //op2:积攒数据
            //op3:解析数据 while
                // 1) 判断数据类型
                // 2) 相应处理
            if(gotError)
            {
                bear::ERROR("deepseek response error!");
                return false;
            }
            buffer.append(data,data_length);
            int pos = 0;
            while((pos = buffer.find("\n\n")) != std::string::npos)
            {
                std::string dataStr = buffer.substr(0,pos);
                buffer.erase(0,pos+2);

                if(dataStr.empty() || dataStr[0] == ':')
                {
                    // 是否为注释行
                    bear::WARN("deepseekProvider streamResponse:This line is empty or is a comment.");
                    continue;
                }

                if(dataStr.compare(0,6,"data: ") == 0)
                {
                    // 是否为终止行
                    if(dataStr == "data: [DONE]")
                    {
                        streamFinish = true;
                        callback("",true);
                        return true;
                    }
                    else
                    {
                        std::string chunk = dataStr.substr(6);
                        //op1:反序列化
                        Json::CharReaderBuilder crb;
                        std::istringstream chunk_stream(chunk);
                        Json::Value responseBody;
                        std::string errorResponse;
                        //op2:解析字段
                        auto ret = Json::parseFromStream(crb,chunk_stream,&responseBody,&errorResponse);
                        if(ret == false)
                        {
                            bear::WARN("deepseekProvider parseFromStream error !");
                            continue;
                        }
                        //op3:处理 + 提取
                        if(responseBody.isMember("choices") && responseBody["choices"].isArray() && !responseBody["choices"].empty())
                        {
                            auto choices = responseBody["choices"][0];
                            if(choices.isMember("delta") && choices["delta"].isObject() && choices["delta"].isMember("content"))
                            {
                                auto content = choices["delta"]["content"].asString();
                                if(!content.empty())
                                {
                                    fullMsg.append(content);
                                }
                                if(callback)
                                    callback(content,false);
                            }
                        }
                    }
                }
            }
            return true;
        };

        //op4:发送请求
        auto ret = client.send(req);
        if(!ret)
        {
            auto errs = ret.error();
            bear::ERROR("deepseekProvider req:Network issues (server connection timeout), DNS lookup failure,{}",to_string(errs));
            return "";
        }
        //op5:完整流程
        if(!streamFinish)
        {
            bear::ERROR("deepseekProvider streamResponse FAILED");
            callback("",false);
            return "";
        }
        bear::INFO("the full content is '{}'",fullMsg);
        return fullMsg;
    }
相关推荐
didiplus2 小时前
Python 入门第三课:让程序"开口说话":90% 新手都忽略的输入输出技巧
后端
明月_清风2 小时前
宿命的对决:深度对比 JavaScript 与 Python 的异步进化论
后端·python
明月_清风3 小时前
别再纠结 Conda 了!2026 年,uv 才是 Python 环境管理的唯一真神
后端·python
salipopl3 小时前
Spring 中的 @ExceptionHandler 注解详解与应用
java·后端·spring
jessecyj3 小时前
SpringBoot详解
java·spring boot·后端
爱吃的小肥羊3 小时前
刚刚!Claude最强大模型泄露,Anthropic紧急封锁
后端
qqty12173 小时前
Spring Boot管理用户数据
java·spring boot·后端
bearpping4 小时前
SpringBoot最佳实践之 - 使用AOP记录操作日志
java·spring boot·后端
一叶飘零_sweeeet4 小时前
线上故障零扩散:全链路监控、智能告警与应急响应 SOP 完整落地指南
java·后端·spring