零基础搭建AI聊天机器人:从GPT-3到本地化部署全攻略

在这里插入图片描述
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站https://www.captainbed.cn/north
在这里插入图片描述

前言

在人工智能技术飞速发展的今天,聊天机器人已经成为众多应用场景中不可或缺的一部分。本文将带领零基础的开发者从零开始,一步步搭建自己的AI聊天机器人,涵盖从使用云端GPT-3 API到完全本地化部署的全过程。我们将详细介绍每个步骤,提供完整的代码示例,并通过流程图帮助理解系统架构。

第一部分:了解AI聊天机器人基础

1.1 聊天机器人发展简史

聊天机器人(Chatbot)的发展经历了几个重要阶段:

  • 基于规则的早期系统(如ELIZA,1966)
  • 统计学习方法(2010年前后)
  • 深度学习革命(Seq2Seq模型,2014)
  • 预训练语言模型时代(GPT,BERT等,2018至今)

1.2 GPT模型家族简介

GPT(Generative Pre-trained Transformer)系列是OpenAI推出的预训练语言模型:

  • GPT-1(2018):1.17亿参数
  • GPT-2(2019):15亿参数
  • GPT-3(2020):1750亿参数
  • GPT-4(2023):参数规模未公开

1.3 系统架构概述

一个完整的AI聊天机器人系统通常包含以下组件:

┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│  用户界面   │───>│ 后端服务器  │───>│ AI模型API   │───>│ 数据库      │
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘
     ▲                   ▲                   ▲                  ▲
     │                   │                   │                  │
┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│ 移动应用/Web│    │ 业务逻辑处理│    │ 本地模型    │    │ 日志系统    │
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘

第二部分:使用GPT-3 API搭建基础聊天机器人

