【AI大模型接入SDK】DeepSeek 流式响应(增量返回)的实现和参数
个人主页艾莉丝努力练剑❄专栏传送门《C语言》《数据结构与算法》《C/C干货分享学习过程记录》《Linux操作系统编程详解》《笔试/面试常见算法从基础到进阶》《Python干货分享》⭐️为天地立心为生民立命为往圣继绝学为万世开太平 艾莉丝的简介文章目录1 ~ SSE 流式响应基础原理1.1 流式响应核心概念1.2 SSE 协议数据规范1.3 cpp-httplib 流式处理机制2 ~ DeepSeek 流式响应接口实现2.1 接口定义与职责边界2.1.1 方法签名2.1.2 回调契约2.2 完整实现流程2.2.1 前置校验2.2.2 请求参数组装2.2.3 消息体构造2.2.4 请求体序列化2.2.5 HTTP 客户端初始化2.2.6 请求头配置2.2.7 流式上下文变量2.2.8 请求对象构建2.2.9 响应处理器2.2.10 数据接收与解析处理器2.2.11 请求发送与异常处理2.2.12 流结束兜底逻辑3 ~ 编译问题定位与修复3.1 错误现象3.2 根因分析3.3 修复方案4 ~ 单元测试用例设计4.1 测试依赖环境4.2 测试用例实现4.2.1 初始化与配置4.2.2 流式回调实现4.2.3 结果断言4.3 入口函数5 ~ 构建运行与结果验证5.1 构建配置5.1.1 CMakeLists.txt5.2 编译执行5.3 验证标准结尾1 ~ SSE 流式响应基础原理1.1 流式响应核心概念定义大模型服务端采用边推理、边生成、边推送的机制通过 HTTP 长连接将生成结果逐块返回客户端而非等待完整结果生成后一次性返回。核心价值降低用户等待感知时长模拟打字机交互效果大幅提升生成式对话的交互体验。技术基础基于 Server-Sent EventsSSEHTTP 流式协议实现是当前大模型对话接口的标准交互模式。1.2 SSE 协议数据规范分隔规则每条事件数据以两个连续换行符\n\n作为事件边界分隔符。标准字段data必填字段承载消息的实际载荷内容。event可选字段标识事件类型。id可选字段消息唯一标识。retry可选字段连接断开后的重连间隔单位为毫秒。流结束标记服务端传输完成后发送data: [DONE]作为流式传输终止标识。1.3 cpp-httplib 流式处理机制核心回调接口content_receiver类型签名std::functionbool(const char* data, size_t len, uint64_t offset, uint64_t totalLength)参数说明data当前接收到的数据块内存指针。len当前数据块的字节长度。offset当前数据块在完整响应体中的字节偏移量。totalLength响应体的总字节长度。返回值语义返回true表示继续接收后续数据返回false表示终止数据接收。工作模式一旦为请求设置content_receiver回调HTTP 请求自动进入非阻塞模式每收到一小块网络数据立即触发回调无需等待完整响应体接收完成。配套回调response_handler在 HTTP 响应头接收完成时触发且仅触发一次用于校验响应状态码与响应头合法性。2 ~ DeepSeek 流式响应接口实现2.1 接口定义与职责边界2.1.1 方法签名namespace ai_chat_sdk { class DeepSeekProvider { public: /** * brief 发送消息并接收流式增量响应 * param messages 历史对话消息列表 * param requestParam 请求参数映射temperature、max_tokens等 * param callback 增量数据回调函数由调用方实现业务逻辑 * return std::string 完整的响应文本所有增量拼接结果 */ std::string sendMessageStream( const std::vectorMessage messages, const std::mapstd::string, std::string requestParam, std::functionvoid(const std::string, bool) callback ); }; }2.1.2 回调契约第一个参数模型返回的增量文本片段仅包含本次新生成的内容。第二个参数流结束标记true表示所有数据已返回完毕false表示仍有后续数据。职责边界SDK 仅负责解析协议并推送增量数据数据的业务处理控制台打印、界面渲染、文件写入等完全由上层调用方实现。2.2 完整实现流程2.2.1 前置校验// 1. 模型可用性校验 if (!isAvailable()) { ERR(DeepSeekProvider sendMessageStream model not available); return ; }校验 API 密钥与服务端点是否已正确初始化不可用则直接返回空字符串并记录错误日志。2.2.2 请求参数组装// 2. 构造请求参数设置默认值 double temperature 0.7; int maxTokens 2048; // 覆盖用户传入的自定义参数 if (requestParam.find(temperature) ! requestParam.end()) { temperature std::stod(requestParam.at(temperature)); } if (requestParam.find(max_tokens) ! requestParam.end()) { maxTokens std::stoi(requestParam.at(max_tokens)); }采用默认值兜底 用户参数覆盖的设计保证参数合法性与灵活性。2.2.3 消息体构造// 3. 构造历史消息JSON数组 Json::Value messageArray(Json::arrayValue); for (const auto message : messages) { Json::Value messageObject; messageObject[role] message._role; messageObject[content] message._content; messageArray.append(messageObject); }将内部Message结构体转换为符合 DeepSeek API 规范的 JSON 消息数组格式。2.2.4 请求体序列化// 4. 构造请求体并序列化 Json::Value requestBody; requestBody[model] getModelName(); requestBody[messages] messageArray; requestBody[temperature] temperature; requestBody[max_tokens] maxTokens; requestBody[stream] true; // 核心字段开启流式响应模式 Json::StreamWriterBuilder writerBuilder; writerBuilder[indentation] ; // 关闭缩进生成紧凑JSON std::string requestBodyStr Json::writeString(writerBuilder, requestBody);与非流式请求的核心差异新增stream: true字段通知服务端以 SSE 格式返回增量数据。2.2.5 HTTP 客户端初始化// 5. 构造HTTP客户端并配置超时 httplib::Client client(_endpoint.c_str()); client.set_connection_timeout(30, 0); // 连接超时30秒 client.set_read_timeout(300, 0); // 读取超时300秒适配长时长流式生成读取超时显著长于普通请求避免大模型长文本生成过程中连接被提前断开。2.2.6 请求头配置// 6. 设置HTTP请求头 httplib::Headers headers { {Authorization, Bearer _apikey}, {Content-Type, application/json}, {Accept, text/event-stream} // 声明客户端接受SSE事件流格式 };新增Accept: text/event-stream请求头明确告知服务端客户端支持流式响应协议。2.2.7 流式上下文变量// 7. 定义流式处理共享上下文变量 std::string buffer; // 数据接收缓冲区用于粘包处理 bool gotError false; // 响应错误标记 std::string errorMsg; // 错误详情描述 bool streamFinish false; // 流式传输完成标记 std::string fullResponse; // 完整响应累积结果所有变量通过 lambda 引用捕获在多个回调函数之间共享状态。2.2.8 请求对象构建// 8. 创建HTTP请求对象 httplib::Request req; req.method POST; req.path /v1/chat/completions; req.headers headers; req.body requestBodyStr;2.2.9 响应处理器// 9. 设置响应处理器响应头到达时触发一次 req.response_handler [](const httplib::Response res) - bool { if (res.status ! 200) { gotError true; errorMsg HTTP status code: std::to_string(res.status); return false; // 状态码异常终止后续数据接收 } return true; // 响应正常继续接收数据 };仅负责 HTTP 层状态校验不处理响应体内容状态异常时直接终止数据流。2.2.10 数据接收与解析处理器// 10. 设置数据接收处理器每收到一块数据触发一次 req.content_receiver [](const char* data, size_t len, size_t offset, size_t totalLength) - bool { // 已发生错误则直接终止 if (gotError) return false; // 数据追加到缓冲区 buffer.append(data, len); // 循环解析所有完整事件块 size_t pos 0; while ((pos buffer.find(\n\n, pos)) ! std::string::npos) { std::string chunk buffer.substr(0, pos); buffer.erase(0, pos 2); // 移除已处理事件分隔符 // 跳过空块与注释行 if (chunk.empty() || chunk.front() :) continue; // 提取data字段载荷 if (chunk.compare(0, 6, data:) 0) { std::string modelData chunk.substr(6); // 检测流结束标记 if (modelData [DONE]) { streamFinish true; return true; } // JSON反序列化 Json::Value modelDataJson; Json::CharReaderBuilder reader; std::string errors; std::istringstream modelDataStream(modelData); if (!Json::parseFromStream(reader, modelDataStream, modelDataJson, errors)) { gotError true; errorMsg JSON parse error: errors; return false; } // 提取增量内容并回调 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; };核心逻辑通过缓冲区实现 TCP 粘包处理严格按照 SSE 协议边界解析事件解析出增量文本后同时完成完整响应累积与上层回调通知。2.2.11 请求发送与异常处理// 11. 发送HTTP请求 auto result client.send(req); if (!result) { // 连接建立阶段失败DNS解析失败、连接超时等 ERR(Network error {}, to_string(result.error())); return ; }注意设置content_receiver后send方法为非阻塞调用返回值仅表示连接建立阶段是否成功。2.2.12 流结束兜底逻辑// 12. 流式结束兜底未收到DONE标记时主动触发结束回调 if (!streamFinish) { WARN(stream ended without [DONE] marker); callback(, true); } return fullResponse;应对网络异常断开等场景保证上层回调总能收到结束通知避免状态机挂死。3 ~ 编译问题定位与修复3.1 错误现象编译报错spdlog日志宏无法格式化httplib::Error类型参数提示类型不匹配。触发代码ERR(Network error {}, result.error());3.2 根因分析httplib::Error是 C11 强类型枚举enum class不支持隐式类型转换spdlog 默认格式化器无法识别该类型。cpp-httplib 库内部提供了std::string to_string(Error error)转换函数与operator重载但未被 spdlog 自动适配。3.3 修复方案// 修复前编译失败 ERR(Network error {}, result.error()); // 修复后编译通过 ERR(Network error {}, to_string(result.error()));显式调用httplib::to_string()将枚举值转换为字符串再传入日志宏进行格式化。4 ~ 单元测试用例设计4.1 测试依赖环境测试框架Google Testgtest日志组件spdlog第三方依赖jsoncpp、cpp-httplib、OpenSSL4.2 测试用例实现4.2.1 初始化与配置#include spdlog/common.h #include ../sdk/include/DeepSeekProvider.h #include ../sdk/include/util/myLog.h TEST(DeepSeekProviderTest, sendMessageStream) { // 实例化Provider对象 auto provider std::make_sharedai_chat_sdk::DeepSeekProvider(); ASSERT_TRUE(provider ! nullptr); // 模型初始化 std::mapstd::string, std::string modelParam; modelParam[api_key] std::getenv(deepseek_apikey); modelParam[endpoint] https://api.deepseek.com; provider-initModel(modelParam); ASSERT_TRUE(provider-isAvailable()); // 请求参数配置 std::mapstd::string, std::string requestParam { {temperature, 0.7}, {max_tokens, 2048} }; // 构造测试消息 std::vectorai_chat_sdk::Message messages; messages.push_back({user, 你是谁?});4.2.2 流式回调实现// 定义增量数据处理回调 auto writeChunk [](const std::string chunk, bool last) { INFO(chunk: {}, chunk); if (last) { INFO([DONE]); } }; // 调用流式接口 std::string fullData provider-sendMessageStream(messages, requestParam, writeChunk);采用 lambda 表达式实现回调将增量内容打印到控制台结束时输出 DONE 标记。4.2.3 结果断言// 校验完整响应非空 ASSERT_FALSE(fullData.empty()); INFO(response: {}, fullData); }4.3 入口函数int main(int argc, char *argv[]) { // 初始化日志库 bite::Logger::initLogger(testLLM, stdout, spdlog::level::debug); // 初始化Google Test testing::InitGoogleTest(argc, argv); return RUN_ALL_TESTS(); }5 ~ 构建运行与结果验证5.1 构建配置5.1.1 CMakeLists.txtcmake_minimum_required(VERSION 3.10) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) project(testLLM) set(CMAKE_BUILD_TYPE Debug) # 添加可执行文件 add_executable(testLLM testLLM.cpp ../sdk/src/util/myLog.cpp ../sdk/src/DeepSeekProvider.cpp ) # 头文件路径 include_directories(${CMAKE_PROJECT_SOURCE_DIR}/../sdk/include) find_package(OpenSSL REQUIRED) include_directories(${OPENSSL_INCLUDE_DIR}) # 编译宏 target_compile_definitions(testLLM PRIVATE CPPHTTPLIB_OPENSSL_SUPPORT) # 链接库 target_link_libraries(testLLM jsoncpp fmt spdlog gtest OpenSSL::SSL OpenSSL::Crypto )5.2 编译执行# 进入构建目录cdtest/build# 生成构建文件cmake..# 清理并编译makecleanmake# 运行测试程序./testLLM5.3 验证标准编译阶段无语法错误与类型错误可执行文件生成成功。运行阶段程序无崩溃退出网络异常时有明确的错误日志输出。流式特性数据按增量逐块返回顺序正确无乱序呈现打字机输出效果。完整性最终累积的完整响应与非流式接口返回结果语义一致无内容缺失。协议合规流结束时正确触发lasttrue回调DONE 标记处理逻辑正常。测试结果gtest 用例输出[ PASSED ]所有断言校验通过。结尾uu们本文的内容到这里就全部结束了艾莉丝在这里再次感谢您的阅读艾莉丝努力练剑C/C Linux 底层探索者 | 一个正在努力练剑的技术博主【关注】跟随我一起深耕技术领域见证每一次成长。❤️【点赞】让优质内容被更多人看见让知识传递更有力量。⭐【收藏】把核心知识点存好在需要时随时查、随时用。【评论】分享你的经验或疑问评论区一起交流避坑不要忘记给博主“一键四连”哦“今日练剑达成”“技术之路难免有困惑但同行的人会让前进更有方向。”结语希望对学习Linux相关内容的uu有所帮助不要忘记给博主“一键四连”哦往期回顾【AI大模型接入SDK】httplib Request参数与HTTP流式响应博主在这里放了一只小狗大家看完了摸摸小狗放松一下吧૮₍ ˶ ˊ ᴥ ˋ˶₎ა

