iT邦幫忙

2025 iThome 鐵人賽

DAY 30
0
AI & Data

讓電腦聽懂人話:30 天 NLP 入門系列 第 30

Day 30|RAG Step 4:Generation、網頁展示

  • 分享至 

  • xImage
  •  

引言

前面三天我們一步步完成了資料前處理、建立向量資料庫、向量檢索、重排序,今天終於要進入最後一塊拼圖:生成(Generation),也就是讓 LLM 把前面找到的知識,轉化成自然、流暢又有邏輯的回答 💬

但是呢除了讓模型生成答案,我們還要讓整個流程「看得見」,這樣才用得爽 XDD 所以今天的任務不只是文字生成,我們還會用 Streamlit 做一個簡單的互動式網頁介面,讓你能直接輸入問題、即時看到系統從檢索、重排序到生成的完整過程~~

這樣就完成我們整個 RAG 實作系列的最終成品:一個屬於自己的 「不唬爛」AI 小助手 🤖


本系列實作的架構圖:

ithomeNLP_RAG/
├── requirements.txt 
├── data/
├── db/           # qdrant vector db
├── indexer.py    # store vector
├── retriever.py  # vector search
├── reranker.txt  # rerank
└── frontend.py   # generation + UI

我們今天會做的是 frontend.py


Generation

這邊使用的是 Gemini 的免費 API,大家可以依自己需求去串接不同的 LLM。但是不同的 LLM 吃 Prompt 的方式不一樣,要再自行研究一下~

import streamlit as st
from collections import defaultdict
from reranker import main as rerank_main
import google.generativeai as genai

# 設定模型
API_KEY="your_API_key"
genai.configure(api_key=API_KEY)

def generation(prompt):
    # 初始化文字模型
    model = genai.GenerativeModel('models/gemini-2.5-flash-lite-preview-06-17')
    response = model.generate_content(prompt)
    
    return response.text

Web UI (Streamlit)

# Streamlit 頁面設定
st.set_page_config(page_title="RAG Demo")
st.title("🤖 📚 RAG Demo")

# 使用者輸入
query = st.text_input("請輸入你的問題:", placeholder="tfidf算法是什麼?")

if st.button("Search") and query.strip():
    st.info("🔍 正在檢索...")
    top_k = 10  # 預設向量檢索為 10 筆
    try:
        # Retriever + Reranker
        original_results, reranked_results = rerank_main(query, top_k)
        st.success(f"✅ 檢索完成,共取得 {len(original_results)} 筆結果")
        
        # 顯示 reranking 後前 5 筆結果
        grouped = defaultdict(list)
        for res in reranked_results[:5]:
            grouped[res.get('source', 'Unknown')].append(res)

        st.subheader("📑 Top 5 相關參考資料")
        for source, items in grouped.items():
            # 按 id 排序
            items_sorted = sorted(items, key=lambda x: x.get('id', ''))

            with st.expander(f"Source: {source} ({len(items_sorted)} chunks)"):
                for i, res in enumerate(items_sorted, 1):
                    st.markdown(f"**{i}.** **Score:** {res.get('similarity_score',0):.4f}")
                    # 顯示前 300 字,如果超過用 ... 截斷
                    content_preview = res.get('content','')[:300]
                    if len(res.get('content','')) > 300:
                        content_preview += "..."
                    st.write(content_preview)
                    st.write("---")

        
        # Generation: 將 top-k content 拼接成 context
        context = "\n\n".join([res['content'] for res in reranked_results[:10]])
        
        # 餵入模型的提示詞
        prompt = f"請根據以下內容回答問題:\n\n{context}\n\n問題: {query}\n答案:"
        
        st.info("📝 正在生成回答...")
        answer = generation(prompt)
        
        # 顯示回答
        st.subheader("🧠 AI 生成回答")
        st.write(answer)
        
    except Exception as e:
        st.error(f"❌ 發生錯誤: {str(e)}")

最後在指令介面將網頁起起來就大功告成啦~~

streamlit run frontend.py

網頁呈現

https://ithelp.ithome.com.tw/upload/images/20251013/20178719ByqODD883P.png
圖一:初始頁面

https://ithelp.ithome.com.tw/upload/images/20251013/20178719mbxhsiol8R.png
圖二:搜尋開始

https://ithelp.ithome.com.tw/upload/images/20251013/20178719FFeTTVPIBD.png
圖三:向量搜尋回 10 筆資料,網頁顯示重排序後前 5 筆

https://ithelp.ithome.com.tw/upload/images/20251013/20178719hOjW8XOFwt.png
圖四:重排序後前 5 筆的內容預覽

https://ithelp.ithome.com.tw/upload/images/20251013/20178719JS4HDkqFjT.png
圖五:LLM 整合檢索資料進行增強生成

結語

我們在今天加入了 Generation 和 Streamlit 展示介面,把整個流程串成一個完整的知識問答系統後,就終於完成 RAG 系列的最後一步啦 🎉🎉🎉

回歸到 RAG 的目的,其實就是把「讓 AI 不再亂講話」這件事一步步具體實踐出來。
現在你已經具備打造自己專屬 AI 助手的能力了!接下來你也可以試著用不同的文件資料集、抽換不同的 embedding 模型、還有改良 Prompt,探索各種方式讓這個系統越來越貼近你的應用場景,提升整體的表現~~

RAG 的旅程到這裡告一段落,這三十天的挑戰也終於走到終點啦 🥹🎊
感謝看到這裡的你們,我們後會有期囉~~👋🏻💅🏻


上一篇
Day 29|RAG Step 3:Reranking 重排序
系列文
讓電腦聽懂人話:30 天 NLP 入門30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言