如何使用C++来实现OPENAI协议通过OLLAMA来与AI大模型通信

目前大模型在使用方面最大的长处就是聊天功能。在聊天中可以谈天说地。可以聊聊人生,可以聊聊历史,可以让它写一首七言绝句,也可作一篇洋洋洒洒的长篇报告。

如何才能与大模型进行沟通呢,一个通用的方法是openai协议,其实质就是用http来向大模型提问所获得回答。想用http,C++程序员第一时间想到的无疑就是curl库。现在github上有个项目叫openai-cpp,就是把curl用为作通信库,封装了主要的openai协议。ollama也支持openai协议,当然也可以用这个openai-cpp来对ollama进行通信,进而可与ollama支持的大模型进行通信。

openai-cpp的项目提供了各个功能的示例,其中,主要功能当然就是chat功能了。下面来谈谈openai-cpp的使用。openai-cpp的这个示例都是用cmake来管理和编译的,因我比较熟悉qt5的qmake,就用它来管理和编译示例项目。

拿openai-cpp的10-chat.cpp来说,它的代码非常简单如下所示

#include "openai.hpp"

#include <iostream>

int main() {
    openai::start();

    auto chat = openai::chat().create(R"(
    {
        "model": "gpt-3.5-turbo",
        "messages":[{"role":"user", "content":"blah"}],
        "max_tokens": 7,
        "temperature": 0
    }
    )"_json);
    std::cout << "Response is:\n" << chat.dump(2) << '\n'; 
}

如何在Qt5下运行这个代码呢。

首先,要把openai::start();替换为openai::start("ollama", "optional_organization",true,"http://127.0.0.1:11434/v1/");在这里,“ollama""optional_organization"两个参数可随便写。"http://127.0.0.1:11434/v1/",固定为本地IP。可根据需要把IP改为其它的IP进行远程操作。

其次,以大家熟悉的windows平台为例,需要在Qt5项目的pro文件中,加入
LIBS += -L$$PWD/curl/lib -lcurl 

并将从curl官网下的win64版的curl中的include,lib,拷贝到项目所在的目录下。

然后,在pro文件中加入

SOURCES += \
        main.cpp

INCLUDEPATH += \
        curl/include \
        openai/nlohmann \
        openai

最后,我们可用系统需要最小的qwen:7b大模型来测试。
将原代码中的
    auto chat = openai::chat().create(R"(
    {
        "model": "gpt-3.5-turbo",
        "messages":[{"role":"user", "content":"blah"}],
        "max_tokens": 7,
        "temperature": 0
    }
    )"_json);
改为
    auto chat = openai::chat().create(R"(
    {
        "model": "qwen:7b",
        "messages":[{"role":"user", "content":"hello"}],
        "max_tokens": 7,
        "temperature": 0
    }
    )"_json);

这样,就可以编译成功了。
在运行前,确认ollama已正确安装并已执行过ollama run qwen:7b命令,第一次执行这个命令,ollama会自动下载qwen:7b的大模型,可能时间较长,差不多4G大小,我的网络用了将近一小时。

试运行一下,有回答了。但回答显然是不全的。这时把"max_tokens": 7,一行改为"max_tokens": 1024,就可以回答全了。

来分析一下代码:
第一行代码,是包含openai-cpp的头文件,事实上,整个openai-cpp的项目,就一个头文件。
主要的代码就是auto chat = openai::chat().create(),括号里的内容是nlohmann的json库,这个库是基于std:string的库。同样,也就只是一个名为json.hpp头文件。代码中的写法是用宏定义的方法生成了一个json字串内容为    
{
        "model": "qwen:7b",
        "messages":[{"role":"user", "content":"hello"}],
        "max_tokens": 1024,
        "temperature": 0
}
的nlohmann的json对象。这种写法作为示例程序很简洁明了,但如果要写成实用的代码则不太方便了。在上述10-chat.cpp中的加入两行
#include <nlohmann/json.hpp>
using json = nlohmann::json;

然后,就可使用nlohmann的json库了。
我是这样写的

        json prompt;
        prompt["model"] = "qwen:7b";
        json msg_json;
        msg_json["role"] = "user";
        msg_json["content"] = "邓丽君是日本人吗?";

        prompt["messages"].push_back(msg_json);
        prompt["max_tokens"] = 1024;
        prompt["temperature"] = 1.5;

