鸿蒙网络编程系列31-使用RCP调用OpenAI接口实现智能助手

server/2024/10/19 11:45:39/

简介

在OpenAI推出GPT系列大模型以后,市场上各种类似的大模型也层出不穷,这些大模型也基本都会兼容OpenAI的接口,在开发基于大模型的应用时,选择使用OpenAI接口作为和后端大模型通讯的标准,可以更好的适配不同厂家的模型。本节将开发一个简单的智能助手,可以支持OpenAI兼容的大模型作为后端使用,本示例将演示如何使用RCP模块调用OpenAI兼容接口,如何把一个对象实例转换为Json字符串作为传递的参数,以及在接收到HTTP响应的字符串后,如何转换为对象实例。

1. 智能助手演示

本示例运行后的界面如图所示:

输入使用的模型信息,包括BaseUrl、API-KEY以及模型名称,示例中使用的是阿里的百炼大模型平台,读者可以根据实际需要选择合适的大模型。输入模型信息后,再输入要提问的问题,然后单击“提问”按钮就可以调用大模型的接口了,提问后的响应界面如下所示:

当然,也可以继续提问,助手会继续回答

2. 智能助手示例编写

下面详细介绍创建该示例的步骤。

步骤1:创建Empty Ability项目。

步骤2:在module.json5配置文件加上对权限的声明:

"requestPermissions": [{"name": "ohos.permission.INTERNET"}]

这里添加了访问互联网的权限。

步骤3:添加OpenAI.ets文件定义OpenAI接口需要的类型,代码如下:


//指定角色提供的消息
export class Message {//角色,在OpenAI里一般有system、user、assistant三种,这里只用到了user和assistantpublic role: string = ""public content: string = ""constructor(role: string, content: string) {this.role = rolethis.content = content}
}//提交给AI的问题
export class ChatInfo {public model: string = ""public messages: Array<Message> = new Array()constructor(model: string, messages: Array<Message>) {this.model = modelthis.messages = messages}
}//AI的一个回答
export class Choice {public finish_reason: string = ""public message: Message = new Message("", "")constructor(finish_reason: string, message: Message) {this.finish_reason = finish_reasonthis.message = message}
}//Token消耗情况
export class Usage {public prompt_tokens: number = 0public completion_tokens: number = 0public total_tokens: number = 0constructor(prompt_tokens: number, completion_tokens: number, total_tokens: number) {this.prompt_tokens = prompt_tokensthis.completion_tokens = completion_tokensthis.total_tokens = total_tokens}
}//AI正常返回的信息
export class ChatResponse {public choices: Array<Choice> = new Array()public object: string = ""public usage: Usage = new Usage(0, 0, 0)public created: number = 0public system_fingerprint: string = ""public model: string = ""public id: string = ""constructor(choices: Array<Choice>, object: string, usage: Usage, created: number, system_fingerprint: string, model: string, id: string) {this.choices = choicesthis.object = objectthis.usage = usagethis.created = createdthis.system_fingerprint = system_fingerprintthis.model = modelthis.id = id}
}

步骤4:在Index.ets文件里添加如下的代码:

import { rcp } from '@kit.RemoteCommunicationKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { ChatInfo, ChatResponse, Message } from './OpenAI';
import { ArrayList } from '@kit.ArkTS';@Entry
@Component
struct Index {@State title: string = '使用RCP调用OpenAI接口实现智能助手';//连接、通讯历史记录@State msgHistory: string = ''//提问的问题@State question: string = "二的三次方等于几"//基地址@State baseUrl: string = "https://dashscope.aliyuncs.com/compatible-mode/v1"//API KEY@State apiKey: string = "sk-b7f3f4ec7a1845159de1a1bcf27aad1a"//模型名称@State modelName: string = "qwen-plus"chatHistory: ArrayList<Message> = new ArrayList()chatAPI: string = "/chat/completions"scroller: Scroller = new Scroller()build() {Row() {Column() {Text(this.title).fontSize(14).fontWeight(FontWeight.Bold).width('100%').textAlign(TextAlign.Center).padding(10)Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {Text("Base Url:").fontSize(14).width(80)TextInput({ text: this.baseUrl }).onChange((value) => {this.baseUrl = value}).width(110).fontSize(11).flexGrow(1)}.width('100%').padding(10)Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {Text("API KEY:").fontSize(14).width(80)TextInput({ text: this.apiKey }).onChange((value) => {this.apiKey = value}).width(110).type(InputType.Password).fontSize(11).flexGrow(1)}.width('100%').padding(10)Flex({ justifyContent: FlexAlign.End, alignItems: ItemAlign.Center }) {Text("模型名称:").fontSize(14).width(80)TextInput({ text: this.modelName }).onChange((value) => {this.modelName = value}).width(110).fontSize(11).flexGrow(1)Button("提问").onClick(() => {this.chat()}).width(100).fontSize(14)}.width('100%').padding(10)Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {Text("您的问题:").fontSize(14).width(80)TextInput({ text: this.question }).onChange((value) => {this.question = value}).width(110).fontSize(11).flexGrow(1)}.width('100%').padding(10)Scroll(this.scroller) {Text(this.msgHistory).textAlign(TextAlign.Start).padding(10).width('100%').backgroundColor(0xeeeeee)}.align(Alignment.Top).backgroundColor(0xeeeeee).height(300).flexGrow(1).scrollable(ScrollDirection.Vertical).scrollBar(BarState.On).scrollBarWidth(20)}.width('100%').justifyContent(FlexAlign.Start).height('100%')}.height('100%')}//对话async chat() {let cfg: rcp.SessionConfiguration = {headers: {'Authorization': `Bearer ${this.apiKey}`,'Content-Type': 'application/json'}}let chatInfo = this.getChatInfo()let postInfo = JSON.stringify(chatInfo)const session = rcp.createSession(cfg);session.post(this.baseUrl+this.chatAPI, postInfo).then(resp => {if(resp.statusCode==200){let chatResp =  resp.toJSON() as ChatResponse;this.msgHistory += `我:${this.question}\r\n`this.msgHistory += `AI:${chatResp.choices[0].message.content}\r\n`this.msgHistory += `(共消耗token:${chatResp.usage.total_tokens},其中提问:${chatResp.usage.prompt_tokens},回答:${chatResp.usage.completion_tokens})\r\n`}}).catch((err: BusinessError) => {console.error(`err: err code is ${err.code}, err message is ${JSON.stringify(err)}`);});}//获取提交给AI的问题getChatInfo() {let newMessage = new Message("user", this.question)this.chatHistory.add(newMessage)let chatInfo: ChatInfo = new ChatInfo(this.modelName, this.chatHistory.convertToArray())return chatInfo}
}

步骤5:编译运行,可以使用模拟器或者真机。
步骤6:按照本节第1部分“智能助手演示”操作即可。

3. 代码分析

在OpenAI.ets文件里定义了OpenAI兼容接口需要的类型,理解这一部分代码需要读者仔细研究OpenAI接口的定义,这里就不展开了。在调用大模型HTTP接口提问的时候,需要传递的参数形式如下所示(以通义千问为例):

curl --location 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions' \
--header "Authorization: Bearer $DASHSCOPE_API_KEY" \
--header 'Content-Type: application/json' \
--data '{"model": "qwen-plus","messages": [{"role": "system","content": "You are a helpful assistant."},{"role": "user", "content": "你是谁?"}]
}'

也就是说要传递两个首部,其中一个包含API_KEY信息,另外还需要在body中传递Json格式的提问信息。定义首部的代码如下:

    let cfg: rcp.SessionConfiguration = {headers: {'Authorization': `Bearer ${this.apiKey}`,'Content-Type': 'application/json'}}

这里把首部信息放入了创建Session时传递的参数里。传递作为body的提问信息代码如下:

let chatInfo = this.getChatInfo()let postInfo = JSON.stringify(chatInfo)const session = rcp.createSession(cfg);session.post(this.baseUrl+this.chatAPI, postInfo)