2.1 获取OpenAI API密钥

  1. 访问OpenAI官网(https://openai.com)
  2. 注册账号并登录
  3. 进入API密钥管理页面(https://platform.openai.com/account/api-keys)
  4. 创建新的API密钥

2.2 搭建Python开发环境

# 创建虚拟环境
python -m venv chatbot-env

# 激活虚拟环境
# Windows:
chatbot-env\Scripts\activate
# macOS/Linux:
source chatbot-env/bin/activate

# 安装必要库
pip install openai flask python-dotenv

2.3 基础聊天机器人实现

创建.env文件存储API密钥:

OPENAI_API_KEY=your_api_key_here

基础聊天机器人代码gpt3_chatbot.py

import os
import openai
from dotenv import load_dotenv

# 加载环境变量
load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")

def chat_with_gpt3(prompt, model="gpt-3.5-turbo", temperature=0.7):
    """
    与GPT-3交互的基础函数
    :param prompt: 用户输入的提示词
    :param model: 使用的模型版本
    :param temperature: 控制生成文本的随机性 (0-1)
    :return: GPT-3生成的回复
    """
    try:
        response = openai.ChatCompletion.create(
            model=model,
            messages=[
                {"role": "system", "content": "You are a helpful assistant."},
                {"role": "user", "content": prompt}
            ],
            temperature=temperature
        )
        return response.choices[0].message['content']
    except Exception as e:
        return f"An error occurred: {str(e)}"

# 简单的命令行交互
if __name__ == "__main__":
    print("GPT-3聊天机器人已启动! 输入'exit'退出")
    while True:
        user_input = input("你: ")
        if user_input.lower() == 'exit':
            break
        response = chat_with_gpt3(user_input)
        print(f"助手: {response}")

2.4 添加对话历史记忆

增强版聊天机器人,能够记住对话上下文:

def chat_with_memory():
    print("GPT-3聊天机器人(带记忆)已启动! 输入'exit'退出")
    conversation_history = [
        {"role": "system", "content": "You are a helpful assistant."}
    ]
    
    while True:
        user_input = input("你: ")
        if user_input.lower() == 'exit':
            break
            
        conversation_history.append({"role": "user", "content": user_input})
        
        try:
            response = openai.ChatCompletion.create(
                model="gpt-3.5-turbo",
                messages=conversation_history,
                temperature=0.7
            )
            assistant_reply = response.choices[0].message['content']
            print(f"助手: {assistant_reply}")
            conversation_history.append({"role": "assistant", "content": assistant_reply})
        except Exception as e:
            print(f"发生错误: {str(e)}")

2.5 创建简单的Web界面

使用Flask创建Web应用app.py

from flask import Flask, request, jsonify, render_template
import os
import openai
from dotenv import load_dotenv

load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")

app = Flask(__name__)

@app.route('/')
def home():
    return render_template('chat.html')

@app.route('/chat', methods=['POST'])
def chat():
    data = request.json
    user_message = data.get('message', '')
    conversation = data.get('conversation', [])
    
    messages = [
        {"role": "system", "content": "You are a helpful assistant."}
    ] + conversation
    
    messages.append({"role": "user", "content": user_message})
    
    try:
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",
            messages=messages,
            temperature=0.7
        )
        assistant_reply = response.choices[0].message['content']
        return jsonify({
            'reply': assistant_reply,
            'conversation': messages + [{"role": "assistant", "content": assistant_reply}]
        })
    except Exception as e:
        return jsonify({'error': str(e)}), 500

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

创建HTML模板templates/chat.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>GPT-3聊天机器人</title>
    <style>
        body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
        #chat-container { border: 1px solid #ddd; border-radius: 5px; height: 500px; overflow-y: auto; padding: 10px; margin-bottom: 10px; }
        .user-message { background-color: #e3f2fd; padding: 8px; margin: 5px; border-radius: 5px; text-align: right; }
        .assistant-message { background-color: #f5f5f5; padding: 8px; margin: 5px; border-radius: 5px; }
        #input-area { display: flex; }
        #user-input { flex-grow: 1; padding: 8px; }
        #send-button { padding: 8px 15px; margin-left: 10px; }
    </style>
</head>
<body>
    <h1>GPT-3聊天机器人</h1>
    <div id="chat-container"></div>
    <div id="input-area">
        <input type="text" id="user-input" placeholder="输入消息...">
        <button id="send-button">发送</button>
    </div>

    <script>
        const chatContainer = document.getElementById('chat-container');
        const userInput = document.getElementById('user-input');
        const sendButton = document.getElementById('send-button');
        
        let conversationHistory = [];
        
        function addMessage(role, content) {
            const messageDiv = document.createElement('div');
            messageDiv.className = `${role}-message`;
            messageDiv.textContent = content;
            chatContainer.appendChild(messageDiv);
            chatContainer.scrollTop = chatContainer.scrollHeight;
        }
        
        async function sendMessage() {
            const message = userInput.value.trim();
            if (!message) return;
            
            addMessage('user', message);
            userInput.value = '';
            
            try {
                const response = await fetch('/chat', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        message: message,
                        conversation: conversationHistory
                    })
                });
                
                const data = await response.json();
                if (data.error) {
                    addMessage('assistant', `错误: ${data.error}`);
                } else {
                    addMessage('assistant', data.reply);
                    conversationHistory = data.conversation;
                }
            } catch (error) {
                addMessage('assistant', `请求失败: ${error}`);
            }
        }
        
        sendButton.addEventListener('click', sendMessage);
        userInput.addEventListener('keypress', (e) => {
            if (e.key === 'Enter') sendMessage();
        });
    </script>
</body>
</html>

第三部分:进阶功能实现

3.1 添加角色设定和个性化

修改聊天函数,支持不同的角色设定:

