1. 建立多代理 AI 旅遊預訂系統 [0]

本教學旨在展示如何使用 LangGraph 框架,結合多個 AI 代理、記憶體和 API,建立一個能處理複雜任務的真實世界 AI 旅遊預訂系統。

1.1. 系統概述與技術堆疊 [0]

  1. AI 旅遊預訂系統將包含四個主要代理 [0]
    1. 航班代理 (Flight Agent):負責處理航班相關資訊 [0]
    2. 飯店代理 (Hotel Agent):負責處理飯店相關資訊 [0]
    3. 行程代理 (Itinerary Agent):負責規劃行程 [0]
    4. 最終代理 (Final Agent):負責整合所有資訊並提供最終輸出 [0]
  2. 每個代理將執行其特定任務,並將輸出傳遞給下一個代理,最終生成完整的旅遊計畫 [0]
  3. 系統將利用以下技術和工具:
    1. LangGraph:作為代理式 AI 框架 [0]
    2. LLaMA 3 (70 億參數模型):透過 Groq 提供的語言模型 [0]
    3. PostgreSQL:作為資料庫,用於儲存應用程式的記憶體,以記住使用者先前的對話,從而提供更好的結果 [0]
    4. Tavily Search API:用於連接 AI 代理到網際網路,以搜尋即時資訊,例如航班和飯店的最新資料 [0]
    5. AviationStack API:提供免費的即時航班資料 [1]
  4. 所有使用的工具和 API 均為免費資源,無需付費帳戶 [1]

1.2. 系統運作示範 [1]

  1. 系統將接收使用者輸入的提示,例如:「規劃一個包含航班、飯店和觀光的日本七日遊,預算在 20 萬以下」 [1]
  2. 系統會依序啟動代理管道並顯示輸出 [1]
    1. 航班代理:提供完整的航空公司詳細資訊 [1]
    2. 飯店代理:提供完整的飯店列表資訊 [1]
    3. 行程代理:建立完整的七日行程計畫和預算明細 [1]
    4. 最終代理:整合所有代理的資料,生成最終的詳細行程計畫 [1]
  3. 系統還會顯示 LLM 呼叫次數、使用的代理數量和狀態 [1]
  4. 使用者可以選擇下載最終的旅遊計畫 [1]

2. 環境設定與 API 金鑰管理 [2]

本節詳細說明如何設定開發環境、安裝必要的 Python 套件,以及獲取和配置所需的 API 金鑰。

2.1. Python 環境設定 [2]

  1. 在 VS Code 中建立一個名為 MultiAgentSystemDemo 的資料夾 [2]
  2. 開啟終端機,建立一個 Python 虛擬環境,例如 LangGraph_ENV3 [2]
    1. 輸入指令:python -m venv LangGraph_ENV3 [2]
  3. 啟用虛擬環境 [2]
    1. Linux/Mac 系統:source LangGraph_ENV3/bin/activate [2]
    2. Windows 系統:.\LangGraph_ENV3\Scripts\activate [2]
  4. 安裝專案所需的所有 Python 套件 [2]

2.2. 獲取 Groq API 金鑰 [2]

  1. Groq 提供了多種語言模型,本專案將使用 LLaMA 3 (70 億參數模型) [2]
  2. 開啟瀏覽器,前往 console.cloud.google.com [2]
  3. 點擊「API 金鑰」並選擇「建立 API 金鑰」 [2]
  4. 為金鑰命名(例如:ASD),勾選同意條款,然後點擊「提交」 [2]
  5. 複製生成的 API 金鑰 [2]
  6. 在 VS Code 的專案資料夾中,建立一個 .env 檔案 [2]
  7. 將複製的 API 金鑰儲存為一個變數,例如 GOC_API_KEY="你的API金鑰" [2]

2.3. 獲取 AviationStack API 金鑰 [3]

  1. AviationStack API 用於獲取即時航班資料 [3]
  2. 開啟瀏覽器,前往 aviationstack.com [3]
  3. 如果是首次使用,請先註冊並登入(可使用電子郵件或 Google 帳戶) [3]
  4. 登入後,點擊「Get Free API」進入儀表板 [3]
  5. 儀表板上會自動顯示一個 API 金鑰,複製該金鑰 [3]
  6. 在 .env 檔案中,將其儲存為變數,例如 AVIATION_STACK_API_KEY="你的API金鑰" [3]

