快速搭建自己的ChatGPT

1,638 阅读2分钟

本文将向您展示如何创建一个简单的基于OpenAI GPT-3.5-turbo的聊天助手。我们将使用Python Flask作为后端服务器,以及Flutter构建跨平台的客户端应用程序。

准备工作

确保您已经安装了以下工具和库:

安装Python库:

pip install openai flask

安装Flutter依赖:

flutter pub add http
flutter pub add provider

创建服务端(Flask)

首先,我们将使用Python和Flask框架搭建一个简单的服务端。在app.py文件中,粘贴以下代码:

import json
from flask import Flask, request, jsonify
import openai

app = Flask(__name__)

openai.api_key = "your_openai_api_key"

# Store session data in a dictionary.
sessions = {}

@app.route("/chat", methods=["POST"])
def chat():
    data = request.get_json()
    session_id = data.get("session_id", "default_session")
    user_message = data.get("message", "")

    if session_id not in sessions:
        sessions[session_id] = [{"role": "system", "content": "You are a helpful assistant."}]

    sessions[session_id].append({"role": "user", "content": user_message})

    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=sessions[session_id],
    )

    assistant_message = response.choices[0].message["content"]
    sessions[session_id].append({"role": "assistant", "content": assistant_message})

    return jsonify({"message": assistant_message})


if __name__ == "__main__":
    app.run(debug=True)

your_openai_api_key替换为您的实际OpenAI API密钥。

现在,运行Flask应用程序:

python app.py

创建客户端(Flutter)

接下来,我们将创建一个简单的跨平台客户端应用程序,使用Flutter框架。将以下代码复制到lib/main.dart文件中:

import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:provider/provider.dart';

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => ChatModel(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: ChatPage(),
    );
  }
}

class ChatPage extends StatelessWidget {
  const ChatPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('GPT Assistant')),
      body: Column(
        children: [
          const Expanded(child: ChatMessagesList()),
          ChatInput(),
        ],
      ),
    );
  }
}

class ChatMessagesList extends StatelessWidget {
  const ChatMessagesList({super.key});

  @override
  Widget build(BuildContext context) {
    return Consumer<ChatModel>(
      builder: (context, chatModel, child) {
        return ListView.builder(
          itemCount: chatModel.messages.length,
          itemBuilder: (context, index) {
            final message = chatModel.messages[index];
            final isUser = message['role'] == 'user';
            final textAlign = isUser ? TextAlign.right : TextAlign.left;
            final text = '${message['content']}';

            return Container(
              padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
              alignment: isUser ? Alignment.topRight : Alignment.topLeft,
              child: Text(text, textAlign: textAlign),
            );
          },
        );
      },
    );
  }
}

class ChatInput extends StatelessWidget {
  final TextEditingController _controller = TextEditingController();

  ChatInput({super.key});

  void _sendMessage(BuildContext context) async {
    if (_controller.text.isNotEmpty) {
      context.read<ChatModel>().addMessage('user', _controller.text);
      final response = await GptApi(sessionId: 'example_session')
          .getAssistantResponse(_controller.text);
      if (kDebugMode) {
        print(response);
      }
      context.read<ChatModel>().addMessage('assistant', response);
      _controller.clear();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
      child: Row(
        children: [
          Expanded(
            child: TextField(
              controller: _controller,
              decoration: const InputDecoration(hintText: 'Type your message'),
              onSubmitted: (value) => _sendMessage(context),
            ),
          ),
          IconButton(
            icon: const Icon(Icons.send),
            onPressed: () => _sendMessage(context),
          ),
        ],
      ),
    );
  }
}

class ChatModel extends ChangeNotifier {
  final List<Map<String, String>> _messages = [];

  List<Map<String, String>> get messages => _messages;

  void addMessage(String role, String content) {
    _messages.add({'role': role, 'content': content});
    notifyListeners();
  }
}

class GptApi {
  final String sessionId;

  GptApi({required this.sessionId});

  Future<String> getAssistantResponse(String message) async {
    try {
      final response = await http
          .post(
        Uri.parse('http://127.0.0.1:5000/chat'),
        headers: {'Content-Type': 'application/json'},
        body: json.encode({
          'session_id': sessionId,
          'message': message,
        }),
      ).timeout(
        const Duration(minutes: 2), // 设置超时时间为5秒
        onTimeout: () {
          // 当请求超时时执行的操作
          throw TimeoutException("请求超时,请稍后重试");
        },
      );

      if (response.statusCode == 200) {
        return json.decode(response.body)['message'];
      } else {
        throw Exception('Failed to get response from GPT assistant');
      }
    } on TimeoutException catch (e) {
      if (kDebugMode) {
        print(e.message);
      }
      return e.message.toString();
    } catch (e) {
      if (kDebugMode) {
        print("发生错误: $e");
      }
      return e.toString();
    }
  }
}

请将<your_server_address>替换为您的Flask服务端的实际地址。 运行Flutter应用程序:

flutter create gpt_assistant 
cd gpt_assistant 
flutter run

现在,您已经成功搭建了一个简单的基于OpenAI GPT-3.5-turbo的聊天助手。客户端应用程序支持与服务端进行连续会话。当您与聊天助手互动时,它将记住之前的消息并在上下文中提供更有意义的回答。请确保Flask服务端正在运行,并根据需要更改客户端代码中的服务器地址。

如果你是第一次运行 flutter web 程序,可能遇到 xhttp error 的错误,请根据以下步骤解决

1- Go to `flutter\bin\cache` and remove a file named: `flutter_tools.stamp`

2- Go to `flutter\packages\flutter_tools\lib\src\web` and open the file `chrome.dart`.

3- Find `'--disable-extensions'`

4- Add `'--disable-web-security'`