def chat_with_persona(prompt, persona="default", conversation_history=None):
    if conversation_history is None:
        conversation_history = []
    
    # 定义不同角色
    personas = {
        "default": "You are a helpful assistant.",
        "scientist": "You are a scientist with expertise in physics and biology. Provide detailed, accurate answers.",
        "chef": "You are a professional chef. Give cooking advice and recipes.",
        "comedian": "You are a stand-up comedian. Make jokes and keep the mood light."
    }
    
    system_message = personas.get(persona, personas["default"])
    
    messages = [{"role": "system", "content": system_message}] + conversation_history
    messages.append({"role": "user", "content": prompt})
    
    try:
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo",
            messages=messages,
            temperature=0.7
        )
        assistant_reply = response.choices[0].message['content']
        return assistant_reply, messages + [{"role": "assistant", "content": assistant_reply}]
    except Exception as e:
        return f"Error: {str(e)}", conversation_history

3.2 实现多轮对话管理

创建对话管理类:

class ConversationManager:
    def __init__(self, system_prompt="You are a helpful assistant.", max_turns=10):
        self.conversation = [{"role": "system", "content": system_prompt}]
        self.max_turns = max_turns
    
    def add_user_message(self, message):
        self.conversation.append({"role": "user", "content": message})
        self._trim_conversation()
    
    def add_assistant_message(self, message):
        self.conversation.append({"role": "assistant", "content": message})
        self._trim_conversation()
    
    def get_conversation(self):
        return self.conversation.copy()
    
    def _trim_conversation(self):
        # 保持对话不超过最大轮数(保留系统提示)
        if len(self.conversation) > self.max_turns * 2 + 1:
            self.conversation = [self.conversation[0]] + self.conversation[-(self.max_turns * 2):]
    
    def change_system_prompt(self, new_prompt):
        self.conversation[0]["content"] = new_prompt
    
    def reset_conversation(self, system_prompt=None):
        if system_prompt is not None:
            self.conversation = [{"role": "system", "content": system_prompt}]
        else:
            self.conversation = [self.conversation[0]]

3.3 添加文件处理能力

实现从文件读取内容并总结的功能:

def process_file(file_path):
    try:
        with open(file_path, 'r', encoding='utf-8') as file:
            content = file.read()
        
        # 对于长文件,先进行分段处理
        if len(content) > 2000:
            summary = summarize_long_document(content)
        else:
            summary = summarize_text(content)
            
        return summary
    except Exception as e:
        return f"Error processing file: {str(e)}"

def summarize_text(text, model="gpt-3.5-turbo"):
    try:
        response = openai.ChatCompletion.create(
            model=model,
            messages=[
                {"role": "system", "content": "You are a helpful assistant that summarizes text."},
                {"role": "user", "content": f"Please summarize the following text:\n\n{text}"}
            ],
            temperature=0.3  # 使用较低的温度以获得更确定的总结
        )
        return response.choices[0].message['content']
    except Exception as e:
        return f"Error summarizing text: {str(e)}"

def summarize_long_document(text, chunk_size=2000, overlap=200):
    # 将长文档分成块
    chunks = []
    start = 0
    while start < len(text):
        end = min(start + chunk_size, len(text))
        chunks.append(text[start:end])
        start = end - overlap  # 添加重叠以避免在句子中间分割
    
    # 总结每个块
    chunk_summaries = []
    for chunk in chunks:
        summary = summarize_text(chunk)
        chunk_summaries.append(summary)
    
    # 总结所有块的总结
    final_summary = summarize_text("\n\n".join(chunk_summaries))
    return final_summary

第四部分:从云端到本地——模型本地化部署

4.1 为什么需要本地化部署

  • 数据隐私和安全考虑
  • 减少API调用成本
  • 定制化模型需求
  • 网络延迟和稳定性

4.2 本地模型选择

4.2.1 轻量级模型推荐
  1. GPT-2 (OpenAI): 虽然比GPT-3小,但仍有不错的表现
  2. LLaMA (Meta): 7B, 13B等不同规模版本
  3. Alpaca (Stanford): 基于LLaMA微调的指令跟随模型
  4. Vicuna:
  5. ChatGLM-6B (清华): 支持中英双语
4.2.2 硬件需求对比
模型最小显存要求推荐显存CPU运行
GPT-2 Small2GB4GB可能
GPT-2 Medium4GB8GB困难
LLaMA-7B8GB16GB不可能
ChatGLM-6B6GB12GB不可能

