1 line
20 KiB
JSON
1 line
20 KiB
JSON
{"repo": "aichijuzidetuzi/rag", "files": {"glm-RAG5.py": {"310c387d58b9c871f8a6461623fe2e8949b10645": {"t": 1785746403.053889, "n": 18292, "h": "7cb9aaa8d2eb5dcb", "c": "import os\nimport time\nimport json\nimport threading\nimport numpy as np\nimport torch\nimport faiss\nfrom flask import Flask, request, jsonify, render_template_string\nfrom transformers import AutoTokenizer, AutoModel\nimport requests\n\napp = Flask(__name__)\n\n\n# ------------------------\n# 升级配置参数\n# ------------------------\nclass EnhancedConfig:\n def __init__(self):\n self.data_files = [r\"Q&A.txt\"]\n self.top_k = 5\n self.similarity_threshold = 0.4\n self.batch_size = 512\n # 嵌入模型路径\n self.embedding_model_path = r\"C:/Users/cy/.cache/modelscope/hub/models/dienstag/chinese-bert-wwm-ext\"\n # DeepSeek API密钥\n self.deepseek_api_key = \"sk-0ae5870ea20141149406634adc7fdba1\"\n # RAG参数\n self.max_context_length = 1500\n self.llm_temperature = 0.3\n self.llm_max_length = 1024\n self.rag_prompt_template = \"\"\"你是古籍问答助手,请基于以下参考内容回答用户的问题。\n\n参考内容:\n{context}\n\n用户问题:{question}\n请用中文详细回答。如果没有明确相关内容,请说\"无相关资料\"。\"\"\"\n\n\nconfig = EnhancedConfig()\n\n\n# ------------------------\n# 模型加载(去除了accelerate依赖)\n# ------------------------\nclass EnhancedModels:\n def __init__(self):\n # 嵌入模型\n self.embed_device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n self.embed_tokenizer = AutoTokenizer.from_pretrained(config.embedding_model_path)\n self.embed_model = AutoModel.from_pretrained(config.embedding_model_path).to(self.embed_device)\n print(f\"✅ 嵌入模型加载成功 | 设备: {self.embed_device}\")\n\n\nmodels = EnhancedModels()\n\n\n# ------------------------\n# 增强版检索系统(保持不变)\n# ------------------------\nclass EnhancedRetriever:\n def __init__(self):\n self.index = None\n self.data = []\n self._build_index()\n\n def _build_index(self):\n all_items = []\n for file_path in config.data_files:\n items = self.parse_qa_file(file_path)\n all_items.extend(items)\n\n if all_items:\n texts = [item[\"content\"] for item in all_items]\n embeddings = self._batch_encode(texts)\n\n self.index = faiss.IndexFlatIP(embeddings.shape[1])\n faiss.normalize_L2(embeddings)\n self.index.add(embeddings)\n self.data = all_items\n print(f\"🛠 索引构建完成 | 数据量: {len(all_items)}\")\n else:\n raise ValueError(\"无有效数据构建索引\")\n\n def parse_qa_file(self, file_path):\n items = []\n question, answer = None, None\n with open(file_path, \"r\", encoding=\"utf-8\") as f:\n for line in f:\n line = line.strip()\n if line.startswith(\"问题:\"):\n question = line.replace(\"问题:\", \"\").strip()\n elif line.startswith(\"答案:\"):\n answer = line.replace(\"答案:\", \"\").strip()\n if question and answer:\n items.append({\n \"content\": f\"问题:{question}\\n答案:{answer}\",\n \"metadata\": {\"source\": file_path},\n \"question\": question,\n \"answer\": answer\n })\n question, answer = None, None\n return items\n\n def _batch_encode(self, texts):\n inputs = models.embed_tokenizer(\n texts,\n padding=True,\n truncation=True,\n max_length=512,\n return_tensors=\"pt\"\n ).to(models.embed_device)\n\n with torch.no_grad():\n outputs = models.embed_model(**inputs)\n return outputs.last_hidden_state[:, 0, :].cpu().numpy()\n\n def search(self, query, top_k=5):\n query_vec = self._batch_encode([query])\n faiss.normalize_L2(query_vec)\n\n distances, indices = self.index.search(query_vec, top_k * 2)\n results = []\n for score, idx in zip(distances[0], indices[0]):\n if idx >= 0 and score >= 0.5:\n results.append({\n \"content\": self.data[idx][\"content\"],\n \"question\": self.data[idx][\"question\"],\n \"answer\": self.data[idx][\"answer\"],\n \"score\": float(score),\n \"index\": int(idx)\n })\n\n return sorted(results, key=lambda x: x[\"score\"], reverse=True)[:top_k]\n\n\nretriever = EnhancedRetriever()\n\n\n# ------------------------\n# 生成系统(直接调用API生成)\n# ------------------------\nclass EnhancedGenerator:\n def generate(self, question):\n # 直接调用DeepSeek API,不提供上下文\n url = \"https://api.deepseek.com/chat/completions\"\n headers = {\n \"Content-Type\": \"application/json\",\n \"Authorization\": f\"Bearer {config.deepseek_api_key}\"\n }\n data = {\n \"model\": \"deepseek-chat\",\n \"messages\": [{\"role\": \"user\", \"content\": question}],\n \"temperature\": config.llm_temperature,\n \"max_tokens\": config.llm_max_length\n }\n\n try:\n response = requests.post(url, headers=headers, json=data)\n response.raise_for_status()\n result = response.json()\n answer = result[\"choices\"][0][\"message\"][\"content\"].strip()\n return answer\n except requests.RequestException as e:\n print(f\"API请求失败: {e}\")\n return \"系统调用API时发生错误\"\n\n\ngenerator = EnhancedGenerator()\n\n\n# ------------------------\n# 后端接口\n# ------------------------\n@app.route(\"/ask\", methods=[\"POST\"])\ndef enhanced_ask():\n start_time = time.time()\n data = request.get_json()\n question = data.get(\"question\", \"\").strip()\n\n if not question:\n return jsonify({\"answer\": \"问题内容为空\", \"sources\": []})\n\n try:\n # 获取搜索结果用于显示参考来源\n search_results = retriever.search(question, config.top_k)\n\n # 直接调用大模型生成答案\n answer = generator.generate(question)\n\n # 格式化参考来源\n sources = [{\n \"record_index\": res[\"index\"],\n \"content\": f\"问题:{res['question']}\\n答案:{res['answer']}\",\n \"metadata\": f\"相似度: {res['score']:.2f} | 来源文件: {os.path.basename(config.data_files[0])}\"\n } for res in search_results]\n\n return jsonify({\n \"answer\": answer,\n \"sources\": sources,\n \"time_cost\": round(time.time() - start_time, 2)\n })\n\n except Exception as e:\n print(f\"处理失败: {str(e)}\")\n return jsonify({\"answer\": \"系统处理时发生错误\", \"sources\": []})\n\n\n@app.route(\"/get_status\")\ndef get_status():\n return jsonify({\n \"doc_count\": len(retriever.data),\n \"qa_count\": len(retriever.data),\n \"sources\": list(set([os.path.basename(f) for f in config.data_files])),\n \"gpu_mem\": f\"{torch.cuda.memory_allocated() / 1e9:.1f}\" if torch.cuda.is_available() else \"N/A\",\n \"speed\": f\"{torch.cuda.memory_allocated() / 1e6:.1f}MB\" # 示例值\n })\n\n\n# ------------------------\n# 整合后的前端界面\n# ------------------------\n@app.route(\"/\")\ndef index():\n return render_template_string(r'''\n <!DOCTYPE html>\n <html lang=\"zh-CN\">\n <head>\n <meta charset=\"UTF-8\">\n <title>古籍智能问答系统</title>\n <link href=\"https://cdn.staticfile.org/font-awesome/6.4.0/css/all.min.css\" rel=\"stylesheet\">\n <style>\n :root {\n --primary-color: #2c3e50;\n --secondary-color: #3498db;\n --background-color: #f8f9fa;\n }\n * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n body {\n font-family: \"Microsoft YaHei\", sans-serif;\n background: var(--background-color);\n display: flex;\n min-height: 100vh;\n }\n .sidebar {\n width: 280px;\n background: white;\n padding: 20px;\n box-shadow: 2px 0 5px rgba(0,0,0,0.1);\n }\n .main-content {\n flex: 1;\n padding: 30px;\n max-width: 1200px;\n }\n .card {\n background: white;\n border-radius: 10px;\n padding: 20px;\n margin-bottom: 20px;\n box-shadow: 0 2px 5px rgba(0,0,0,0.1);\n }\n .status-bar {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 15px;\n margin-bottom: 25px;\n }\n .status-item {\n padding: 15px;\n border-radius: 8px;\n background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);\n }\n textarea {\n width: 100%;\n padding: 15px;\n border: 2px solid #e0e0e0;\n border-radius: 8px;\n resize: vertical;\n font-size: 16px;\n }\n button {\n background: var(--secondary-color);\n color: white;\n border: none;\n padding: 12px 25px;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.3s;\n }\n button:hover {\n opacity: 0.9;\n transform: translateY(-1px);\n }\n .example-questions {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 15px;\n margin: 20px 0;\n }\n .answer-container {\n background: white;\n padding: 20px;\n border-radius: 10px;\n margin-top: 25px;\n white-space: pre-wrap;\n }\n /* 新增的模型切换动画样式 */\n .model-switch-overlay {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0,0,0,0.5);\n display: flex;\n justify-content: center;\n align-items: center;\n z-index: 1000;\n display: none;\n }\n .model-switch-content {\n background: white;\n padding: 30px;\n border-radius: 10px;\n text-align: center;\n box-shadow: 0 5px 15px rgba(0,0,0,0.3);\n }\n .model-switch-spinner {\n font-size: 40px;\n margin-bottom: 20px;\n color: var(--secondary-color);\n }\n .model-switch-text {\n font-size: 18px;\n font-weight: bold;\n }\n </style>\n </head>\n <body>\n <!-- 模型切换遮罩层 -->\n <div class=\"model-switch-overlay\" id=\"modelSwitchOverlay\">\n <div class=\"model-switch-content\">\n <div class=\"model-switch-spinner\">\n <i class=\"fas fa-spinner fa-spin\"></i>\n </div>\n <div class=\"model-switch-text\">\n 模型切换中,请稍候...\n </div>\n </div>\n </div>\n\n <!-- 侧边栏 -->\n <div class=\"sidebar\">\n <div class=\"card\">\n <h2><i class=\"fas fa-cog\"></i> 系统配置</h2>\n <div style=\"margin-top:15px\">\n <label>选择大模型:</label>\n <select id=\"modelSelect\" style=\"width:100%;padding:8px;margin-top:5px\" onchange=\"handleModelChange()\">\n <option value=\"DeepSeek\">DeepSeek-1.3B</option>\n <option value=\"ChatGLM3-6B\">ChatGLM2-6b</option>\n <option value=\"Qwen-1.8B\">Qwen-1.8B</option>\n </select>\n </div>\n </div>\n\n <div class=\"card\" style=\"margin-top:20px\">\n <h3><i class=\"fas fa-database\"></i> 知识库状态</h3>\n <div id=\"knowledgeStatus\" style=\"margin-top:10px;color:#666\">\n 加载中...\n </div>\n </div>\n\n <div class=\"card\" style=\"margin-top:20px\">\n <h3><i class=\"fas fa-chart-bar\"></i> 性能监控</h3>\n <div id=\"gpuStatus\" style=\"margin-top:10px;color:#666\">\n GPU内存: 加载中...\n </div>\n </div>\n </div>\n\n <!-- 主内容 -->\n <div class=\"main-content\">\n <h1 style=\"color:var(--primary-color);margin-bottom:25px\">\n <i class=\"fas fa-scroll\"></i> 古籍文献智能问答系统\n </h1>\n\n <!-- 状态栏 -->\n <div class=\"status-bar\">\n <div class=\"status-item\">\n <div>当前模型</div>\n <div id=\"currentModel\" style=\"font-weight:bold;color:var(--secondary-color)\">-</div>\n </div>\n <div class=\"status-item\">\n <div>检索模式</div>\n <div style=\"font-weight:bold;color:#27ae60\">混合检索</div>\n </div>\n <div class=\"status-item\">\n <div>Embedding模型</div>\n <div style=\"font-weight:bold;color:#e67e22\">chinese-bert-wwm-ext</div>\n </div>\n </div>\n\n <!-- 问题输入 -->\n <textarea id=\"questionInput\" rows=\"4\" placeholder=\"请输入您的问题,例如:《道德经》中'道可道非常道'如何解释?\"></textarea>\n\n <!-- 示例问题 -->\n <div class=\"example-questions\">\n <button onclick=\"setExample('孔子认为学习的快乐和君子的修养体现在哪些方面?')\">示例问题1</button>\n <button onclick=\"setExample('有子如何论述孝悌与“仁”的关系?')\">示例问题2</button>\n <button onclick=\"setExample('王阳明\"格物致知\"说的核心要义是什么?')\">示例问题3</button>\n </div>\n\n <button onclick=\"submitQuestion()\" style=\"width:100%;padding:15px;font-size:16px\">\n <i class=\"fas fa-paper-plane\"></i> 提交问题\n </button>\n\n <!-- 回答区域 -->\n <div id=\"answerSection\" style=\"display:none;margin-top:30px\">\n <div class=\"card\">\n <h3><i class=\"fas fa-comment-dots\"></i> 智能回答</h3>\n <div id=\"answerContent\" class=\"answer-container\" style=\"margin-top:15px\"></div>\n </div>\n\n <div class=\"card\" style=\"margin-top:20px\">\n <h3><i class=\"fas fa-book-open\"></i> 参考来源</h3>\n <div id=\"sourceDocuments\" style=\"margin-top:15px\"></div>\n </div>\n </div>\n </div>\n\n <script>\n // 初始化状态\n function updateStatus() {\n // 更新模型选择\n const modelSelect = document.getElementById('modelSelect');\n document.getElementById('currentModel').textContent = modelSelect.value;\n\n // 获取系统状态(示例数据)\n fetch('/get_status').then(r => r.json()).then(data => {\n document.getElementById('knowledgeStatus').innerHTML = `\n 问答条数:${data.qa_count} 条<br>\n 数据来源:${data.sources.join(', ')}\n `;\n document.getElementById('gpuStatus').textContent = \n `GPU内存:${data.gpu_mem} | 处理速度:${data.speed}`;\n });\n }\n\n // 示例问题填充\n function setExample(question) {\n document.getElementById('questionInput').value = question;\n }\n\n // 处理模型切换\n function handleModelChange() {\n const overlay = document.getElementById('modelSwitchOverlay');\n overlay.style.display = 'flex';\n\n // 模拟模型切换过程(实际应用中这里可以发送请求到后端切换模型)\n setTimeout(() => {\n overlay.style.display = 'none';\n updateStatus(); // 更新状态显示新模型\n }, 2000); // 2秒后关闭遮罩\n }\n\n // 提交问题\n function submitQuestion() {\n const question = document.getElementById('questionInput').value.trim();\n if (!question) return alert('请输入问题内容');\n\n const answerSection = document.getElementById('answerSection');\n const answerContent = document.getElementById('answerContent');\n const sources = document.getElementById('sourceDocuments');\n\n answerSection.style.display = 'block';\n answerContent.innerHTML = '<i class=\"fas fa-spinner fa-spin\"></i> 正在生成回答...';\n sources.innerHTML = '';\n\n fetch('/ask', {\n method: 'POST',\n headers: {'Content-Type': 'application/json'},\n body: JSON.stringify({ question: question })\n }).then(r => r.json()).then(data => {\n answerContent.innerHTML = data.answer;\n if(data.sources && data.sources.length > 0) {\n sources.innerHTML = data.sources.map((s, i) => `\n <div class=\"source-card\" style=\"margin:10px 0;padding:10px;border-left:3px solid #3498db\">\n <b>来源 ${i+1}</b><br>\n ${s.content}<br>\n <small style=\"color:#666\">${s.metadata}</small>\n </div>\n `).join('');\n } else {\n sources.innerHTML = '<div style=\"color:#666\">未找到相关参考内容</div>';\n }\n }).catch(e => {\n answerContent.innerHTML = '请求失败,请重试';\n console.error(e);\n });\n }\n\n // 初始状态更新\n updateStatus();\n setInterval(updateStatus, 5000); // 每5秒更新状态\n </script>\n </body>\n </html>\n ''')\n\n\n# ------------------------\n# 定时刷新线程(保持知识库更新)\n# ------------------------\ndef periodic_refresh(interval=300):\n while True:\n time.sleep(interval)\n try:\n retriever._build_index()\n print(\"✅ 知识库已刷新\")\n except Exception as e:\n print(f\"刷新失败: {e}\")\n\n\nrefresh_thread = threading.Thread(target=periodic_refresh, daemon=True)\nrefresh_thread.start()\n\nif __name__ == \"__main__\":\n # 验证测试\n sample_input = \"测试模型是否加载成功\"\n test_vector = retriever._batch_encode([sample_input])\n print(f\"嵌入测试输出shape: {test_vector.shape}\")\n\n test_output = generator.generate(\"测试生成\")\n print(f\"生成测试结果: {test_output}\")\n\n app.run(host=\"0.0.0.0\", port=5000, debug=True)"}}}} |