新闻详情

新闻详情

首页 / 资讯中心 / 详情

基于Flask与SQLite构建轻量级自定义问答系统实战

发布时间:2026/9/3 8:29:29来源:尧图网络
基于Flask与SQLite构建轻量级自定义问答系统实战
之前在做一些创意项目时想给角色设定添加互动问答功能但市面上的标准问答系统要么太死板要么集成复杂。后来发现通过 Python 和一些简单的 Web 技术完全可以自己搭建一个轻量、有趣且高度自定义的“私设”问答系统。本文就将手把手带你实现一个名为“超中二私设德家ask”的问答系统第二版ask②从环境搭建、核心逻辑到前端展示完整覆盖开发全流程。无论你是想为个人项目增添趣味交互还是学习后端逻辑与前端的结合都能从本文获得可直接复用的代码和清晰的实现思路。1. 背景与核心概念什么是“私设ASK系统”在二次元同人创作、角色设定俗称“私设”以及一些轻量级互动社区中开发者或创作者常常希望为自己的“原创角色”或“世界观”提供一个粉丝或用户可以进行提问Ask并由系统或管理员进行回复的互动平台。这不同于传统的客服系统或论坛它更强调氛围的营造、角色的代入感以及问答内容的个性化展示。我们将其称为“私设ASK系统”。它的核心功能通常包括用户前端一个展示角色形象、接收用户输入问题的界面。问答管理后端用于存储问题、管理回复可以是预设答案也可以是手动回复的逻辑。展示逻辑将问答以对话气泡、时间线等富有表现力的形式展现出来。“超中二私设德家ask再袭ask②”这个标题可以理解为该系统的第二个迭代版本可能在UI交互、数据处理能力或管理功能上进行了增强。本文将以此为目标构建一个功能完整的简易版本。2. 环境准备与版本说明本项目是一个标准的 Web 应用采用前后端分离的简易架构。后端使用 Python 的 Flask 框架提供 API 服务前端使用纯 HTML/CSS/JavaScript 进行交互数据存储使用轻量级的 SQLite 数据库。开发环境与版本操作系统Windows 10/11, macOS, 或 Linux (如 Ubuntu 20.04) 均可。Python版本 3.8 或以上。这是后端运行的基础。包管理工具pip(通常随 Python 安装)。核心 Python 库Flask(Web 框架)Flask-CORS(处理前端跨域请求)Flask-SQLAlchemy(ORM操作数据库)前端无需额外安装使用现代浏览器Chrome 90, Firefox 88即可。IDE/编辑器VS Code, PyCharm, Sublime Text 等任选。数据库SQLite (Python 内置支持无需单独安装)。版本兼容性说明本文示例代码基于上述主流稳定版本编写。不同小版本间可能存在细微差异但核心逻辑通用。如果遇到依赖冲突请优先确保Flask和SQLAlchemy版本的兼容性。3. 项目结构与核心原理拆解在开始编码前我们先规划好项目结构并理解核心的数据流。项目目录结构ask_system_v2/ ├── app.py # Flask 后端主程序 ├── config.py # 配置文件可选 ├── models.py # 数据库模型定义 ├── extensions.py # 扩展初始化如数据库对象 ├── requirements.txt # Python 依赖列表 ├── static/ # 静态资源文件夹 │ ├── css/ │ │ └── style.css # 前端样式 │ ├── js/ │ │ └── main.js # 前端交互逻辑 │ └── images/ # 存放角色图片等 └── templates/ # 前端HTML模板本例使用单页也可放这里 └── index.html # 主页面核心数据流原理用户在前端(index.html) 输入问题点击提交。前端 JavaScript(main.js) 捕获事件通过fetchAPI 将问题数据以 JSON 格式发送到后端指定 API 端点 (例如/api/ask)。后端 Flask(app.py) 接收到请求路由/api/ask对应的视图函数开始工作。视图函数解析请求数据进行必要的验证如内容非空然后通过SQLAlchemy 模型(models.py) 将新的“问题”记录存入 SQLite 数据库。同时后端可以在这里实现“自动回复”逻辑例如关键词匹配或者默认将问题状态标记为“待回复”。存储成功后后端返回一个成功的响应包含新问题的ID等信息给前端。前端收到成功响应后可以立即将这个问题显示在页面的“提问列表”中状态为“等待回复”或者重新加载所有问答。另一个后端路由例如/api/qa负责在页面加载或前端请求时从数据库获取所有问答数据并以 JSON 格式返回给前端进行渲染。4. 完整实战案例从零搭建ASK系统接下来我们按照步骤逐一实现每个部分。4.1 初始化项目与安装依赖首先创建项目根目录并初始化 Python 虚拟环境推荐用于隔离依赖。# 在终端中执行 mkdir ask_system_v2 cd ask_system_v2 python -m venv venv # 创建虚拟环境 # 激活虚拟环境 # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate # 创建 requirements.txt 文件并写入依赖 echo Flask2.3.3 Flask-CORS4.0.0 Flask-SQLAlchemy3.0.5 requirements.txt # 安装依赖 pip install -r requirements.txt4.2 构建后端Flask应用与数据库模型步骤1创建数据库模型 (models.py)这里定义我们的数据表。一个最简单的QA模型包含问题、答案、状态、时间等字段。# models.py from datetime import datetime from extensions import db # 我们从 extensions.py 导入 db class QA(db.Model): 问答数据模型 id db.Column(db.Integer, primary_keyTrue) question db.Column(db.Text, nullableFalse) # 用户问题 answer db.Column(db.Text) # 回复内容初始可为空 is_answered db.Column(db.Boolean, defaultFalse) # 是否已回复 created_at db.Column(db.DateTime, defaultdatetime.utcnow) # 提问时间 answered_at db.Column(db.DateTime) # 回复时间 def to_dict(self): 将模型实例转换为字典便于JSON序列化 return { id: self.id, question: self.question, answer: self.answer, is_answered: self.is_answered, created_at: self.created_at.isoformat() if self.created_at else None, answered_at: self.answered_at.isoformat() if self.answered_at else None }步骤2创建扩展文件 (extensions.py)将数据库等扩展对象单独管理避免循环导入。# extensions.py from flask_sqlalchemy import SQLAlchemy from flask_cors import CORS # 初始化扩展但先不绑定app db SQLAlchemy() cors CORS()步骤3创建Flask主应用 (app.py)这是后端的核心负责路由、业务逻辑和启动。# app.py from flask import Flask, request, jsonify from extensions import db, cors from models import QA import os from datetime import datetime def create_app(): app Flask(__name__) # 配置 app.config[SQLALCHEMY_DATABASE_URI] sqlite:///ask.db # 数据库文件路径 app.config[SQLALCHEMY_TRACK_MODIFICATIONS] False app.config[SECRET_KEY] your-secret-key-here # 请在生产环境中更改 # 初始化扩展 db.init_app(app) cors.init_app(app) # 允许前端跨域请求 # 创建数据库表如果不存在 with app.app_context(): db.create_all() # API路由定义 app.route(/api/ask, methods[POST]) def post_question(): 接收用户提问 data request.get_json() question_text data.get(question, ).strip() if not question_text: return jsonify({error: 问题内容不能为空}), 400 new_qa QA(questionquestion_text) db.session.add(new_qa) db.session.commit() # 这里可以添加简单的自动回复逻辑示例 # 例如检测到特定关键词“你好”则自动回复 if 你好 in question_text or hello in question_text.lower(): new_qa.answer 你好我是德家的守护灵有什么可以帮你的吗 new_qa.is_answered True new_qa.answered_at datetime.utcnow() db.session.commit() return jsonify({success: True, qa: new_qa.to_dict()}), 201 app.route(/api/qa, methods[GET]) def get_all_qa(): 获取所有问答记录按时间倒序 all_qa QA.query.order_by(QA.created_at.desc()).all() return jsonify({qa_list: [qa.to_dict() for qa in all_qa]}) app.route(/api/qa/int:qa_id/answer, methods[POST]) def post_answer(qa_id): 管理员回复问题简易版实际需要身份验证 data request.get_json() answer_text data.get(answer, ).strip() if not answer_text: return jsonify({error: 回复内容不能为空}), 400 qa QA.query.get_or_404(qa_id) qa.answer answer_text qa.is_answered True qa.answered_at datetime.utcnow() db.session.commit() return jsonify({success: True, qa: qa.to_dict()}) # 可选一个简单的前端页面路由 app.route(/) def index(): return app.send_static_file(index.html) # 需要将index.html放在static文件夹 return app if __name__ __main__: app create_app() app.run(debugTrue, port5000) # 调试模式运行在5000端口4.3 构建前端交互界面与动态逻辑步骤1创建主页面 (static/index.html)这是一个单页面应用的核心HTML结构。!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title超中二私设德家ASK系统②/title link relstylesheet href{{ url_for(static, filenamecss/style.css) }} link relstylesheet hrefhttps://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css /head body div classcontainer header classheader h1i classfas fa-robot/i 德家私设ASK信箱 · 第二弹/h1 p classsubtitle向「德家」的角色们提问吧可能会收到意想不到的回复哦~/p /header main classmain-content section classask-section h2i classfas fa-question-circle/i 提出你的问题/h2 div classinput-group textarea idquestionInput placeholder输入你想问的话...例如德家的起源是什么 rows3/textarea button idsubmitBtn classbtn-primary i classfas fa-paper-plane/i 发送提问 /button /div p classhint提示试试包含“你好”可能会触发自动回复/p /section section classqa-section h2i classfas fa-comments/i 问答记录/h2 div classcontrols button idrefreshBtn classbtn-secondary i classfas fa-sync-alt/i 刷新列表 /button span classstats idstats加载中.../span /div div classqa-list idqaListContainer !-- 问答列表将通过JS动态插入 -- div classloading正在加载问答记录.../div /div /section !-- 管理员回复区域实际项目中应隐藏或加权限 -- section classadmin-section idadminSection styledisplay: none; border-top: 2px dashed #ccc; margin-top: 2rem; padding-top: 1rem; h3i classfas fa-user-shield/i 管理员回复/h3 div classinput-group input typenumber idadminQaId placeholder输入要回复的问答ID textarea idadminAnswerInput placeholder输入回复内容... rows2/textarea button idadminSubmitBtn classbtn-admin i classfas fa-reply/i 提交回复 /button /div /section /main footer classfooter pPowered by Flask SQLite | 超中二私设德家ASK系统 v2.0/p /footer /div script src{{ url_for(static, filenamejs/main.js) }}/script /body /html步骤2添加样式 (static/css/style.css)让界面看起来更“中二”和有趣。/* style.css */ * { margin: 0; padding: 0; box-sizing: border-box; font-family: Segoe UI, Microsoft YaHei, sans-serif; } body { background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); color: #e0e0ff; min-height: 100vh; padding: 20px; line-height: 1.6; } .container { max-width: 900px; margin: 0 auto; background-color: rgba(30, 30, 60, 0.85); border-radius: 20px; padding: 30px; box-shadow: 0 15px 35px rgba(0, 0, 0, 0.5); border: 1px solid #4a4a9a; } .header { text-align: center; margin-bottom: 40px; padding-bottom: 20px; border-bottom: 2px solid #6c63ff; } .header h1 { color: #6c63ff; font-size: 2.8rem; margin-bottom: 10px; text-shadow: 0 0 10px rgba(108, 99, 255, 0.5); } .header .subtitle { color: #a0a0ff; font-size: 1.1rem; font-style: italic; } .main-content section { margin-bottom: 40px; background: rgba(40, 40, 80, 0.6); padding: 25px; border-radius: 15px; border-left: 5px solid #6c63ff; } h2, h3 { color: #8a84ff; margin-bottom: 20px; display: flex; align-items: center; gap: 10px; } .input-group { display: flex; flex-direction: column; gap: 15px; margin-bottom: 15px; } textarea, input[typenumber] { width: 100%; padding: 15px; background-color: rgba(255, 255, 255, 0.1); border: 1px solid #5555aa; border-radius: 10px; color: #fff; font-size: 1rem; resize: vertical; transition: border 0.3s; } textarea:focus, input[typenumber]:focus { outline: none; border-color: #6c63ff; box-shadow: 0 0 0 2px rgba(108, 99, 255, 0.3); } button { padding: 12px 25px; border: none; border-radius: 10px; font-size: 1rem; font-weight: bold; cursor: pointer; transition: all 0.3s ease; display: inline-flex; align-items: center; justify-content: center; gap: 8px; } .btn-primary { background: linear-gradient(to right, #6c63ff, #8a84ff); color: white; } .btn-primary:hover { background: linear-gradient(to right, #5a52e0, #7872e0); transform: translateY(-2px); box-shadow: 0 5px 15px rgba(108, 99, 255, 0.4); } .btn-secondary { background-color: rgba(255, 255, 255, 0.1); color: #a0a0ff; border: 1px solid #5555aa; } .btn-secondary:hover { background-color: rgba(255, 255, 255, 0.2); } .btn-admin { background: linear-gradient(to right, #ff6b6b, #ff8e8e); color: white; } .hint { font-size: 0.9rem; color: #ffcc66; font-style: italic; margin-top: 10px; } .controls { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; } .stats { color: #88ff88; font-weight: bold; } .qa-list { display: flex; flex-direction: column; gap: 25px; } .qa-item { background: rgba(50, 50, 90, 0.7); border-radius: 15px; padding: 20px; border: 1px solid #444488; transition: transform 0.3s; } .qa-item:hover { transform: translateX(5px); border-color: #6c63ff; } .qa-item.pending { border-left: 5px solid #ffcc00; } .qa-item.answered { border-left: 5px solid #00cc88; } .q-content, .a-content { margin-bottom: 15px; } .q-header, .a-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; color: #aaaaff; font-size: 0.9rem; } .q-text { color: #ffffff; font-size: 1.1rem; line-height: 1.5; } .a-text { color: #ccffcc; background: rgba(0, 80, 80, 0.3); padding: 15px; border-radius: 10px; font-size: 1.05rem; border-left: 3px solid #00cc88; } .badge { display: inline-block; padding: 3px 10px; border-radius: 12px; font-size: 0.8rem; font-weight: bold; } .badge.pending { background-color: #ffcc0033; color: #ffcc00; } .badge.answered { background-color: #00cc8833; color: #00cc88; } .loading { text-align: center; padding: 40px; color: #aaaaff; font-size: 1.2rem; } .footer { text-align: center; margin-top: 40px; padding-top: 20px; border-top: 1px solid #444488; color: #8888cc; font-size: 0.9rem; }步骤3编写前端交互逻辑 (static/js/main.js)这是连接前端与后端API的桥梁。// main.js document.addEventListener(DOMContentLoaded, function() { const API_BASE_URL http://127.0.0.1:5000/api; // 后端API地址 const questionInput document.getElementById(questionInput); const submitBtn document.getElementById(submitBtn); const refreshBtn document.getElementById(refreshBtn); const qaListContainer document.getElementById(qaListContainer); const statsEl document.getElementById(stats); const adminSection document.getElementById(adminSection); const adminQaIdInput document.getElementById(adminQaId); const adminAnswerInput document.getElementById(adminAnswerInput); const adminSubmitBtn document.getElementById(adminSubmitBtn); // 显示管理员区域仅用于演示实际应通过登录验证控制 adminSection.style.display block; // 页面加载时获取问答列表 fetchQAList(); // 提交新问题 submitBtn.addEventListener(click, postQuestion); questionInput.addEventListener(keypress, function(e) { if (e.key Enter !e.shiftKey) { e.preventDefault(); postQuestion(); } }); // 刷新列表 refreshBtn.addEventListener(click, fetchQAList); // 管理员提交回复 adminSubmitBtn.addEventListener(click, postAdminAnswer); // 函数获取并渲染问答列表 async function fetchQAList() { try { qaListContainer.innerHTML div classloading正在加载问答记录.../div; const response await fetch(${API_BASE_URL}/qa); if (!response.ok) throw new Error(HTTP error! status: ${response.status}); const data await response.json(); renderQAList(data.qa_list); updateStats(data.qa_list); } catch (error) { console.error(获取问答列表失败:, error); qaListContainer.innerHTML div classerror加载失败: ${error.message}. 请检查后端服务是否运行。/div; } } // 函数渲染问答列表到页面 function renderQAList(qaList) { if (!qaList || qaList.length 0) { qaListContainer.innerHTML div classqa-item还没有任何提问快来成为第一个提问者吧/div; return; } let html ; qaList.forEach(qa { const isAnswered qa.is_answered; const answerContent qa.answer ? qa.answer : em等待回复中.../em; const answeredAt qa.answered_at ? new Date(qa.answered_at).toLocaleString(zh-CN) : --; const askedAt new Date(qa.created_at).toLocaleString(zh-CN); html div classqa-item ${isAnswered ? answered : pending} div classq-content div classq-header spani classfas fa-user-circle/i 提问者 #${qa.id}/span span classbadge ${isAnswered ? answered : pending} ${isAnswered ? i classfas fa-check-circle/i 已回复 : i classfas fa-clock/i 待回复} /span /div div classq-text${escapeHtml(qa.question)}/div div classq-timei classfar fa-clock/i ${askedAt}/div /div div classa-content div classa-header spani classfas fa-robot/i 德家回复/span span${answeredAt}/span /div div classa-text${isAnswered ? escapeHtml(answerContent) : answerContent}/div /div /div ; }); qaListContainer.innerHTML html; } // 函数更新统计信息 function updateStats(qaList) { const total qaList.length; const answered qaList.filter(qa qa.is_answered).length; const pending total - answered; statsEl.innerHTML i classfas fa-chart-bar/i 总计: ${total} | 已回复: ${answered} | 待回复: ${pending}; } // 函数提交新问题 async function postQuestion() { const questionText questionInput.value.trim(); if (!questionText) { alert(请输入问题内容); questionInput.focus(); return; } submitBtn.disabled true; submitBtn.innerHTML i classfas fa-spinner fa-spin/i 发送中...; try { const response await fetch(${API_BASE_URL}/ask, { method: POST, headers: { Content-Type: application/json, }, body: JSON.stringify({ question: questionText }) }); const result await response.json(); if (response.ok) { questionInput.value ; // 清空输入框 alert(提问成功 (result.qa.is_answered ? 触发了自动回复 : )); fetchQAList(); // 刷新列表 } else { alert(提交失败: (result.error || 未知错误)); } } catch (error) { console.error(提交问题失败:, error); alert(网络错误提交失败。请检查后端服务。); } finally { submitBtn.disabled false; submitBtn.innerHTML i classfas fa-paper-plane/i 发送提问; } } // 函数管理员提交回复 async function postAdminAnswer() { const qaId adminQaIdInput.value.trim(); const answerText adminAnswerInput.value.trim(); if (!qaId || isNaN(qaId)) { alert(请输入有效的问答ID); adminQaIdInput.focus(); return; } if (!answerText) { alert(请输入回复内容); adminAnswerInput.focus(); return; } adminSubmitBtn.disabled true; adminSubmitBtn.innerHTML i classfas fa-spinner fa-spin/i 提交中...; try { const response await fetch(${API_BASE_URL}/qa/${qaId}/answer, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ answer: answerText }) }); const result await response.json(); if (response.ok) { alert(回复成功); adminQaIdInput.value ; adminAnswerInput.value ; fetchQAList(); // 刷新列表 } else { alert(回复失败: (result.error || 未知错误)); } } catch (error) { console.error(管理员回复失败:, error); alert(操作失败请检查网络和后端。); } finally { adminSubmitBtn.disabled false; adminSubmitBtn.innerHTML i classfas fa-reply/i 提交回复; } } // 简单的HTML转义防止XSS攻击 function escapeHtml(text) { const div document.createElement(div); div.textContent text; return div.innerHTML; } });4.4 运行与验证步骤1启动后端服务在项目根目录下确保虚拟环境已激活运行 Flask 应用。# 在终端中确保当前目录为 ask_system_v2 python app.py如果一切正常终端会显示类似以下信息* Serving Flask app app * Debug mode: on WARNING: This is a development server. Do not use it in a production deployment. * Running on http://127.0.0.1:5000步骤2访问前端页面打开浏览器访问http://127.0.0.1:5000。你应该能看到我们设计好的“中二风”界面。步骤3功能测试提问在文本框中输入“你好你是谁”点击“发送提问”。页面会刷新并且你的问题会出现在列表中并且因为触发了关键词“你好”会立即显示一个自动回复。查看列表点击“刷新列表”按钮或等待自动刷新可以看到所有历史问答。管理员回复在下方管理员区域找到某个未回复问题的ID列表第一列数字填入ID和回复内容点击“提交回复”。刷新后该问题的状态和内容会更新。检查数据库在项目根目录会生成一个ask.db文件这就是 SQLite 数据库。你可以使用 DB Browser for SQLite 等工具打开查看qa表的数据。4.5 结果说明至此一个具备完整前后端的“私设ASK系统”第二版就搭建完成了。它实现了以下核心功能用户提问通过前端界面提交问题并存入数据库。自动回复后端内置了简单的关键词如“你好”匹配逻辑可实现自动回复。问答展示前端动态地从后端API获取数据并以美观的对话气泡形式展示区分已回复和待回复状态。管理回复提供了一个简易的管理接口可以手动为问题添加回复。数据持久化使用 SQLite 数据库所有问答记录在服务重启后不会丢失。5. 常见问题与排查思路在开发和部署过程中你可能会遇到以下问题问题现象可能原因解决思路访问http://127.0.0.1:5000报错Not Found或空白页1.app.py中未正确定义根路由/。2.index.html未放在static文件夹下或send_static_file路径错误。1. 检查app.py中是否有app.route(/)的路由。2. 确认index.html文件位于项目根目录/static/下。前端点击按钮无反应浏览器控制台报Failed to fetch或跨域错误1. 后端 Flask 服务未运行。2.main.js中的API_BASE_URL地址或端口错误。3. 未安装或未正确初始化Flask-CORS。1. 检查终端确保python app.py正在运行且无报错。2. 核对main.js第3行的API_BASE_URL是否与后端运行地址一致。3. 确认app.py中已正确初始化cors CORS(app)。提交问题或回复时后端返回400 Bad Request请求的 JSON 数据格式不正确或缺少必要字段。1. 打开浏览器开发者工具的“网络(Network)”选项卡查看发送的请求负载(Payload)。2. 确保发送的数据是{question: 内容}或{answer: 内容}格式的 JSON。数据库操作失败如OperationalError: no such table数据库表没有创建。1. 确保app.py中db.create_all()在应用上下文中被调用。2. 删除旧的ask.db文件重启 Flask 应用让其重新建表。前端样式或JS完全没加载HTML 中引用的静态文件路径错误。1. 检查index.html中link和script标签的href/src属性。2. 确保static/css/style.css和static/js/main.js文件存在。自动回复功能不生效1. 关键词匹配逻辑有误。2. 后端代码修改后未重启服务。1. 检查app.py中post_question函数里的自动回复逻辑if 你好 in question_text部分。2. 修改后端代码后需要重启Flask 服务。6. 最佳实践与工程建议这个简易版本为了演示清晰省略了很多生产环境必需的要素。如果你打算将其用于真实项目请务必考虑以下优化点身份验证与授权现状管理员回复接口对所有人开放极不安全。改进集成 Flask-Login、JWT 或 OAuth2为管理操作添加登录验证。至少应设置一个简单的 API 密钥验证。数据验证与清洗现状仅做了简单的非空检查。改进对用户输入进行严格的验证和清理防止 XSS 攻击和 SQL 注入虽然 SQLAlchemy 使用参数化查询但内容清洗仍需进行。可以使用bleach库清理 HTML。错误处理与日志现状错误处理较为简单。改进在后端实现全局错误处理器返回结构化的错误信息。使用 Python 的logging模块记录运行日志和错误日志便于排查问题。代码结构与可维护性现状所有路由逻辑都在app.py中。改进使用 Flask 的蓝本Blueprint将不同的功能模块如认证、问答API、管理后台拆分到不同的文件中。前端安全现状使用了简单的escapeHtml函数。改进对于富文本回复考虑使用安全的 Markdown 渲染器如marked并严格过滤而不是直接使用innerHTML。数据库优化现状使用 SQLite适合轻量级应用。改进如果数据量增大或需要并发可迁移至 PostgreSQL 或 MySQL。为created_at字段添加索引以提高查询排序效率。部署与配置现状使用 Flask 开发服务器和硬编码密钥。改进生产环境务必使用 Gunicorn、uWSGI 等 WSGI 服务器并通过环境变量或配置文件管理密钥、数据库连接等敏感信息。功能扩展方向富文本与图片允许提问和回复中包含图片、表情或简单的格式。分类与标签为问答打上标签如“剧情相关”、“角色设定”方便筛选。用户系统让提问者可以注册、登录查看自己的提问历史。邮件通知当问题被回复时发送邮件通知提问者。更智能的自动回复集成简单的 NLP 或规则引擎实现更丰富的自动互动。通过遵循这些最佳实践你可以将这个学习原型逐步打磨成一个健壮、可用的真实项目。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

