iT邦幫忙

2025 iThome 鐵人賽

DAY 12
0

HI!大家好,我是 Shammi 😊

接著是小專案的實際測試階段囉!

在 Day 11,我將對話機器人建立了基本的對話邏輯(我還命名為「阿米」呢),讓它能持續與使用者互動。

到目前為止,機器人都只能在 Colab 的終端機裡運作。今天特別用 Web 介面賦予機器人一個看得見、摸得著的外殼,將所有核心功能整合進一個簡單的 Web 測試介面。

這一步是將我的小專案從一個「程式碼」轉變為一個「應用程式」,讓任何人都能輕鬆地與你的 AI 機器人對話。

🌐 一、為什麼需要一個 Web 介面?

👉 使用者體驗:一個好的使用者介面(UI)能讓互動更直觀。比起在終端機輸入指令,一個網頁表單更符合大眾的使用習慣。

👉 方便展示:Web 介面可以輕鬆地與他人分享專案成果,無論是給朋友、老師還是潛在的雇主,都能一鍵展示機器人是如何運作的。

👉 統整功能:Web 介面能整合所有流程(從輸入問題、檢索、到生成回答)都包裝在一個簡單的頁面中,讓整個專案看起來更完整。

🌐 二、Streamlit 簡介:用最少的程式碼建立網頁

在 Streamlit 和 Gradio 這兩個主流工具中,我選擇 Streamlit。Streamlit 是一個專為資料科學家和機器學習工程師設計的 Python 函式庫,它最大的特色就是:只用 Python 程式碼,就能建立出美觀的 Web 應用,無需任何網頁開發的知識,真得超好用!!!

這讓 Streamlit 成為小專案能夠快速原型開發(或測試)的完美工具,也不過度浪費開發中資源。

🌐 三、實作教學:用 Streamlit 建立網頁介面

為了讓 Streamlit 應用程式能夠獨立運作,必須確保它能夠載入所有需要的資料,包括 FAISS 索引和儲存的文字區塊(stored_chunks)。

步驟 1:安裝所有套件並處理 PDF 資料

將 Day 1 到 Day 9 的所有程式碼整合在同一個 Colab 筆記本中,並重新執行一次。這將會完成所有前置作業,並產生必要的檔案。

請注意!這一次我們將新增一個步驟:將 stored_chunks 列表也儲存成檔案,以便 Streamlit 應用程式載入。

請讀者直接在同一頁程式面新增以下程式碼,能比較不同的對話呈現方式

#安裝所有必要的套件
!pip install streamlit google-generativeai pypdf faiss-cpu cloudflared
#處理PDF並儲存必要檔案
import google.generativeai as genai
from google.colab import userdata
from pypdf import PdfReader
from langchain.text_splitter import RecursiveCharacterTextSplitter
import faiss
import numpy as np
import pickle

# ========== Day 1-6: 載入與切割 PDF ==========
#假設你的PDF檔案已上傳至Colab
pdf_path = "17sdgs_0801.pdf" #請替換為你的PDF檔案名稱

reader = PdfReader(pdf_path)
pdf_text = ""
for page in reader.pages:
    pdf_text += page.extract_text()

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,
    chunk_overlap=200,
    length_function=len
)
stored_chunks = text_splitter.split_text(pdf_text)

print(f"成功將 PDF 內容切分為 {len(stored_chunks)} 個區塊。")

# ========== Day 7-8:向量化與建立 FAISS 索引 ==========

#從Colab金鑰管理員中取得金鑰
GOOGLE_API_KEY = userdata.get('GOOGLE_API_KEY')
genai.configure(api_key=GOOGLE_API_KEY)

def get_embedding(text, task_type="RETRIEVAL_DOCUMENT"):
    response = genai.embed_content(
        model="models/text-embedding-004",
        content=text,
        task_type=task_type
    )
    return np.array(response['embedding']).astype('float32')

all_embeddings = [get_embedding(chunk) for chunk in stored_chunks]

vector_array = np.array(all_embeddings)
dimension = vector_array.shape[1]
index = faiss.IndexFlatL2(dimension)
index.add(vector_array)
faiss.write_index(index, "sdgs_faiss.index")

# ========== 新增步驟: 儲存 stored_chunks ==========
with open('stored_chunks.pkl', 'wb') as f:
    pickle.dump(stored_chunks, f)

print("✅ 所有前置準備完成!已產生 sdgs_faiss.index 和 stored_chunks.pkl")

參考結果:
https://ithelp.ithome.com.tw/upload/images/20250910/20151627F9LO0vStai.jpg

步驟 2:撰寫 Streamlit 應用程式程式碼

請執行以下程式碼儲存格,這會將 app.py 檔案寫入你的 Colab 環境。

%%writefile app.py

import streamlit as st
import faiss
import numpy as np
import google.generativeai as genai
from google.colab import userdata
import pickle

# ========== 核心 RAG 函式與設定 ==========

GOOGLE_API_KEY = userdata.get('GOOGLE_API_KEY')
genai.configure(api_key=GOOGLE_API_KEY)