2.4. 獲取 Tavily Search API 金鑰 [3]

  1. Tavily Search API 允許 AI 代理從網際網路搜尋資料 [3]
  2. 開啟瀏覽器,前往 tavily.com [3]
  3. 如果是首次使用,請先註冊並登入(可使用電子郵件或 Google 帳戶) [3]
  4. 登入後,儀表板上會自動顯示一個 API 金鑰,複製該金鑰 [3]
  5. 在 .env 檔案中,將其儲存為變數,例如 TAVILY_API_KEY="你的API金鑰" [3]
  6. 儲存 .env 檔案 [3]

2.5. 安裝 PostgreSQL 資料庫 [3]

  1. PostgreSQL 用於儲存應用程式的長期記憶體,即使用者對話歷史 [3]
  2. 如果已安裝,可跳過此步驟 [3]
  3. 開啟瀏覽器,搜尋「PostgreSQL 下載」,並點擊官方網站連結 [3]
  4. 選擇您的作業系統(例如 Windows),然後點擊「下載安裝程式」 [3]
  5. 選擇最新的版本進行下載 [3]
  6. 執行下載的 .exe 檔案,並按照螢幕上的指示進行安裝 [3]
    1. 重要提示:在安裝過程中,會要求設定一個密碼,請務必記住此密碼,因為它將用於連接 LangGraph 應用程式與資料庫 [3]
    2. 預設使用者名稱通常為 PostgreSQL,建議將密碼也設定為 PostgreSQL 以便記憶 [4]
    3. 記住連接埠號碼,通常是 5432 或 5433 [4]
  7. 安裝完成後,搜尋並開啟 PGAdmin4 [4]
  8. 如果成功開啟 PGAdmin 4,表示 PostgreSQL 資料庫已成功安裝 [4]
  9. 在 PGAdmin 4 中,點擊伺服器旁的箭頭,輸入您在安裝時設定的密碼以登入 [4]

3. 程式碼實作:核心模組與資料庫連接 [5]

本節將逐步介紹如何建立專案的核心程式碼,包括匯入必要的套件、設定資料庫連接,以及建立用於資料搜尋的工具函數。

3.1. 建立 main.py 檔案並匯入套件 [5]

  1. 在 MultiAgentSystemDemo 資料夾中,建立一個名為 main.py 的檔案 [5]
  2. 匯入以下必要的 Python 套件:
    1. os 模組:用於處理環境變數 [5]
    2. TypedDict 和 Annotated:用於建立 LangGraph 的共享記憶體狀態 [5]
    3. operator 模組:用於更新共享記憶體中的訊息列表 [5]
    4. StateGraph, START, END:用於建立 LangGraph 的圖形結構 [5]
    5. PostgresSaver:用於將 LangGraph 應用程式連接到 PostgreSQL 資料庫,以實現長期記憶體功能 [6]
    6. AnyMessage, HumanMessage, AIMessage, SystemMessage:用於表示不同類型的訊息 [6]
      1. SystemMessage:代表提供給系統的初始提示 [6]
      2. AIMessage:代表 AI 代理的回覆 [6]
      3. HumanMessage:代表使用者輸入的訊息 [6]
      4. AnyMessage:可以儲存任何類型的訊息 [6]
    7. ChatGroq:用於連接 Groq 上的 LLaMA 3 模型 [7]

3.2. 建立 tools 資料夾及工具函數 [7]

  1. 在 MultiAgentSystemDemo 資料夾中,建立一個名為 tools 的子資料夾 [7]
  2. 在 tools 資料夾中,建立兩個 Python 檔案:tavily_tool.py 和 flight_tools.py [7]
  3. flight_tools.py 檔案:
    1. 匯入 requests 模組和 load_dotenv 函數 [7]
    2. 使用 load_dotenv() 載入 .env 檔案中的 API 金鑰 [7]
    3. 定義 search_flights 函數:
      1. 接收 query 作為參數(使用者查詢) [7]
      2. 設定 AviationStack API 的 URL 和參數,包括 AVIATION_STACK_API_KEY 和 limit=5(獲取五個結果) [8]
      3. 使用 requests.get() 發送請求並獲取航班資料 [8]
      4. 將回應儲存為 JSON 格式到 data 變數中 [8]
      5. 建立一個空列表 flights,用於儲存整理後的航班資料 [8]
      6. 遍歷 data 中的每個航班,提取 airline、departure、arrival 和 status 等資訊 [9]
      7. 將這些資訊以字典形式添加到 flights 列表中 [9]
      8. 返回 flights 列表 [9]
  4. tavily_tool.py 檔案:
    1. 匯入 TavilyClient 模組和 load_dotenv 函數 [9]
    2. 使用 load_dotenv() 載入 .env 檔案中的 API 金鑰 [9]
    3. 建立 TavilyClient 實例,並將 TAVILY_API_KEY 儲存到 client 變數中 [9]
    4. 定義 tavily_search 函數:
      1. 接收 query 作為參數(使用者查詢) [9]
      2. 使用 client.search() 執行網際網路搜尋,設定 max_results=5(獲取五個結果) [9]
      3. 將搜尋結果儲存到 response 變數中 [9]
      4. 建立一個空列表 results,用於儲存整理後的搜尋結果 [9]
      5. 遍歷 response 中的每個結果,提取 title、url 和 snippet (內容) [9]
      6. 將這些資訊以字典形式添加到 results 列表中 [9]
      7. 返回 results 列表 [9]

