cha

如何2小时做一个chatGPT的手机聊天机器人?

这几天OpenAI公布的chatGPT项目非常火,大家都在和这个AI聊得十分开心。但是OpenAI没有中国开放注册,即使注册成功了,也需要全程挂代理才能使用,并不是十分方便。那么有没有什么好的解决方法呢?

我们可以利用Multi-Session ChatGPT API,这是一个基于ChatGPT且支持服务多个用户的API,只用一个OpenAI账号,一次部署就可以满足多人同时对话的需求。我们可以利用这个API制作一个简单的手机聊天机器人APP。

具体步骤十分简单,只需要2小时就可以做出一个自己的聊天机器人APP,下面先给大家看一下最后APP的效果视频:

如何2小时做一个chatGPT的手机聊天机器人?(图1)

https://www.zhihu.com/video/1583900497802727424

一、准备阶段

1 准备OpenAI账号

首先我们进入OpenAI官网,使用邮箱注册账号。

在注册过程中会要求我们使用国外的手机号进行验证,如果自己没有国外的手机号的话推荐使用虚拟手机号进行注册。(如果您已经有可以使用的国外手机号便可以跳过这一步)

打开地址:在线接受短信的虚拟号码 - SMS-Activate


如何2小时做一个chatGPT的手机聊天机器人?(图2)

在左侧选择服务中输入OpenAI,选择一个最便宜的国家即可。

注意购买后的短信有效期是 20 分钟,我们需要尽快注册

2 环境要求

Node版本大于14

Java Development Kit [JDK] 11

3 开发环境搭建

  1. 前端环境搭建:
本文以Windows作为开发平台,Android作为目标平台。

环境搭建部分可以参考ReactNative中文文档,或我之前的文章Azem:我没有GPU也不懂AI,怎么做一个AI生成图片的APP?中关于环境搭建的部分,这里我们暂且略过。

2. 后端准备工作:

后端我们使用智源基于ChatGPT开发的支持服务多个用户的ChatGPT API。

首先我们将代码clone下来。

git clone --recurse-submodules https://github.com/shiyemin/ChatGPT-MS
pip install -r requirements.txt

修改根目录下的config.json.example,去掉.example后缀并在文件中填入OpenAI的账户和密码。

之后我们需要准备一台可以访问openai服务的美国、日本、韩国或新加坡ip的VPS;部署ChatGPT-MS;用ChatGPT-MS暴露的API就可以搭自己自国内的服务了。

API的参数如下:

{"message":"Who are you?", "user": "Azem"}

二、开发阶段

初始化项目

进入一个空目录在命令行工具输入:

npx react-native init chatGPTApp --template react-native-template-typescript

等待项目初始化完成后,进入TextToImgAPP文件夹就可以看到初始化后的项目源代码了。此时请确保已经运行了模拟器或者连接了Android真机,在项目目录中输入以下命令:

yarn android
// 或者
yarn react-native run-android

此命令会对项目的原生部分进行编译,如果配置没有问题,你应该可以看到应用自动安装到设备上并开始运行。注意第一次运行时需要下载大量编译依赖,耗时可能数十分钟。如果项目成功运行,你将在模拟器或者真机中看到如下画面。


如何2小时做一个chatGPT的手机聊天机器人?(图3)

在项目初始化并成功启动后,我们便可以开始我们的开发工作了。

安装依赖

我们制作的APP需要引入一些依赖库,接下来我来手把手的教大家安装

1. React Navigation

我们需要APP具备登录后转跳的功能,因此我们需要将React Navigation引入我们的项目。

详情请参阅React Navigation官方文档

在项目的根目录输入:

yarn add @react-navigation/native @react-navigation/native-stack react-native-screens react-native-safe-area-context

安装完后进入android/app/src/main/java//MainActivity.java 在MainActivity类中添加一下代码:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
}

并在文件顶部添加:

import android.os.Bundle;

2. react-native-gifted-chat 组件

在根目录中输入:

yarn add react-native-gifted-chat

3. zustand

zustand是一个轻量级的状态管理工具。相比于redux,它更加的简单易用,要编写的模板代码量更少。我们就使用zustand管理我们APP中的各种状态。

yarn add zustand

4. react-native-toast-message

yarn add react-native-toast-message

编写App.tsx

进入项目根目录的App.tsx并将全部内容替换为以下代码:

import React from 'react'
import {LoginScreen} from './src/Screens/LoginScreen'
import {ChatScreen} from './src/Screens/ChatScreen'
import {createNativeStackNavigator} from '@react-navigation/native-stack'
import {NavigationContainer} from '@react-navigation/native'
import {RootStackParamList} from './src/Utils/RootStackParamListType'
import Toast from 'react-native-toast-message'