这时,那个chat代码就可改为openai::chat.create(prompt);

这时,prompt就可填入任何你想问的问题。你会发现,用英文提问,回答的是英文,是中文提问,则回答中文。
这个json对象的参数中,有两个重要的参数。“max_tokens”,可以认为就是指定回答的内容长度,长度不够,则会出现回答不全的情况。"temperature"参数,是指定回答的随机性。10-chat的代码中是0,你会发现每次回答的内容都一样。事实上,查到的资料表明,这个参数的取值是在0~2之间。如我取的是1.5,随机性就已经很强了,同一个问题,回答的实质内容基本一样,但表达的文字不一样。给人的感觉就是大模型像个人在回答了,而不是机械的给出一个始终不变的标准答案。

接下来,我们来试试让大模型更像个人。向人提问,“什么最好”,正常人在回答这个问题时肯定不知道你问的哪个方面的问题,不知如何回答,但如果之前向这个人问过,“米饭好吃吗”,那这个人就可据此认为你问的“什么最好”应该是指什么最好吃。这就是上下文。openai同样也支持这个上下文。“messages"这个参数就是可以把你的提问保存下来,如果你能把大模型对这个问题的问答也保存下来,大模型就更能理解你的上下文关系了。

下面我把最终的完整代码贴出来供大家参考。

#include "openai.hpp"

using namespace openai;

#include <iostream>

#include <vector>

using namespace std;

#include <nlohmann/json.hpp>
using json = nlohmann::json;

#include <QDebug>


int main() {

    auto& openai = openai::start("ollama", "optional_organization",true,"http://127.0.0.1:11434/v1/");

    json prompt;
    prompt["model"] = "qwen:7b";

    json msg_json;
    msg_json["role"] = "user";
    msg_json["content"] = "邓丽君是日本人吗?";

    prompt["messages"].push_back(msg_json);
    prompt["max_tokens"] = 1024;
    prompt["temperature"] = 1.5; //经试验,temperature取值在0~2之间有效

    qDebug()<<"Q:"<<QString::fromStdString(msg_json["content"]);

    auto chat = openai.chat.create(prompt);

    //qDebug()<< "Response is:\n"<<QString::fromStdString(chat.dump(2));

    string res = chat.dump(2);
    //取出返回的json字串中的content内容关输出
    json jr = json::parse(res);
    // 先获取choices数组中的第一个元素(示例中只有一个元素)
    json choice = jr["choices"][0];
    // 再从对应的message对象中获取content字段
    string role = choice["message"]["role"];
    //qDebug()<<"role:"<<QString::fromStdString(role);
    string content = choice["message"]["content"];
    qDebug()<<"A:"<<QString::fromStdString(content);

    //prompt["messages"].clear(); //实践证明,只是没有清除messages的内容,默认是上下文有效的。

    //加入回答到上文中,这个添加似乎对AI联系上下文回答效果就是让AI接下来的问答知道是指邓丽君。与上文的效果相同。在有上文的情况下,看不出明显区别
    msg_json["role"] = "assistant";
    msg_json["content"] = content;
    prompt["messages"].push_back(msg_json);


    msg_json["role"] = "user";
    msg_json["content"] = "她为什么总唱日本歌?";
    prompt["messages"].push_back(msg_json);

    qDebug()<<"Q:"<<QString::fromStdString(msg_json["content"]);

    auto chat_1 = openai.chat.create(prompt);

    res = chat_1.dump(2);

    //qDebug()<< "Response is:\n"<<QString::fromStdString(res);

    jr = json::parse(res);
    // 先获取choices数组中的第一个元素(示例中只有一个元素)
    choice = jr["choices"][0];

    content = choice["message"]["content"];
    qDebug()<<"A:"<< QString::fromStdString(content);

}

在代码中,使用了qDebug来在windows控制台上输出结果,这是因为这样做可以在windows的控制台上正确的输出utf-8编码的中文了。

为方便大家,我把完整的qt5下的项目上传以供大家正载

https://download.csdn.net/download/hugerat/90291612