相关新闻

ThreadX开源全解析:嵌入式RTOS选型与资源索引

ThreadX开源全解析:嵌入式RTOS选型与资源索引

简介:这是一份面向嵌入式开发者和实时操作系统学习者的完整资源包,内容覆盖系统源码、教学电子书与配套教学文件。压缩包内共有一百三十四个文件,主体为一百个C语言源码文件与十二个头文件,可完整查看任务管理、同步互斥与内存管理…

2026/9/3 20:46:51 阅读更多 →
攻击者终端被信息窃取程序反向感染暴露钓鱼攻击基础设施研究 —— 基于 Blind Eagle 关联哥伦比亚定向攻击案例

攻击者终端被信息窃取程序反向感染暴露钓鱼攻击基础设施研究 —— 基于 Blind Eagle 关联哥伦比亚定向攻击案例

摘要 网络钓鱼攻击长期依赖攻击者自身的运营终端、恶意软件构建环境与基础设施完成攻击活动,这些资产通常处于防御方视野之外。本文以 LevelBlue 披露的 Blind Eagle 关联攻击组织基础设施暴露事件为研究样本,该事件中攻击者运营终端被第三方信息窃取程…

2026/9/3 20:46:51 阅读更多 →
MOE基于结构的药物设计(十三):Pharmacophore Constraints——如何让Docking保留关键蛋白–配体相互作用?

