使用C++在Qt框架下调用DeepSeek的API接口实现自己的简易桌面小助手

项目背景

随着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之后,必须想办法保存下来,不然后续将无法再次复制。所以复制完就直接保存到安全的位置就可以了。

然后充一块钱试试水。

效果展示


感谢大家!

相关推荐
熙曦Sakura1 小时前
【C++】类和对象(匿名对象)
开发语言·c++·算法
Blue.ztl4 小时前
菜鸟之路Day25一一前端工程化(二)
开发语言·前端·javascript
java1234_小锋5 小时前
说说你对Java里Integer缓存的理解?
java·开发语言
xy_optics6 小时前
matlab的meshgrid
开发语言·matlab
一问三不知_8 小时前
pyqt5报错:qt.qpa.plugin: Could not find the Qt platform plugin “xcb“(已解决)
开发语言·python·qt·ubuntu·conda·bug
小白的高手之路8 小时前
Pytorch中的数据加载
开发语言·人工智能·pytorch·python·深度学习·机器学习
ok0608 小时前
JavaScript(JS)单线程影响速度
开发语言·javascript·ecmascript
液态不合群8 小时前
C# 中比较实用的关键字,基础高频面试题!
开发语言·c#
饼干帅成渣9 小时前
我又又又又又又又又更新了~~~纯手工编写C++画图,有注释~~~~~~
开发语言·c++
菜萝卜子9 小时前
【Go 】Go 语言中的 channel介绍
开发语言·后端·golang