4.3 部署ChatGLM-6B本地模型

4.3.1 环境准备
# 创建新的虚拟环境
python -m venv local-chatbot-env
source local-chatbot-env/bin/activate  # Linux/macOS
# local-chatbot-env\Scripts\activate  # Windows

# 安装基础依赖
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118  # 根据CUDA版本调整
pip install transformers sentencepiece accelerate gradio
4.3.2 基础本地聊天机器人

local_chatbot.py:

from transformers import AutoTokenizer, AutoModel
import torch
import gradio as gr

# 加载模型和分词器
model_path = "THUDM/chatglm-6b"
tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
model = AutoModel.from_pretrained(model_path, trust_remote_code=True).half().cuda()
model = model.eval()

def chat(message, history):
    response, history = model.chat(tokenizer, message, history=history)
    return response

# 创建Gradio界面
demo = gr.ChatInterface(
    fn=chat,
    examples=["你好", "介绍一下你自己", "如何学习人工智能"],
    title="ChatGLM-6B本地聊天机器人"
)

demo.launch(server_name="0.0.0.0", server_port=7860)
4.3.3 量化模型以减少资源占用

对于显存不足的情况,可以使用量化模型:

# 修改模型加载部分
model = AutoModel.from_pretrained(model_path, trust_remote_code=True).quantize(8).half().cuda()  # 8位量化
# 或者
model = AutoModel.from_pretrained(model_path, trust_remote_code=True).quantize(4).half().cuda()  # 4位量化
4.3.4 与Flask应用集成

创建local_app.py:

from flask import Flask, request, jsonify
from transformers import AutoTokenizer, AutoModel
import torch

app = Flask(__name__)

# 初始化模型
tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True)
model = AutoModel.from_pretrained("THUDM/chatglm-6b", trust_remote_code=True).half().cuda()
model.eval()

@app.route('/chat', methods=['POST'])
def chat():
    data = request.json
    message = data.get('message', '')
    history = data.get('history', [])
    
    try:
        response, new_history = model.chat(tokenizer, message, history=history)
        return jsonify({
            'reply': response,
            'history': new_history
        })
    except Exception as e:
        return jsonify({'error': str(e)}), 500

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

4.4 性能优化技巧

4.4.1 使用vLLM加速推理
pip install vllm
from vllm import LLM, SamplingParams

llm = LLM(model="THUDM/chatglm-6b")
sampling_params = SamplingParams(temperature=0.7, top_p=0.9)

def generate(prompt):
    outputs = llm.generate([prompt], sampling_params)
    return outputs[0].outputs[0].text
4.4.2 实现流式响应

修改Flask应用支持流式响应:

@app.route('/stream_chat', methods=['POST'])
def stream_chat():
    message = request.json.get('message', '')
    history = request.json.get('history', [])
    
    def generate():
        current_length = 0
        for response, new_history in model.stream_chat(tokenizer, message, history):
            # 只发送新增的部分
            new_part = response[current_length:]
            current_length = len(response)
            yield f"data: {json.dumps({'delta': new_part})}\n\n"
        
        yield "data: [DONE]\n\n"
    
    return Response(generate(), mimetype='text/event-stream')

前端JavaScript代码调整:

function setupStreamingChat() {
    const eventSource = new EventSource('/stream_chat?message=' + encodeURIComponent(userMessage));
    
    eventSource.onmessage = function(e) {
        if (e.data === 'data: [DONE]') {
            eventSource.close();
            return;
        }
        
        const data = JSON.parse(e.data.replace('data: ', ''));
        // 处理data.delta
        appendToChat(data.delta);
    };
}

第五部分:完整项目实战

5.1 系统架构设计

┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   客户端    │    │   Web服务器  │    │  AI模型服务  │    │   数据库     │
│ (Web/移动)  │───>│  (Flask)    │───>│ (本地/云端) │───>│ (PostgreSQL)│
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘
     ▲                   ▲                   ▲                  ▲
     │                   │                   │                  │
┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   用户界面   │    │ 业务逻辑处理 │    │ 模型管理    │    │ 日志系统    │
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘

5.2 数据库设计

创建用户和对话记录表:

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(50) UNIQUE NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    password_hash VARCHAR(128) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE conversations (
    id SERIAL PRIMARY KEY,
    user_id INTEGER REFERENCES users(id),
    title VARCHAR(100) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE messages (
    id SERIAL PRIMARY KEY,
    conversation_id INTEGER REFERENCES conversations(id),
    role VARCHAR(10) NOT NULL,  -- 'user' or 'assistant'
    content TEXT NOT NULL,
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

5.3 完整后端实现

app_full.py:

from flask import Flask, request, jsonify, make_response
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
import jwt
import datetime
from functools import wraps
from transformers import AutoTokenizer, AutoModel
import torch
from config import Config

app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)

# 数据库模型
class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(50), unique=True, nullable=False)
    email = db.Column(db.String(100), unique=True, nullable=False)
    password_hash = db.Column(db.String(128), nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    conversations = db.relationship('Conversation', backref='user', lazy=True)

class Conversation(db.Model):
    __tablename__ = 'conversations'
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    title = db.Column(db.String(100), nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    updated_at = db.Column(db.DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
    messages = db.relationship('Message', backref='conversation', lazy=True)

class Message(db.Model):
    __tablename__ = 'messages'
    id = db.Column(db.Integer, primary_key=True)
    conversation_id = db.Column(db.Integer, db.ForeignKey('conversations.id'), nullable=False)
    role = db.Column(db.String(10), nullable=False)
    content = db.Column(db.Text, nullable=False)
    timestamp = db.Column(db.DateTime, default=datetime.datetime.utcnow)

# 认证装饰器
def token_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = None
        if 'Authorization' in request.headers:
            token = request.headers['Authorization'].split(" ")[1]
        
        if not token:
            return jsonify({'message': 'Token is missing!'}), 401
            
        try:
            data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=["HS256"])
            current_user = User.query.get(data['user_id'])
        except:
            return jsonify({'message': 'Token is invalid!'}), 401
            
        return f(current_user, *args, **kwargs)
    return decorated

# 初始化AI模型
tokenizer = AutoTokenizer.from_pretrained(Config.MODEL_PATH, trust_remote_code=True)
model = AutoModel.from_pretrained(Config.MODEL_PATH, trust_remote_code=True).half().cuda()
model.eval()

# 用户认证路由
@app.route('/register', methods=['POST'])
def register():
    data = request.json
    hashed_password = generate_password_hash(data['password'], method='sha256')
    new_user = User(
        username=data['username'],
        email=data['email'],
        password_hash=hashed_password
    )
    db.session.add(new_user)
    db.session.commit()
    return jsonify({'message': 'User created successfully'}), 201

@app.route('/login', methods=['POST'])
def login():
    auth = request.json
    user = User.query.filter_by(username=auth['username']).first()
    
    if not user or not check_password_hash(user.password_hash, auth['password']):
        return jsonify({'message': 'Invalid credentials!'}), 401
    
    token = jwt.encode({
        'user_id': user.id,
        'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=24)
    }, app.config['SECRET_KEY'])
    
    return jsonify({'token': token})

# 对话路由
@app.route('/conversations', methods=['GET'])
@token_required
def get_conversations(current_user):
    conversations = Conversation.query.filter_by(user_id=current_user.id).order_by(Conversation.updated_at.desc()).all()
    output = []
    for conv in conversations:
        output.append({
            'id': conv.id,
            'title': conv.title,
            'updated_at': conv.updated_at
        })
    return jsonify({'conversations': output})

@app.route('/conversations', methods=['POST'])
@token_required
def create_conversation(current_user):
    data = request.json
    new_conversation = Conversation(
        user_id=current_user.id,
        title=data.get('title', 'New Conversation')
    )
    db.session.add(new_conversation)
    db.session.commit()
    return jsonify({'conversation_id': new_conversation.id}), 201

@app.route('/conversations/<int:conversation_id>/messages', methods=['GET'])
@token_required
def get_messages(current_user, conversation_id):
    conversation = Conversation.query.filter_by(id=conversation_id, user_id=current_user.id).first()
    if not conversation:
        return jsonify({'message': 'Conversation not found'}), 404
    
    messages = Message.query.filter_by(conversation_id=conversation_id).order_by(Message.timestamp.asc()).all()
    output = []
    for msg in messages:
        output.append({
            'role': msg.role,
            'content': msg.content,
            'timestamp': msg.timestamp
        })
    return jsonify({'messages': output})

@app.route('/conversations/<int:conversation_id>/chat', methods=['POST'])
@token_required
def chat(current_user, conversation_id):
    conversation = Conversation.query.filter_by(id=conversation_id, user_id=current_user.id).first()
    if not conversation:
        return jsonify({'message': 'Conversation not found'}), 404
    
    message = request.json.get('message', '')
    if not message:
        return jsonify({'message': 'Message cannot be empty'}), 400
    
    # 保存用户消息
    user_message = Message(
        conversation_id=conversation_id,
        role='user',
        content=message
    )
    db.session.add(user_message)
    
    # 获取历史消息
    history_messages = Message.query.filter_by(conversation_id=conversation_id).order_by(Message.timestamp.asc()).all()
    history = []
    for msg in history_messages:
        history.append({'role': msg.role, 'content': msg.content})
    
    # 获取AI回复
    try:
        response, new_history = model.chat(tokenizer, message, history=history)
        
        # 保存AI回复
        assistant_message = Message(
            conversation_id=conversation_id,
            role='assistant',
            content=response
        )
        db.session.add(assistant_message)
        db.session.commit()
        
        return jsonify({'reply': response})
    except Exception as e:
        db.session.rollback()
        return jsonify({'message': str(e)}), 500

if __name__ == '__main__':
    with app.app_context():
        db.create_all()
    app.run(host='0.0.0.0', port=5000)

5.4 前端界面增强

使用Vue.js创建更丰富的前端界面:

static/js/app.js:

const app = Vue.createApp({
    data() {
        return {
            currentView: 'login',
            username: '',
            password: '',
            email: '',
            token: localStorage.getItem('token') || '',
            user: null,
            conversations: [],
            currentConversation: null,
            messages: [],
            newMessage: '',
            isLoading: false,
            isRegistering: false
        }
    },
    mounted() {
        if (this.token) {
            this.verifyToken();
        }
    },
    methods: {
        async verifyToken() {
            try {
                const response = await fetch('/api/me', {
                    headers: { 'Authorization': `Bearer ${this.token}` }
                });
                if (response.ok) {
                    this.user = await response.json();
                    this.currentView = 'chat';
                    this.loadConversations();
                } else {
                    localStorage.removeItem('token');
                    this.token = '';
                }
            } catch (error) {
                console.error('Token verification failed:', error);
            }
        },
        async login() {
            try {
                const response = await fetch('/login', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        username: this.username,
                        password: this.password
                    })
                });
                
                const data = await response.json();
                if (response.ok) {
                    this.token = data.token;
                    localStorage.setItem('token', this.token);
                    await this.verifyToken();
                } else {
                    alert(data.message || 'Login failed');
                }
            } catch (error) {
                console.error('Login error:', error);
                alert('Login failed');
            }
        },
        async register() {
            try {
                const response = await fetch('/register', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        username: this.username,
                        email: this.email,
                        password: this.password
                    })
                });
                
                if (response.ok) {
                    alert('Registration successful! Please login.');
                    this.isRegistering = false;
                } else {
                    const data = await response.json();
                    alert(data.message || 'Registration failed');
                }
            } catch (error) {
                console.error('Registration error:', error);
                alert('Registration failed');
            }
        },
        logout() {
            localStorage.removeItem('token');
            this.token = '';
            this.user = null;
            this.currentView = 'login';
        },
        async loadConversations() {
            try {
                const response = await fetch('/conversations', {
                    headers: { 'Authorization': `Bearer ${this.token}` }
                });
                this.conversations = (await response.json()).conversations;
            } catch (error) {
                console.error('Failed to load conversations:', error);
            }
        },
        async selectConversation(conversation) {
            this.currentConversation = conversation;
            try {
                const response = await fetch(`/conversations/${conversation.id}/messages`, {
                    headers: { 'Authorization': `Bearer ${this.token}` }
                });
                this.messages = (await response.json()).messages;
                this.scrollToBottom();
            } catch (error) {
                console.error('Failed to load messages:', error);
            }
        },
        async createNewConversation() {
            try {
                const response = await fetch('/conversations', {
                    method: 'POST',
                    headers: { 
                        'Content-Type': 'application/json',
                        'Authorization': `Bearer ${this.token}`
                    },
                    body: JSON.stringify({ title: 'New Conversation' })
                });
                
                if (response.ok) {
                    await this.loadConversations();
                    const data = await response.json();
                    this.selectConversation({ id: data.conversation_id, title: 'New Conversation' });
                }
            } catch (error) {
                console.error('Failed to create conversation:', error);
            }
        },
        async sendMessage() {
            if (!this.newMessage.trim() || !this.currentConversation) return;
            
            const message = this.newMessage;
            this.newMessage = '';
            this.isLoading = true;
            
            // 添加用户消息到界面
            this.messages.push({
                role: 'user',
                content: message,
                timestamp: new Date().toISOString()
            });
            
            this.scrollToBottom();
            
            try {
                const response = await fetch(`/conversations/${this.currentConversation.id}/chat`, {
                    method: 'POST',
                    headers: { 
                        'Content-Type': 'application/json',
                        'Authorization': `Bearer ${this.token}`
                    },
                    body: JSON.stringify({ message })
                });
                
                if (response.ok) {
                    const data = await response.json();
                    this.messages.push({
                        role: 'assistant',
                        content: data.reply,
                        timestamp: new Date().toISOString()
                    });
                    await this.loadConversations();  // 刷新对话列表,更新最后活跃时间
                } else {
                    const error = await response.json();
                    this.messages.push({
                        role: 'assistant',
                        content: `Error: ${error.message}`,
                        timestamp: new Date().toISOString()
                    });
                }
            } catch (error) {
                console.error('Failed to send message:', error);
                this.messages.push({
                    role: 'assistant',
                    content: `Error: ${error.message}`,
                    timestamp: new Date().toISOString()
                });
            } finally {
                this.isLoading = false;
                this.scrollToBottom();
            }
        },
        scrollToBottom() {
            this.$nextTick(() => {
                const container = this.$refs.messagesContainer;
                if (container) {
                    container.scrollTop = container.scrollHeight;
                }
            });
        }
    }
});