const Stack = createNativeStackNavigator()

const App = () => {
return (

screenOptions={{
headerShown: false,
}}>





)
}

export default App

编写登录页面

在根目录下创建src目录,并在src目录下创建Screens目录。在其中创建LoginScreen.tsx文件并填入以下代码,登录页按钮所需的图标可以自行替换。

import React, {useState} from 'react'
import {Image, StyleSheet, Text, TextInput, TouchableOpacity, View} from 'react-native'
import {NativeStackScreenProps} from '@react-navigation/native-stack'
import {RootStackParamList} from '../Utils/RootStackParamListType'
import { setMessages, setUser } from "../Utils/CommonStore";

type LoginScreenPropsType = NativeStackScreenProps

export const LoginScreen: React.FC = ({navigation}) => {
const [userInput, setUserInput] = useState('')

const continues = () => {
if (userInput.length > 0) {
navigation.navigate('Chat')
setUser({_id: `00${Date.now()}`, name: userInput})
setMessages([])
}
}

return (



请输入用户名
UserName
style={styles.input}
value={userInput}
onChangeText={userName => {
setUserInput(userName)
}}
/>


continues()}>




)
}

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F4F5F7',
},
input: {
marginTop: 32,
height: 50,
borderWidth: 1,
borderColor: '#22223B',
borderRadius: 30,
paddingHorizontal: 16,
color: '#22223B',
fontWeight: '600',
},
circle: {
width: 510,
height: 510,
borderRadius: 255,
backgroundColor: '#FFFFFF',
position: 'absolute',
left: -120,
top: -60,
},
title: {
marginTop: 32,
fontSize: 30,
fontWeight: '800',
color: '#4A4E69',
},
subTitle: {
fontSize: 15,
fontWeight: '400',
color: '#4A4E69',
},
continue: {
width: 70,
height: 70,
borderRadius: 70 / 2,
backgroundColor: '#8D99AE',
alignItems: 'center',
justifyContent: 'center',
},
})

编写聊天页面

然后在src/Screens/下创建LoginScreen.tsx文件并填入以下代码:

import React from 'react'
import {SafeAreaView} from 'react-native'
import {NativeStackScreenProps} from '@react-navigation/native-stack'
import {RootStackParamList} from '../Utils/RootStackParamListType'
import {useMessages, useUser} from '../Utils/CommonStore'
import { Bubble, GiftedChat, IMessage } from "react-native-gifted-chat";
import {sendChatFunc} from '../Utils/ChatGPTFunc'
import { showToast } from "../Utils/ShowToast";

type ChatScreenPropsType = NativeStackScreenProps

export const ChatScreen: React.FC = () => {
const user = useUser()
const messages = useMessages()

return (

messages={messages}
user={user}
onSend={(message: IMessage[]) => {
sendChatFunc(message).catch(err => showToast(err))
}}
renderAvatar={() => null}
renderBubble={props => {
return (
{...props}
wrapperStyle={{
left: {
backgroundColor: '#D8E2DC',
},
}}
/>
);}}
/>

)
}

编写工具函数

在创建目录src/Utils我们把我们用到的工具函数统一放在这个目录下。

SendMessage.ts

在目录src/Utils下创建SendMessage.ts文件,并填入以下代码:

注意:请将fetch中的url替换为你自己的API地址

export class sendMessageBody {
message: string
user: string
constructor(message: string, user: string) {
this.message = message
this.user = user
}
}

export type replyMessage={
response:string
}

export function sendMessage(body: sendMessageBody) {
return new Promise((resolve, reject) => {
// 请将url换成你自己的API地址
fetch('http://192.168.1.4:5000/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
})
.then(response => {
if (response.ok) {
const res:Promise = response.json()
resolve(res)
} else {
return reject(response.status)
}
})
.catch(error => reject(error))
})
}

CommonStore.ts

在目录src/Utils下创建CommonStore.ts文件,并填入以下代码:

import create from 'zustand'
import {IMessage, User} from 'react-native-gifted-chat'

const commonStore = create(() => ({
user: {_id: 0, name: 'defaultUser'} as User,
messages: [] as IMessage[],
}))

export const useUser = () => commonStore(s => s.user);

export const setUser = (payload: User) => commonStore.setState({ user: payload });

export const getUserSnap = () => commonStore.getState().user;

export const useMessages = () => commonStore(s => s.messages)

export const setMessages = (payload: IMessage[]) => commonStore.setState({messages: payload})