3.3. 設定 Groq LLM 和 PostgreSQL 資料庫連接 [9]

  1. 在 main.py 中,初始化 ChatGroq 模型,指定使用 llama3-8b-8192 模型 [9]
  2. 建立 PostgreSQL 資料庫:
    1. 在 PGAdmin 4 中,右鍵點擊「資料庫」,選擇「建立」->「資料庫」 [9]
    2. 輸入資料庫名稱,例如 LangGraph_Memory_Demo,然後點擊「儲存」 [9]
    3. 記住這個資料庫名稱,因為它將用於連接 LangGraph 應用程式 [9]
  3. 設定資料庫 URL:
    1. 在 main.py 中,定義 DATABASE_URL 變數,格式如下: postgresql://使用者名稱:密碼@localhost:連接埠號碼/資料庫名稱 [9]
    2. 範例:postgresql://PostgreSQL:PostgreSQL@localhost:5432/LangGraph_Memory_Demo [9]
    3. 這裡的「使用者名稱」和「密碼」是您在安裝 PostgreSQL 時設定的,「連接埠號碼」是 PostgreSQL 的預設連接埠(通常是 5432 或 5433),「資料庫名稱」是您剛才建立的資料庫名稱 [9]
  4. 從 .env 檔案中獲取 DATABASE_URL [9]

4. 建立 LangGraph 狀態與代理 [9]

本節將詳細說明如何定義 LangGraph 的共享狀態(Shared Memory),以及如何建立和配置四個 AI 代理(節點),每個代理負責特定的任務。

4.1. 定義共享狀態 (TravelState) [9]

  1. 在 LangGraph 中,狀態 (State) 是所有節點(代理)共享的記憶體 [9]
  2. 建立一個名為 TravelState 的類別,繼承自 TypedDict,用於定義共享狀態的結構 [10]
  3. TravelState 包含以下鍵值對:
    1. messages:Annotated[List[AnyMessage], operator.add] [10]
      1. 儲存所有類型的訊息(使用者、AI、系統),以列表形式存在 [10]
      2. operator.add 表示當有新訊息時,會將其添加到現有訊息列表的末尾,而不是覆蓋,從而保留對話上下文 [10]
    2. user_query:str [10]
      1. 儲存使用者輸入的原始查詢 [10]
      2. 作為後續代理的輸入 [10]
    3. flight_result:str [10]
      1. 儲存航班代理的輸出結果 [10]
    4. hotel_result:str [10]
      1. 儲存飯店代理的輸出結果 [10]
    5. itinerary:str [10]
      1. 儲存行程代理生成的行程計畫 [10]
    6. llm_calls:int [10]
      1. 追蹤 LLM 被呼叫的次數,用於成本監控 [10]
  4. 這種共享狀態確保了所有代理都能存取和更新必要的資訊,並維持對話的上下文 [10]

4.2. 建立航班代理 (Flight Agent) [10]

  1. 建立一個名為 flight_agent 的函數,接收 TravelState 作為輸入 [10]
  2. 從 TravelState 中提取 user_query [10]
  3. 呼叫 search_flights 函數(來自 flight_tools.py),並將 user_query 作為參數傳入,獲取航班資料 [10]
  4. 更新 TravelState:
    1. 將獲取的航班資料儲存到 flight_result 鍵中 [10]
    2. 將「航班結果已獲取」訊息添加到 messages 列表中 [10]
    3. 如果有使用 LLM,則將 llm_calls 增加 1(在此代理中未直接使用 LLM,可註解此行) [10]
  5. 返回更新後的 TravelState [10]