app.mount('#app');

第六部分:部署与优化

6.1 生产环境部署

6.1.1 使用Gunicorn部署Flask应用
pip install gunicorn
gunicorn -w 4 -b 0.0.0.0:5000 app_full:app
6.1.2 使用Nginx作为反向代理

示例Nginx配置/etc/nginx/sites-available/chatbot:

server {
    listen 80;
    server_name yourdomain.com;
    
    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    
    location /static/ {
        alias /path/to/your/app/static/;
    }
}
6.1.3 使用Supervisor管理进程

/etc/supervisor/conf.d/chatbot.conf:

[program:chatbot]
command=/path/to/venv/bin/gunicorn -w 4 -b 127.0.0.1:5000 app_full:app
directory=/path/to/your/app
user=www-data
autostart=true
autorestart=true
stderr_logfile=/var/log/chatbot.err.log
stdout_logfile=/var/log/chatbot.out.log

6.2 性能监控与优化

6.2.1 添加性能监控
from prometheus_client import start_http_server, Counter, Histogram
import time

# 定义指标
REQUEST_COUNT = Counter('request_count', 'App Request Count', ['method', 'endpoint', 'http_status'])
REQUEST_LATENCY = Histogram('request_latency_seconds', 'Request latency', ['endpoint'])

# 添加监控中间件
@app.before_request
def before_request():
    request.start_time = time.time()

