【项目】基于SDK实现的智能聊天助手(使用api接入ChatGPT )------(三)
·
前言
gpt的接入其实和deepseek的接入是类似的,也是参考官方文档进行接入。
一:大模型的初始化
大模型的初始化主要是设置模型的根端点和api key,openai提供的根端点是https://api.openai.com
//初始化模型
//这里传入的参数就是api_key 和 endpoint
bool ChatgptProvider::initModel(const std::map<std::string,std::string>& modelConfig)
{
auto it1 = modelConfig.find("api_key");
if(it1 == modelConfig.end())
{
ERR("api_key not find!");
return false;
}
else
{
_apiKey = it1->second;
}
auto it2 = modelConfig.find("endpoint");
if(it2 == modelConfig.end())
{
ERR("endPoint not find!");
return false;
}
else
{
_endPoint = it2->second;
}
_isAvailable = true;
INFO("model innit sucess!");
return true;
}
//2. 检测模型是否有效
bool ChatgptProvider::isAvailable() const
{
//直接返回_isAvailable就行
return _isAvailable;
}
//4. 获取模型名称
std::string ChatgptProvider::getModelName() const
{
return "gpt-4o-mini";
}
//5. 获取模型描述
std::string ChatgptProvider::getModelDesc() const
{
return "我可以回答各种问题,提供建议,帮助学习新知识,或者进行有趣的对话。我的目标是为用户提供准确和有用的支持。如果你有任何问题或需要帮助,随时可以问我!";
}
二:ChatGPT的参数
ChatGPT的参数文档的地址是:https://platform.openai.com/docs/api-reference/chat/create
官方文档提供的参数十分多,我们这里只是选一些必要的参数 。。
model(string) :模型名称
message (array):历史对话,里面的参数包含了role和content字段
role(string):角色
content(string):对话
temperature(double):温度值,用来控制模型回答的准确性,默认值越低,模型回答的就越准确(有默认值)
stream(boolean):流式响应(默认未开启)
max_tokens(integer):⽣成内容的最⼤token数
请求头参数:
| 字段名称 | 字段类型 | 说明 |
|---|---|---|
| Content-Type | string | 固定为 application/json |
| Authorization | string | 认证信息,格式:Bearer {api_key} |
请求体参数:
| 字段名称 | 字段类型 | 说明 |
|---|---|---|
| model | string | 使用的模型名称 |
| message | array | 历史对话列表,数组元素为 object |
| temperature | string | 采样温度,控制随机性 |
| max_tokens | integer | 生成内容的最大 token 数 |
Responses API
对于新创建的项⽬,官⽅更推荐优先使⽤Responses API。
官方文档:https://platform.openai.com/docs/api-reference/responses/create
Chat Completions API:是传统的聊天接,简单好⽤,但只能处理⽂本。
Responses API:以事件驱动,⽀持多模态和复杂交互,更适合做应⽤级AI。
两者的参数的区别:
| 对比维度 | Chat Completions API | Responses API |
|---|---|---|
| 定位 | 面向对话生成,典型是聊天机器人、客服问答 | 面向多模态智能助理,统一承载文本、语音、图像、工具调用 |
| 输入形式 | messages 数组:[{ role, content }] | 统一的 input,可混合文本、音频、图像等,更灵活 |
| 输出形式 | 一段完整文本(可流式,但只有文字) | 事件流(semantic events):text.delta、audio.delta、tool_call、completed 等 |
| 流式能力 | 仅支持文本逐 token / 逐字符返回 | 原生支持多模态流式,粒度更细 |
| 多模态支持 | 主要是文本(部分模型支持图像输入) | 原生支持文本、音频、图像,能边生成文字边输出语音 |
| 可控性 | 一次请求 = 一次完整回复,中途不可干预 | 生成过程中可打断、分支、调用工具 |
| 典型场景 | 聊天机器人、FAQ、简单问答系统 | AI 助理、语音对话机器人、智能办公、多模态应用 |
| 生活化示例 | 老师一次性看题、看代码、给完整解释 | 助理像“事件驱动系统”,不断抛出状态和动作 |
Responses API :
URL:https://api.openai.com+/v1/responses
三:模型全量返回
代码:
//3. 发送消息给模型
//1-全量返回
std::string ChatgptProvider::sendMessage(const std::vector<Message>& message,std::map<std::string,std::string>& requestParam)
{
//检测模型是否有效
if(isAvailable() == false)
{
ERR("model is mot available!!!");
return "";
}
//构造请求参数,模型名称,消息列表,温度值,maxtokens数,是否开启流式响应
double temperature = 0.7;//设置一个默认模型温度值
int max_output_tokens = 2048;//设置模型回复最大token数
auto it1 = requestParam.find("temperature");
if(it1 != requestParam.end())
{
temperature = std::stod(it1->second);
}
auto it2 = requestParam.find("max_output_tokens");
if(it2 != requestParam.end())
{
max_output_tokens = std::stod(it2->second);
}
//构建消息列表
Json::Value messageArray(Json::arrayValue);
for(auto messages : message)
{
Json::Value messageJson(Json::objectValue);
messageJson["role"] = messages._role;
messageJson["content"] = messages._content;
messageArray.append(messageJson);
}
//构造请求体
Json::Value requestBody;
requestBody["model"] = getModelName();
requestBody["input"] = messageArray;
requestBody["temperature"] = temperature;
requestBody["max_output_tokens"] = max_output_tokens;
//序列化
Json::StreamWriterBuilder writerBuilder;
writerBuilder["indentation"] = "";
std::string requestBodyStr = Json::writeString(writerBuilder,requestBody);
//创建http客户端
httplib::Client client(_endPoint.c_str());
client.set_connection_timeout(30,0);
client.set_read_timeout(30,0);
//设置代理服务
client.set_proxy("127.0.0.1", 7890);
//设置请求头参数
httplib::Headers headers =
{
{"Authorization","Bearer "+ _apiKey},
//{"Content-Type","application/json"}
};
//发送post请求
auto response = client.Post("/v1/responses",headers,requestBodyStr,"application/json");
if(!response)
{
ERR("chatgpt response return fasle");
return "";
}
//检测响应是否成功
if(response->status != 200)
{
ERR("chatgpt response return fasle:{}",response->status);
return "";
}
INFO("ChatGPTProvider API reponse body : {}",response->body);
//反序列化
Json::CharReaderBuilder reader;
std::istringstream responseStream(response->body);
std::string errorJson;
Json::Value responseJson;
if(!Json::parseFromStream(reader, responseStream , &responseJson, &errorJson))
{
ERR("ChatProvider sendMessage parse response body failed");
return "";
}
// 8. 从响应体中提取模型返回的消息内容
if (responseJson.isMember("output") && responseJson["output"].isArray() && !responseJson["output"].empty())
{
// 模型的回复刚好是 output 数组的第 0 个元素
auto output = responseJson["output"][0];
if (output.isMember("content") && output["content"].isArray() && !output["content"].empty() && output["content"][0].isMember("text"))
{
std::string replyString = output["content"][0]["text"].asString();
INFO("ChatProvider sendMessage replyString: {}", replyString);
return replyString;
}
}
ERR("ChatGPTProvider sendMessage parse response body failed, errorJson: {}",errorJson);
return "";
}
四:模型流式返回
//2-流式返回
std::string ChatgptProvider::sendMessagestream(const std::vector<Message>& message,
std::map<std::string, std::string>& requestParam,
std::function<void(const std::string&, bool)> callBack)
{
//判读模型是否有效
if(!isAvailable())
{
ERR("Chat GPT is not available");
return "";
}
//构造请求参数
double temperature = 0.7;
int max_output_tokens = 2048;
auto it1 = requestParam.find("temperature");
if(it1 != requestParam.end())
{
temperature = std::stod(it1->second);
}
auto it2 = requestParam.find("max_output_tokens");
if(it2 != requestParam.end())
{
max_output_tokens = std::stod(it2->second);
}
//构建消息列表
Json::Value messageArray(Json::arrayValue);
for(auto messages : message)
{
Json::Value messageJson(Json::objectValue);
messageJson["role"] = messages._role;
messageJson["content"] = messages._content;
messageArray.append(messageJson);
}
//构造请求体
Json::Value requestBody;
requestBody["model"] = getModelName();
requestBody["input"] = messageArray;
requestBody["temperature"] = temperature;
requestBody["max_output_tokens"] = max_output_tokens;
requestBody["stream"] = true;
//序列化
Json::StreamWriterBuilder writerBuilder;
writerBuilder["indentation"] = "";
std::string requestBodyStr = Json::writeString(writerBuilder,requestBody);
//创建http客户端
httplib::Client client(_endPoint.c_str());
client.set_connection_timeout(30,0);
client.set_read_timeout(30,0);
//设置代理服务
client.set_proxy("127.0.0.1", 7890);
//设置请求头参数
// 设置请求头参数
httplib::Headers headers =
{
{"Authorization", "Bearer " + _apiKey},
{"Content-Type", "application/json"},
{"Accept", "text/event-stream"},
};
//流式处理相关变量
std::string buffer;//用于存储当前处理的部分数据
bool gotError = false;//是否遇到错误
std::string errorMsg;//错误信息
int statusCode = 0;//http状态码
bool streamFinish = false;//fasle代表流式响应未结束
std::string fullResponse; //用于存储完整的响应内容
//创建请求对象
httplib::Request request;
request.method = "POST";
request.path = "/v1/responses";
request.body = requestBodyStr;
request.headers = headers;
//设置响应处理器
request.response_handler = [&](const httplib::Response& res)->bool
{
statusCode = res.status;
if(statusCode != 200)
{
gotError = true;
errorMsg = "ChatGPTProvider sendMessageStream POST request failed, http status code: "+std::to_string(statusCode);
return false; //返回false代表请求失败
}
return true; //返回true代表请求成功
};
//设置内容接受处理器
request.content_receiver = [&](const char* data, size_t dataLength,size_t offset,size_t totalLength)->bool
{
//如果http接收失败,就不用接收后面的数据
if(gotError)
{
return false;
}
buffer.append(data,dataLength); //将当前数据追加到缓冲区
INFO("ChatGPTProvider sendMessageStream content_receiver data:{}",buffer);
//检查是否收到完整的事件流数据
size_t pos = 0;
while((pos = buffer.find("\n\n",pos))!=std::string::npos)
{
std::string event = buffer.substr(0,pos);//提取当前事件
buffer.erase(0,pos+2); //删除已处理的部分
//解析事件类型和具体数据的位置
std::istringstream eventStream(event); //将事件字符串转换为流
std::string eventType; //事件类型
std::string eventData; //事件数据
std::string line; //用于存储当前行数据
while(std::getline(eventStream, line))
{
if(line.empty())//如果当前行为空行,跳过
{
continue;
}
if(line.compare(0,6,"event:")==0)//如果当前行为事件类型行
{
eventType = line.substr(7);//提取事件类型 //从第7个字符开始提取,因为"event:"占6个字符
}
else if(line.compare(0,5,"data:")==0)//如果当前行为事件数据行
{
eventData = line.substr(6);//提取事件数据
}
}
//对模型的结果进行序列化
Json::Value chunk;
Json::CharReaderBuilder reader;
std::string errs;
std::istringstream eventDataStream(eventData); //将事件数据字符串转换为流
//parseFromStream 尝试从流中解析JSON数据,返回false代表解析失败,true代表解析成功
if(!Json::parseFromStream(reader,eventDataStream,&chunk,&errs))
{
gotError = true;//标记遇到错误
ERR("ChatGPTProvider sendMessageStream parseFromStream failed, errorMsg: {}",errs);//记录错误信息
continue;//跳过当前事件,继续处理下一个事件
}
if(eventType == "response.output_text.delta")
{
if(chunk.isMember("delta")&&chunk["delta"].isString())//如果delta字段存在且是字符串类型
{
std::string delta = chunk["delta"].asString();//提取delta字段的字符串值
callBack(delta,false);//将delta返回给调用者,false表示不是完整响应
}
}
else if(eventType == "response.output_item.done")
{
if(chunk.isMember("item")&&chunk["item"].isObject())//如果item字段存在且是对象类型
{
Json::Value item = chunk["item"];//提取item字段的对象值
if(item.isMember("content")&&item["content"].isArray()&&!item["content"].empty()&&item["content"][0].isMember("text")&&item["content"][0]["text"].isString())
{
fullResponse+=item["content"][0]["text"].asString();//将文本内容追加到完整响应中
}
}
}
else if(eventType == "response.completed")
{
streamFinish = true;//标记流完成
callBack("",true);//将完整响应返回给调用者,true表示是完整响应
return true;//返回成功
}
}
return true;//返回成功
};
//发送POST请求
auto result = client.send(request);
if(!result)
{
//发送POST请求失败
ERR("ChatGPTProvider sendMessageStream POST request failed, error: {}",to_string(result.error()));//记录错误信息
return "";
}
//确保流完成
if(!streamFinish)
{
WARN("stream ended without response.completion.done event");//记录警告信息
callBack("",true);//将完整响应返回给调用者,true表示是完整响应
return "";
}
return fullResponse;//返回成功
}
更多推荐


所有评论(0)