4.3. 建立飯店代理 (Hotel Agent) [11]

  1. 建立一個名為 hotel_agent 的函數,接收 TravelState 作為輸入 [11]
  2. 從 TravelState 中提取 user_query [11]
  3. 將 user_query 與「最佳飯店」字串結合,形成新的查詢 [11]
  4. 呼叫 tavily_search 函數(來自 tavily_tool.py),並將新的查詢作為參數傳入,獲取飯店資料 [11]
  5. 更新 TravelState:
    1. 將獲取的飯店資料儲存到 hotel_result 鍵中 [11]
    2. 將「飯店資訊已獲取」訊息添加到 messages 列表中 [11]
    3. 如果有使用 LLM,則將 llm_calls 增加 1(在此代理中未直接使用 LLM,可註解此行) [11]
  6. 返回更新後的 TravelState [11]

4.4. 建立行程代理 (Itinerary Agent) [11]

  1. 建立一個名為 itinerary_agent 的函數,接收 TravelState 作為輸入 [11]
  2. 從 TravelState 中提取 user_query、flight_result 和 hotel_result [11]
  3. 建立一個提示 (prompt),將這些資訊整合起來,指示 LLM 建立旅遊行程 [11]
  4. 使用 LLM.invoke() 呼叫 LLM (LLaMA 3),並提供系統訊息(「您是一位專業的旅遊規劃師」)和整合後的提示 [11]
  5. LLM 將根據這些資訊生成行程計畫,並將回應儲存到 response 變數中 [11]
  6. 更新 TravelState:
    1. 將 LLM 生成的行程內容(response.content)儲存到 itinerary 鍵中 [12]
    2. 將 LLM 的回應訊息添加到 messages 列表中 [12]
    3. 將 llm_calls 增加 1,因為此代理確實使用了 LLM [13]
  7. 返回更新後的 TravelState [12]

4.5. 建立最終代理 (Final Agent) [13]

  1. 建立一個名為 final_agent 的函數,接收 TravelState 作為輸入 [13]
  2. 從 TravelState 中提取 flight_result、hotel_result 和 itinerary [13]
  3. 建立一個最終提示,指示 LLM 整合所有資訊,生成最終的旅遊回應 [13]
  4. 使用 LLM.invoke() 呼叫 LLM (LLaMA 3),並提供最終提示 [13]
  5. LLM 將生成最終的旅遊計畫回應 [13]
  6. 更新 TravelState:
    1. 將 LLM 的回應訊息添加到 messages 列表中 [13]
    2. 將 llm_calls 增加 1,因為此代理也使用了 LLM [13]
  7. 返回更新後的 TravelState [13]

5. LangGraph 圖形建構與執行 [13]

本節將說明如何使用 LangGraph 建立圖形結構、定義代理之間的連接,以及如何編譯和執行多代理應用程式,並展示其在終端機中的運作。

5.1. 建立 LangGraph 圖形 [13]

  1. 初始化 StateGraph,並將之前定義的 TravelState 作為其狀態 [13]
    1. graph = StateGraph(TravelState) [13]
  2. 添加節點 (Nodes):將每個代理函數作為節點添加到圖形中 [14]
    1. graph.add_node("flight_agent", flight_agent) [14]
    2. graph.add_node("hotel_agent", hotel_agent) [14]
    3. graph.add_node("itinerary_agent", itinerary_agent) [14]
    4. graph.add_node("final_agent", final_agent) [14]
  3. 定義邊緣 (Edges):建立代理之間的連接,定義工作流程 [14]
    1. graph.add_edge(START, "flight_agent"):從 START 節點開始,連接到 flight_agent [14]
    2. graph.add_edge("flight_agent", "hotel_agent"):flight_agent 完成後,連接到 hotel_agent [15]
    3. graph.add_edge("hotel_agent", "itinerary_agent"):hotel_agent 完成後,連接到 itinerary_agent [15]
    4. graph.add_edge("itinerary_agent", "final_agent"):itinerary_agent 完成後,連接到 final_agent [15]
    5. graph.add_edge("final_agent", END):final_agent 完成後,連接到 END 節點,表示工作流程結束 [15]

5.2. 設定記憶體 (PostgreSQL) [15]

  1. 在編譯圖形之前,需要設定記憶體功能,以便將對話歷史儲存到 PostgreSQL 資料庫 [15]
  2. 建立 PostgresSaver 實例,並傳入之前設定的 DATABASE_URL [16]
    1. memory = PostgresSaver(connection_string=DATABASE_URL) [16]
  3. 這將 LangGraph 應用程式與 PostgreSQL 資料庫連接起來,允許儲存和檢索對話 [16]