@app.after_request
def after_request(response):
    latency = time.time() - request.start_time
    REQUEST_LATENCY.labels(request.path).observe(latency)
    REQUEST_COUNT.labels(request.method, request.path, response.status_code).inc()
    return response

# 启动监控服务器
start_http_server(8000)
6.2.2 模型缓存优化

实现简单的模型响应缓存:

from functools import lru_cache
import hashlib

@lru_cache(maxsize=1000)
def get_cached_response(prompt, history_hash):
    # 实际调用模型的代码
    response, new_history = model.chat(tokenizer, prompt, history=history)
    return response, new_history

def hash_history(history):
    return hashlib.md5(str(history).encode()).hexdigest()

@app.route('/cached_chat', methods=['POST'])
def cached_chat():
    data = request.json
    message = data.get('message', '')
    history = data.get('history', [])
    history_hash = hash_history(history)
    
    response, new_history = get_cached_response(message, history_hash)
    return jsonify({'reply': response, 'history': new_history})

第七部分:进一步学习方向

7.1 模型微调

使用自己的数据微调模型以获得更好的领域表现:

from transformers import Trainer, TrainingArguments

training_args = TrainingArguments(
    output_dir='./results',
    num_train_epochs=3,
    per_device_train_batch_size=4,
    save_steps=10_000,
    save_total_limit=2,
    logging_dir='./logs',
)

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=train_dataset,
    eval_dataset=eval_dataset
)

