这几天OpenAI公布的chatGPT项目非常火,大家都在和这个AI聊得十分开心。但是OpenAI没有中国开放注册,即使注册成功了,也需要全程挂代理才能使用,并不是十分方便。那么有没有什么好的解决方法呢?
我们可以利用Multi-Session ChatGPT API,这是一个基于ChatGPT且支持服务多个用户的API,只用一个OpenAI账号,一次部署就可以满足多人同时对话的需求。我们可以利用这个API制作一个简单的手机聊天机器人APP。
具体步骤十分简单,只需要2小时就可以做出一个自己的聊天机器人APP,下面先给大家看一下最后APP的效果视频:
目录
一、准备阶段
1 准备OpenAI账号
首先我们进入OpenAI官网,使用邮箱注册账号。
在注册过程中会要求我们使用国外的手机号进行验证,如果自己没有国外的手机号的话推荐使用虚拟手机号进行注册。(如果您已经有可以使用的国外手机号便可以跳过这一步)
打开地址:在线接受短信的虚拟号码 – SMS-Activate
在左侧选择服务中输入OpenAI,选择一个最便宜的国家即可。
注意购买后的短信有效期是 20 分钟,我们需要尽快注册
2 环境要求
Node版本大于14
Java Development Kit [JDK] 11
3 开发环境搭建
前端环境搭建:本文以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的参数如下:
二、开发阶段
初始化项目
进入一个空目录在命令行工具输入:
等待项目初始化完成后,进入TextToImgAPP文件夹就可以看到初始化后的项目源代码了。此时请确保已经运行了模拟器或者连接了Android真机,在项目目录中输入以下命令:
yarn android
// 或者
yarn react-native run-android此命令会对项目的原生部分进行编译,如果配置没有问题,你应该可以看到应用自动安装到设备上并开始运行。注意第一次运行时需要下载大量编译依赖,耗时可能数十分钟。如果项目成功运行,你将在模拟器或者真机中看到如下画面。
在项目初始化并成功启动后,我们便可以开始我们的开发工作了。
安装依赖
我们制作的APP需要引入一些依赖库,接下来我来手把手的教大家安装
1. React Navigation
我们需要APP具备登录后转跳的功能,因此我们需要将React Navigation引入我们的项目。
详情请参阅React Navigation官方文档
在项目的根目录输入:
安装完后进入android/app/src/main/java/<your package name>/MainActivity.java 在MainActivity类中添加一下代码:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
}并在文件顶部添加:
2. react-native-gifted-chat 组件
在根目录中输入:
3. zustand
zustand是一个轻量级的状态管理工具。相比于redux,它更加的简单易用,要编写的模板代码量更少。我们就使用zustand管理我们APP中的各种状态。
4. react-native-toast-message
编写App.tsx
进入项目根目录的App.tsx并将全部内容替换为以下代码:
const Stack = createNativeStackNavigator<RootStackParamList>()
const App = () => { return ( <NavigationContainer> <Stack.Navigator screenOptions={{ headerShown: false, }}> <Stack.Screen name=“Login” component={LoginScreen} /> <Stack.Screen name=“Chat” component={ChatScreen} /> </Stack.Navigator> <Toast /> </NavigationContainer> ) }
export default App
编写登录页面
在根目录下创建src目录,并在src目录下创建Screens目录。在其中创建LoginScreen.tsx文件并填入以下代码,登录页按钮所需的图标可以自行替换。
type LoginScreenPropsType = NativeStackScreenProps<RootStackParamList, Login>
export const LoginScreen: React.FC<LoginScreenPropsType> = ({navigation}) => { const [userInput, setUserInput] = useState<string>()
const continues = () => { if (userInput.length > 0) { navigation.navigate(Chat) setUser({_id: `00${Date.now()}`, name: userInput}) setMessages([]) } }
return ( <View style={styles.container}> <View style={styles.circle} /> <View style={{marginHorizontal: 32, marginTop: 160}}> <Text style={styles.title}>请输入用户名</Text> <Text style={styles.subTitle}>UserName</Text> <TextInput style={styles.input} value={userInput} onChangeText={userName => { setUserInput(userName) }} /> </View> <View style={{alignItems: flex-end, marginTop: 100, marginRight: 32}}> <TouchableOpacity style={styles.continue} onPress={() => continues()}> <Image style={{height: 30, width: 30}} source={require(../Images/arrowRight.png)} /> </TouchableOpacity> </View> </View> ) }
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文件并填入以下代码:
type ChatScreenPropsType = NativeStackScreenProps<RootStackParamList, Chat>
export const ChatScreen: React.FC<ChatScreenPropsType> = () => { const user = useUser() const messages = useMessages()
return ( <SafeAreaView style={{flex: 1}}> <GiftedChat messages={messages} user={user} onSend={(message: IMessage[]) => { sendChatFunc(message).catch(err => showToast(err)) }} renderAvatar={() => null} renderBubble={props => { return ( <Bubble {…props} wrapperStyle={{ left: { backgroundColor: #D8E2DC, }, }} /> );}} /> </SafeAreaView> ) }
编写工具函数
在创建目录src/Utils我们把我们用到的工具函数统一放在这个目录下。
SendMessage.ts
在目录src/Utils下创建SendMessage.ts文件,并填入以下代码:
注意:请将fetch中的url替换为你自己的API地址
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<replyMessage> = response.json() resolve(res) } else { return reject(response.status) } }) .catch(error => reject(error)) }) }
CommonStore.ts
在目录src/Utils下创建CommonStore.ts文件,并填入以下代码:
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文件,并填入以下代码:
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文件,并填入以下代码:
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,将会在模拟器或真机中看到以下画面:
输入用户名转跳到下一页后就可以开始和ai聊天了。
三、打包发布
本部分基于ReactNative官方文档,如遇到困难请阅读官方文档寻求进一步帮助。
1 生成签名密钥
Android 要求所有应用都有一个数字签名才会被允许安装在用户手机上,我们需要首先生成一个新的签名密钥,首先找到 jdk 的安装目录,在 Windows 上keytool命令放在 JDK 的 bin 目录中(比如C:\Program Files\Java\jdkx.x.x_x\bin)。打开 CMD 或者 powershell 进入该目录,然后输入以下指令:
该条命令会要求你设置一段密码与相关发行信息,请妥善保存你设置的密码。在运行完上述指令后,会在目录下生成一个文件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 测试发行版本
输入以下命令可以在设备上安装发行版本:
注意:你可能需要先将真机或虚拟机中安装的测试版APP删除,否则可能出现无法安装的情况