文章目录
- [一、整体目录结构(服务端 和 前端)](#一、整体目录结构(服务端 和 前端))
- 二、接口设计
-
- [1. RESTful API](#1. RESTful API)
- [2. 接口设计](#2. 接口设计)
-
- [2.1 创建新会话](#2.1 创建新会话)
- [2.2 获取会话列表](#2.2 获取会话列表)
- [2.3 获取可用模型列表](#2.3 获取可用模型列表)
- [2.4 删除会话](#2.4 删除会话)
- [2.5 获取历史消息](#2.5 获取历史消息)
- [2.6 发送消息 - 全量返回](#2.6 发送消息 - 全量返回)
- [2.7 发送消息 - 流式响应](#2.7 发送消息 - 流式响应)
- 三、服务端实现
-
- [1. ChatServer.cpp(服务器实现)的完整解析](#1. ChatServer.cpp(服务器实现)的完整解析)
- [2. main.cpp(主程序)的作用](#2. main.cpp(主程序)的作用)
- [3. CMakeLists.txt 构建 AIChatServer 的流程](#3. CMakeLists.txt 构建 AIChatServer 的流程)
- 三、前端实现(由AI实现)
-
- [1. 生成前端页面的提示词(要包含详细的json协议描述)](#1. 生成前端页面的提示词(要包含详细的json协议描述))
- [2. 前端页面的效果展示](#2. 前端页面的效果展示)
- [四、从前端 到服务端 再到SDK 的 HTTP 通讯全流程](#四、从前端 到服务端 再到SDK 的 HTTP 通讯全流程)
-
- [1. 整体架构概览](#1. 整体架构概览)
- [2. 普通响应(全量返回)流程](#2. 普通响应(全量返回)流程)
- [3. 流式响应(SSE)流程](#3. 流式响应(SSE)流程)
- [4. 关键差异对比](#4. 关键差异对比)
一、整体目录结构(服务端 和 前端)
bash
ChatServer/
├── ChatServer.cpp # 服务器实现
├── ChatServer.h # 服务器头文件
├── main.cpp # 程序入口
├── CMakeLists.txt # CMake 构建配置
└── build/ # 构建输出目录
├── AIChatServer # 可执行文件
├── ChatServer.conf # 配置文件
├── www/ # 静态资源目录
│ ├── index.html
│ ├── style.css
│ └── app.js
└── CMakeFiles/... # CMake 构建中间文件
二、接口设计
1. RESTful API
由于整个和大模型交互都是基于HTTP协议,聊天助手将来也是浏览器借助HTTP协议和服务器交互,因此本项目接口采用 RESTful API风格进行设计,它是基于HTTP协议的应用接口设计规划,提供了一种通过标准化操作和资源访问模式进行客户端和服务器通信的方式。
REST是 Representational State Transfer 的缩写,翻译过来就是表现层状态转移。
-
资源(Resource)
RESTful API 中的每一个对象、实体或数据都被抽象为一个资源。例如,用户、文章 等都可以
作为资源。每个资源都通过⼀个唯⼀的 URI (统⼀资源标识符)标识。
-
URI(统一资源标识)
URI是用于标识资源的地址。 RESTful API 中,通常使用 URL (统一资源定位符)作为URI 。例如:
(1) /users/123 表示 id 为 123 的用户资源
(2) /posts/456 表示 id 为 456 的文章资源
(3) HTTP动作(HTTP Methods)
RESTful API 依赖于 HTTP 协议的常见方法来对资源进行操作,每个 HTTP 方法对应不同的操作:
(1) GET :获取资源
(2) POST :创建新的资源
(3) PUT :更新资源
(4) DELETE :删除资源
-
无状态
每个请求都是独立的,服务器不会保存客户端任何会话状态。客户端发来的每一个请求,都必须包含服务器处理该请求所需的所有信息。
-
表现层状态转移(Representational State Transfer)
资源的表现形式可以是 JSON 、 XML 、 HTML 等格式,通常 RESTful API 使用 JSON 作为数据交换格式,因为它轻量且易于解析。
请求URL: POST /api/user/id
请求参数
| 字段名称 | 字段类型 | 字段说明 |
|---|---|---|
| requestId | string | 请求Id |
| sessionId | string | 客户端会话Id |
| name | string | 用户名 |
| string | 邮箱 |
bash
{
"requestId": "string",
"errorCode": 0,
"errorMsg": "",
"result" : {
"userId": "string"
}
}
返回响应: 200 OK
| 字段名称 | 字段类型 | 字段说明 |
|---|---|---|
| requestId | string | 请求Id |
| errorCode | integer | 错误码;0-成功 |
| errorMsg | string | 错误信息 |
| result | object | 响应结果 |
| userId | string | 用户Id |
cpp
{
"requestId": "string",
"errorCode": 0,
"errorMsg": "",
"result" : {
"userId": "string"
}
}
2. 接口设计
2.1 创建新会话
请求URL: POST /api/session
请求参数:
| 字段名称 | 字段类型 | 字段说明 |
|---|---|---|
| model | string | 模型名称 |
cpp
{
"model" : "string"
}
返回响应: 200 OK
| 字段名称 | 字段类型 | 字段说明 |
|---|---|---|
| success | bool | 是否成功 |
| message | string | 结果描述 |
| data | object | 响应数据 |
| session_id | string | 会话id |
| model | string | 模型名称 |
cpp
{
"success" : "bool",
"message" : "string",
"data":
{
"session_id" : "string",
"model" : "string"
}
}
2.2 获取会话列表
请求URL: GET /api/sessions
返回响应: 200 OK
| 字段名称 | 字段类型 | 字段说明 |
|---|---|---|
| success | bool | 是否成功 |
| message | string | 结果描述 |
| data | array | 响应数据 |
| id | string | 会话id |
| model | string | 模型名称 |
| created_at | int64_t | 创建时间戳 |
| updated_at | int64_t | 更新时间戳 |
| message_count | int | 对话次数 |
| first_user_message | string | 本次会话的第⼀条正文消息 |
cpp
{
"success" : "bool",
"message" : "string",
"data": "array"[
{
"id" : "string",
"model" : "string",
"created_at" : "int64_t",
"updated_at" : "int64_t",
"message_count" : "int",
"first_user_message" : "string"
}
]
}
2.3 获取可用模型列表
请求URL: GET /api/models
返回响应: 200 OK
| 字段名称 | 字段类型 | 字段说明 |
|---|---|---|
| success | bool | 是否成功 |
| message | string | 结果描述 |
| data | object | 响应数据 |
| name | string | 模型名称 |
| desc | string | 模型描述 |
cpp
{
"success" : "bool",
"message" : "string",
"data": "array"[
{
"name" : "string",
"desc" : "string"
}
]
}
2.4 删除会话
请求URL: DELETE /api/session/${session_id}
返回响应: 200 OK
| 字段名称 | 字段类型 | 字段说明 |
|---|---|---|
| success | bool | 是否成功 |
| message | string | 结果描述 |
cpp
{
"success" : "bool",
"message" : "string"
}
2.5 获取历史消息
请求URL: GET /api/session/${session_id}/history
返回响应: 200 OK
| 字段名称 | 字段类型 | 字段说明 |
|---|---|---|
| success | bool | 是否成功 |
| message | string | 结果描述 |
| data | array | 响应数据 |
| id | string | 消息id |
| role | string | 消息类型 |
| content | string | 消息正文 |
| timestamp | int64_t | 消息时间戳(秒级) |
cpp
{
"success" : "bool",
"message" : "string",
"data": "array"[
{
"id" : "string",
"role" : "string",
"content" : "string",
"timestamp" : 0,
}
]
}
2.6 发送消息 - 全量返回
请求URL: POST /api/message
请求参数:
| 字段名称 | 字段类型 | 字段说明 |
|---|---|---|
| session_id | string | 会话id |
| message | string | 消息内容 |
bash
{
"session_id" : "string",
"message" : "string"
}
返回响应: 200 OK
| 字段名称 | 字段类型 | 字段说明 |
|---|---|---|
| success | bool | 是否成功 |
| message | string | 结果描述 |
| data | object | 响应数据 |
| session_id | string | 会话id |
| response | string | 模型返回 |
cpp
{
"success" : "bool",
"message" : "string",
"data":
{
"session_id" : "string",
"response" : "string"
}
}
2.7 发送消息 - 流式响应
请求URL: POST /api/message/async
请求参数:
| 字段名称 | 字段类型 | 字段说明 |
|---|---|---|
| session_id | string | 会话id |
| message | string | 消息内容 |
bash
{
"session_id" : "string",
"message" : "string"
}
返回响应: 200 OK
流式响应,格式如下
cpp
data: 正文
data: 正文
data: 正文
data: [DONE]
三、服务端实现
1. ChatServer.cpp(服务器实现)的完整解析
- ChatServer.h
cpp
#pragma once
#include <httplib.h>
#include <memory>
#include <ai_chat_sdk/ChatSDK.h>
namespace ai_chat_server{
// 服务器配置信息
struct ServerConfig{
std::string host = "0.0.0.0"; // 服务器绑定ip
int port = 8080; // 服务器绑定端口
std::string logLevel = "INFO"; // 日志级别
// 模型需要的配置信息
double temperature = 0.7; // 温度参数
int maxTokens = 1024; // 最大token数
// API Key
std::string deepseekAPIKey; // deepseek API Key
std::string geminiAPIKey; // gemini API Key
std::string chatGPTAPIKey; // chatGPT API Key
// Ollama
std::string ollamaModelName; // Ollama模型名称
std::string ollamaModelDesc; // Ollama模型描述
std::string ollamaEndpoint; // Ollama的url地址
};
class ChatServer{
public:
ChatServer(const ServerConfig& config);
bool start(); // 启动服务器
void stop(); // 停止服务器
bool isRunning()const; // 是否正在运行
private:
// 构造响应体 (默认是错误响应体,JSON格式)
std::string buildResponse(const std::string& message, bool success = false);
// 处理创建会话请求
void handleCreateSessionRequest(const httplib::Request& request, httplib::Response& response);
// 处理获取会话列表请求
void handleGetSessionListsRequest(const httplib::Request& request, httplib::Response& response);
// 处理获取模型列表请求
void handleGetModelListsRequest(const httplib::Request& request, httplib::Response& response);
// 处理删除会话请求
void handleDeleteSessionRequest(const httplib::Request& request, httplib::Response& response);
// 处理获取历史消息请求
void handleGetHistoryMessagesRequest(const httplib::Request& request, httplib::Response& response);
// 处理发送消息请求-全量返回
void handleSendMessageRequest(const httplib::Request& request, httplib::Response& response);
// 处理发送消息请求-增量返回
void handleSendMessageStreamRequest(const httplib::Request& request, httplib::Response& response);
// 设置HTTP路由规则
void setHttpRoutes();
private:
ServerConfig _config; // 服务器配置信息
std::unique_ptr<httplib::Server> _chatServer = nullptr; // HTTP服务器
std::shared_ptr<ai_chat_sdk::ChatSDK> _chatSDK = nullptr; // 聊天SDK
std::atomic<bool> _isRunning = {false}; // 是否正在运行
};
} // end ai_chat_server
- ChatServer.cpp
cpp
#include "ChatServer.h"
#include <ai_chat_sdk/util/mylog.h>
#include <jsoncpp/json/json.h>
#include <thread>
namespace ai_chat_server{
ChatServer::ChatServer(const ServerConfig& config)
: _config(config)
{
// 初始化ChatSDK实例 (参数是 数据库名称, 可以不填, 默认是 "ai_chat.db")
_chatSDK = std::make_shared<ai_chat_sdk::ChatSDK>();
// 提供所有模型提供者的配置信息
std::vector<std::shared_ptr<ai_chat_sdk::Config>> config_list;
config_list.push_back(std::make_shared<ai_chat_sdk::ApiConfig>("deepseek-v4-flash", config.temperature, config.maxTokens, config.deepseekAPIKey,
"https://api.deepseek.com"));
config_list.push_back(std::make_shared<ai_chat_sdk::ApiConfig>("gemini-2.5-flash", config.temperature, config.maxTokens, config.geminiAPIKey,
"https://generativelanguage.googleapis.com"));
config_list.push_back(std::make_shared<ai_chat_sdk::ApiConfig>("gpt-5.4-mini", config.temperature, config.maxTokens, config.chatGPTAPIKey,
"https://api.openai.com"));
config_list.push_back(std::make_shared<ai_chat_sdk::OllamaConfig>(config.ollamaModelName, config.temperature, config.maxTokens, config.ollamaEndpoint,
config.ollamaModelDesc));
// ai_chat_sdk::ChatSDK对象 注册并初始化所有模型提供者
if(!_chatSDK->registerAndInit_AllProviders(config_list))
{
ERR("Failed to register and initialize all providers.");
return;
}
INFO("All providers registered and initialized successfully.");
// 创建http服务器实例
_chatServer = std::make_unique<httplib::Server>();
}
// 启动服务器
bool ChatServer::start()
{
if(_isRunning.load())
{
ERR("ChatServer is running!!!");
return false;
}
// 设置路由规则
setHttpRoutes();
// 设置静态资源的路径
// 前端页面相关的所有文件都放在www目录下 注意:将来前端页面名称命名为index.html
// 当用户在浏览器中输入:http://ip:port/index.html http://ip:port也能访问index.html页面
// 在httplib中,默认情况下,如果请求路径中只有ip和端口,httplib默认会使用index.html文件
_chatServer->set_mount_point("/", "./www");
// 为了不卡服务器,不卡主线程,服务器在单独的线程中运行
std::thread serverThread([this](){
INFO("ChatServer start on {} :{}", _config.host, _config.port);
// 启动服务器 (开始监听 HTTP 请求)
_chatServer->listen(_config.host, _config.port);
// httplib::Server::listen() 是 阻塞的,会一直运行直到调用 httplib::Server::stop() 方法
INFO("ChatServer end on {} :{}", _config.host, _config.port);
});
serverThread.detach(); // 分离线程,将线程资源从主线程中分离出来,避免主线程阻塞
_isRunning.store(true);
INFO("ChatServer start success!!!");
return true;
}
// 停止服务器
void ChatServer::stop()
{
if(!_isRunning.load()){
ERR("ChatServer is not running!!!");
return;
}
// 当你调用 httplib::Server::stop() 方法:
// 1. 正在运行的 HTTP 服务器会停止接受新连接 - 服务器不再接受新的 HTTP 请求
// 2. 等待现有连接完成 - 允许当前正在处理的请求完成(优雅关闭)
// 3. 释放服务器资源 - 清理服务器占用的资源
if(_chatServer){
_chatServer->stop();
// httplib::Server::stop() 方法用于 停止正在运行的 HTTP 服务器
}
_isRunning.store(false);
INFO("ChatServer stop success!!!");
}
// 是否正在运行
bool ChatServer::isRunning()const
{
return _isRunning.load();
}
// 构造响应体 (默认是错误响应体,JSON格式)
std::string ChatServer::buildResponse(const std::string& message, bool success)
{
Json::Value root;
root["success"] = success;
root["message"] = message;
// 先定义 Json::StreamWriterBuilder工厂类
Json::StreamWriterBuilder swber;
// 通过工厂类 Json::StreamWriterBuilder 构建Json::StreamWriter类对象
std::unique_ptr<Json::StreamWriter> swb(swber.newStreamWriter());
// 通过Json::StreamWriter类中的 writer接口进行序列化
std::stringstream ss;
swb->write(root, &ss);
std::string response = ss.str();
return response;
}
// 处理创建会话请求
void ChatServer::handleCreateSessionRequest(const httplib::Request& request, httplib::Response& response)
{
Json::Value val;
// 从请求体中获取JSON字符串
std::string json_str = request.body;
// 先定义 Json::CharReaderBuilder工厂类
Json::CharReaderBuilder srder;
std::unique_ptr<Json::CharReader> srd(srder.newCharReader());
// 通过Json::CharReader类中的 parse接口进行反序列化
std::string error;
bool ret = srd->parse(json_str.c_str(), json_str.c_str() + json_str.size(), &val, &error);
if(!ret)
{
// 解析JSON字符串失败
std::string errorJsonStr = buildResponse("Failed to parse JSON string", false);
response.set_content(errorJsonStr, "application/json");
response.status = 400; // 客户端发送的请求有语法错误,服务器无法理解或处理该请求
return;
}
std::string model_name = val["model"].asString();
if(model_name.empty())
{
std::string errorJsonStr = buildResponse("Model name is empty.", false);
response.set_content(errorJsonStr, "application/json");
response.status = 400; // 客户端发送的请求不符合规范,服务器无法处理该请求
return;
}
// 创建会话
std::string session_id = _chatSDK->createSession(model_name);
if(session_id.empty())
{
std::string errorJsonStr = buildResponse("Failed to create session.", false);
response.set_content(errorJsonStr, "application/json");
// 可能是模型名字有误,请用户检查
response.status = 500; // 服务器内部错误,无法创建会话
return;
}
// 构造响应体 (JSON格式)
Json::Value resp;
resp["success"] = true;
resp["message"] = "Session created successfully.";
Json::Value data;
data["session_id"] = session_id;
data["model"] = model_name;
resp["data"] = data;
// 先定义 Json::StreamWriterBuilder工厂类
Json::StreamWriterBuilder swber;
std::unique_ptr<Json::StreamWriter> swb(swber.newStreamWriter());
// 通过Json::StreamWriter类中的 writer接口进行序列化
std::stringstream ss;
swb->write(resp, &ss);
response.set_content(ss.str(), "application/json");
response.status = 200; // 成功
}
// 处理获取会话列表请求
void ChatServer::handleGetSessionListsRequest(const httplib::Request& request, httplib::Response& response)
{
std::vector<std::string> session_list = _chatSDK->getSessionList();
Json::Value resp;
resp["success"] = true;
resp["message"] = "Session list retrieved successfully.";
for(const auto& session_id : session_list)
{
std::shared_ptr<ai_chat_sdk::Session> session = _chatSDK->getSession(session_id);
session->_messages = _chatSDK->getSessionHistory(session_id);
Json::Value val;
val["id"] = session_id;
val["model"] = session->_model_name;
val["created_at"] = static_cast<int64_t>(session->_created_at);
val["updated_at"] = static_cast<int64_t>(session->_updated_at);
val["message_count"] = session->_messages.size();
val["first_user_message"] = "";
if(!session->_messages.empty())
{
val["first_user_message"] = session->_messages[0]._content;
}
resp["data"].append(val);
}
// 先定义 Json::StreamWriterBuilder工厂类
Json::StreamWriterBuilder swber;
std::unique_ptr<Json::StreamWriter> swb(swber.newStreamWriter());
// 通过Json::StreamWriter类中的 writer接口进行序列化
std::stringstream ss;
swb->write(resp, &ss);
response.set_content(ss.str(), "application/json");
response.status = 200; // 成功
}
// 处理获取模型列表请求
void ChatServer::handleGetModelListsRequest(const httplib::Request& request, httplib::Response& response)
{
std::vector<ai_chat_sdk::ModelInfo> model_list = _chatSDK->getAvailableModels();
Json::Value resp;
resp["success"] = true;
resp["message"] = "Model list retrieved successfully.";
for(const auto& model : model_list)
{
Json::Value val;
val["name"] = model._name;
val["desc"] = model._desc;
resp["data"].append(val);
}
// 先定义 Json::StreamWriterBuilder工厂类
Json::StreamWriterBuilder swber;
std::unique_ptr<Json::StreamWriter> swb(swber.newStreamWriter());
// 通过Json::StreamWriter类中的 writer接口进行序列化
std::stringstream ss;
swb->write(resp, &ss);
response.set_content(ss.str(), "application/json");
response.status = 200; // 成功
}
// 处理删除会话请求
void ChatServer::handleDeleteSessionRequest(const httplib::Request& request, httplib::Response& response)
{
// 获取会话id,注意:会话id是一个路径参数
std::string sessionId = request.matches[1];
// 删除会话
bool success = _chatSDK->deleteSession(sessionId);
if(!success)
{
std::string errorJsonStr = buildResponse("delete session failed, session not found", false);
response.set_content(errorJsonStr, "application/json");
response.status = 404; // 会话不存在
return;
}
std::string successJsonStr = buildResponse("delete session successfully", true);
response.set_content(successJsonStr, "application/json");
response.status = 200; // 成功
}
// 处理获取历史消息请求
void ChatServer::handleGetHistoryMessagesRequest(const httplib::Request& request, httplib::Response& response)
{
// 获取会话id,注意:会话id是一个路径参数
std::string sessionId = request.matches[1];
// 检查会话是否存在
std::shared_ptr<ai_chat_sdk::Session> session = _chatSDK->getSession(sessionId);
if(!session)
{
std::string errorJsonStr = buildResponse("session not found", false);
response.set_content(errorJsonStr, "application/json");
response.status = 404; // 会话不存在
return;
}
// 获取会话历史消息
std::vector<ai_chat_sdk::Message> message_list = _chatSDK->getSessionHistory(sessionId);
Json::Value resp;
resp["success"] = true;
resp["message"] = "Session history messages retrieved successfully.";
for(const auto& message : message_list)
{
Json::Value val;
val["id"] = message._id;
val["role"] = message._role;
val["content"] = message._content;
val["timestamp"] = static_cast<int64_t>(message._create_at);
resp["data"].append(val);
}
// 先定义 Json::StreamWriterBuilder工厂类
Json::StreamWriterBuilder swber;
std::unique_ptr<Json::StreamWriter> swb(swber.newStreamWriter());
// 通过Json::StreamWriter类中的 writer接口进行序列化
std::stringstream ss;
swb->write(resp, &ss);
response.set_content(ss.str(), "application/json");
response.status = 200; // 成功
}
// 处理发送消息请求-全量返回
void ChatServer::handleSendMessageRequest(const httplib::Request& request, httplib::Response& response)
{
Json::Value val;
// 从请求体中获取JSON字符串
std::string json_str = request.body;
// 先定义 Json::CharReaderBuilder工厂类
Json::CharReaderBuilder srder;
std::unique_ptr<Json::CharReader> srd(srder.newCharReader());
// 通过Json::CharReader类中的 parse接口进行反序列化
std::string error;
bool ret = srd->parse(json_str.c_str(), json_str.c_str() + json_str.size(), &val, &error);
if(!ret)
{
// 解析JSON字符串失败
std::string errorJsonStr = buildResponse("Failed to parse JSON string", false);
response.set_content(errorJsonStr, "application/json");
response.status = 400; // 客户端发送的请求有语法错误,服务器无法理解或处理该请求
return;
}
// 从JSON字符串中提取会话id和消息内容
std::string sessionId = val["session_id"].asString();
std::string message = val["message"].asString();
if(sessionId.empty() || message.empty()){
std::string errorJsonStr = buildResponse("session_id or message is empty");
response.status = 400; // 解析请求参数失败
response.set_content(errorJsonStr, "application/json");
return;
}
// 发送消息
std::string responseMessage = _chatSDK->sendMessage(sessionId, message);
if(responseMessage.empty()){
std::string errorJsonStr = buildResponse("send message failed", false);
response.set_content(errorJsonStr, "application/json");
response.status = 500; // 发送消息失败, 服务器内部错误
return;
}
// 解析响应消息
Json::Value resp;
resp["success"] = true;
resp["message"] = "Message sent successfully.";
Json::Value data;
data["session_id"] = sessionId;
resp["response"] = responseMessage;
resp["data"] = data;
// 先定义 Json::StreamWriterBuilder工厂类
Json::StreamWriterBuilder swber;
std::unique_ptr<Json::StreamWriter> swb(swber.newStreamWriter());
// 通过Json::StreamWriter类中的 writer接口进行序列化
std::stringstream ss;
swb->write(resp, &ss);
response.set_content(ss.str(), "application/json");
response.status = 200; // 成功
}
// 处理发送消息请求-增量返回 (SSE格式)
void ChatServer::handleSendMessageStreamRequest(const httplib::Request& request, httplib::Response& response)
{
Json::Value val;
// 从请求体中获取JSON字符串
std::string json_str = request.body;
// 先定义 Json::CharReaderBuilder工厂类
Json::CharReaderBuilder srder;
std::unique_ptr<Json::CharReader> srd(srder.newCharReader());
// 通过Json::CharReader类中的 parse接口进行反序列化
std::string error;
bool ret = srd->parse(json_str.c_str(), json_str.c_str() + json_str.size(), &val, &error);
if(!ret)
{
// 解析JSON字符串失败
std::string errorJsonStr = buildResponse("Failed to parse JSON string", false);
response.set_content(errorJsonStr, "application/json");
response.status = 400; // 客户端发送的请求有语法错误,服务器无法理解或处理该请求
return;
}
// 从JSON字符串中提取会话id和消息内容
std::string sessionId = val["session_id"].asString();
std::string message = val["message"].asString();
if(sessionId.empty() || message.empty()){
std::string errorJsonStr = buildResponse("session_id or message is empty");
response.status = 400; // 解析请求参数失败
response.set_content(errorJsonStr, "application/json");
return;
}
// 准备流式响应 (SSE格式)
response.status = 200; // 成功
response.set_header("Cache-Control", "no-cache"); // 不使用缓存,服务器立即将数据发送到网络
response.set_header("Connection", "keep-alive"); // 保持连接,服务器不会关闭连接
response.set_header("Access-Control-Allow-Origin", "*"); // 允许跨域请求
response.set_header("Access-Control-Allow-Headers", "*"); // 允许所有请求头
// set_chunked_content_provider:告诉服务器,响应内从不是一次性发送的,而是分多次逐步发送给客户端,一般用在实时生成响应内容 或者 流式数据传输场景
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){
// 将chunk转换为SSE数据格式
// Json::valueToQuotedString: 对chunk进行Json转换,目的防止chunk中包含一些特殊字符来破坏数据格式,比如:在chunk中包含了两个连续的换行,就会影响SSE数据格式
std::string sseData = "data: " + Json::valueToQuotedString(chunk.c_str()) + "\n\n";
// 需要将模型返回的结果 chunk 发送给客户端
dataSink.write(sseData.c_str(), sseData.size()); // 将数据写入响应流,即立即发送给客户端,该方法不会等待缓冲区满之后发送
// 处理结束标记
if(last){
// 流向响应结束
std::string doneData = "data: [DONE]\n\n";
dataSink.write(doneData.c_str(), doneData.size());
dataSink.done(); // 表示流式响应结束
return false; // 不再有后续数据
}
return true;
};
// 先给客户端发送一个空的数据块,避免客户端长时间的等待
writeChunk("", false);
// 发送消息流
_chatSDK->sendMessageStream(sessionId, message, writeChunk);
return false; // 不再有后续数据
});
}
// 设置HTTP路由规则
void ChatServer::setHttpRoutes()
{
// 处理创建会话请求
_chatServer->Post("/api/session", [this](const httplib::Request& request, httplib::Response& response){
handleCreateSessionRequest(request, response);
});
// 处理获取会话列表请求
_chatServer->Get("/api/sessions", [this](const httplib::Request& request, httplib::Response& response){
handleGetSessionListsRequest(request, response);
});
// 处理获取模型列表请求
_chatServer->Get("/api/models", [this](const httplib::Request& request, httplib::Response& response){
handleGetModelListsRequest(request, response);
});
// 处理删除会话请求
_chatServer->Delete("/api/session/(.*)", [this](const httplib::Request& request, httplib::Response& response){
handleDeleteSessionRequest(request, response);
});
// 处理获取历史消息请求
_chatServer->Get("/api/session/(.*)/history", [this](const httplib::Request& request, httplib::Response& response){
handleGetHistoryMessagesRequest(request, response);
});
// 处理发送消息请求-全量返回
_chatServer->Post("/api/message", [this](const httplib::Request& request, httplib::Response& response){
handleSendMessageRequest(request, response);
});
// 处理发送消息请求-增量返回
_chatServer->Post("/api/message/async", [this](const httplib::Request& request, httplib::Response& response){
handleSendMessageStreamRequest(request, response);
});
}
} // namespace ai_chat_sdk
(1) 文件概览
| 属性 | 说明 |
|---|---|
| 文件路径 | ChatServer/ChatServer.cpp |
| 命名空间 | ai_chat_server |
| 核心功能 | HTTP API 服务器,对外提供 AI 聊天服务接口 |
| 依赖库 | httplib(HTTP)、jsoncpp(JSON)、ai_chat_sdk(AI SDK) |
(2) 整体架构
ChatServer.cpp
│
├── [构造] ChatServer(config) → 初始化 SDK、注册模型提供者
├── [启动] start() → 注册路由、挂载静态文件、启动监听
├── [停止] stop() → 优雅关闭服务器
├── [状态] isRunning() → 返回运行状态
├── [工具] buildResponse() → 构建 JSON 响应体
├── [路由] setHttpRoutes() → 注册所有 API 路由
│
├── [API] handleCreateSessionRequest() → POST /api/session
├── [API] handleGetSessionListsRequest() → GET /api/sessions
├── [API] handleGetModelListsRequest() → GET /api/models
├── [API] handleDeleteSessionRequest() → DELETE /api/session/{id}
├── [API] handleGetHistoryMessagesRequest() → GET /api/session/{id}/history
├── [API] handleSendMessageRequest() → POST /api/message
└── [API] handleSendMessageStreamRequest() → POST /api/message/async
(3) 构造函数 解析
cpp
ChatServer::ChatServer(const ServerConfig& config) : _config(config)
初始化流程:
┌─────────────────────────────────────────────────────┐
│ 1. 创建 ChatSDK 实例 │
│ _chatSDK = make_shared<ChatSDK>() │
│ │
│ 2. 构建模型提供者配置列表 │
│ ├── ApiConfig("deepseek-v4-flash") │
│ ├── ApiConfig("gemini-2.5-flash") │
│ ├── ApiConfig("gpt-5.4-mini") │
│ └── OllamaConfig(...) │
│ │
│ 3. 注册并初始化所有提供者 │
│ _chatSDK->registerAndInit_AllProviders(...) │
│ │
│ 4. 创建 HTTP 服务器实例 │
│ _chatServer = make_unique<httplib::Server>() │
└─────────────────────────────────────────────────────┘
成员变量:
| 成员 | 类型 | 说明 |
|---|---|---|
_config |
ServerConfig |
服务器配置 |
_chatSDK |
shared_ptr<ChatSDK> |
AI SDK 实例 |
_chatServer |
unique_ptr<httplib::Server> |
HTTP 服务器实例 |
_isRunning |
atomic<bool> |
运行状态标记(线程安全) |
(4) start() 启动流程
cpp
bool ChatServer::start()
┌─────────────────────────────────────────────────────┐
│ 1. 检查运行状态(防止重复启动) │
│ if(_isRunning) return false; │
│ │
│ 2. 注册路由规则 │
│ setHttpRoutes() │
│ ├── POST /api/session │
│ ├── GET /api/sessions │
│ ├── GET /api/models │
│ ├── DELETE /api/session/(.*) │
│ ├── GET /api/session/(.*)/history │
│ ├── POST /api/message │
│ └── POST /api/message/async │
│ │
│ 3. 设置静态文件挂载点 │
│ set_mount_point("/", "./www") │
│ │
│ 4. 在新线程中启动 HTTP 监听 │
│ _chatServer->listen(host, port) │
│ serverThread.detach() │
│ │
│ 5. 标记运行状态 │
│ _isRunning.store(true) │
└─────────────────────────────────────────────────────┘
关键设计:
- 使用
detach()分离线程,避免主线程阻塞 listen()是阻塞调用,会一直运行直到调用stop()
(5) API 接口详解
(5.1) 创建会话 --- handleCreateSessionRequest
POST /api/session
请求体: {"model": "deepseek-v4-flash"}
响应体: {"success": true, "data": {"session_id": "xxx", "model": "..."}}
处理流程:
请求体 JSON 字符串
│
▼
反序列化 → Json::Value 对象
│
▼
提取 model 字段
│
▼
_chatSDK->createSession(model_name) → session_id
│
▼
构建响应 JSON
│
▼
response.set_content(json_str, "application/json")
错误码:
| 状态码 | 条件 |
|---|---|
| 400 | JSON 解析失败、model 名称为空 |
| 500 | 创建会话失败 |
(5.2) 获取会话列表 --- handleGetSessionListsRequest
GET /api/sessions
响应体: {"success": true, "data": [{...}, {...}]}
处理流程:
_chatSDK->getSessionList() → 获取所有 session_id
│
▼
遍历每个 session_id
├── _chatSDK->getSession(id) → 获取会话信息
├── _chatSDK->getSessionHistory(id) → 获取历史消息
│
▼
构建响应:
{
"id": "xxx",
"model": "deepseek-v4-flash",
"created_at": 1234567890,
"updated_at": 1234567890,
"message_count": 5,
"first_user_message": "你好"
}
(5.3) 获取模型列表 --- handleGetModelListsRequest
GET /api/models
响应体: {"success": true, "data": [{"name": "deepseek-v4-flash", "desc": "..."}]}
处理流程:
_chatSDK->getAvailableModels() → 返回模型信息列表
│
▼
遍历构建响应
(5.4) 删除会话 --- handleDeleteSessionRequest
DELETE /api/session/{session_id}
响应体: {"success": true, "message": "delete session successfully"}
特点 :使用正则路由 /api/session/(.*) 捕获路径参数
request.matches[1] = "session_id"
│
▼
_chatSDK->deleteSession(sessionId)
│
├── 成功 → 200
└── 失败 → 404
(5.5) 获取历史消息 --- handleGetHistoryMessagesRequest
GET /api/session/{session_id}/history
响应体: {"success": true, "data": [{"id": 1, "role": "user", "content": "..."}]}
处理流程:
request.matches[1] = "session_id"
│
▼
检查会话是否存在 (_chatSDK->getSession)
│
├── 不存在 → 404
│
└── 存在 ↓
_chatSDK->getSessionHistory(sessionId)
│
▼
遍历消息列表构建响应
{
"id": message._id,
"role": message._role,
"content": message._content,
"timestamp": message._create_at
}
(5.6) 发送消息(全量)--- handleSendMessageRequest
POST /api/message
请求体: {"session_id": "xxx", "message": "你好"}
响应体: {"success": true, "response": "AI 回复", "data": {...}}
处理流程:
解析请求体
↓
提取 session_id + message
↓
_chatSDK->sendMessage(sessionId, message)
↓ (阻塞等待 AI 完整回复)
返回完整响应
特点 :sendMessage() 是同步阻塞调用,等待 AI 模型生成完整回复后才返回。
(5.7) 发送消息(流式)--- handleSendMessageStreamRequest
POST /api/message/async
请求体: {"session_id": "xxx", "message": "你好"}
响应: SSE 流式数据
处理流程:
┌─────────────────────────────────────────────────────┐
│ 1. 设置响应头 │
│ Content-Type: text/event-stream │
│ Cache-Control: no-cache │
│ Connection: keep-alive │
│ │
│ 2. 注册 chunked content provider │
│ set_chunked_content_provider(...) │
│ │
│ 3. 回调函数中: │
│ ├── writeChunk("", false) // 先发空块 │
│ ├── sendMessageStream(...) // 调用 SDK │
│ │ ├── 收到 "你" → write // 实时发送 │
│ │ ├── 收到 "好" → write // 实时发送 │
│ │ └── 收到结束 → [DONE] // 发送结束标记 │
│ └── return false // 不再提供数据 │
└─────────────────────────────────────────────────────┘
SSE 数据格式:
data: "你"
data: "好"
data: [DONE]
(6) 工具函数
- buildResponse()
cpp
std::string ChatServer::buildResponse(const std::string& message, bool success)
作用:快速构建统一的 JSON 错误/成功响应体。
输出格式:
json
{
"success": false,
"message": "错误描述"
}
(7) 数据流图
┌─────────────────────────────────────────────────────────────────┐
│ Client (前端) │
└──────────────────────────────┬──────────────────────────────────┘
│ HTTP Request
▼
┌─────────────────────────────────────────────────────────────────┐
│ ChatServer (HTTP 层) │
│ setHttpRoutes() → 路由匹配 → 分发到处理函数 │
└──────────────────────────────┬──────────────────────────────────┘
│ 调用 SDK 接口
▼
┌─────────────────────────────────────────────────────────────────┐
│ ChatSDK (业务逻辑层) │
│ createSession() / sendMessage() / getSessionList() / ... │
└──────────────────────────────┬──────────────────────────────────┘
│ 调用 Provider
▼
┌─────────────────────────────────────────────────────────────────┐
│ Provider (AI 模型层) │
│ DeepSeek / Gemini / GPT / Ollama │
└──────────────────────────────┬──────────────────────────────────┘
│ HTTP 请求
▼
┌─────────────────────────────────────────────────────────────────┐
│ 外部 AI API 服务 │
│ api.deepseek.com / api.openai.com / ... │
└─────────────────────────────────────────────────────────────────┘
(8) 设计模式与最佳实践
| 模式/实践 | 实现位置 | 说明 |
|---|---|---|
| RAII | 构造函数 | shared_ptr/unique_ptr 自动管理资源 |
| 原子操作 | _isRunning |
atomic<bool> 保证线程安全 |
| 线程分离 | start() |
detach() 避免主线程阻塞 |
| 正则路由 | 路由注册 | (.*) 捕获动态路径参数 |
| 流式响应 | handleSendMessageStreamRequest |
SSE + Chunked 实现实时推送 |
| 统一错误格式 | buildResponse() |
所有错误响应 JSON 格式一致 |
| 优雅关闭 | stop() |
_chatServer->stop() 等待现有请求完成 |
(9) 总结
ChatServer.cpp 是整个项目的HTTP API 网关层,职责是:
- 接收 HTTP 请求 → 路由分发
- 参数解析与校验 → JSON 反序列化
- 调用 SDK → 委托业务逻辑
- 构建响应 → JSON 序列化返回
- 流式推送 → SSE 实时传输 AI 回复
2. main.cpp(主程序)的作用
- main.cpp
cpp
#include "ChatServer.h"
#include <iostream>
#include <gflags/gflags.h>
#include <ai_chat_sdk/util/mylog.h>
// 定义gflags参数
DEFINE_string(host, "0.0.0.0", "服务器绑定的地址");
DEFINE_int32(port, 8080, "服务器绑定的端口号");
DEFINE_string(log_level, "INFO", "日志级别");
DEFINE_double(temperature, 0.7, "温度值,影响生成文本的随机性");
DEFINE_int32(max_tokens, 2048, "最大token数");
// Ollama配置参数
DEFINE_string(ollama_model_name, "", "Ollama模型名称");
DEFINE_string(ollama_model_desc, "", "Ollama模型描述");
DEFINE_string(ollama_endpoint, "", "Ollama API地址");
// 配置文件路径
DEFINE_string(config_file, "./ChatServer.conf", "配置文件路径");
// 版本号
const std::string VERSION = "AIChatServer v1.0.0";
// 验证配置参数
bool validate_Config(ai_chat_server::ServerConfig& config) {
// 验证温度值
if (config.temperature < 0.0 || config.temperature > 2.0) {
ERR("错误: 温度值必须在0.0到2.0之间,当前值: {}", config.temperature);
return false;
}
// 验证最大token数
if (config.maxTokens <= 0) {
ERR("错误: 最大token数必须为正数, 当前值: {}", config.maxTokens);
return false;
}
// 验证至少有一个API密钥不为空
if (config.deepseekAPIKey.empty() && config.chatGPTAPIKey.empty() && config.geminiAPIKey.empty()) {
ERR("错误: 至少需要提供一个有效的API密钥");
return false;
}
// 验证Ollama配置参数
if (!config.ollamaModelName.empty())
{
if (config.ollamaModelDesc.empty() || config.ollamaEndpoint.empty()) {
ERR("错误: 如果提供了Ollama模型名称, 则必须同时提供模型描述 和 url地址");
return false;
}
}
return true;
}
// 从环境变量获取API密钥
std::string getEnvVar(const std::string& key) {
char* value = std::getenv(key.c_str());
return value ? std::string(value) : "";
}
// 显示接口说明
void showAPIInfo()
{
std::cout << "\nChatServer API接口说明:\n";
std::cout << " POST /api/session - 创建新会话\n";
std::cout << " GET /api/sessions - 获取所有会话列表\n";
std::cout << " GET /api/models - 获取可用模型列表\n";
std::cout << " DELETE /api/session/{session_id} - 删除指定会话\n";
std::cout << " GET /api/session/{session_id}/history - 获取会话历史消息\n";
std::cout << " POST /api/message - 发送消息(全量返回)\n";
std::cout << " POST /api/message/async - 发送消息(流式返回)\n";
std::cout << "\n使用示例:\n";
std::cout << " # 基本启动\n";
std::cout << " ./AIChatServer\n";
std::cout << "\n # 指定端口启动\n";
std::cout << " ./AIChatServer --port=9000\n";
std::cout << "\n # 使用指定配置文件\n";
std::cout << " ./AIChatServer --config_file=my_config.conf\n";
std::cout << "\n # 设置环境变量后启动\n";
std::cout << " export DEEPSEEK_API_KEY=your_api_key\n";
std::cout << " ./AIChatServer\n";
}
int main(int argc, char** argv)
{
// 显示帮助信息(当使用-h或--help时,显示接口说明并退出程序)
// 比如:./AIChatServer -h 或 ./AIChatServer --help
if (argc == 2 && (std::string(argv[1]) == "-h" || std::string(argv[1]) == "--help"))
{
showAPIInfo();
return 0;
}
// gflags::SetVersionString 是 Google gflags 库中的函数,用于 设置程序的版本信息
// 当用户使用 -v 或 --version 参数时,gflags 会自动显示你设置的版本信息并退出程序
gflags::SetVersionString(VERSION);
// 1. 先从配置文件读取参数 (覆盖默认值)
// 第一个参数是配置文件路径,第二个参数是程序名称 (用于构造错误提示),
// 第三个参数是bool值: true - 遇到错误(文件不存在、格式错误等)时,程序终止;
// false - 遇到错误时,只记录警告,程序继续运行
gflags::ReadFromFlagsFile(FLAGS_config_file.c_str(), argv[0], false);
// 2. 再解析命令行参数(命令行参数会覆盖配置文件的值)
// gflags::ParseCommandLineFlags() - 解析命令行参数,会自动处理 -h / --help 和 -v / --version 参数
gflags::ParseCommandLineFlags(&argc, &argv, true);
// 构建ServerConfig对象
ai_chat_server::ServerConfig config;
config.host = FLAGS_host;
config.port = FLAGS_port;
config.logLevel = FLAGS_log_level;
config.temperature = FLAGS_temperature;
config.maxTokens = FLAGS_max_tokens;
// 从命令行参数获取Ollama配置
config.ollamaModelName = FLAGS_ollama_model_name;
config.ollamaModelDesc = FLAGS_ollama_model_desc;
config.ollamaEndpoint = FLAGS_ollama_endpoint;
// 从环境变量获取API密钥
config.deepseekAPIKey = getEnvVar("deepseek_apikey");
config.chatGPTAPIKey = getEnvVar("chatgpt_apikey");
config.geminiAPIKey = getEnvVar("gemini_apikey");
// 验证配置参数
if (!validate_Config(config)) {
ERR("配置验证失败,请检查参数设置");
return 1;
}
// 设置日志级别
spdlog::level::level_enum logLevel = spdlog::level::info; // 默认INFO级别
if (config.logLevel == "TRACE") logLevel = spdlog::level::level_enum::trace;
else if (config.logLevel == "DEBUG") logLevel = spdlog::level::level_enum::debug;
else if (config.logLevel == "INFO") logLevel = spdlog::level::level_enum::info;
else if (config.logLevel == "WARNING") logLevel = spdlog::level::level_enum::warn;
else if (config.logLevel == "ERROR") logLevel = spdlog::level::level_enum::err;
else if (config.logLevel == "CRITICAL") logLevel = spdlog::level::level_enum::critical;
// 初始化日志组件
MyLog::Logger::instance().init_logger("ChatServer", logLevel, "stdout");
// 显示当前配置
INFO("AIChatServer 启动配置:");
INFO(" 版本: {}", VERSION);
INFO(" 主机: {}", config.host);
INFO(" 端口: {}", config.port);
INFO(" 日志级别: {}", config.logLevel);
INFO(" 温度值: {}", config.temperature);
INFO(" 最大Token: {}", config.maxTokens);
INFO(" DeepSeek API Key: {}", (config.deepseekAPIKey.empty() ? "未设置" : "已设置"));
INFO(" ChatGPT API Key: {}", (config.chatGPTAPIKey.empty() ? "未设置" : "已设置"));
INFO(" Gemini API Key: {}", (config.geminiAPIKey.empty() ? "未设置" : "已设置"));
INFO(" Ollama 模型: {}", (config.ollamaModelName.empty() ? "未设置" : config.ollamaModelName));
INFO(" Ollama 模型描述: {}", (config.ollamaModelDesc.empty() ? "未设置" : config.ollamaModelDesc));
INFO(" Ollama URL地址: {}", (config.ollamaEndpoint.empty() ? "未设置" : config.ollamaEndpoint));
// 创建并启动ChatServer
ai_chat_server::ChatServer server(config);
if (server.start()) {
INFO("ChatServer 启动成功!");
INFO("服务器地址: http://{}:{}", config.host, config.port);
// 主线程循环等待,让服务器在单独线程中运行
while (server.isRunning()) {
std::this_thread::sleep_for(std::chrono::seconds(100));
}
}
else {
ERR("ChatServer 启动失败!");
return 1;
}
return 0;
}
- build/ChatServer.conf
bash
# ChatServer配置文件
# 服务器设置
-host=0.0.0.0
-port=8080
# 日志设置
-log_level=INFO
# AI模型参数
-temperature=0.7
-max_tokens=2048
# Ollama模型配置 (可选)
-ollama_model_name=deepseek-r1:1.5b
-ollama_model_desc=本地部署deepseek-r1:1.5b模型,采用专家混合架构,专注于深度理解与推理
#-ollama_endpoint=http://localhost:11434
-ollama_endpoint=http://127.0.0.1:11434
main.cpp 是整个 AIChatServer 程序的入口文件,负责应用程序的启动、配置和初始化。
(1) 核心职责
| 职责 | 说明 |
|---|---|
| 程序入口 | 提供 main() 函数,是程序执行的起点 |
| 参数解析 | 使用 gflags 库解析命令行参数 |
| 配置加载 | 从配置文件、命令行、环境变量三层加载配置 |
| 配置验证 | 校验配置参数合法性 |
| 日志初始化 | 初始化 spdlog 日志系统 |
| 服务启动 | 创建 ChatServer 并启动 |
(2) 关键代码分析
- 命令行参数定义
cpp
DEFINE_string(host, "0.0.0.0", "服务器绑定的地址");
DEFINE_int32(port, 8080, "服务器绑定的端口号");
DEFINE_double(temperature, 0.7, "温度值,影响生成文本的随机性");
DEFINE_int32(max_tokens, 2048, "最大token数");
DEFINE_string(config_file, "./ChatServer.conf", "配置文件路径");
使用 gflags 库定义命令行参数,支持运行时动态配置。
-
配置加载优先级(三层覆盖)
┌──────────────────────────────────────────────┐
│ 优先级由高到低: │
│ │
│ 1. 命令行参数 (最优先) │
│ ./AIChatServer --port=9000 │
│ │
│ 2. 配置文件 (次之) │
│ ChatServer.conf 中的配置 │
│ │
│ 3. 默认值 (最低) │
│ DEFINE_xxx 中设置的默认值 │
└──────────────────────────────────────────────┘
代码实现:
cpp
// 步骤1:从配置文件读取(覆盖默认值)
gflags::ReadFromFlagsFile(FLAGS_config_file.c_str(), argv[0], false);
// 步骤2:解析命令行参数(覆盖配置文件)
gflags::ParseCommandLineFlags(&argc, &argv, true);
// 从环境变量获取 API 密钥
config.deepseekAPIKey = getEnvVar("deepseek_apikey");
config.chatGPTAPIKey = getEnvVar("chatgpt_apikey");
config.geminiAPIKey = getEnvVar("gemini_apikey");
- 配置验证
cpp
bool validate_Config(ai_chat_server::ServerConfig& config) {
// 验证温度值范围
if (config.temperature < 0.0 || config.temperature > 2.0) {
return false;
}
// 验证至少有一个 API 密钥
if (config.deepseekAPIKey.empty() &&
config.chatGPTAPIKey.empty() &&
config.geminiAPIKey.empty()) {
return false;
}
// ...
}
确保关键参数合法,防止运行时错误。
- 日志初始化
cpp
// 设置日志级别
spdlog::level::level_enum logLevel = spdlog::level::level_enum::info;
// 初始化日志组件
MyLog::Logger::instance().init_logger("ChatServer", logLevel, "stdout");
配置日志系统,支持不同级别输出。
- 服务启动与运行
cpp
// 创建并启动 ChatServer
ai_chat_server::ChatServer server(config);
if (server.start()) {
INFO("ChatServer 启动成功!");
// 主线程循环等待,让服务器在单独线程中运行
while (server.isRunning()) {
std::this_thread::sleep_for(std::chrono::seconds(100));
}
}
启动流程:
main()
├── 解析参数
├── 加载配置
├── 验证配置
├── 初始化日志
├── 创建 ChatServer 对象
├── 调用 server.start() # 启动 HTTP 服务器
│ └── 在新线程中运行 listen() # 不阻塞主线程
│
└── while 循环等待 # 主线程保活
└── 每100秒检查一次运行状态
(3) 总结
main.cpp 是 ChatServer 的"启动器",主要工作:
- 收集配置:从配置文件、命令行、环境变量汇总配置
- 检查配置:验证参数合法性,避免运行时崩溃
- 启动服务:创建 ChatServer 对象并启动
- 维持运行:主线程通过循环保持程序不退出
3. CMakeLists.txt 构建 AIChatServer 的流程
前提:SDK目录下的文件,要进行编译形成
libai_chat_sdk.a,并进行安装操作,头文件安装到 include/ai_chat_sdk/,库文件安装到 lib/
- CMakeLists.txt
bash
# 设置Cmake的最小版本
cmake_minimum_required(VERSION 3.10)
# 项目名称
project(AIChatServer)
# 设置C++标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 设置构建类型Debug
set(CMAKE_BUILD_TYPE Debug)
# 添加可执行文件
add_executable(AIChatServer main.cpp ChatServer.cpp)
# 设置输出目录
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
# 查找依赖库
find_package(OpenSSL REQUIRED)
# 查找 ai_chat_sdk 库
find_package(ai_chat_sdk CONFIG REQUIRED)
# 添加依赖关系
target_link_libraries(AIChatServer PRIVATE ai_chat_sdk::ai_chat_sdk gflags)
(1) 构建流程总览
CMakeLists.txt 构建产物
│ │
├── 1. 设置基本配置 │
├── 2. 指定源文件 ────────────────→│ main.cpp.o, ChatServer.cpp.o
├── 3. 查找依赖 ─────────────────→│ libai_chat_sdk.a, gflags, OpenSSL
├── 4. 链接库 ───────────────────→│ AIChatServer (可执行文件)
└── 5. 设置输出目录 ─────────────→│ build/AIChatServer
(2) 逐行解析
(2.1) 指定源文件
cmake
add_executable(AIChatServer main.cpp ChatServer.cpp)
作用 :告诉 CMake 将 main.cpp 和 ChatServer.cpp 编译链接成名为 AIChatServer 的可执行文件。
编译过程:
main.cpp ChatServer.cpp
│ │
▼ ▼
main.cpp.o ChatServer.cpp.o (编译阶段:生成目标文件)
│ │
└────────┬────────┘
▼
AIChatServer (链接阶段:生成可执行文件)
(2.2) 设置输出目录
cmake
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
作用 :指定可执行文件的输出目录为构建目录(build/)。
${CMAKE_BINARY_DIR} = ChatServer/build/
最终输出:ChatServer/build/AIChatServer
(2.3) 查找依赖库
cmake
find_package(OpenSSL REQUIRED) # 查找 OpenSSL 库
find_package(ai_chat_sdk CONFIG REQUIRED) # 查找 ai_chat_sdk 库
find_package 的作用:在系统中查找指定的库,并设置相关变量(头文件路径、库文件路径等)。
ai_chat_sdk 的查找机制:
ai_chat_sdkConfig.cmake 配置文件
路径:sdk/build/ai_chat_sdkConfig.cmake
该文件定义了:
- ai_chat_sdk::ai_chat_sdk 目标
- 头文件路径
- 依赖关系(如 jsoncpp、httplib 等)
找到的库文件:
sdk/build/libai_chat_sdk.a (静态库)
(2.4) 链接依赖库
cmake
target_link_libraries(AIChatServer PRIVATE ai_chat_sdk::ai_chat_sdk gflags)
作用:将依赖库链接到可执行文件。
| 依赖 | 来源 | 作用 |
|---|---|---|
ai_chat_sdk::ai_chat_sdk |
自定义库 | AI 聊天 SDK 核心功能 |
gflags |
系统库 | 命令行参数解析 |
OpenSSL |
系统库 | HTTPS 支持(通过 ai_chat_sdk 间接依赖) |
链接过程:
AIChatServer
│
├── libai_chat_sdk.a (AI SDK 静态库)
│ ├── jsoncpp (JSON 解析)
│ ├── httplib (HTTP 客户端/服务器)
│ └── sqlite3 (数据库)
│
├── gflags (命令行参数)
│
└── OpenSSL (加密/HTTPS)
(3) 完整构建流程
┌──────────────────────────────────────────────────────────────┐
│ 构建命令执行 │
├──────────────────────────────────────────────────────────────┤
│ │
│ cd ChatServer/build │
│ cmake .. │
│ │ │
│ ├── 读取 CMakeLists.txt │
│ ├── 解析 add_executable(AIChatServer ...) │
│ ├── find_package(ai_chat_sdk CONFIG REQUIRED) │
│ │ └── 查找 ai_chat_sdkConfig.cmake │
│ ├── 生成 Makefile │
│ └── 输出:build/Makefile │
│ │
│ make │
│ │ │
│ ├── 编译 main.cpp → main.cpp.o │
│ ├── 编译 ChatServer.cpp → ChatServer.cpp.o │
│ ├── 链接所有目标文件和库 │
│ │ main.cpp.o + ChatServer.cpp.o + libai_chat_sdk.a │
│ │ + gflags + OpenSSL → AIChatServer │
│ └── 输出:build/AIChatServer │
│ │
└──────────────────────────────────────────────────────────────┘
(4) 编译命令示例
CMake 生成的编译命令(简化版):
bash
# 编译 main.cpp
g++ -std=c++17 -g -I../ -I../sdk/include -I/usr/include \
-c main.cpp -o main.cpp.o
# 编译 ChatServer.cpp
g++ -std=c++17 -g -I../ -I../sdk/include -I/usr/include \
-c ChatServer.cpp -o ChatServer.cpp.o
# 链接生成可执行文件
g++ -std=c++17 -g main.cpp.o ChatServer.cpp.o \
-L../sdk/build -lai_chat_sdk \
-lgflags -lssl -lcrypto \
-o AIChatServer
(5) 依赖关系图
AIChatServer (可执行文件)
│
├── 源文件
│ ├── main.cpp # 程序入口、配置解析
│ └── ChatServer.cpp # HTTP 服务器实现
│
├── 静态库
│ └── libai_chat_sdk.a # AI 聊天 SDK
│ ├── DeepSeekProvider.cpp
│ ├── GeminiProvider.cpp
│ ├── GPTProvider.cpp
│ ├── OllamaProvider.cpp
│ ├── ChatSDK.cpp
│ └── Session.cpp
│
└── 系统库
├── gflags # 命令行参数
├── ssl # HTTPS
├── crypto # 加密
└── pthread # 线程支持
最终结果 :在
build/目录下生成可执行文件AIChatServer。
三、前端实现(由AI实现)
1. 生成前端页面的提示词(要包含详细的json协议描述)
bash
假设你是⼀个具有十年前端开发经验的资深工程师,请帮我按照如下要求实现⼀个前端页面。
我实现了⼀个智能聊天助手的服务器,服务器地址是***.**.**.**,端口是8080。
服务端代码以及所有接口均进行了严格的测试,没有问题。
一、服务器提供以下功能:
1. 获取所有会话列表
2. 获取可用模型
3. 创建新会话
4. 发送消息 - 流式响应
5. 删除会话
6. 获取会话历史消息
二、接口的请求和响应参数以json格式组织,接口具体定义如下:
1. 获取会话列表
请求URL:GET /api/sessions
返回响应:200 OK
响应格式:
{
"success" : "bool", # 是否成功
"message" : "string", # 结果描述
"data": "array"[ # 响应数据
{
"id" : "string", # 会话id
"model" : "string", # 模型名称
"created_at" : "int64_t", # 会话创建时间
"updated_at" : "int64_t", # 会话更新时间
"message_count" : "int", # 对话次数
"first_user_message" : "string" # 本次会话的 第⼀条正文消息
}
]
}
2. 获取可用模型
请求URL:GET /api/models
返回响应:200 OK
响应格式:
{
"success" : "bool", # 是否成功
"message" : "string", # 结果描述
"data": "array"[ # 响应数据
{
"name" : "string", # 模型名称
"desc" : "string" # 模型描述
}
]
}
3. 创建新会话
请求URL:POST /api/session
请求参数:
{
"model" : "string" # 模型名称
}
返回响应:200 OK
响应格式
{
"success" : "bool", # 是否成功
"message" : "string", # 结果描述
"data": # 响应数据
{
"session_id" : "string", # 会话id
"model" : "string" # 模型名称
}
}
4. 发送消息 - 流式响应
请求URL:POST /api/message/async
请求参数:
{
"session_id" : "string", # 会话id
"message" : "string" # 消息内容
}
返回响应:200 OK
流式响应,格式如下 (正文是经Json::valueToQuotedString转换过的字符串)
data: 正文\n\n
data: 正文\n\n
data: 正文\n\n
data: [DONE]\n\n
5. 删除会话
请求URL:DELETE /api/session/{session_id}
返回响应:200 OK
响应格式
{
"success" : "bool", # 是否成功
"message" : "string" # 结果描述
}
6. 获取会话历史
请求URL:GET /api/session/{session_id}/history
返回响应:200 OK
响应格式
{
"success" : "bool",
"message" : "string",
"data": "array"[
{
"id" : "string",
"role" : "string",
"content" : "string",
"timestamp" : 0,
}
]
}
结合以上内容帮我生成⼀个前端页面,类似于deepseek的网页聊天助手,页面要求如下:
1. 前端页面的html、JavaScript、css等代码分开存放
2. 页面左上角有⼀个AIChat图标,图标之后跟 "AI聊天助手" 的文本,文本之后紧跟一个按钮,按
钮上文本为"新建对话",点击按钮之后,会创建新会话。
3. 页面左侧栏为历史会话列表,每条会话信息展示:会话更改时间、会话第⼀条消息、模型名称、以
及删除会话按钮,按钮文本为"...",点击删除按钮会删除该会话,同步删除服务器上session_id的
会话
4. 网页右侧默认为⼀个图标,图标下是⼀段文本,"选择⼀个历史对话继续,或创建新的对话,开启您
的AI之旅",文本下是⼀个按钮,按钮文本是"新建对话", 点击按钮会创建新会话
5. 点击新建对话按钮,要弹出⼀个无边框窗口,窗口内部以网格方式展示所支持的模型,支持所有模
型在会话中以单选按钮展示且包含模型描述,用户需要选择本次会话的模型,选择好之后点击确定按
钮,本次会话就创建完成,模型选择窗口关闭;点击取消按钮则不创建会话,模型选择窗口也自动关
闭。注意:每次只能选择⼀个模型
6. 会话创建成功之后,主页面85%高度为聊天消息显式区域,消息展示区域左侧展示模型消息,右侧展
示用户消息,每条消息下方紧跟消息收发时间
7. 消息显式区域下方为用户输⼊问题的编辑框,在未输⼊文本时,框内提示"请输⼊您的消息...
(Enter发送,Shift+Enter换行)",用户输入文本后提示消失,消息编辑框右侧为发送按钮,按钮下
显式用户消息文本总字数/输入文本最大限制(最大限制2000字)
8. 从服务器获取到index.html之后,客户端需自动到服务器获取会话列表,将会话列表信息显式在页
面左侧
9. 点击页面左上角和右侧偏下位置"新建对话"按钮时,需要到服务器获取支持的模型列表,用户选择
模型并点击确定按钮之后,创建新会话,将该会话加入页面左侧,开始新⼀轮会话
10. 对于流式消息,服务器返回的是标准SSE格式,比如:
data: 文本数据\n\n
data: 文本数据\n\n
data: [DONE]\n\n
对服务器返回的流式数据以markdown格式解析,代码放到代码块中语法高亮显式,代码支持⼀键复制
注意:服务器返回的流式数据可能会较多,避免数据过长而导致界面变形或者乱码
(注:文本数据是经Json::valueToQuotedString转换过的字符串)
11. 要求整个页面协调美观,颜色搭配舒适
build/www目录下的 index.html、app.js 和 style.css 均由AI生成
2. 前端页面的效果展示
本项目是带领大家从零手搓各种大模型接入的SDK,在此基础上,实现一个智能网页版的AI聊天助手。
该应用目前支持以下功能:
- 获取会话列表
- 新建会话
- 获取支持模型
- 发送消息
- 获取指定会话的历史聊天记录
- 删除会话
(1)【主界面】

(2)【模型选择】
点击新建对话选项,就会弹出模型选择(选择模型后,创建新会话成功)

目前已接入deepseek-v4-flash、gpt-5.4-mini、gemini-2.5-flash模型,以及deepseek-r1:1.5b的本地接入,学习之后同学们也可以接入其他更多模型。
(3)【开始会话】

(4)【删除会话】

四、从前端 到服务端 再到SDK 的 HTTP 通讯全流程
1. 整体架构概览
┌──────────────┐ HTTP/JSON ┌──────────────┐ HTTP/JSON ┌──────────────────┐
│ 前端页面 │ ◄──────────────────► │ ChatServer │ ◄──────────────────► │ SDK (Provider) │
│ (app.js) │ 普通/SSE 流式 │ (C++ httplib) │ httplib Client │ DeepSeek/GPT.. │
└──────────────┘ └──────────────┘ └──────────────────┘
关键文件:
- 前端: app.js 和 index.html
- 服务端: ChatServer.cpp / main.cpp
- SDK: ChatSDK.cpp / DeepSeekProvider.cpp
2. 普通响应(全量返回)流程
API 端点:
POST /api/message
第一步: 前端发起请求
在 app.js中,前端未实现 非流式接口 /api/message。但如果使用普通响应,app.js的代码应该如下所示:
javascript
// 前端发送普通请求 (伪代码示例)
const response = await fetch('/api/message', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
session_id: currentSessionId,
message: "你好"
})
});
const result = await response.json();
// result = { success: true, message: "...", data: {...}, response: "AI回复全文" }
第二步: ChatServer 接收并处理
在 ChatServer.cpp(L326-L387) 中,handleSendMessageRequest 处理流程:
1. 解析请求体 JSON → 提取 session_id 和 message
2. 调用 _chatSDK->sendMessage(sessionId, message) → 同步等待,返回完整回复
3. 将完整回复包装成 JSON 响应 {"success":true, "response":"完整回复", "data":{...}}
4. 设置 Content-Type: application/json,HTTP 200 返回
- 关键代码段:
cpp
// ChatServer.cpp 第326-387行
void ChatServer::handleSendMessageRequest(...) {
// 1. 解析请求体
std::string sessionId = val["session_id"].asString();
std::string message = val["message"].asString();
// 2. 同步调用SDK,阻塞等待完整结果
std::string responseMessage = _chatSDK->sendMessage(sessionId, message);
// 3. 一次性返回完整JSON
Json::Value resp;
resp["success"] = true;
resp["response"] = responseMessage; // ← 完整的AI回复
response.set_content(ss.str(), "application/json");
response.status = 200;
}
第三步: SDK 层处理
在 ChatSDK.cpp(L193-L234) 中,sendMessage 流程:
1. 从 SessionManager 获取会话模型名称
2. 从数据库读取历史消息 + 添加当前用户消息
3. 构建请求参数 {temperature, max_tokens}
4. 调用 _LLM_Manager.sendMessage(model_name, messages, params)
5. 将 AI 回复存入数据库
6. 返回完整回复字符串
第四步: Provider 层调用外部 AI API
在 DeepSeekProvider.cpp(L50-L148) 中,sendMessage 流程:
1. 构建请求体 JSON: {model, messages, stream: false, temperature, max_tokens}
2. 创建 httplib::Client,设置 30s 连接超时、60s 读取超时
3. 设置请求头: Authorization: Bearer xxx, Content-Type: application/json
4. POST → https://api.deepseek.com/chat/completions
5. 同步等待响应(阻塞)
6. 解析响应 JSON → 提取 choices[0].message.content
7. 返回完整字符串
- 关键代码:
cpp
// DeepSeekProvider.cpp 第50-148行
request_body["stream"] = false; // ← 关键:非流式
httplib::Client client(_base_url);
client.set_connection_timeout(30, 0);
client.set_read_timeout(60, 0);
// 同步阻塞 POST
httplib::Result res = client.Post("/chat/completions", headers,
request_body_str, "application/json");
// 等待完整响应后,一次性解析
std::string content = resp["choices"][0]["message"]["content"].asString();
return content; // 返回完整回复
-
普通响应 - 完整时序图
前端(app.js) ChatServer SDK(ChatSDK) AI API(DeepSeek)
│ │ │ │
│ POST /api/message │ │ │
│ {session_id, message} │ │ │
│────────────────────────►│ │ │
│ │ sendMessage(id, msg) │ │
│ │────────────────────────►│ │
│ │ │ POST /chat/completions│
│ │ │ stream:false │
│ │ │──────────────────────►│
│ │ │ │
│ │ │ ◄── 等待AI生成完整回复 │
│ │ │ │
│ │ │ {choices:[{message: │
│ │ │ {content:"完整回复"}}]}│
│ │ │◄──────────────────────│
│ │ │ │
│ │ 返回完整字符串 │ │
│ │◄────────────────────────│ │
│ │ │ │
│ HTTP 200 │ │ │
│ {success:true, │ │ │
│ response:"完整回复"} │ │ │
│◄────────────────────────│ │ │
│ │ │ │
│ 一次性渲染完整回复 │ │ │
3. 流式响应(SSE)流程
API 端点:
POST /api/message/async
这是项目实际使用的方式,涉及三层流式传递。
第一步: 前端发起 SSE 流式请求
在 app.js(L458-L521)中:
javascript
async function sendMessage() {
// 1. 发起 fetch 请求
const response = await fetch('/api/message/async', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ session_id: currentSessionId, message: message })
});
// 2. 获取 ReadableStream 读取器
const reader = response.body.getReader();
const decoder = new TextDecoder();
// 3. 循环读取流式数据块
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n\n'); // SSE 以 \n\n 分隔事件
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') continue;
const parsed = JSON.parse(data);
fullContent += parsed; // ← 累积内容
// 4. 实时更新 DOM(增量渲染)
if (!assistantMessage) {
assistantMessage = addMessage('assistant', fullContent);
} else {
contentDiv.innerHTML = parseMarkdown(fullContent); // 覆盖更新
}
}
}
}
}
前端关键技术点:
- 使用
fetch+response.body.getReader()读取流 - 解析 SSE 格式:
data: xxx\n\n - 收到
[DONE]标记表示流结束 - 每次收到 chunk 后增量更新 DOM(用 markdown 重新渲染整个累积内容)
第二步: ChatServer 设置 SSE 流式响应
在 ChatServer.cpp(L389-L452) 中,handleSendMessageStreamRequest:
cpp
void ChatServer::handleSendMessageStreamRequest(...) {
// 1. 设置 SSE 响应头
response.set_header("Cache-Control", "no-cache");
response.set_header("Connection", "keep-alive");
response.set_header("Access-Control-Allow-Origin", "*");
// 2. 使用 chunked_content_provider 实现流式输出
response.set_chunked_content_provider("text/event-stream",
[this, sessionId, message](size_t offset, httplib::DataSink& dataSink) -> bool {
// 3. 定义 writeChunk 回调函数
auto writeChunk = [&](const std::string& chunk, bool last) {
// 将 chunk 包装成 SSE 格式:data: "内容"\n\n
std::string sseData = "data: " + Json::valueToQuotedString(chunk.c_str()) + "\n\n";
dataSink.write(sseData.c_str(), sseData.size()); // 立即发送到客户端
if (last) {
// 发送结束标记
std::string doneData = "data: [DONE]\n\n";
dataSink.write(doneData.c_str(), doneData.size());
dataSink.done(); // 标记流结束
}
};
// 4. 先发空块避免客户端长时间等待
writeChunk("", false);
// 5. 调用 SDK 流式接口,传入 writeChunk 回调
_chatSDK->sendMessageStream(sessionId, message, writeChunk);
return false;
});
}
ChatServer 流式关键技术点:
set_chunked_content_provider是 httplib 的流式响应机制Content-Type: text/event-stream声明 SSE 格式DataSink::write()每收到一个 chunk 就立即推送给客户端- 通过回调函数
writeChunk将 SDK 的流式输出桥接到 HTTP 响应流
第三步: SDK 层流式转发
在 ChatSDK.cpp(L236-L283)中,sendMessageStream:
cpp
std::string ChatSDK::sendMessageStream(const std::string sessionId,
const std::string& message,
std::function<void(const std::string&, bool)> callback) {
// 1. 获取会话模型、构建历史消息(同普通响应)
// 2. 调用 LLMManager 的流式接口,透传 callback
std::string response = _LLM_Manager.sendMessageStream(model_name, message_lists,
request_param, callback);
// 3. 将完整回复存入数据库
_session_Manager.addMessage(sessionId, Message("assistant", response));
return response;
}
第四步: Provider 层流式调用外部 AI API
在 DeepSeekProvider.cpp(L151-L336) 中,sendMessageStream --- 这是最核心的流式实现:
cpp
std::string DeepSeekProvider::sendMessageStream(..., callback) {
// 1. 构建请求体,stream: true
request_body["stream"] = true; // ← 关键差异
// 2. 创建 httplib::Client,读取超时设为 300s
httplib::Client client(_base_url);
client.set_read_timeout(300, 0); // 流式需要更长超时
// 3. 构建 httplib::Request 对象,设置两个关键回调
httplib::Request req;
req.method = "POST";
req.path = "/chat/completions";
// 3a. 响应头处理回调
req.response_handler = [&](const httplib::Response& res) {
if (res.status != 200) {
gotError = true;
return false; // 终止请求
}
return true; // 继续接收
};
// 3b. 响应体流式回调(核心!)
req.content_receiver = [&](const char* data, size_t len, ...) {
buffer.append(data, len);
// 按 \n\n 分割 SSE 事件
while ((pos = buffer.find("\n\n")) != std::string::npos) {
std::string event = buffer.substr(0, pos);
buffer.erase(0, pos + 2);
if (event.compare(0, 6, "data: ") == 0) {
std::string jsonStr = event.substr(6);
if (jsonStr == "[DONE]") {
callback("", true); // ← 通知上层:流结束
streamFinish = true;
return true;
}
// 解析 JSON,提取 delta.content
std::string content = chunk["choices"][0]["delta"]["content"].asString();
fullResponse += content;
callback(content, false); // ← 通知上层:增量内容
}
}
return true; // 继续接收
};
// 4. 发送请求
auto res = client.send(req);
return fullResponse;
}
Provider 流式关键技术点:
- 请求体
stream: true - 使用
httplib::Request的content_receiver回调接收流式数据 - 每个 chunk 是 SSE 格式:
data: {"choices":[{"delta":{"content":"xxx"}}]}\n\n - 解析后提取
delta.content(注意:流式是delta,非流式是message) - 遇到
[DONE]标记流结束 - 读取超时从 60s 提升到 300s
-
流式响应 - 完整时序图
前端(app.js) ChatServer SDK AI API(DeepSeek)
│ │ │ │
│ POST /api/message/async│ │ │
│ {session_id, message} │ │ │
│────────────────────────►│ │ │
│ │ │ │
│ ◄── SSE 连接建立 ────── │ │ │
│ Content-Type: │ │ │
│ text/event-stream │ │ │
│ │ │ │
│ │ sendMessageStream() │ │
│ │ + callback(writeChunk) │ │
│ │────────────────────────►│ │
│ │ │ POST /chat/completions │
│ │ │ stream:true │
│ │ │──────────────────────────►│
│ │ │ │
│ │ │ ◄── data: {"choices": │
│ │ │ [{"delta": │
│ │ │ {"content":"你"}}]} │
│ │ │ callback("你", false) │
│ │ ◄── callback("你") ────│ │
│ data: "你"\n\n │ │ │
│◄────────────────────────│ │ │
│ 渲染 "你" │ │ │
│ │ │ ◄── data: {"choices": │
│ │ │ [{"delta": │
│ │ │ {"content":"好"}}]} │
│ │ │ callback("好", false) │
│ │ ◄── callback("好") ────│ │
│ data: "好"\n\n │ │ │
│◄────────────────────────│ │ │
│ 渲染 "你好" │ │ │
│ │ │ │
│ ... 持续流式传输 ... │ │
│ │ │ │
│ │ │ ◄── data: [DONE] │
│ │ │ callback("", true) │
│ │ ◄── callback("", true)─│ │
│ data: [DONE]\n\n │ │ │
│◄────────────────────────│ │ │
│ 流结束 │ │ │
4. 关键差异对比
| 维度 | 普通响应 (/api/message) |
流式响应 (/api/message/async) |
|---|---|---|
| 前端 | fetch → response.json() 一次性解析 |
fetch → response.body.getReader() 循环读取 |
| ChatServer | response.set_content(json, "application/json") |
response.set_chunked_content_provider("text/event-stream", ...) |
| SDK 请求体 | "stream": false |
"stream": true |
| SDK 超时 | 连接30s / 读取60s | 连接30s / 读取300s |
| SDK HTTP 方式 | client.Post() 同步阻塞 |
client.send(req) + content_receiver 回调 |
| AI API 响应 | choices[0].message.content(完整) |
choices[0].delta.content(增量) |
| SDK 解析 | 一次性 Json::Reader::parse() |
逐行 \n\n 分割 + 逐个解析 |
| 结束标记 | 无 | [DONE] |
| 用户体验 | 等待全部生成后一次性显示 | 逐字/逐句实时显示 |