以下提及課程為 LLM Twin: Building Your Production-Ready AI Replica
在整理完在 GenAI 開發過程中,隨著規模大,影響開發效率最顯著的部份後,接著回課程內容,由於現在還在鐵人賽期間,每天能產出的篇幅有限,我的修課方式會是每天從課程中提取一個重點來延伸撰寫。
今天的主題聚焦於設計模式,在第一堂爬蟲系統開發中的應用中,作者介紹了 LinkedIn、Medium、GitHub 和 Substack 等平台的資料爬取工具的程式碼,並將內容分別整理為文章、社群貼文和程式碼進行儲存。而這即是借用設計模式的幫助,不僅提高了程式的重用性、也增強了系統的可維護性和擴展性。
其中作者主要使用了三種設計模式,首先是在 BaseAbstractCrawler 類別中實踐 模板方法模式(Template Method Pattern),將共用邏輯像是登入與頁面滾動這些重複的部分集中管理,減少冗餘的程式碼;而 策略模式(Strategy Pattern) 則讓系統更有彈性和擴展性,使得增加新類型的爬蟲變得簡單而直觀;最後是 工廠方法模式(Factory Method Pattern) 的運用,讓系統可以依據不同參數動態調整的爬蟲實體,提強了整個系統的適應能力。
這樣的設計為接下來的擴展帶來了便利性,舉例來說,當需要新增新平台(如Twitter)的爬蟲工具時,我們只需創建一個新的 TwitterCrawler 類別並實作其中的功能,就可以在不改動現有程式的情況下,實現無縫整合。不僅簡化了開發流程,同時維持系統的一致性和可靠性。
在總共 23 個設計模式當中,我挑選了六個使用頻率高、也是相對好理解的方法來介紹,並各自列舉幾個能在 GenAI 開發應用的情境,配合了一個程式範例:
設計模式 | 重點 | GenAI 應用 |
---|---|---|
單例模式 (Singleton) | 確保全局唯一實例 | LLM 模型初始化 |
工廠模式 (Factory Method) | 靈活創建對象 | 跨型態檔案讀取 |
觀察者模式 (Observer) | 實現事件驅動 | 資料庫更新觸發處理 |
模板模式 (Template Method) | 定義算法骨架 | 資料前處理流程框架 |
裝飾器模式 (Decorator) | 動態添加功能 | 靈活組合資料處理步驟 |
策略模式 (Strategy) | 動態切換算法 | 檢索策略調整 |
import time
class LargeLanguageModel:
_instance = None
def __new__(cls):
if cls._instance is None:
print("初始化大型語言模型...")
cls._instance = super().__new__(cls)
cls._instance.model = "GPT-4" # 假設使用 GPT-4
time.sleep(2) # 模擬模型加載時間
return cls._instance
def generate_response(self, prompt: str) -> str:
return f"這是 {self.model} 對 '{prompt}' 的回應:..."
def chat_with_ai(prompt: str) -> str:
llm = LargeLanguageModel()
return llm.generate_response(prompt)
def main():
prompts = [
"什麼是機器學習?",
"解釋一下深度學習",
"AI 的未來發展趨勢是什麼?"
]
# 當多次呼叫 LLM 時,只會建立一個實例
for prompt in prompts:
print(f"\n用戶: {prompt}")
response = chat_with_ai(prompt)
print(f"AI: {response}")
if __name__ == "__main__":
main()
LargeLanguageModel
類使用 __new__
實現單例模式。首次創建實體時,它會初始化模型。chat_with_ai
函數每次調用都會獲取 LLM 實例,但實際上總是使用同一個實體。from abc import ABC, abstractmethod
import csv
from typing import List, Dict
class DocumentLoader(ABC):
@abstractmethod
def load(self, file_path: str) -> str:
pass
class PDFLoader(DocumentLoader):
def load(self, file_path: str) -> str:
# 在實際應用中,這裡會使用如 PyPDF2 之類的庫來讀取 PDF
print(f"使用 PDF 加載器讀取文件:{file_path}")
return f"這是從 {file_path} 讀取的 PDF 內容"
class TXTLoader(DocumentLoader):
def load(self, file_path: str) -> str:
print(f"使用 TXT 加載器讀取文件:{file_path}")
with open(file_path, 'r', encoding='utf-8') as file:
return file.read()
class DocumentLoaderFactory:
@staticmethod
def create_loader(file_type: str) -> DocumentLoader:
if file_type.lower() == "pdf":
return PDFLoader()
elif file_type.lower() == "txt":
return TXTLoader()
else:
raise ValueError(f"不支持的文件類型: {file_type}")
# 使用範例
def process_document(file_path: str, file_type: str):
factory = DocumentLoaderFactory()
loader = factory.create_loader(file_type)
content = loader.load(file_path)
return content
def main():
# 模擬處理不同類型的文件
pdf_content = process_document("example.pdf", "pdf")
txt_content = process_document("example.txt", "txt")
if __name__ == "__main__":
main()
DocumentLoader
是一個抽象基類,定義了文件加載器的通用接口。PDFLoader
和 TXTLoader
是具體的加載器類,實現了特定文件類型的加載邏輯。DocumentLoaderFactory
是工廠類,根據文件類型創建相應的加載器。process_document
函數展示了如何使用工廠來處理不同類型的文件,而不需要知道具體的加載器實現細節。這種設計允許我們輕鬆地添加新的文件類型支持(如 CSV、JSON 等),只需創建新的加載器類並在工廠中添加相應的邏輯。這提高了系統的可擴展性和維護性,特別適合處理多種數據源的RAG系統。
接下來的四個案例會在明天繼續介紹:)
ref.
Refactoring.Guru | design-patterns