这里把提问信息的实例chatInfo通过JSON.stringify函数转为了Json字符串,然后把这个字符串通过session.post函数传递给了大模型。大模型响应问题的时候,返回的也是字符串,为方便后续调用,把它转为了ChatResponse类型的实例,代码如下所示:

session.post(this.baseUrl+this.chatAPI, postInfo).then(resp => {if(resp.statusCode==200){let chatResp =  resp.toJSON() as ChatResponse;this.msgHistory += `我:${this.question}\r\n`this.msgHistory += `AI:${chatResp.choices[0].message.content}\r\n`this.msgHistory += `(共消耗token:${chatResp.usage.total_tokens},其中提问:${chatResp.usage.prompt_tokens},回答:${chatResp.usage.completion_tokens})\r\n`}})

(本文作者原创,除非明确授权禁止转载)

本文源码地址:

https://gitee.com/zl3624/harmonyos_network_samples/tree/master/code/rcp/OpenAIWithRCP

本系列源码地址:

https://gitee.com/zl3624/harmonyos_network_samples


http://www.ppmy.cn/server/133035.html

相关文章

C++《string的模拟实现》

在之前的篇章C《string》中我们已经了解了string中关于构造、容量、访问、修改操作等函数的功能&#xff0c;以及初步学习了这些函数的使用该如何使用。通过学习string内的各个函数后我们可以发现在解决一些要使用到字符串的环境下有了string内的这些函数操作能大大简化&#x…

Python 将网页保存为图片(Chrome内核)

一、背景介绍 之前写过一篇将网页保存为图片的文章 C# 将网页保存为图片&#xff08;利用WebBrowser&#xff09;_c# webbrowser 把网页内容转换成图片-CSDN博客​​​​​​ 这里有个弊端&#xff0c;C# WebBrowser使用的是IE内核&#xff0c;目前很多网站都不支持IE了&…

jetson agx orin 的pytorch、torchvision安装

首先使用conda创建一个虚拟环境&#xff0c;python3.8&#xff0c;只能使用3.8&#xff0c;因为后面使用NVIDIA提供的torch包对py环境有要求。 使用 conda search cudatoolkit查看自己支持的cuda版本&#xff0c;我选的11.4 然后进入英伟达的torch网站 pytorch英伟达下载地址 …

时间序列预测模型之一文讲透 MA 模型

ARIMA 模型&#xff08;自回归积分滑动平均模型&#xff09;是时间序列分析中的一种广泛应用的模型&#xff0c;这个模型在各个领域发挥着巨大的作用&#xff0c;如股票市场的价格预测、经济中 GDP 增长率预测、供应链中销售量和库存需求预测、气象中气温和降水量的预测等等。为…

Python爬虫进阶:高效数据采集的艺术

在当今数据驱动的世界里&#xff0c;高效的网络爬虫技术已经成为每个数据科学家和后端工程师的必备技能。本文将深入探讨一些高级的Python爬虫技术&#xff0c;这些技术不仅能够大幅提升你的爬虫效率&#xff0c;还能帮助你应对各种复杂的爬虫场景。 1. 异步爬虫&#xff1a;协…

基于SpringBoot+Vue+uniapp微信小程序的宿舍报修系统的详细设计和实现

项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不是配置文件。Spring Boot 通过自动化配置和约…

数据库系统原理——第三章 关系数据库标准语言SQL

文章目录 1.SQL的特点2.SQL的组成3SQL语句3.1数据库的基本操作3.2 基本表的定义、修改、删除3.3索引的建立与删除3.4数据更新3.5数据查询3.5.1单表查询3.5.2连接查询3.5.2.1内连接&#xff08;INNER JOIN&#xff09;3.5.2.2左连接&#xff08;LEFT JOIN&#xff09;3.5.2.3右连…

iOS IPA上传到App Store Connect的三种方案详解

引言 在iOS应用开发中&#xff0c;完成开发后的重要一步就是将IPA文件上传到App Store Connect以便进行测试或发布到App Store。无论是使用Xcode进行原生开发&#xff0c;还是通过uni-app、Flutter等跨平台工具生成的IPA文件&#xff0c;上传到App Store的流程都是类似的。苹果…