五脏六腑

此博客介绍了人体五脏六腑的相关知识。五脏包括心、肝、脾、肺、肾,六腑有小肠、胆、胃、大肠、膀胱、三焦。详细阐述了各脏器的功能,如心主宰生命活动,肝贮藏血液等。
五脏六腑,是人体各内脏的总称。心、肝、脾、肺、肾,叫五脏;小肠、胆、胃、大肠、膀胱、三焦,叫六腑。心是人体生命活动的主宰。肝有贮藏血液和调节器血量的功能。脾有营养物质的消化、吸收并运输全身的功能。肺“管呼吸,主气”,肾有“藏精”、“生髓”、“主骨”的功能。小肠主要功能是接受食物后分别清浊。胆分泌胆汁,有助于消化食物。胃受纳食物,再经脾将营养输出,以供养全身。大肠的功能是传导糟粕之物,通过肛门排出体外。膀胱主要是贮藏和排泄尿液。三焦不是一个独立的脏器主体,而是按脏腑部位和功能分为三个部位:心、肺为上焦,脾、胃为中焦,肝、肾、大小肠、膀胱为下焦。
import os import sys import requests from PyQt5.QtWidgets import ( QApplication, QMainWindow, QFileDialog, QLabel, ) from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer from PyQt5.QtGui import QTextCursor from docx import Document from 智能问答系统_ui import Ui_aiWindow # LangChain 模块 from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_ollama import OllamaEmbeddings from langchain_community.document_loaders import TextLoader, Docx2txtLoader from langchain_community.vectorstores import FAISS def is_valid_path(path): """检查路径是否为英文""" try: path.encode('ascii') return True except UnicodeEncodeError: return False def is_writable(path): """检查路径是否可写""" test_file = os.path.join(path, ".write_test") try: with open(test_file, 'w') as f: f.write("test") os.remove(test_file) return True except Exception as e: print(f"❌ 路径不可写:{e}") return False class aiWindow(QMainWindow): def __init__(self): super().__init__() self.ui = Ui_aiWindow() self.ui.setupUi(self) self.resize(700, 500) # 初始化路径 self.knowledge_dir = os.path.abspath("./knowledge_files") self.persist_directory = "D:/faiss_index" # 改为英文路径 # 主动创建 knowledge_files 文件夹 if not os.path.exists(self.knowledge_dir): try: os.makedirs(self.knowledge_dir) print(f"✅ 主动创建知识库目录: {self.knowledge_dir}") except Exception as e: print(f"❌ 创建知识库目录失败: {e}") else: print(f"📁 知识库目录已存在: {self.knowledge_dir}") # 主动创建 faiss_index 文件夹 if not os.path.exists(self.persist_directory): try: os.makedirs(self.persist_directory) print(f"✅ 主动创建 FAISS 目录: {self.persist_directory}") except Exception as e: print(f"❌ 创建 FAISS 目录失败: {e}") else: print(f"📁 FAISS 目录已存在: {self.persist_directory}") # 检查路径是否可写 if not is_writable(self.persist_directory): print("⚠️ 警告:该目录不可写,请检查权限") # 检查路径是否为英文 if not is_valid_path(self.persist_directory): print("⚠️ 警告:路径包含非英文字符,可能导致 FAISS 写入失败") # 初始化变量 self.knowledge_db = None self.build_thread = None self.qa_thread = None self._is_building = False # 页面切换按钮 self.ui.stackedWidget.setCurrentIndex(0) self.ui.pushButton.clicked.connect(lambda: self.ui.stackedWidget.setCurrentIndex(0)) self.ui.pushButton_2.clicked.connect(lambda: self.ui.stackedWidget.setCurrentIndex(1)) # 知识列表加载 self.load_existing_files() self.ui.listWidget.itemClicked.connect(self.preview_file) # 自动加载向量数据库 self.load_vector_db() # 构建数据库按钮 self.ui.pushButton_5.clicked.connect(self.start_build_vector_db) # 提交问题按钮 self.ui.pushButton_6.clicked.connect(self.ask_question) # 上传/删除文件 self.ui.pushButton_3.clicked.connect(self.add_file) self.ui.pushButton_4.clicked.connect(self.remove_file) # 新增:检查 FAISS 文件状态按钮 self.ui.pushButton_7.clicked.connect(self.check_faiss_files) self.show() def check_faiss_files(self): """检查 FAISS 文件是否存在""" index_path = os.path.join(self.persist_directory, "index.faiss") pkl_path = os.path.join(self.persist_directory, "index.pkl") index_exists = os.path.exists(index_path) pkl_exists = os.path.exists(pkl_path) msg = ( f"📄 FAISS 文件状态:\n" f"index.faiss: {'✅ 存在' if index_exists else '❌ 不存在'}\n" f"index.pkl: {'✅ 存在' if pkl_exists else '❌ 不存在'}" ) self.ui.textEdit.append(msg) def load_existing_files(self): self.ui.listWidget.clear() for file in os.listdir(self.knowledge_dir): if file.endswith((".txt", ".docx")): self.ui.listWidget.addItem(file) def add_file(self): file_paths, _ = QFileDialog.getOpenFileNames(self, "选择知识库文件", "", "文档文件 (*.txt *.docx)") for path in file_paths: filename = os.path.basename(path) dest = os.path.join(self.knowledge_dir, filename) if not os.path.exists(dest): with open(path, "rb") as src, open(dest, "wb") as dst: dst.write(src.read()) self.ui.listWidget.addItem(filename) def preview_file(self): item = self.ui.listWidget.currentItem() if not item: return filename = item.text() path = os.path.join(self.knowledge_dir, filename) try: if filename.endswith(".txt"): with open(path, "r", encoding="utf-8") as f: content = f.read() elif filename.endswith(".docx"): doc = Document(path) content = "\n".join([p.text for p in doc.paragraphs]) else: content = "不支持的格式" except Exception as e: content = f"读取失败: {e}" self.ui.textEdit_2.setPlainText(content) def remove_file(self): selected_item = self.ui.listWidget.currentItem() if not selected_item: return filename = selected_item.text() path = os.path.join(self.knowledge_dir, filename) try: if os.path.exists(path): os.remove(path) # 删除向量库中与该文件相关的条目 if os.path.exists(self.persist_directory): embeddings = OllamaEmbeddings(model="bge-m3:latest") db = FAISS.load_local( self.persist_directory, embeddings, allow_dangerous_deserialization=True ) to_delete = [] for doc_id, doc in db.docstore._dict.items(): if doc.metadata.get("source") == filename: to_delete.append(doc_id) if to_delete: db.delete(to_delete) db.save_local(self.persist_directory) show_toast(f"✅ 已从向量库中删除 {len(to_delete)} 条与 {filename} 相关的记录。") except Exception as e: show_toast(f"❌ 删除向量记录或文件时出错:{e}") self.ui.listWidget.takeItem(self.ui.listWidget.row(selected_item)) self.ui.textEdit_2.clear() def load_vector_db(self): """加载 FAISS 向量数据库""" try: print("🔍 开始加载向量数据库...") embeddings = OllamaEmbeddings(model="bge-m3:latest") print("✅ 已初始化 OllamaEmbeddings") if not os.path.exists(self.persist_directory): print("❌ 向量库目录不存在,无法加载") return index_path = os.path.join(self.persist_directory, "index.faiss") pkl_path = os.path.join(self.persist_directory, "index.pkl") print(f"📄 检查文件是否存在: {index_path} - {os.path.exists(index_path)}") print(f"📄 检查文件是否存在: {pkl_path} - {os.path.exists(pkl_path)}") if not (os.path.exists(index_path) and os.path.exists(pkl_path)): print("❌ index.faiss 或 index.pkl 缺失,跳过加载") return print("📥 正在加载向量数据库...") self.knowledge_db = FAISS.load_local( self.persist_directory, embeddings, allow_dangerous_deserialization=True ) print("✅ 向量数据库加载成功") except Exception as e: print("❌ 加载向量数据库失败:", e) def start_build_vector_db(self): print("🚀 开始构建向量数据库线程...") self.build_thread = BuildVectorDBThread(self.knowledge_dir, self.persist_directory) self.build_thread.status_signal.connect(self.update_status) self.build_thread.result_signal.connect(lambda _: self.load_vector_db()) # 构建完成后重新加载 self.build_thread.error_signal.connect(self.show_error) self.build_thread.finished.connect(self.build_thread.deleteLater) self.build_thread.start() def update_status(self, msg): cursor = self.ui.textEdit.textCursor() cursor.movePosition(QTextCursor.End) cursor.insertText(msg + "\n") self.ui.textEdit.setTextCursor(cursor) def show_error(self, error): self.ui.textEdit.append(error) def ask_question(self): question = self.ui.lineEdit.text() if not question or not self.knowledge_db: return self.ui.textEdit.append("⏳ 正在获取答案,请稍候...") self.ui.pushButton_6.setEnabled(False) # 禁用按钮 self.qa_thread = QAWorkerThread(question, self.knowledge_db) self.qa_thread.status_signal.connect(self.update_status) self.qa_thread.result_signal.connect(self.show_ask_result) self.qa_thread.error_signal.connect(self.show_error) # 在线程结束时重新启用按钮 self.qa_thread.finished.connect(lambda: self.ui.pushButton_6.setEnabled(True)) self.qa_thread.finished.connect(self.qa_thread.deleteLater) self.qa_thread.start() def show_ask_result(self, result): # 去除星号 cleaned_result = result.replace("**", "") self.ui.textEdit.setPlainText(cleaned_result) class BuildVectorDBThread(QThread): status_signal = pyqtSignal(str) result_signal = pyqtSignal(str) error_signal = pyqtSignal(str) def __init__(self, knowledge_dir, persist_directory, parent=None): super().__init__(parent) self.knowledge_dir = knowledge_dir self.persist_directory = persist_directory def run(self): print("📚 正在构建向量数据库...") # 确保存在 os.makedirs(self.persist_directory, exist_ok=True) print("📁 persist_directory 确保存在:", self.persist_directory) if not os.path.exists(self.knowledge_dir): self.error_signal.emit("❌ 知识库目录不存在") print("❌ 错误:知识库目录不存在") return documents = [] processed_files_path = os.path.join(self.knowledge_dir, ".processed_files.txt") print("📄 加载已处理文件记录:", processed_files_path) if os.path.exists(processed_files_path): with open(processed_files_path, "r", encoding="utf-8") as f: processed_files = set(f.read().splitlines()) print(f"✅ 已读取 {len(processed_files)} 个已处理文件名") else: processed_files = set() print("🆕 未找到已处理文件记录,将创建新的记录") new_processed_files = list(processed_files) for file in os.listdir(self.knowledge_dir): file_path = os.path.join(self.knowledge_dir, file) print(f"🔎 检查文件: {file}") if not (file.endswith(".txt") or file.endswith(".docx")): print(f"🚫 跳过非支持格式文件: {file}") continue if file not in processed_files: try: print(f"⚙️ 正在加载文件: {file}") if file.endswith(".txt"): loader = TextLoader(file_path, encoding="utf-8") else: loader = Docx2txtLoader(file_path) docs = loader.load() print(f"📄 从 {file} 中加载了 {len(docs)} 个文档") for doc in docs: doc.metadata["source"] = file documents.extend(docs) new_processed_files.append(file) self.status_signal.emit(f"✅ 文件 {file} 处理完成") except Exception as e: self.status_signal.emit(f"⚠️ 加载文件 {file} 出错:{e}") print(f"⚠️ 加载文件 {file} 出错:{e}") if not documents: self.status_signal.emit("✅ 没有新文件需要处理。") print("✅ 没有新文件需要处理,跳过构建") return print("✂️ 开始分割文档...") text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50) docs = text_splitter.split_documents(documents) print(f"🧩 分割完成,共生成 {len(docs)} 个文本块") self.status_signal.emit(f"🧩 分割完成,共生成 {len(docs)} 个文本块") print("🧠 初始化 OllamaEmbeddings(模型:bge-m3:latest )") try: embeddings = OllamaEmbeddings(model="bge-m3:latest") print("✅ embeddings 初始化成功") index_path = os.path.join(self.persist_directory, "index.faiss") pkl_path = os.path.join(self.persist_directory, "index.pkl") if os.path.exists(index_path) and os.path.exists(pkl_path): print("📥 正在加载现有向量数据库...") db = FAISS.load_local(self.persist_directory, embeddings, allow_dangerous_deserialization=True) else: print("🆕 正在创建新的向量数据库...") db = FAISS.from_documents(docs, embeddings) print("🔄 正在添加新文档到向量数据库...") db.add_documents(docs) print("💾 正在保存向量数据库...") db.save_local(self.persist_directory) print("✅ 向量数据库已保存,路径:", self.persist_directory) self.status_signal.emit("✅ 向量数据库保存成功") print("📝 正在更新已处理文件列表...") with open(processed_files_path, "w", encoding="utf-8") as f: f.write("\n".join(new_processed_files)) print("✅ 已更新已处理文件列表") self.result_signal.emit("🎉 向量数据库构建完成") except Exception as e: self.error_signal.emit(f"❌ 构建数据库时发生错误:{e}") print(f"❌ 构建数据库时发生错误:{e}") class QAWorkerThread(QThread): status_signal = pyqtSignal(str) result_signal = pyqtSignal(str) error_signal = pyqtSignal(str) def __init__(self, question, knowledge_db, parent=None): super().__init__(parent) self.question = question self.knowledge_db = knowledge_db def run(self): try: # 检查 Ollama 是否运行 response = requests.get("http://127.0.0.1:11434/api/version") if response.status_code != 200: raise Exception("❌ Ollama 服务未启动,请先启动服务。") # 检查模型是否存在 model_response = requests.get("http://127.0.0.1:11434/api/tags") models = model_response.json().get("models", []) if not models: raise Exception("❌ 未找到可用模型,请先拉取模型。") available_models = [m["name"] for m in models] if "EntropyYue/chatglm3:6b" not in available_models: raise Exception("❌ 模型 v 不存在,请先拉取该模型。") # 检索知识库 docs = self.knowledge_db.similarity_search(self.question, k=4) if not docs: raise Exception("❌ 未找到相关知识,请尝试其他问题。") context = "\n\n".join([ f"文档{i + 1}:\n{doc.page_content.replace('**', '').replace('*', '')}" for i, doc in enumerate(docs) ]) prompt = f"""你是一个中医智能问答助手,你的任务是根据提供的上下文内容详细回答问题。请仔细阅读上下文,结合中医理论和术语进行理解。你需要: 1. 分析是阴证还是阳证,提供详细解释 2. 分析五脏六腑是哪里出了问题,提供详细解释 3. 给出建议或注意事项 即使上下文中没有完全匹配的信息,也请尝试根据已有内容进行合理推测和解释。如果你无法合理推理出答案,请回答“知识库中未找到相关信息”。 请严格按照以下格式输出,不要使用Markdown格式,不要添加额外内容: 【分析阴阳】 ... 【分析五脏六腑】 ... 【建议或注意事项】 ... 上下文: {context} 问题: {self.question} 答案:""" # 发送请求 response = requests.post( "http://127.0.0.1:11434/api/chat", json={ "model": "EntropyYue/chatglm3:6b", "messages": [{"role": "user", "content": prompt}], "stream": False } ) answer = response.json().get("message", {}).get("content", "未获取到有效回答") answer = answer.replace("**", "") self.result_signal.emit(f"问题:{self.question}\n答案:{answer}\n{'-' * 30}") except Exception as e: self.error_signal.emit(f"❌ 处理过程中发生错误:{str(e)}") class ToastLabel(QLabel): def __init__(self, text, duration=2000): super().__init__(text) self.setStyleSheet(''' background-color: #333; color: #fff; padding: 10px; border-radius: 5px; ''') self.setAlignment(Qt.AlignCenter) self.setWordWrap(True) self.setFixedSize(250, 80) self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.ToolTip) self.setAttribute(Qt.WA_ShowWithoutActivating) QTimer.singleShot(0, self.center_on_screen) QTimer.singleShot(duration, lambda: (self.hide(), self.deleteLater())) def center_on_screen(self): screen = QApplication.primaryScreen() screen_rect = screen.availableGeometry() x = (screen_rect.width() - self.width()) // 2 y = (screen_rect.height() - self.height()) // 2 self.move(x, y) def show_toast(message, duration=2000): toast = ToastLabel(message, duration) toast.show() if __name__ == '__main__': from PyQt5 import QtCore from PyQt5 import QtGui QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling, True) QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True) QtGui.QGuiApplication.setAttribute(QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough) app = QApplication(sys.argv) # 设置 DPI 适配 screen = app.screens()[0] dpi = screen.logicalDotsPerInch() base_dpi = 96.0 scale_factor = dpi / base_dpi font_size = max(12, int(12 * scale_factor)) font = app.font() font.setPointSize(font_size) app.setFont(font) window = aiWindow() sys.exit(app.exec()) 能不能把k值和prompt的内容用textedit显示出来,能够随时进行修改
08-12
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值