MOE基于结构的药物设计(十三):Pharmacophore Constraints——如何让Docking保留关键蛋白–配体相互作用?

上一篇中,我们利用 Template-Based Docking,在保留 Tarceva analog 的 quinazoline core 结合模式的同时,探索了柔性侧链的不同构象。但这种方法依赖配体与 template 之间存在明确的共同骨架。当研究对象从一个 Tarceva analog 扩展到一批结构…

2026/9/3 20:46:51 阅读更多 →

最新新闻

阿里云ECS服务器从零部署 OpenClaw 龙虾|保姆级教程:对接 QQ 机器人单聊操控 AI 数字员工

阿里云ECS服务器从零部署 OpenClaw 龙虾|保姆级教程:对接 QQ 机器人单聊操控 AI 数字员工

OpenClaw是什么 年初,由 ClawdBot 更名而来的 OpenClaw 热度暴涨。它是一款可以直接操控电脑完成各类工作的 AI 数字员工,支持读写本地文件、编写代码、执行各类自动化任务,可以 724 小时不间断运行。最方便的是,你拿出手机&…

2026/9/3 21:32:41 阅读更多 →
2018年以前,做一个情感分析要标注1万条数据;GPT说:不用,让模型先“读完”互联网

2018年以前,做一个情感分析要标注1万条数据;GPT说:不用,让模型先“读完”互联网