该项目在curl/lib文件夹下,有一个名为libcurl-x64.dll的文件,要正常运示例,需将此文件拷贝到编译生成的exe文件的相同文件夹下,或者你的windows安装了libcurl并配置好了环境变量。由名字可知它是一个64位的文件,应此Qt5的项目在编译时,应选择64位的编译器,比如我用的是mingw 64-bit

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/4582.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ChromeOS 132 版本更新

ChromeOS 132 版本更新 1. 企业定制化 Chrome Web Store 管理员现在可以使用新设置定制 Chrome Web Store 以适应他们管理的用户&#xff0c;包括以下功能&#xff1a; 添加公司标志添加首页横幅和自定义公告策划扩展集合实施基于类别的控制 这些设置可以通过管理员控制台进…

力扣动态规划-5【算法学习day.99】

前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;建议灵神的题单和代码随想录&#xff09;和记录自己的学习过程&#xff0c;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关…

国内汽车法规政策标准解读:GB 44495-2024《汽车整车信息安全技术要求》

目录 背景 概述 标准适用范围 汽车信息安全管理体系要求&#xff08;第5章&#xff09; 信息安全基本要求&#xff08;第6章&#xff09; 信息安全技术要求&#xff08;第7章&#xff09; ◆ 外部连接安全要求&#xff1a; ◆通信安全要求&#xff1a; ◆软件升级安全…

Apache SeaTunnel 2.3.9 正式发布:多项新特性与优化全面提升数据集成能力

近日&#xff0c;Apache SeaTunnel 社区正式发布了最新版本 2.3.9。本次更新新增了Helm 集群部署、Transform 支持多表、Zeta新API、表结构转换、任务提交队列、分库分表合并、列转多行 等多个功能更新&#xff01; 作为一款开源、分布式的数据集成平台&#xff0c;本次版本通过…

EasyControl:首个登陆AWS Marketplace的中国MDM先锋

在全球数字化与移动化浪潮中&#xff0c;企业对安全、高效的移动设备管理&#xff08;MDM&#xff09;需求日益增长。EasyControl作为国内MDM领域的佼佼者&#xff0c;凭借成熟的技术和创新的解决方案&#xff0c;成为国内首个成功上线亚马逊AWS Marketplace的MDM产品&#xff…

OpenCV简介、OpenCV安装

OpenCV简介、OpenCV安装 本文目录&#xff1a; 零、时光宝盒 一、OpenCV简介 二、OpenCV图像处理基础知识 三、OpenCV-Python环境安装 2.1、纯python环境下安装OpenCV 2.2、Anaconda管理环境下安装 OpenCV 四、如何用OpenCV 中进行读取展示图像 五、OpenCV读取图像、显…

【语言处理和机器学习】概述篇(基础小白入门篇)

前言 自学笔记&#xff0c;分享给语言学/语言教育学方向的&#xff0c;但对语言数据处理感兴趣但是尚未入门&#xff0c;却需要在论文中用到的小伙伴&#xff0c;欢迎大佬们补充或绕道。ps&#xff1a;本文不涉及公式讲解&#xff08;文科生小白友好体质&#xff09;&#xff…

ARP 表、MAC 表、路由表、跨网段 ARP

文章目录 一、ARP 表1、PC2、路由器 - AR22203、交换机 - S57004、什么样的设备会有 ARP 表&#xff1f; 二、MAC 表什么样的设备会有 MAC 表&#xff1f; 三、路由表什么样的设备会有路由表&#xff1f; 四、抓取跨网段 ARP 包 所谓 “透明” 就是指不用做任何配置 一、ARP 表…

Spring的IoC、Bean、DI的简单实现,难度:※※※

目录 场景描述 第一步&#xff1a;初始化Maven项目 第二步&#xff1a;Maven导入Spring包&#xff08;给代码&#xff09; 第三步&#xff1a;创建Spring配置文件 第四步 创建Bean 第五步 简单使用Bean &#xff08;有代码&#xff09; 第六步 通过依赖注入使用Bean&…

wireshark工具简介

目录 1 wireshark介绍 2 wireshark抓包流程 2.1 选择网卡 2.2 停止抓包 2.3 保存数据 3 wireshark过滤器设置 3.1 显示过滤器的设置 3.2 抓包过滤器 4 wireshark的封包列表与封包详情 4.1 封包列表 4.2 封包详情 参考文献 1 wireshark介绍 wireshark是非常流行的网络…