5.3. 編譯圖形與執行應用程式 [16]

  1. 編譯圖形:使用 graph.compile() 方法編譯圖形,並傳入 memory 實例作為檢查點 (checkpoint) [16]
    1. app = graph.compile(checkpointer=memory) [16]
    2. 編譯後的圖形儲存在 app 變數中,現在可以執行 [16]
  2. 執行應用程式 (終端機):
    1. 在 main.py 檔案中,使用 if __name__ == "__main__": 區塊來執行應用程式 [16]
    2. 設定 config 字典,包含 configurable 鍵和 thread_id,例如 {"configurable": {"thread_id": "user_aarohi"}} [16]
      1. thread_id 是每個使用者的唯一識別碼,用於從資料庫中檢索該使用者的對話歷史,實現長期記憶體功能 [16]
    3. 提示使用者輸入旅行請求:user_input = input("Enter your travel request: ") [16]
    4. 呼叫編譯後的 app,傳入使用者輸入和 config [16]
      1. response = app.invoke({"messages": [HumanMessage(content=user_input)], "user_query": user_input}, config=config) [16]
      2. 這裡將使用者輸入作為 HumanMessage 儲存到 messages 列表中,並將原始查詢儲存到 user_query 鍵中 [17]
    5. 列印最終回應到終端機 [17]
  3. 測試執行:
    1. 在終端機中執行 python main.py [17]
    2. 輸入旅行請求,例如:「規劃一個七日遊」 [17]
    3. 系統將輸出完整的七日行程計畫,包括航班和飯店資訊 [17]

6. 建立 Streamlit 前端介面 [17]

本節將介紹如何使用 Streamlit 框架為多代理 AI 旅遊預訂系統建立一個互動式的前端介面,以提供更友善的使用者體驗。

6.1. 建立 frontend.py 檔案並匯入模組 [17]

  1. 在專案資料夾中,建立一個名為 frontend.py 的檔案 [17]
  2. 匯入 streamlit 模組,以及 main.py 檔案中編譯好的 app 實例 [17]
    1. from main import app [17]
    2. 這樣可以重複使用 main.py 中定義的所有代理邏輯和圖形結構 [17]
  3. 匯入其他必要的 Streamlit 模組,例如 st [17]

6.2. 設定 Streamlit 頁面 [18]

  1. 使用 st.set_page_config() 設定頁面配置 [18]
    1. 設定頁面標題,例如:「AI 旅遊預訂系統」 [18]
    2. 設定頁面圖示 [18]
    3. 設定頁面佈局 [18]
  2. 使用 st.markdown() 嵌入自訂 CSS 樣式,用於控制頁面的背景顏色、邊框、字體等視覺效果 [18]
    1. 這些樣式定義了按鈕、文字框和圖片的顯示方式,例如圓角邊框、懸停效果等 [19]

6.3. 設計側邊欄 (Sidebar) [19]

  1. 使用 Streamlit 的側邊欄功能 (st.sidebar) 建立左側導航欄 [19]
  2. 在側邊欄中添加圖片和文字,例如「AI Travel Planner」 [19]
  3. 側邊欄還會顯示其他相關資訊,例如代理列表等 [19]

6.4. 設計主內容區域 [19]

  1. 在主內容區域中,顯示多張背景圖片,營造視覺效果 [19]
  2. 顯示系統的標題和簡介文字 [19]
  3. 建立輸入文字框,讓使用者輸入旅行請求 [20]
    1. st.text_input("Enter your travel request", key="user_input") [20]
  4. 建立一個按鈕,例如「生成我的旅行計畫」 (st.button("Generate My Travel Plan")) [20]
  5. 當使用者點擊按鈕後,系統將執行以下操作:
    1. 從文字框中獲取使用者輸入 [20]
    2. 呼叫從 main.py 匯入的 app 實例,傳入使用者輸入和 thread_id 配置 [20]
    3. 顯示代理管道的執行過程和每個代理的輸出(航班、飯店、行程、最終回應) [20]
    4. 提供一個下載按鈕,讓使用者可以下載完整的旅行計畫 [20]

6.5. 執行 Streamlit 應用程式 [20]

  1. 在終端機中,導航到專案資料夾 [20]
  2. 執行指令:streamlit run frontend.py [20]
  3. Streamlit 應用程式將在瀏覽器中開啟,使用者可以透過圖形介面與 AI 旅遊預訂系統互動 [20]