2018年以前,做一个情感分析要标注1万条数据;GPT说:不用,让模型先“读完”互联网一句话先睹为快:GPT不是Transformer架构的简单复用,而是一场以“Decoder-Only 因果语言建模”为核心的生成式AI范式革命——…

2026/9/3 21:32:41 阅读更多 →
集团企业运营管理转型五步法【附全文阅读】

集团企业运营管理转型五步法【附全文阅读】

这份 50 页集团运营转型五步法 PPT 是制造、重工、能源类企业精益咨询、管理提升项目投标、内训宣讲核心实战方案,标准化落地框架复用价值极高。文档独创准备 - 诊断 - 设计 - 计划实施 - 固化完善五步闭环转型方法论,完整覆盖运营系统、管理体系、员工理…

2026/9/3 21:32:41 阅读更多 →
C++工程化:CMake构建与多文件项目管理

C++工程化:CMake构建与多文件项目管理

本文是 C 系列教程的第 26 篇。上一篇完成了并发编程三篇,本篇进入工程化阶段:CMake 构建系统从零到实战、多文件项目组织、可执行文件与静态/动态库构建、跨平台配置与安装部署,覆盖 10 个完整示例代码。一、为什么需要 CMake 1.1 从手动编译…

2026/9/3 21:32:41 阅读更多 →
二战美军M2迫击炮全解析:60毫米轻迫与4.2英寸重迫的实战分工

