kidaid-chatbot / src /streamlit_app.py
DeepLearning101's picture
Update src/streamlit_app.py
424a5d0 verified
import streamlit as st
import google.generativeai as genai
import os
# 0. 頁面配置與 CSS 注入(隱藏側邊欄捲軸)
st.set_page_config(page_title="育兒成 - 兒童發展線上小助手", page_icon="🧸", layout="wide")
st.markdown(
"""
<style>
/* 1. 隱藏側邊欄捲軸 */
[data-testid="stSidebar"] section::-webkit-scrollbar { display: none; }
[data-testid="stSidebar"] section { -ms-overflow-style: none; scrollbar-width: none; }
/* 2. 全域字體縮小,適應手機螢幕 */
html, body, [class*="st-"] {
font-size: 14px !important;
}
/* 3. 調整範例按鈕樣式:變矮、字體變小、支援多行自動折行 */
.stButton button {
width: 100%;
padding: 6px 10px; /* 縮小上下內距 */
border-radius: 12px;
border: 1px solid #4CAF50; /* 溫暖的綠色系 */
color: #4CAF50;
background-color: transparent;
font-size: 13px !important;
line-height: 1.3;
min-height: auto;
white-space: normal;
height: auto;
}
/* 4. 極度壓縮主畫面空白 */
.block-container {
padding-top: 1.2rem !important;
padding-bottom: 5rem !important;
padding-left: 0.8rem !important;
padding-right: 0.8rem !important;
}
/* 5. 隱藏 Header 背景與右側選單,但保留左側展開按鈕 */
header[data-testid="stHeader"] {
background-color: rgba(0,0,0,0) !important;
}
header[data-testid="stHeader"] #MainMenu {
visibility: hidden;
}
div[data-testid="collapsedControl"] {
visibility: visible !important;
background-color: #F0F2F6 !important;
color: #4CAF50 !important;
border-radius: 0 5px 5px 0;
top: 5px;
}
</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. 基礎設定與連結
HOME_URL = "https://kidaid.org.tw/"
# 設定本地端的 MD 檔案對應字典 (大小寫必須與上傳的檔名完全一致)
KNOWLEDGE_MAP = {
"🧠 認知發展": "Cognition_Content.md",
"🗣️ 語言發展": "Language.md",
"🏃 粗大動作": "GrossMotor.md",
"🖐️ 精細動作": "FineMotor.md",
"❤️ 社會情緒": "Emotion.md",
"🥦 飲食對策": "CountermeasuresDietary.md",
"🧩 ASD/早療對策": "CountermeasuresADHD.md"
}
# 2. 批量讀取本地端檔案內容 (使用絕對路徑)
def fetch_all_knowledge():
combined_knowledge = ""
# 自動獲取 app.py 所在的資料夾絕對路徑,確保雲端環境找得到檔案
base_dir = os.path.dirname(os.path.abspath(__file__))
with st.spinner("正在載入育兒發展與療育資訊..."):
for category, filename in KNOWLEDGE_MAP.items():
# 將資料夾路徑與檔案名稱結合
file_path = os.path.join(base_dir, filename)
try:
with open(file_path, "r", encoding="utf-8") as f:
combined_knowledge += f"\n\n## 【領域:{category}】\n"
combined_knowledge += f.read()
except FileNotFoundError:
st.warning(f"無法載入 {category}:系統在路徑 `{file_path}` 找不到檔案,請確認檔名大小寫與上傳位置。")
except Exception as e:
st.error(f"讀取 {filename} 時發生錯誤:{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.title("⚙️ 知識庫狀態")
st.caption("以下資料嘗試從系統載入中:")
for category, filename in KNOWLEDGE_MAP.items():
st.markdown(f"📄 **{category}** (`{filename}`)")
st.markdown("---")
st.markdown(f"🔗 [前往 育兒成 官方網站]({HOME_URL})")
if st.button("🔄 重新載入知識庫"):
st.session_state.knowledge = fetch_all_knowledge()
st.success("資料已重新載入!")
st.markdown("---")
# 開發者偵錯工具:按下去可以看到系統當下資料夾裡到底有哪些檔案
with st.expander("🛠️ 開發者偵錯:檢查檔案列表"):
base_dir = os.path.dirname(os.path.abspath(__file__))
st.write(f"目前工作目錄:`{base_dir}`")
st.write("目前資料夾內的所有檔案與資料夾:")
try:
st.write(os.listdir(base_dir))
except Exception as e:
st.write(f"無法讀取目錄內容:{e}")
# 4. 主介面與範例問句
st.title("🧸 育兒成 - 線上衛教小助手")
st.caption("關於 0-5 歲兒童的認知、語言、動作、情緒、飲食及早療對策,歡迎直接詢問!(資料來源:育兒成 | 全方位兒童發展整合照護平臺)")
# 顯示專屬範例問句按鈕
example_cols = st.columns(5)
examples = [
"🥦 小孩挑食不吃青菜,有什麼應對的小撇步嗎?",
"🏃 2-3歲的孩子粗大動作應該發展到什麼程度?",
"🖐️ 怎麼訓練 4-5 歲幼兒的握筆姿勢?",
"🧩 ASD 孩子一直重複旋轉硬幣,我該立刻制止他嗎?",
"❤️ 孩子生氣時容易崩潰,該怎麼設立冷靜角落?"
]
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"""
你現在是『育兒成 | 全方位兒童發展整合照護平臺』的專業線上客服與育兒小助手。
你的說話風格溫柔、有耐心、具備同理心,且充滿專業知識,就像一位溫暖的早期療育專家或兒童發展衛教師。
以下是本平台最新的衛教資訊(包含認知、語言、粗大動作、精細動作、社會情緒、飲食、ASD早療對策等):
---
{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:
error_msg = str(e)
if "429" in error_msg or "quota" in error_msg.lower():
return "⚠️ **系統提示:**\n\n目前詢問的家長較多,小助手稍微忙不過來了!請您稍等幾分鐘後再試喔!"
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"])