
简介本资源是一套基于HTML前端与Python后端协同实现的智能问答系统完整源码面向Web开发初学者、AI入门实践者及高校课程设计学生解决自然语言交互式问答场景下的前后端集成问题。资源共79个文件含55个HTML页面构建用户友好界面13个Python源文件实现核心问答逻辑含RAG-Milvus向量检索、Sentence-BERT文本编码、Milvus向量库操作等模块另有pyc编译文件、.env环境配置、LICENSE授权文件及Windows批处理脚本压缩包大小30.69MB结构清晰体现典型AI Web应用分层架构。已有513人学习下载读者可直接部署运行获得包含知识库加载、文本分块、向量化检索、答案生成全流程的可调试工程尤其适合理解RAG架构落地细节与HTML-Python轻量级AI系统集成模式。1. 用 HTML 搭前端、Python 做后端跑通一个可调试的智能问答系统最小闭环你不需要部署大模型也不必调用云 API就能在本地启动一个真正能“听懂问题、返回答案”的问答系统——核心就两层用户在 HTML 页面输入问题Python 后端接收请求、匹配或生成答案、再以 JSON 返回给前端渲染。这不是 Demo而是工程师日常快速验证逻辑、交付原型、嵌入内部工具的真实路径。它适合刚学完 Flask 或 FastAPI 的 Python 开发者也适合前端想补全全栈链路的 HTML/CSS/JS 实践者更关键的是它避开了所有第三方服务依赖所有代码可控、可断点、可改规则、可加日志。标题里“基于 HTML 和 Python”不是并列罗列技术栈而是明确分工HTML 负责用户可见的交互界面与 DOM 渲染Python 负责语义理解、知识检索与响应生成——二者通过标准 HTTP 协议通信不耦合、不黑盒、不 magic。下面我们就从零开始把这套最小可行系统跑起来每一步都带可验证命令和参数说明。2. 用 Flask 构建轻量问答后端路由设计、请求解析与答案生成逻辑2.1 为什么选 Flask 而非 FastAPI 或 Django对“智能问答系统”这类以文本处理为主、无复杂用户权限或数据库事务需求的场景Flask 是最直接的选择它没有隐式依赖、启动快、调试友好、HTTP 层透明。FastAPI 虽然性能高且自带 OpenAPI 文档但其异步模型在纯 CPU 密集型文本匹配中并无优势反而增加 asyncio 调试成本Django 则过度厚重ORM 和 admin 后台在此类项目中完全冗余。Flask 的app.route直观映射 URL 到函数request.get_json()明确提取 POST 数据jsonify()确保响应格式统一——这正是“智能问答”原型阶段最需要的确定性。安装只需一条命令且不强制要求特定 Python 版本pip install flask2.3.3提示固定flask2.3.3是因该版本对 Python 3.8–3.12 兼容稳定且无已知 CORS 默认阻断问题避免使用 3.x 版本其 Werkzeug 依赖变更可能导致request.json在某些请求头下为None。2.2 定义/ask接口接收问题、执行匹配、返回结构化响应后端核心是一个单文件app.py包含三部分逻辑初始化 Flask 应用、加载问答知识库此处用字典模拟、定义/askPOST 路由。注意不使用任何 NLP 框架如 spaCy、transformers作为初始依赖先确保管道通再叠加能力。# app.py from flask import Flask, request, jsonify import json import os app Flask(__name__) # 模拟知识库键为问题关键词值为预设答案实际项目中可替换为 SQLite 查询或向量检索 QA_KNOWLEDGE { python 安装: 请访问 https://www.python.org/downloads/ 下载对应系统安装包运行时勾选 Add Python to PATH。, html 网页制作: 基础结构需包含 !doctype htmlhtml langzh-cnheadmeta charsetutf-8.../headbody.../body/html。, 源码: 指未经编译的原始程序代码通常以 .py、.html、.js 等后缀保存可直接阅读和修改。, python 教程: 推荐官方文档 https://docs.python.org/zh-cn/3/tutorial/或《Effective Python》第2版。, 三步点金: 该词未收录于当前知识库请提供更具体上下文或检查术语拼写。 } app.route(/ask, methods[POST]) def handle_ask(): try: # 1. 解析 JSON 请求体 data request.get_json() if not data or question not in data: return jsonify({error: 缺少 question 字段}), 400 question str(data[question]).strip() if not question: return jsonify({error: question 不能为空}), 400 # 2. 简单关键词匹配后续可替换为 TF-IDF 或 Sentence-BERT answer 抱歉暂未找到相关信息。 for keyword, ans in QA_KNOWLEDGE.items(): if keyword.lower() in question.lower(): answer ans break # 3. 返回标准 JSON 响应 return jsonify({ status: success, question: question, answer: answer, timestamp: int(__import__(time).time()) }) except Exception as e: return jsonify({error: f服务器内部错误: {str(e)}}), 500 if __name__ __main__: app.run(host127.0.0.1, port5000, debugTrue)关键参数说明host127.0.0.1绑定本地回环地址禁止外部网络访问保障开发阶段安全port5000显式指定端口避免与 Node.js 或其他服务冲突debugTrue启用热重载与详细错误页仅限开发环境上线前必须设为Falserequest.get_json()要求前端发送Content-Type: application/json否则返回None—— 这是常见 400 错误根源将在前端章节验证。2.3 启动服务并验证接口可用性运行命令后终端应输出* Running on http://127.0.0.1:5000。此时用curl手动测试接口是否响应正确curl -X POST http://127.0.0.1:5000/ask \ -H Content-Type: application/json \ -d {question: python 安装}预期返回{ answer: 请访问 https://www.python.org/downloads/ 下载对应系统安装包运行时勾选 Add Python to PATH。, question: python 安装, status: success, timestamp: 1717023456 }注意若返回{error: 缺少 question 字段}说明curl未带-H Content-Type: application/json若返回空页或 500则检查app.py是否有语法错误或QA_KNOWLEDGE字典末尾是否遗漏逗号。3. 用标准 HTML 构建交互界面DOCTYPE 声明、表单提交与动态渲染3.1 严格遵循 W3C 推荐的 HTML5 文档类型与元信息标题中反复出现的!doctype htmlhtml langzh-cnheadmeta charsetutf-8不是装饰而是确保浏览器正确解析中文、避免乱码、启用现代 DOM API 的基础。很多“问答页面无法显示中文答案”的问题根源就是缺失meta charsetutf-8或lang属性导致字体回退失败。以下是最小合法 HTML 结构已通过 W3C Validator 验证!doctype html html langzh-cn head meta charsetutf-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title本地智能问答系统/title style body { font-family: Microsoft YaHei, sans-serif; line-height: 1.6; max-width: 800px; margin: 0 auto; padding: 20px; } #question-input { width: 100%; padding: 12px; font-size: 16px; border: 1px solid #ddd; border-radius: 4px; } #submit-btn { background: #007bff; color: white; padding: 12px 24px; font-size: 16px; border: none; border-radius: 4px; cursor: pointer; } #answer-display { margin-top: 20px; padding: 15px; background: #f8f9fa; border-left: 4px solid #007bff; } .loading { color: #6c757d; font-style: italic; } /style /head body h1 本地智能问答系统/h1 p输入问题点击提交Python 后端将返回匹配答案。/p form idqa-form input typetext idquestion-input placeholder例如python 安装、html 网页制作、源码... required button typesubmit idsubmit-btn提问/button /form div idanswer-display/div script document.getElementById(qa-form).addEventListener(submit, async function(e) { e.preventDefault(); // 阻止默认表单提交 const input document.getElementById(question-input); const display document.getElementById(answer-display); const btn document.getElementById(submit-btn); const question input.value.trim(); if (!question) return; // 显示加载状态 display.innerHTML p classloading正在查询.../p; btn.disabled true; try { const response await fetch(http://127.0.0.1:5000/ask, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ question }) }); if (!response.ok) { throw new Error(HTTP ${response.status}: ${response.statusText}); } const data await response.json(); if (data.error) { display.innerHTML pstrong❌ 错误/strong${data.error}/p; } else { display.innerHTML pstrong❓ 您问/strong${escapeHtml(data.question)}/p pstrong✅ 回答/strong${escapeHtml(data.answer)}/p psmall响应时间: ${new Date(data.timestamp * 1000).toLocaleString()}/small/p ; } } catch (err) { display.innerHTML pstrong⚠️ 请求失败/strong${err.message}/p; } finally { btn.disabled false; } }); // 防 XSS对用户输入内容做 HTML 转义 function escapeHtml(unsafe) { return unsafe .replace(//g, amp;) .replace(//g, lt;) .replace(//g, gt;) .replace(//g, quot;) .replace(//g, #039;); } /script /body /html关键设计点说明langzh-cn告知屏幕阅读器和搜索引擎使用简体中文语音与索引meta nameviewport确保在手机上正常缩放避免“桌面版网页无法触控”问题fetch中显式设置headers: { Content-Type: application/json }与后端request.get_json()要求严格对应escapeHtml()函数防止用户输入scriptalert(1)/script导致 XSS这是 HTML 端必须的安全兜底btn.disabled true防重复提交提升用户体验。3.2 本地打开 HTML 文件的正确方式双击.html文件用浏览器打开时Chrome/Firefox 会因fetch跨域限制报错CORS policy: No Access-Control-Allow-Origin header is present。正确做法是用 Python 内置 HTTP 服务器托管 HTML使其与后端同源http://localhost:8000→http://127.0.0.1:5000仍属跨域但可通过 Flask 配置解决# 在存放 index.html 的目录下执行 python -m http.server 8000然后访问http://localhost:8000此时页面发起的fetch(http://127.0.0.1:5000/ask)属于跨域请求需在 Flask 中启用 CORS# 在 app.py 开头添加 from flask_cors import CORS CORS(app) # 允许所有来源仅用于开发安装依赖pip install flask-cors4.0.0提示flask-cors4.0.0是兼容 Flask 2.3.x 的稳定版本生产环境必须用CORS(app, origins[https://yourdomain.com])限定域名禁用通配符*。4. 知识库升级从字典匹配到 SQLite 存储与模糊查询4.1 为什么必须脱离硬编码字典当问答条目超过 20 条手动维护QA_KNOWLEDGE字典会迅速失控重复答案、关键词遗漏、更新不同步、无法按热度排序。真实项目中知识库需支持增删改查、版本管理、多条件检索。SQLite 是零配置、单文件、Python 内置的完美选择——无需安装数据库服务.db文件可随项目 Git 提交且支持全文检索FTS5。创建qa.db并初始化表结构# 创建数据库并进入 SQLite 命令行 sqlite3 qa.db-- 启用 FTS5 全文检索比 LIKE 更准、更快 CREATE VIRTUAL TABLE qa_fts USING fts5( question UNINDEXED, answer, tokenizeunicode61 ); -- 插入初始数据与原字典一致但支持中文分词 INSERT INTO qa_fts (question, answer) VALUES (python 安装, 请访问 https://www.python.org/downloads/ 下载对应系统安装包运行时勾选 Add Python to PATH。), (html 网页制作, 基础结构需包含 !doctype htmlhtml langzh-cnheadmeta charsetutf-8.../headbody.../body/html。), (源码, 指未经编译的原始程序代码通常以 .py、.html、.js 等后缀保存可直接阅读和修改。), (python 教程, 推荐官方文档 https://docs.python.org/zh-cn/3/tutorial/或《Effective Python》第2版。), (三步点金, 该词未收录于当前知识库请提供更具体上下文或检查术语拼写。);4.2 修改 Flask 后端用 FTS5 实现模糊匹配与高亮替换app.py中的知识库查询逻辑使用sqlite3模块执行 FTS5 查询。关键改进支持“python安装”“安装python”等变体匹配并返回匹配度分数# 在 app.py 开头添加 import sqlite3 # 替换 handle_ask 函数中的匹配逻辑 def search_qa(question): conn sqlite3.connect(qa.db) conn.row_factory sqlite3.Row # 支持字典式取值 cursor conn.cursor() # FTS5 查询自动处理中文分词rank 返回相关性分数 cursor.execute( SELECT question, answer, rank FROM qa_fts WHERE qa_fts MATCH ? ORDER BY rank LIMIT 1 , [f{question}]) # 双引号表示短语匹配 row cursor.fetchone() conn.close() if row: return row[answer], row[question] return 抱歉暂未找到相关信息。, None app.route(/ask, methods[POST]) def handle_ask(): # ...保留原有 request 解析和错误处理... # 替换原匹配逻辑 answer, matched_question search_qa(question) return jsonify({ status: success, question: question, matched_question: matched_question or question, answer: answer, timestamp: int(__import__(time).time()) })FTS5 参数说明tokenizeunicode61SQLite 内置中文分词器支持 Unicode 标准无需额外依赖MATCH ?FTS5 专用语法?占位符防止 SQL 注入rank内置相关性评分数值越小越匹配ORDER BY rank确保返回最优结果LIMIT 1只取最高分答案避免前端需处理多条。4.3 验证 SQLite 查询效果对比关键词匹配与 FTS5用curl测试原字典无法覆盖的查询# 原字典匹配失败因无空格 curl -X POST http://127.0.0.1:5000/ask \ -H Content-Type: application/json \ -d {question: python安装} # FTS5 成功匹配自动分词 # 返回{answer: 请访问 https://www.python.org/downloads/ ..., ...}注意若返回空结果检查qa.db文件是否在app.py同级目录SQLite 路径是相对路径os.getcwd()决定根目录。可在search_qa函数开头加print(DB path:, os.path.abspath(qa.db))调试。5. 前端增强添加历史记录、答案复制与加载动画5.1 用 localStorage 持久化存储最近 10 条问答用户关闭页面后历史记录消失是体验断点。localStorage提供 5MB 本地存储无需后端参与。在 HTMLscript中追加以下逻辑// 加载时读取历史记录 let history JSON.parse(localStorage.getItem(qaHistory)) || []; renderHistory(); // 提交成功后存入历史 function addToHistory(question, answer) { const entry { question, answer, timestamp: new Date().toISOString() }; history.unshift(entry); if (history.length 10) history.pop(); // 仅保留最新10条 localStorage.setItem(qaHistory, JSON.stringify(history)); renderHistory(); } // 渲染历史列表 function renderHistory() { const historyEl document.createElement(div); historyEl.id history-section; historyEl.innerHTML h2 最近问答/h2; if (history.length 0) { historyEl.innerHTML pem暂无历史记录/em/p; } else { historyEl.innerHTML ul stylepadding-left:20px;; history.forEach((item, i) { const timeStr new Date(item.timestamp).toLocaleTimeString([], {hour: 2-digit, minute:2-digit}); historyEl.innerHTML li stylemargin:8px 0; padding:8px; background:#f8f9fa; border-radius:4px; strong[${i1}] ${timeStr}/strongbr span stylecolor:#007bffQ:/span ${escapeHtml(item.question)}br span stylecolor:#28a745A:/span ${escapeHtml(item.answer.substring(0, 50))}${item.answer.length 50 ? ... : } /li ; }); historyEl.innerHTML /ul; } // 插入到页面底部 document.body.appendChild(historyEl); }将addToHistory(data.question, data.answer)插入到fetch成功回调的末尾。5.2 一键复制答案提升知识复用效率在答案区域添加复制按钮使用navigator.clipboard.writeText()// 在答案渲染后追加复制按钮 display.innerHTML button onclickcopyToClipboard(${escapeHtml(data.answer)}) stylemargin-top:10px; padding:6px 12px; background:#28a745; color:white; border:none; border-radius:4px; font-size:14px; 复制答案 /button ; // 全局复制函数 window.copyToClipboard async function(text) { try { await navigator.clipboard.writeText(text); alert(答案已复制到剪贴板); } catch (err) { alert(复制失败 err); } };提示navigator.clipboard需在 HTTPS 或 localhost 环境下工作若用python -m http.server启动地址为http://localhost:8000满足条件。5.3 加载动画优化用 CSS 实现骨架屏占位替换原p classloading正在查询.../p为更专业的骨架屏/* 在 style 中追加 */ .skeleton { background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); background-size: 200% 200%; animation: loading 1.5s infinite; height: 24px; border-radius: 4px; margin: 10px 0; } keyframes loading { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }// 替换 loading 显示逻辑 display.innerHTML div classskeleton/divdiv classskeleton stylewidth:80%/div;此动画模拟内容加载过程比文字提示更符合现代 Web 体验且无 JS 依赖。6. 生产就绪检查端口冲突、HTTPS 代理与静态资源分离6.1 避免端口占用检测 5000 和 8000 是否被占用开发中常遇OSError: [Errno 98] Address already in use。用 Python 脚本一键检测并杀掉进程# check_ports.py import subprocess import sys def kill_port(port): try: result subprocess.run([lsof, -ti:, str(port)], capture_outputTrue, textTrue, checkTrue) pids result.stdout.strip().split(\n) for pid in pids: if pid: subprocess.run([kill, -9, pid]) print(f已终止 PID {pid}占用端口 {port}) except subprocess.CalledProcessError: print(f端口 {port} 未被占用) if __name__ __main__: for port in [5000, 8000]: kill_port(port)Windows 用户将lsof替换为netstat -ano | findstr :{port}taskkill /PID。6.2 用 Nginx 反向代理实现 HTTPS 与路径统一生产环境必须 HTTPS且前端 HTML 与后端 API 应同域。用 Nginx 将https://qa.example.com/代理到本地服务# /etc/nginx/sites-available/qa.conf server { listen 443 ssl; server_name qa.example.com; ssl_certificate /path/to/fullchain.pem; ssl_certificate_key /path/to/privkey.pem; location / { root /var/www/qa; try_files $uri $uri/ /index.html; } location /api/ { 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; } }对应修改前端fetch地址为/api/askFlask 中CORS(app, origins[https://qa.example.com])。6.3 静态资源分离HTML/CSS/JS 与 Python 代码解耦最终项目结构应清晰分离关注点qa-system/ ├── backend/ │ ├── app.py │ ├── qa.db │ └── requirements.txt ├── frontend/ │ ├── index.html │ ├── style.css │ └── script.js └── README.mdrequirements.txt内容Flask2.3.3 Flask-Cors4.0.0部署时backend目录用gunicorn启动替代app.run()pip install gunicorn gunicorn -w 2 -b 127.0.0.1:5000 app:app-w 2启动 2 个 worker 进程应对并发-b绑定地址app:app指app.py中的app实例。本文还有配套的精品资源点击获取