项目背景
随着DeepSeek的爆火,最近的DeepSeek也进行了新一轮技术的更新,为了拥抱新时代,我们也要不断学习新的知识,难的底层原理我们接触不到,简单的调用还能难住我们?
因为在网络上搜集到的资源都是用Python语言来进行调用,提供C++调用接口的教程较少,所以我就打算出一节关于整个流程的分析,供大家闲暇之余整来玩玩。废话不多说,我们直接开始:
首先:创建一个Qt项目,为项目命名,此时自动生成五个文件。
main.cpp、mainwindow.h、mainwindow.cpp、mainwindow.ui、还有一个.pro项目文件。
此外,我们再额外建一个类:DeepSeekAPI,就需要创建对应的.h和.cpp文件。
完事之后,此时的文件包括:

交互逻辑
我们先来完成页面逻辑的问题,暂不考虑接口调用的事情
在ui文件中,只需要有三个控件即可:QPlainTextEdit、QLineEdit、QPushButton。【我分别命名为:ChatBox、inputBox、btnSend】
ui设计

当然,在这块你怎么喜欢怎么设计,包括设置背景图、改变调色板、大小、布局等的自定义安排。
这不重要,重要的是每个控件的使用,但不要违背事实或习惯。
在MainWindow的主页面初始化时(构造函数中),我们应该将chatBox设置为只读状态。
cpp
ui->ChatBox->setReadOnly(true);//对话框只读
点击按钮
分析:点击按钮-》输入框文本清除-》对话框出现我的对话-》AI推理(同时屏蔽按钮的可用属性)-》推理结果显示到对话框上。OK,步骤清晰了,那么剩下的就是完善代码了:
cpp
//按钮-发送请求
connect(ui->btnSend,&QPushButton::clicked,this,[&](){
QString userInput = ui->inputBox->text();
if(userInput.isEmpty()) return;
ui->inputBox->clear();//输入框清空
ui->btnSend->setEnabled(false);//禁用发送按钮,放置频繁发送请求
ui->ChatBox->appendPlainText("User: " + userInput);//加入对话框
startInference();//开始推理-》推理完成后的任务也交给它了
});
主窗体类:
cpp
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void startInference();
private:
Ui::MainWindow *ui;
private:
DeepSeekAPI *apiClient = new DeepSeekAPI();// API客户端
};
我们只需要在主窗体添加一个startInference-开始推理的方法,然后添加一个成员:DeepSeekAPI对象指针。然后实现推理方法的逻辑就可以了:
推理逻辑
前端逻辑
推理逻辑的步骤:①采用异步通信方式,调用API的推理接口:inferMessage,将输入框的内容添加进去(如果这块使用控件文本,上面就不要将文本框清空,不然这块就获取不到内容了,不过后面不用这个东西这块知道逻辑就可以了)②异步通信时,使用同类型的监视器监视异步线程,使用setFuture(future)进行绑定监视。③建立信号槽,本次连接是将异步推理线程结束的信号与处理逻辑连接上:
接收异步线程传输的结果-》将结果添加到对话框-》发送按钮重新设置为可使用-》删除监视器
cpp
void MainWindow::startInference(){
//异步推理
QFuture<QString> future = apiClient->inferMessage(ui->inputBox->text());
QFutureWatcher<QString> *futureWatcher = new QFutureWatcher<QString>();
futureWatcher->setFuture(future);
connect(futureWatcher,&QFutureWatcher<QString>::finished,this,[=](){
QString result = futureWatcher->result();
ui->ChatBox->appendPlainText("AI: "+result);
ui->btnSend->setEnabled(true);
futureWatcher->deleteLater();
});
}
后端逻辑
前面都是从用户与客户端交互的角度完成的逻辑代码,但是真正的推理是后端使用网络通信进行访问DeepSeek官方的API的。
DeepSeekAPI类的定义:
cpp
class DeepSeekAPI :QObject {
QString apiKey_ = "sk-**********";//这里是API_KEY,需要从DeepSeek创建获取,后面会介绍具体方法
public:
//Qt提供的网络管理器,方便我们进行网络通信
QNetworkAccessManager* networkManager_ = new QNetworkAccessManager();
DeepSeekAPI(QObject *parent = nullptr);
~DeepSeekAPI();
//对话推理
QFuture<QString> inferMessage(const QJsonArray& messages);
};
我们看到,这里的对话推理的逻辑中传入的并不是一个单纯的文本信息,而是Json格式的信息的一个数组。那么我们回过头去把这个数组构建出来再使用。
补充一:MainWindow类内添加成员:
cpp
//对话信息格式
struct ChatMessage {
QString role; // "user" 或 "assistant"
QString content;
};
class MainWindow{
...
private:
QList<ChatMessage> chatHistory; // 存储所有对话记录
};
补充二:点击按钮后的操作:
cpp
connect(... ,
ui->ChatBox->appendPlainText("User: " + userInput);
chatHistory.append({"user",userInput});//在推理前 新加的内容-{role = "user" , content="...."}
startInference();
);
补充三:在异步推理之前进行上下文处理:
cpp
//上下文处理
QJsonArray messageArray;
for(const ChatMessage &msg:chatHistory){
QJsonObject messageObj;//创建对象
messageObj["role"]=msg.role;//设置键值对
messageObj["content"]=msg.content;//设置键值对
messageArray.append(messageObj);//添加对象
}
//保留近5轮对话
if(chatHistory.size()>20){
//由于Token限制,为避免溢出,采用截断旧消息的方式
chatHistory = chatHistory.mid(chatHistory.size() - 10);
}
//异步推理--这时候就可以传入消息数组了
QFuture<QString> future = apiClient->inferMessage(messageArray);
这时候,我们就可以进行真正的推理了:
第一步构建 请求体:
cpp
//构建请求体
QJsonObject content;
content["model"] = "deepseek-chat";//选取通用模型
//content["model"] = "deepseek-reasoner";//R1模型
content["messages"] = messages;
QJsonDocument jmsg = QJsonDocument(content);//转换格式
第二步:创建网络请求头:
cpp
//异步通信接口
QFutureInterface<QString> interface;
interface.reportStarted(); // 标记future已开始
// 确保networkManager_在主线程创建
QNetworkRequest request;
request.setUrl(QUrl("https://api.deepseek.com/v1/chat/completions")); //url路由
request.setRawHeader("Authorization", ("Bearer " + apiKey_).toUtf8()); //apikey
request.setRawHeader("Content-Type", "application/json"); //数据格式 json
request.setRawHeader("max_tokens","500"); //生成最大长度
request.setRawHeader("stream","false");
request.setRawHeader("temperature","1.3"); //控制多样性(0-2)越大越随机,通用对话1.3
request.setRawHeader("top_p","0.9"); /控制生成范围(0-1)不建议与temperature一块使用
request.setRawHeader("stop","\n\n");//连续空行时终止
第三步:从DeepSeek的服务器端接收请求后的反应:
cpp
QNetworkReply* reply = networkManager_->post(request, jmsg.toJson());
QObject::connect(reply, &QNetworkReply::finished, ...);
return interface.future();//返回异步通信结果
最后实现接收到请求后的逻辑:
cpp
// 连接完成信号
QObject::connect(reply, &QNetworkReply::finished, [interface, reply]() mutable {
if (reply->error() == QNetworkReply::NoError) {
QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
QString result = doc.object()["choices"].toArray().first().toObject()["message"].toObject()["content"].toString();
interface.reportResult(result); // 设置结果
}
reply->deleteLater();
interface.reportFinished(); // 标记future完成
});
如果有错误,可以使用代码获取:
else {
// 错误处理
qDebug() << "错误:" << reply->errorString();
qDebug() << "HTTP 状态码:" << reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug() << "错误详情:" << reply->readAll();
interface.reportResult(QString()); // 返回空字符串表示错误
}
至此,代码模块就完成了,非常简单吧,那么剩下的就是如何获取你的APIKEY:
获取APIKEY

切记:在下面获取玩APIKey之后,必须想办法保存下来,不然后续将无法再次复制。所以复制完就直接保存到安全的位置就可以了。



然后充一块钱试试水。
效果展示

感谢大家!