C# OpenCvSharp 部署文档矫正,包括文档扭曲/模糊/阴影等情况

目录 说明 效果 模型 项目 代码 下载 参考 C# OpenCvSharp 部署文档矫正&#xff0c;包括文档扭曲/模糊/阴影等情况 说明 地址&#xff1a;https://github.com/RapidAI/RapidUnDistort 修正文档扭曲/模糊/阴影等情况&#xff0c;使用onnx模型简单轻量部署&#xff0c…

编辑器Vim基本模式和指令 --【Linux基础开发工具】

文章目录 一、编辑器Vim 键盘布局二、Linux编辑器-vim使用三、vim的基本概念正常/普通/命令模式(Normal mode)插入模式(Insert mode)末行模式(last line mode) 四、vim的基本操作五、vim正常模式命令集插入模式从插入模式切换为命令模式移动光标删除文字复制替换撤销上一次操作…

LeetCode 110.平衡二叉树

题目描述 给定一个二叉树&#xff0c;判断它是否是平衡二叉树。 示例 1&#xff1a; 示例 2&#xff1a; 输入&#xff1a;root [1,2,2,3,3,null,null,4,4] 输出&#xff1a;false 示例 3&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;true 提示&#xff1a; …

Asp .Net Core 实现微服务:集成 Ocelot+Nacos+Swagger+Cors实现网关、服务注册、服务发现

什么是 Ocelot ? Ocelot是一个开源的ASP.NET Core微服务网关&#xff0c;它提供了API网关所需的所有功能&#xff0c;如路由、认证、限流、监控等。 Ocelot是一个简单、灵活且功能强大的API网关&#xff0c;它可以与现有的服务集成&#xff0c;并帮助您保护、监控和扩展您的…

Express中间件

目录 Express中间件 中间件的概念 next函数 全局中间与局部中间件 多个中间件 中间的5个注意事项 中间的分类 应用级中间件 路由级中间件 错误级中间件 Express内置中间件 express.json express.urlencoded 第三方中间件​编辑 自定义中间件 Express中间件 中间…

Linux 高级路由与流量控制-用 tc qdisc 管理 Linux 网络带宽

大家读完记得觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 此分享内容比较专业&#xff0c;很多与硬件和通讯规则及队列&#xff0c;比较底层需要有技术功底人员深入解读。 Linux 的带宽管理能力 足以媲美许多高端、专用的带宽管理系统。 1 队列&#xff0…

要获取本地的公网 IP 地址(curl ifconfig.me)

文章目录 通过命令行查询&#xff08;适用于 Linux/Mac/Windows&#xff09;Linux/MacWindows 注意事项 要获取本地的公网 IP 地址&#xff0c;可以通过以下简单的方法&#xff1a; 通过命令行查询&#xff08;适用于 Linux/Mac/Windows&#xff09; Linux/Mac 打开终端。输入…

项目开发实践——基于SpringBoot+Vue3实现的在线考试系统(七)

文章目录 一、题库管理模块实现1、新增题目功能实现1.1 页面设计1.2 前端功能实现1.3 后端功能实现1.4 效果展示2、题目列表功能实现2.1 页面设计2.2 前端功能实现2.3 后端功能实现2.3.1 后端查询题目列表接口实现2.3.2 后端编辑试题接口实现2.4 效果展示二、代码下载一、题库管…

Python文本处理:LDA主题聚类模型

一、模型简介 LDA&#xff08;Latent Dirichlet Allocation&#xff09;是一种生成式概率模型&#xff0c;用于发现文本数据中隐藏的主题分布。本项目基于Python实现LDA主题模型&#xff0c;包含文本预处理、最佳主题数目选择、关键词提取、词云生成以及PyLDAvis可视化等步骤。…

4.JoranConfigurator解析logbak.xml

文章目录 一、前言二、源码解析GenericXMLConfiguratorlogback.xml解析通过SaxEvent构建节点model解析model节点DefaultProcessor解析model 三、总结 一、前言 上一篇介绍了logback模块解析logback.mxl文件的入口, 我们可以手动指定logback.xml文件的位置, 也可以使用其它的名…