trainer.train()

7.2 多模态扩展

集成图像识别能力:

from transformers import VisionEncoderDecoderModel, ViTFeatureExtractor, AutoTokenizer
import torch
from PIL import Image

model = VisionEncoderDecoderModel.from_pretrained("nlpconnect/vit-gpt2-image-captioning")
feature_extractor = ViTFeatureExtractor.from_pretrained("nlpconnect/vit-gpt2-image-captioning")
tokenizer = AutoTokenizer.from_pretrained("nlpconnect/vit-gpt2-image-captioning")

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)

def caption_image(image_path):
    image = Image.open(image_path)
    if image.mode != "RGB":
        image = image.convert(mode="RGB")
    
    pixel_values = feature_extractor(images=[image], return_tensors="pt").pixel_values
    pixel_values = pixel_values.to(device)
    
    output_ids = model.generate(pixel_values, max_length=16, num_beams=4)
    preds = tokenizer.batch_decode(output_ids, skip_special_tokens=True)
    return preds[0].strip()

7.3 语音交互集成

添加语音输入输出功能:

import speech_recognition as sr
from gtts import gTTS
import os

def speech_to_text():
    r = sr.Recognizer()
    with sr.Microphone() as source:
        print("请说话...")
        audio = r.listen(source)
        
    try:
        text = r.recognize_google(audio, language='zh-CN')
        return text
    except Exception as e:
        print("识别错误:", e)
        return None

def text_to_speech(text, lang='zh-cn'):
    tts = gTTS(text=text, lang=lang)
    tts.save("response.mp3")
    os.system("mpg123 response.mp3")  # Linux
    # os.system("afplay response.mp3")  # macOS
    # os.system("start response.mp3")   # Windows

结语

通过本文,我们从最基础的GPT-3 API调用开始,逐步构建了一个功能完善的AI聊天机器人,并最终实现了本地化部署。这个过程涵盖了:

  1. 基础API调用和简单交互
  2. 上下文记忆和对话管理
  3. Web界面开发
  4. 本地模型选择和部署
  5. 完整系统架构实现
  6. 生产环境部署和优化

希望这篇详尽的指南能够帮助你顺利搭建自己的AI聊天机器人,并为你的进一步探索打下坚实基础。记住,AI领域发展迅速,持续学习和实践是关键!
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北辰alk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值