shield-chatbot / src /streamlit_app.py
DeepLearning101's picture
Update src/streamlit_app.py
2d99bfc verified
Raw
History Blame
6.37 kB
import streamlit as st
import google.generativeai as genai
import requests
import os
# 0. 頁面配置與 CSS 注入(隱藏側邊欄捲軸)
st.set_page_config(page_title="AI 新知小助手", page_icon="📚", layout="wide")
st.markdown(
"""
<style>
/* 隱藏側邊欄捲軸 */
[data-testid="stSidebar"] section::-webkit-scrollbar {
display: none;
}
[data-testid="stSidebar"] section {
-ms-overflow-style: none;
scrollbar-width: none;
}
/* 調整範例按鈕樣式 */
.stButton button {
width: auto;
padding: 5px 15px;
border-radius: 20px;
}
</style>
""",
unsafe_allow_html=True
)
# 從環境變數中取得 API Key
api_key = os.environ.get("GEMINI_API_KEY")
if not api_key:
st.error("請確認已經在 Space 的 Settings 設定了 GEMINI_API_KEY")
st.stop()
genai.configure(api_key=api_key)
# 1. 基礎設定與連結
BASE_URL = "https://raw.githubusercontent.com/Deep-Learning-101/deep-learning-101.github.io/main/"
LOGO_URL = f"{BASE_URL}images/DeepLearning101-LOGO.png"
HOME_URL = "https://deep-learning-101.github.io"
KNOWLEDGE_MAP = {
"大型語言模型 (LLM)": {
"raw_url": f"{BASE_URL}Large-Language-Model.md",
"page_url": "https://deep-learning-101.github.io/Large-Language-Model",
"repo_url": "https://github.com/Deep-Learning-101/Natural-Language-Processing-Paper/blob/main/Large-Language-Model.md"
},
"自然語言處理 (NLP)": {
"raw_url": f"{BASE_URL}Natural-Language-Processing.md",
"page_url": "https://deep-learning-101.github.io/Natural-Language-Processing",
"repo_url": "https://github.com/Deep-Learning-101/Natural-Language-Processing-Paper"
},
"語音處理 (Speech)": {
"raw_url": f"{BASE_URL}Speech-Processing.md",
"page_url": "https://deep-learning-101.github.io/Speech-Processing",
"repo_url": "https://github.com/Deep-Learning-101/Speech-Processing-Paper"
},
"電腦視覺 (CV)": {
"raw_url": f"{BASE_URL}Computer-Vision.md",
"page_url": "https://deep-learning-101.github.io/Computer-Vision",
"repo_url": "https://github.com/Deep-Learning-101/Computer-Vision-Paper"
}
}
# 2. 批量抓取內容
def fetch_all_knowledge():
combined_knowledge = ""
with st.spinner("正在同步 GitHub 最新資訊..."):
for category, info in KNOWLEDGE_MAP.items():
try:
response = requests.get(info["raw_url"])
response.raise_for_status()
combined_knowledge += f"\n\n## 【領域:{category}】\n"
combined_knowledge += response.text
except Exception as e:
st.warning(f"無法同步 {category} 的資料:{e}")
return combined_knowledge
# 初始化 Session State
if "knowledge" not in st.session_state:
st.session_state.knowledge = fetch_all_knowledge()
if "messages" not in st.session_state:
st.session_state.messages = []
# 用於處理範例按鈕點擊的狀態
if "example_prompt" not in st.session_state:
st.session_state.example_prompt = None
# 3. 側邊欄設計
with st.sidebar:
st.markdown(f'<a href="{HOME_URL}" target="_blank"><img src="{LOGO_URL}" width="100%"></a>', unsafe_allow_html=True)
st.title("⚙️ 知識庫狀態")
for category, info in KNOWLEDGE_MAP.items():
with st.expander(category):
st.markdown(f"🔗 [瀏覽網頁]({info['page_url']})")
st.markdown(f"📂 [GitHub 原始碼]({info['repo_url']})")
st.markdown("---")
if st.button("🔄 手動更新知識庫"):
st.session_state.knowledge = fetch_all_knowledge()
st.success("資料已重新抓取!")
# 4. 主介面與範例問句
st.title("📚 AI 演算法與論文社群助手")
st.caption("知識庫涵蓋 LLM、NLP、Speech、CV。歡迎直接提問!")
# 顯示範例問句按鈕
example_cols = st.columns(3)
examples = [
"🤖 總結 LLM 的最新趨勢",
"🗣️ 語音處理有哪些新技術?",
"👁️ CV 領域目前的懶人包重點"
]
for col, ex in zip(example_cols, examples):
if col.button(ex):
st.session_state.example_prompt = ex
# 5. 模型回覆邏輯(含額度限制處理)
def get_gemini_response(user_input):
system_instruction = f"""
你是一位專業的 AI 技術分析專家。
以下是從 GitHub 同步的技術資訊:
---
{st.session_state.knowledge}
---
請嚴格基於上述提供的資訊來回答問題。
"""
try:
model = genai.GenerativeModel(
model_name="gemini-flash-lite-latest",
system_instruction=system_instruction
)
chat = model.start_chat(history=[])
response = chat.send_message(user_input)
return response.text
except Exception as e:
# 捕捉 API 額度滿了 (429) 或其他錯誤
error_msg = str(e)
if "429" in error_msg or "quota" in error_msg.lower():
return "⚠️ **系統提示:API 使用額度已達上限**\n\n由於目前使用人數較多,Google AI Studio 的免費額度已暫時耗盡。請稍等幾分鐘後再試,或聯絡管理員更新 API Key。"
else:
return f"❌ **發生預期外錯誤**\n\n訊息:{error_msg}"
# 6. 對話邏輯
# 判斷是否有範例按鈕被點擊,或是使用者自行輸入
prompt = st.chat_input("想瞭解哪方面的技術?")
if st.session_state.example_prompt:
prompt = st.session_state.example_prompt
st.session_state.example_prompt = None # 用完即清空
if prompt:
st.session_state.messages.append({"role": "user", "content": prompt})
# 顯示所有歷史訊息
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# 產生新回覆
with st.chat_message("assistant"):
response_text = get_gemini_response(prompt)
st.markdown(response_text)
st.session_state.messages.append({"role": "assistant", "content": response_text})
st.rerun() # 強制重新整理以保持對話流暢
else:
# 僅在沒有新輸入時顯示歷史訊息
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])