前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站
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密钥
- 访问OpenAI官网(https://openai.com)
- 注册账号并登录
- 进入API密钥管理页面(https://platform.openai.com/account/api-keys)
- 创建新的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 轻量级模型推荐
- GPT-2 (OpenAI): 虽然比GPT-3小,但仍有不错的表现
- LLaMA (Meta): 7B, 13B等不同规模版本
- Alpaca (Stanford): 基于LLaMA微调的指令跟随模型
- Vicuna:
- ChatGLM-6B (清华): 支持中英双语
4.2.2 硬件需求对比
模型 | 最小显存要求 | 推荐显存 | CPU运行 |
---|---|---|---|
GPT-2 Small | 2GB | 4GB | 可能 |
GPT-2 Medium | 4GB | 8GB | 困难 |
LLaMA-7B | 8GB | 16GB | 不可能 |
ChatGLM-6B | 6GB | 12GB | 不可能 |
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聊天机器人,并最终实现了本地化部署。这个过程涵盖了:
- 基础API调用和简单交互
- 上下文记忆和对话管理
- Web界面开发
- 本地模型选择和部署
- 完整系统架构实现
- 生产环境部署和优化
希望这篇详尽的指南能够帮助你顺利搭建自己的AI聊天机器人,并为你的进一步探索打下坚实基础。记住,AI领域发展迅速,持续学习和实践是关键!