import os import gradio as gr import logging from typing import List, Dict, Any # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) # 从环境变量中获取 API Key def get_api_key() -> str: api_key = os.environ.get("OPENAI_API_KEY") if not api_key: logger.error("环境变量中未找到 OPENAI_API_KEY") raise ValueError("环境变量中未找到 OPENAI_API_KEY,请先设置。") return api_key # 初始化 LLM def initialize_llm(api_key: str): try: from langchain_openai.chat_models import ChatOpenAI logger.info("初始化 LLM...") return ChatOpenAI( temperature=0, model="gpt-4o-mini", api_key=api_key ) except Exception as e: logger.error(f"初始化 LLM 失败: {str(e)}") raise # 定义 Prompt 模板 def initialize_prompt_template(): from langchain_core.prompts import PromptTemplate template_text = """请根据以下 context 回答问题,答案请使用 Markdown 格式输出。 如果 context 存在latex表达式,请正确书写。 如果 context 中包含图表链接,请在回答中原封不动地加入图表,并在每个包含"/images"的链接前添加前缀 "https://huggingface.co/spaces/zliang/palynogeology/resolve/main"。 Question: {question} Context: {context}""" return PromptTemplate( input_variables=["question", "context"], template=template_text ) # 将检索到的文档内容格式化为字符串 def format_docs(docs) -> str: return "\n\n".join(doc.page_content for doc in docs) # 加载本地 FAISS 向量库 def initialize_vectorstore(api_key: str): try: from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import FAISS logger.info("初始化向量存储...") embed = OpenAIEmbeddings( model="text-embedding-3-small", api_key=api_key ) if not os.path.exists("faiss_db"): logger.error("未找到 'faiss_db' 目录") raise FileNotFoundError("未找到 'faiss_db' 目录,请先构建向量库。") return FAISS.load_local("faiss_db", embed, allow_dangerous_deserialization=True) except Exception as e: logger.error(f"初始化向量存储失败: {str(e)}") raise # 构建检索器 def initialize_retriever(db): return db.as_retriever( search_type="mmr", search_kwargs={"score_threshold": 0.5, "k": 3} ) # 构造问答链 def initialize_qa_chain(llm, prompt, retriever): from langchain_core.runnables import RunnablePassthrough from langchain_core.output_parsers import StrOutputParser qa_chain = ( { "context": retriever | format_docs, "question": RunnablePassthrough(), } | prompt | llm | StrOutputParser() ) return qa_chain # 根据用户输入的问题调用问答链 def answer_question(qa_chain, question: str) -> str: try: logger.info(f"处理问题: {question}") answer = qa_chain.invoke(question) return answer except Exception as e: logger.error(f"调用问答链时出错: {str(e)}", exc_info=True) return "⚠️ 出错了,请稍后重试。如果问题持续存在,请联系管理员。" # 返回《孢粉地质学》书籍简介 def get_book_introduction() -> str: introduction = """ # 《孢粉地质学》  ## 简介 《孢粉地质学》是一本系统介绍孢粉与孢子在地质学中应用的重要专著。本书由国内顶尖专家编撰,融合了最新的研究成果和实践经验。 ## 内容亮点 - **孢粉与孢子的形成与保存** 阐述孢粉在沉积环境中的生成过程及保存机制。 - **鉴定与分类方法** 详细介绍如何通过形态学、化学特征对孢粉进行鉴定与分类。 - **地层对比与古环境重建** 探讨孢粉在地层划分、沉积环境重建、古气候研究等方面的应用。 - **案例分析与实践应用** 配合丰富的实例解析,为读者提供理论与实践相结合的指导。 ## 适读人群 本书适合地质学、古生物学及相关领域的研究人员和学生参考,不仅为学术研究提供了坚实的理论基础,同时也为野外勘查和实际应用提供了实用工具。 👉 欢迎在"书籍问答"标签页中提问,获取更多关于书中内容的详细解读! """ return introduction # 定义常见问题列表 def get_faq_list() -> List[Dict[str, str]]: return [ {"question": "孢粉是什么?", "category": "基础概念"}, {"question": "如何采集孢粉样本?", "category": "实验方法"}, {"question": "孢粉分析的主要步骤有哪些?", "category": "实验方法"}, {"question": "如何鉴定孢粉的年代?", "category": "年代学"}, {"question": "孢粉数据如何应用于地层对比?", "category": "地层学"}, {"question": "常见的孢粉类型有哪些?", "category": "分类学"}, {"question": "孢粉如何指示古气候变化?", "category": "古环境"}, {"question": "孢粉研究在石油勘探中的应用", "category": "应用领域"}, {"question": "孢粉与孢子有什么区别?", "category": "基础概念"}, {"question": "电子显微镜在孢粉研究中的应用", "category": "技术方法"} ] # 构建自定义 Gradio 界面 def create_custom_ui(qa_chain): # 主题配置 theme = gr.Theme( primary_hue="green", secondary_hue="emerald", neutral_hue="gray", font=[gr.themes.GoogleFont("Source Sans Pro"), "system-ui", "sans-serif"], ) # 自定义CSS custom_css = """ .container {max-width: 1000px; margin: auto;} .header-logo {text-align: center; padding: 20px 0; margin-bottom: 20px;} .header-title {font-size: 2.5rem; font-weight: 700; margin: 0.5rem 0;} .header-subtitle {font-size: 1.25rem; color: #555; margin-bottom: 1rem;} .footer {text-align: center; margin-top: 40px; padding: 20px 0; font-size: 0.9rem; color: #666;} .card {border-radius: 10px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); padding: 20px; margin-bottom: 20px; background-color: white;} .faq-item {cursor: pointer; padding: 10px; border-radius: 5px; margin: 5px 0; transition: all 0.2s;} .faq-item:hover {background-color: #f0f9f0;} .category-tag {display: inline-block; font-size: 0.8rem; padding: 2px 8px; border-radius: 12px; background-color: #e0f2e0; color: #2e7d32; margin-left: 10px;} .loading-indicator {display: flex; justify-content: center; align-items: center; height: 100px;} .sample-questions-header {font-weight: 600; margin: 15px 0 10px 0;} .search-box {margin-bottom: 15px;} /* 自定义滚动条 */ ::-webkit-scrollbar {width: 8px; height: 8px;} ::-webkit-scrollbar-track {background: #f1f1f1; border-radius: 10px;} ::-webkit-scrollbar-thumb {background: #c1e0c1; border-radius: 10px;} ::-webkit-scrollbar-thumb:hover {background: #8bc48b;} /* 响应式调整 */ @media (max-width: 768px) { .header-title {font-size: 2rem;} .header-subtitle {font-size: 1rem;} } """ with gr.Blocks(theme=theme, css=custom_css) as demo: # 注入 KaTeX 样式表以支持LaTeX公式渲染 gr.HTML( '' ) # 应用标题和介绍 with gr.Row(elem_classes="header-logo"): gr.HTML("""
探索孢粉地质学的奥秘,获取专业知识解答
© 2025 孢粉地质学数字平台 | 由 GPT-4o 提供支持
如有问题或建议,请联系我们