export const getMessagesSnap = () => commonStore.getState().messages

ChatGPTFunc.ts

在目录src/Utils下创建ChatGPTFunc.ts文件,并填入以下代码:

import {GiftedChat} from 'react-native-gifted-chat'
import {getMessagesSnap, getUserSnap, setMessages} from './CommonStore'
import {IMessage} from 'react-native-gifted-chat/lib/Models'
import {replyMessage, sendMessage, sendMessageBody} from './SendMessage'
import {showToast} from './ShowToast'

function parseBOTMessage(response: replyMessage): IMessage {
return {
_id: `${Math.floor(Math.random() * 1000)}${Date.now()}`,
text: response.response,
createdAt: Date.now(),
user: {
_id: 0,
name: 'ChatGPT',
},
}
}

export const sendChatFunc = async (chatMessage: IMessage[]) => {
setMessages(GiftedChat.append(getMessagesSnap(), chatMessage))
const userChatMessage = chatMessage[chatMessage.length - 1]

if (!userChatMessage) {
return
}

const userSendMessage = new sendMessageBody(userChatMessage.text, getUserSnap().name as string)

try {
const reply = (await sendMessage(userSendMessage)) as replyMessage
const messages = GiftedChat.append(getMessagesSnap(), [parseBOTMessage(reply)])
setMessages(messages)
} catch (err) {
showToast(String(err))
}
}

ShowToast.ts

在目录src/Utils下创建ShowToast.ts文件,并填入以下代码:

import Toast from 'react-native-toast-message'

export const showToast = (text:string) => {
Toast.show({
type: 'error',
text1: '出错了',
text2: text,
});
}

RootStackParamListType.ts

在目录src/Utils下创建RootStackParamListType.ts文件,并填入以下代码:

export type RootStackParamList = {
Login: undefined;
Chat: undefined;
};

本地测试

在完成上述代码的编写后保存文件,按下 R 键,或是在开发者菜单中选择 Reload,将会在模拟器或真机中看到以下画面:


如何2小时做一个chatGPT的手机聊天机器人?(图4)

输入用户名转跳到下一页后就可以开始和ai聊天了。


如何2小时做一个chatGPT的手机聊天机器人?(图5)

三、打包发布

本部分基于ReactNative官方文档,如遇到困难请阅读官方文档寻求进一步帮助。

1 生成签名密钥

Android 要求所有应用都有一个数字签名才会被允许安装在用户手机上,我们需要首先生成一个新的签名密钥,首先找到 jdk 的安装目录,在 Windows 上keytool命令放在 JDK 的 bin 目录中(比如C:\Program Files\Java\jdkx.x.x_x\bin)。打开 CMD 或者 powershell 进入该目录,然后输入以下指令:

keytool -genkeypair -v -storetype PKCS12 -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000

该条命令会要求你设置一段密码与相关发行信息,请妥善保存你设置的密码。在运行完上述指令后,会在目录下生成一个文件my-release-key.keystore

2 设置 gradle 变量

my-release-key.keystore文件放到你工程中的android/app文件夹下,打开项目目录/android/gradle.properties并添加如下代码:

MYAPP_RELEASE_STORE_FILE=my-release-key.keystore
MYAPP_RELEASE_KEY_ALIAS=my-key-alias
MYAPP_RELEASE_STORE_PASSWORD=*****
MYAPP_RELEASE_KEY_PASSWORD=*****

其中*****是第一步时设置的密码 进入项目目录/android/app/build.gradle添加如下配置

...
android {
...
defaultConfig { ... }
signingConfigs {
release {
if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) {
storeFile file(MYAPP_RELEASE_STORE_FILE)
storePassword MYAPP_RELEASE_STORE_PASSWORD
keyAlias MYAPP_RELEASE_KEY_ALIAS
keyPassword MYAPP_RELEASE_KEY_PASSWORD
}
}
}
buildTypes {
release {
...
signingConfig signingConfigs.release
}
}
}
...

3 生成APK包

在项目根目录的终端中输入以下指令:

cd android
./gradlew assembleRelease

生成的 APK 文件位于android/app/build/outputs/apk/release/app-release.apk,它已经可以用来发布了。

4 测试发行版本

输入以下命令可以在设备上安装发行版本:

npx react-native run-android --variant=release
注意:你可能需要先将真机或虚拟机中安装的测试版APP删除,否则可能出现无法安装的情况

完整项目代码:https://github.com/MX0723/chatGPTAPP

邀请

您可能还会对下面的文章感兴趣:

登录 注册 退出