微信聊天记录备份与导出完整指南:10分钟把聊天记录归档、整理、分析一遍 2026/9/3 9:11:38

微信聊天记录备份与导出完整指南:10分钟把聊天记录归档、整理、分析一遍

微信聊天记录备份与导出完整指南:10分钟把聊天记录归档、整理、分析一遍 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_…

阅读更多 →
Anki Cloze 验证机制完整指南:填空不生效时快速定位问题的自查清单 2026/9/3 9:11:38

Anki Cloze 验证机制完整指南:填空不生效时快速定位问题的自查清单

Anki Cloze 验证机制完整指南:填空不生效时快速定位问题的自查清单 【免费下载链接】anki Anki is a smart spaced repetition flashcard program 项目地址: https://gitcode.com/GitHub_Trending/an/anki Anki 的 Cloze(完形填空)是背…

阅读更多 →
Strapi媒体库完整指南:本地存储与S3/Cloudinary部署的存储Provider全解析 2026/9/3 9:11:38

Strapi媒体库完整指南:本地存储与S3/Cloudinary部署的存储Provider全解析

Strapi媒体库完整指南:本地存储与S3/Cloudinary部署的存储Provider全解析 【免费下载链接】strapi 🚀 Strapi is the leading open-source headless CMS. It’s 100% JavaScript/TypeScript, fully customizable, and developer-first. 项目地址: http…

阅读更多 →
【Vue知识点总结】Vue2与Vue3中的 v-model 2026/9/3 9:11:38

【Vue知识点总结】Vue2与Vue3中的 v-model

目录 一、v-model 的本质 二、v-model不能直接使用的情况 三、代码示例 一、v-model 的本质 v-model 是 Vue 框架中实现数据双向绑定的核心指令。 v-model 的实现机制 v-model 并非特殊语法,而是一个语法糖,其本质是 prop 绑定与 event 监听的组合。Vue 2 与 Vue 3 的主…

阅读更多 →
用Python拆解体育数据:17岁跑出11秒08意味着什么? 2026/9/3 9:11:38

用Python拆解体育数据:17岁跑出11秒08意味着什么?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Matlab实现Mie散射计算与谐振条件分析 2026/9/3 9:08:38

Matlab实现Mie散射计算与谐振条件分析

简介:本资源面向光学仿真与电磁散射研究领域的高校师生、科研人员及工程技术人员,提供基于Mie理论的球形粒子散射特性建模与谐振分析工具,重点解决原始球体及核壳结构涂层球体在不同波长、尺寸参数下的散射系数、吸收系数与消光系数精确计算问…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