k06181303/taiwan-travel-agent
Taiwan Travel Agent
以 LangGraph + FastAPI + Claude API 打造的多步驟台灣旅遊 AI Agent,展示 Production-ready 的 Agent 工作流設計。
   
🎯 專案亮點
🏗️ 系統架構
POST /chat/stream (SSE)
│
▼
┌───────────────────────────────────────────────────────┐
│ LangGraph Workflow │
│ │
│ ┌─────────────────┐ │
│ │ intent_parser │ Claude tool_use → JSON schema │
│ └────────┬────────┘ │
│ │ fan-out(並行執行) │
│ ┌──────┼──────┐ │
│ ▼ ▼ ▼ │
│ attr food transport │
│ _rag _rag _node │
│ └──────┼──────┘ │
│ │ fan-in(等全部完成) │
│ ▼ │
│ ┌─────────────────┐ │
│ │ itinerary │ 整合三來源生成行程草稿 │
│ └────────┬────────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ reviewer │ ◄─── interrupt(),等使用者審核 │
│ └────────┬────────┘ │
│ ┌────┴────┐ │
│ OK 修改要求 │
│ │ │ │
│ ▼ └──────────► itinerary(重跑) │
│ final_output │
│ _node → END │
└───────────────────────────────────────────────────────┘
│
▼
POST /chat/feedback ─────► Command(resume=feedback) → LangGraph resumeRAG Pipeline 細節
使用者查詢
│
├─ Stage 1: Retrieval(高召回)
│ ├─ Dense: bge-m3 向量檢索(ChromaDB, cosine HNSW)→ top-20
│ └─ Sparse: BM25(jieba 中文斷詞)→ top-20
│ └─ RRF 融合 → top-20
│
└─ Stage 2: Reranking(高精準)
└─ bge-reranker-v2-m3(Cross-Encoder)→ top-5🚀 快速啟動
方法一:Docker(推薦)
# 1. Clone repo
git clone https://github.com/<your-username>/taiwan-travel-agent.git
cd taiwan-travel-agent
# 2. 設定環境變數
cp .env.example .env
# 編輯 .env,填入你的 ANTHROPIC_API_KEY
# 取得方式:https://console.anthropic.com/
# 3. 啟動服務(首次 build 需約 10-15 分鐘,下載 AI 模型)
docker-compose up --build
# 4. 初始化向量資料庫(首次執行)
docker-compose exec app python scripts/seed.py
# 5. 測試
curl http://localhost:8000/health注意:sentence-transformers+torch約 3-4GB,首次 build 請耐心等待。
方法二:本機開發
# 1. 建立虛擬環境
python -m venv .venv
.venv\Scripts\activate # Windows
# source .venv/bin/activate # Mac/Linux
# 2. 安裝套件
pip install -r requirements.txt
# 3. 設定環境變數
cp .env.example .env
# 填入 ANTHROPIC_API_KEY
# 4. 初始化向量資料庫
python scripts/seed.py
# 5. 啟動開發 server
uvicorn app.main:app --reload --port 8000📺 API 使用範例
同步版(快速測試)
curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{"query": "我想去台北玩 2 天,喜歡老街跟小吃,預算 5000 元"}'SSE 串流版(完整 HITL 流程)
Step 1:發起請求,取得行程草稿
curl -N -X POST http://localhost:8000/chat/stream \
-H "Content-Type: application/json" \
-d '{"query": "我想去台北玩 2 天,喜歡老街跟小吃", "thread_id": "user-001"}'
# SSE 回應格式:
# data: {"event": "node_complete", "node": "intent_parser", "data": {...}}
# data: {"event": "node_complete", "node": "attraction_rag", "data": {...}}
# data: {"event": "node_complete", "node": "food_rag", "data": {...}}
# data: {"event": "node_complete", "node": "transport", "data": {...}}
# data: {"event": "node_complete", "node": "itinerary", "data": {...}}
# data: {"event": "review_request", "thread_id": "user-001", "itinerary_draft": "..."}Step 2:要求修改行程
curl -X POST http://localhost:8000/chat/feedback \
-H "Content-Type: application/json" \
-d '{"thread_id": "user-001", "feedback": "把第 2 天下午換成去淡水老街"}'
# 回應:{"status": "revised", "itinerary_draft": "...(新行程)..."}Step 3:核准行程
curl -X POST http://localhost:8000/chat/feedback \
-H "Content-Type: application/json" \
-d '{"thread_id": "user-001", "feedback": "OK"}'
# 回應:{"status": "approved", "final_output": "...(最終行程)..."}Swagger UI
啟動後瀏覽 http://localhost:8000/docs 即可互動測試所有 API。
🛠️ 技術棧
💡 實作細節
為什麼用 LangGraph 而不是純 LangChain Chain?
LangChain 的 Chain 是線性的,無法優雅處理:
- 並行:景點、美食、交通三個 RAG 節點同時執行,用
add_edgefan-out 實作 - 條件分支:HITL 後可能回到
itinerary節點重做 - 中斷/恢復:
interrupt()+MemorySaver讓工作流能在任意節點暫停並 resume
HITL 怎麼透過 interrupt() 實現?
# reviewer_node 裡呼叫 interrupt(),工作流暫停並存 checkpoint
feedback = interrupt({
"type": "review_request",
"itinerary_draft": draft,
})
# 外部透過 Command(resume=feedback) 恢復
result = travel_graph.invoke(Command(resume=request.feedback), config)ChromaDB 多執行緒安全問題
LangGraph fan-out 會在多執行緒中並行執行節點。ChromaDB 的 Rust binding(v0.5+)不能跨執行緒共用同一個 client。
解法:使用 threading.local() 讓每個執行緒持有獨立的 ChromaDB client,完全規避競爭問題。
混合 RAG 精度
📁 專案結構
taiwan-travel-agent/
├── app/
│ ├── main.py # FastAPI 入口,CORS 設定
│ ├── config.py # pydantic-settings 讀取 .env
│ ├── agents/
│ │ ├── graph.py # LangGraph 工作流(fan-out, HITL, 條件分支)
│ │ ├── state.py # AgentState(TypedDict)
│ │ └── nodes/
│ │ ├── intent_parser.py # Claude tool_use → 意圖 JSON
│ │ ├── attraction_rag.py # 景點向量檢索
│ │ ├── food_rag.py # 美食向量檢索
│ │ ├── transport.py # 交通建議(Claude LLM)
│ │ ├── itinerary.py # 行程生成(整合三來源)
│ │ └── reviewer.py # HITL 審核(interrupt)
│ ├── rag/
│ │ ├── embeddings.py # bge-m3 sentence-transformers
│ │ ├── vector_store.py # ChromaDB(thread-safe)
│ │ └── retriever.py # 混合檢索 + reranking
│ └── api/
│ ├── routes.py # /chat, /chat/stream, /chat/feedback
│ └── schemas.py # Pydantic request/response 模型
├── data/
│ ├── attractions.json # 台灣景點 50 筆
│ └── foods.json # 台灣美食 50 筆
├── scripts/
│ └── seed.py # 初始化 ChromaDB 向量資料
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
└── .env.example🧪 測試
# 檢查向量資料庫筆數
python -c "
from app.rag.vector_store import count_documents
print('attractions:', count_documents('attractions'))
print('foods:', count_documents('foods'))
"
# 健康檢查
curl http://localhost:8000/health📝 履歷描述(STAR 格式)
Taiwan Travel Agent | LangGraph + FastAPI + Claude API | 2026
以 LangGraph StateGraph 設計五步驟 AI Agent Pipeline(意圖解析 → 並行 RAG 檢索 → 行程生成 → HITL 審核 → 輸出),實作 interrupt()/Command(resume=) Human-in-the-Loop 審核機制與 FastAPI SSE 即時串流。RAG 採用兩階段混合檢索(bge-m3 向量 + BM25 jieba)+ Cross-Encoder reranking,估計提升精度約 30%。Docker Compose 一鍵部署。
Built with [Claude Code](https://claude.ai/claude-code) — Anthropic's official CLI for AI-assisted development.