二战美军M2迫击炮全解析:60毫米轻迫与4.2英寸重迫的实战分工

这次我们来看一套容易被标题误导、但实战价值非常高的曲射武器系统:二战美军的M2迫击炮家族。单看“射程四千四百米”这句话,很多人会想到某种大口径重型迫击炮;再看“轻迫”两个字,又会想到步兵连里那门可以扛着跑的小炮。这种拧…

2026/9/3 21:32:41 阅读更多 →
Qt 控制台工程实战:从零创建无界面命令行工具

Qt 控制台工程实战:从零创建无界面命令行工具

简介:面向Qt初学者的一套控制台工程示例包,演示如何利用Qt框架创建并管理非GUI应用程序,解决只需命令行交互却希望沿用Qt核心库、信号槽与跨平台能力的开发场景。资源共3个文件,包含cpp源码、pro工程配置和Qt Creator用户配置文件…

2026/9/3 21:31:41 阅读更多 →

日新闻

AI智能体辅助JS逆向:从V8环境搭建到补环境实战

AI智能体辅助JS逆向:从V8环境搭建到补环境实战

先别急着点开,这不是劝退文,而是想讲清楚一件事:用 AI 做逆向值不值得学?如果要用,怎么搭一套“V8 环境 AI 智能体”来提升效率。最近逆向圈、爬虫圈都在聊 AI Agent、AST 工程逆向、JS 逆向这些词,很多新手…

2026/9/3 0:00:29 阅读更多 →
安卓设备通过修改机型信息解锁游戏高帧率:原理、操作与风险指南

安卓设备通过修改机型信息解锁游戏高帧率:原理、操作与风险指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/3 0:00:29 阅读更多 →
ARM版OpenJDK 11安装部署全攻略:下载、配置与避坑指南

ARM版OpenJDK 11安装部署全攻略:下载、配置与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/3 0:00:29 阅读更多 →

周新闻

备战数据库管理工程师校招:索引、事务、备份恢复核心考点解析

备战数据库管理工程师校招:索引、事务、备份恢复核心考点解析

每年校招季我都会接触不少准备数据库方向笔试的同学,看到最多的状态就是:简历上写着“熟悉 MySQL”“了解索引优化”,一碰到数据库管理工程师的笔试卷,却在索引、事务、锁、备份恢复这些题目上翻车。网易这套 2018 校园招聘数据库…

2026/9/3 4:22:22 阅读更多 →
数字电路时序基石:深入理解建立时间与保持时间

数字电路时序基石:深入理解建立时间与保持时间

1. 这不是“背公式”的事:时间参数到底在约束什么你翻过数字电路教材,一定见过这两个词:建立时间(Setup Time)和保持时间(Hold Time)。它们常被并列写在触发器(Flip-Flop&#xff09…

2026/9/3 4:22:01 阅读更多 →
蓝桥杯国赛超声波测距机:从单片机原理到嵌入式系统实战

蓝桥杯国赛超声波测距机:从单片机原理到嵌入式系统实战

1. 项目缘起:从赛题到超声波测距机的诞生第八届蓝桥杯单片机设计与开发国赛的题目,我至今记忆犹新。它没有直接给出一个花哨的名字,而是用“超声波测距机”这个朴实无华的功能描述,精准地勾勒出了考核的核心。对于当时备赛的我而言…

2026/9/3 4:22:59 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/3 4:17:49 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/3 4:18:56 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/3 4:21:44 阅读更多 →