#載入儲存的 FAISS 索引檔案和 stored_chunks 列表
try:
    index = faiss.read_index("sdgs_faiss.index")
    with open('stored_chunks.pkl', 'rb') as f:
        stored_chunks = pickle.load(f)
except FileNotFoundError:
    st.error("找不到必要的檔案!請確認你已經執行過前置準備的程式碼。")
    st.stop()

#定義向量化函式(檢索用)
def get_embedding(text, task_type="RETRIEVAL_QUERY"):
    response = genai.embed_content(
        model="models/text-embedding-004",
        content=text,
        task_type=task_type
    )
    return np.array(response['embedding']).astype('float32').reshape(1, -1)

#定義 LLM 串接函式
model = genai.GenerativeModel('gemini-1.5-flash')
def generate_response(query, retrieved_chunks):
    context = "\n---\n".join(retrieved_chunks)
    system_prompt = (
        "你是一個名為阿米的孩子,個性純真、善良,且充滿愛與同理心。\n"
        "你的任務是根據提供的資料,用溫暖、親切、簡潔而深刻的口氣,引導使用者認識 SDGs 的應用。\n"
    )
    prompt = f"""
    {system_prompt}
    請根據以下提供的參考資料,簡潔且精準地回答使用者的問題。
    請使用 Markdown 語法來格式化你的回答,讓回答更有條理。
    如果參考資料中沒有相關資訊,請禮貌地告知使用者,並鼓勵他們提出與 SDGs 相關的問題。
    
    使用者問題:{query}
    參考資料:
    {context}
    """
    response = model.generate_content(prompt)
    return response.text

#整合RAG
def get_rag_answer(query):
    query_vector = get_embedding(query)
    distances, indices = index.search(query_vector, k=3)
    retrieved_chunks = [stored_chunks[i] for i in indices[0]]
    final_answer = generate_response(query, retrieved_chunks)
    return final_answer

# ========== Streamlit 介面程式碼 ==========

st.title("🌱 SDGs 知識機器人 - 阿米")
st.write("嗨,我是阿米!一個能回答關於永續發展目標 (SDGs) 的小機器人。請問有什麼想知道的嗎?")

#建立一個文字輸入框
user_query = st.text_input("請輸入你的問題:", placeholder="例如:永續利用海洋資源有哪些具體目標?")

#建立一個按鈕,當按下時執行 RAG 流程
if st.button("發送問題"):
    if user_query:
        with st.spinner("阿米正在努力思考中..."):
            answer = get_rag_answer(user_query)
        st.markdown("---")
        st.markdown("### 阿米的回答:")
        st.markdown(answer)
    else:
        st.warning("請輸入你的問題!")

注意:在操作的過程中,需要「全部執行」,過去的程式碼也要一一被執行,包含PDF重新上傳到 DAY 11 的內部回應要「EXIT」,再執行此段程式碼。有可能你會遇到步驟3在跑程式,雖此步驟的狀態為「over」,但別擔心,直接再執行此段即可解決!

步驟 3:在 Colab 中運行 Streamlit 應用程式

最後再執行以下指令,就可以啟動 Streamlit 服務。

這個指令會把我們的 Python 程式變成一個可以互動的網頁。

!streamlit run app.py & npx localtunnel --port 8501

如果遇到密碼問題請用以下程式碼

!streamlit run app.py & npx cloudflared tunnel --url http://localhost:8501

參考結果:

執行完後會出現以下網址,請點選。
https://ithelp.ithome.com.tw/upload/images/20250910/20151627IrwAXG1Ftk.jpg
實際測試畫面:
https://ithelp.ithome.com.tw/upload/images/20250910/201516271dJmTxvFd8.jpg

總結

今天的篇幅,主要是將所有流程整合進一個獨立運作的 Web 應用程式中。
也成功地將以下幾點關鍵技術串接起來:
👉 資料準備:已經能夠從 PDF 中提取文字、進行切割,並儲存為 AI 機器人可以讀取的檔案。
👉 檢索核心:建立了一個強大的 FAISS 索引,讓機器人可以從你的知識庫中快速、精準地找到相關資料。
👉 生成能力:將檢索到的資料餵給 LLM,並使用精心設計的 Prompt,賦予了機器人「阿米」獨特的個性和說話方式。
👉 介面化:用 Streamlit 這個強大的工具,為機器人穿上了一件美觀又易於使用的外衣,讓任何人都能透過網址與它對話。

嘿嘿!這篇完成的時候蠻有成就感的!讓我的 AI 對話機器人不再只是一個終端機裡的程式,而是一個可以讀取完整 PDF 內容,並隨時分享的 Web 應用程式了!到這個步驟,如果讀者也有一起完成的話,已經完整掌握了 RAG 架構的核心精髓囉!


上一篇
Day 11【對話邏輯】 建立基本的對話流程與回覆格式化,讓機器人能「說人話」
下一篇
Day 13【功能挑戰】 如何優化回覆品質?
系列文
建構跨平台AI對話機器人:從LINE到Telegram實踐SDGs推廣的30天專案紀實16
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言