
許多人滿懷熱情地走進健身房,相信自己正走在實現健身目標的正確道路上。但由於飲食計劃不合理和缺乏方向,健身效果往往不盡如人意。聘請私人教練和昂貴的健身器材並非總是可行。正因如此,我撰寫了這篇文章,向您展示如何利用 LangChain 的強大功能打造您的健身教練。有了它,您現在可以以最低的成本獲得根據您的目標定製的鍛鍊和飲食建議。讓我們開始運用這項令人驚歎的技術,將其變成您的健身副駕駛吧!
為什麼使用Langchain?
Langchain 透過將大型語言模型 (LLM) 與工具、資料來源和記憶體相結合,使您在構建高階 AI 應用程式時能夠做更多的事情。您可以建立代理來呼叫函式、查詢資訊和管理狀態對話,而無需使用純文字提示符呼叫 LLM。對於健身教練,Langchain 允許您將 LLM 智慧與自定義邏輯相結合——例如,建立鍛鍊建議、跟蹤進度和獲取健康資料——這樣您就可以成為一名更智慧的互動教練,而無需自己弄清楚所有事情。
先決條件
要使用 LangChain 建立您的健身教練,您需要:
- 一個用於訪問語言模型的 OpenAI API 金鑰
- 一個用於使用網路搜尋的 SerpAPI 服務金鑰
- Python 基礎知識
好了,您現在可以開始了。
如何構建您的健身教練?
在本節中,我將演示如何使用 Langchain 代理建立您的健身教練。請確保您已根據先決條件做好一切準備。我將逐步指導您構建解決方案,並解釋每個步驟在實現結果中所起的作用。
FitCoach AI 是一款對話式健身教練,它持續收集使用者資料,並使用 OpenAI 的 LangChain 代理生成個性化的鍛鍊和飲食計劃。
核心依賴項
要安裝構建健身代理所需的所有庫,請在命令列中執行以下命令:
pip install gradio langchain openai serper-dev python-doten
pip install gradio langchain openai serper-dev python-doten
pip install gradio langchain openai serper-dev python-doten
一旦所有依賴項都到位,我們就開始匯入該任務的所有相關模組:
from typing import List, Tuple, Optional
from langchain_openai import ChatOpenAI
from langchain.memory import ConversationBufferMemory
from langchain.agents import initialize_agent, AgentType
from langchain.tools import BaseTool
# Load environment variables
import os
import gradio as gr
import traceback
import datetime
from typing import List, Tuple, Optional
from langchain_openai import ChatOpenAI
from langchain.memory import ConversationBufferMemory
from langchain.agents import initialize_agent, AgentType
from langchain.tools import BaseTool
import json
import requests
import dotenv
# Load environment variables
dotenv.load_dotenv()
import os
import gradio as gr
import traceback
import datetime
from typing import List, Tuple, Optional
from langchain_openai import ChatOpenAI
from langchain.memory import ConversationBufferMemory
from langchain.agents import initialize_agent, AgentType
from langchain.tools import BaseTool
import json
import requests
import dotenv
# Load environment variables
dotenv.load_dotenv()
SerperSearchTool類
功能:提供即時網頁搜尋功能,獲取最新的健身/營養資訊。
主要功能:
- 整合 Serper API 獲取 Google 搜尋結果
- 返回包含標題、摘要和 URL 的前 5 條格式化搜尋結果
- 具有可接受的故障模式和超時保護
- 支援同步和非同步
# ----------- SERPER SEARCH TOOL ------------
class SerperSearchTool(BaseTool):
description: str = "Searches the web for real-time information and returns structured results"
def _run(self, query: str) -> str:
"""Search the web using Serper API"""
api_key = os.getenv("SERPER_API_KEY")
return "Error: SERPER_API_KEY not found in environment variables"
url = "https://google.serper.dev/search"
payload = json.dumps({"q": query})
'Content-Type': 'application/json'
response = requests.post(url, headers=headers, data=payload, timeout=10)
response.raise_for_status()
search_results = response.json()
# Extract and format organic results
if 'organic' in search_results:
for item in search_results['organic'][:5]: # Limit to top 5 results
"title": item.get('title', ''),
"link": item.get('link', ''),
"snippet": item.get('snippet', '')
# Format results in a readable way
formatted_results = "Search Results:\n\n"
for i, result in enumerate(results, 1):
formatted_results += f"{i}. {result['title']}\n"
formatted_results += f" {result['snippet']}\n"
formatted_results += f" URL: {result['link']}\n\n"
return "No search results found."
except requests.exceptions.RequestException as e:
return f"Error performing search - Network issue: {str(e)}"
return f"Error performing search: {str(e)}"
async def _arun(self, query: str) -> str:
"""Async version of search"""
# ----------- SERPER SEARCH TOOL ------------
class SerperSearchTool(BaseTool):
name: str = "search_web"
description: str = "Searches the web for real-time information and returns structured results"
def _run(self, query: str) -> str:
"""Search the web using Serper API"""
try:
api_key = os.getenv("SERPER_API_KEY")
if not api_key:
return "Error: SERPER_API_KEY not found in environment variables"
url = "https://google.serper.dev/search"
payload = json.dumps({"q": query})
headers = {
'X-API-KEY': api_key,
'Content-Type': 'application/json'
}
response = requests.post(url, headers=headers, data=payload, timeout=10)
response.raise_for_status()
search_results = response.json()
# Extract and format organic results
results = []
if 'organic' in search_results:
for item in search_results['organic'][:5]: # Limit to top 5 results
results.append({
"title": item.get('title', ''),
"link": item.get('link', ''),
"snippet": item.get('snippet', '')
})
# Format results in a readable way
if results:
formatted_results = "Search Results:\n\n"
for i, result in enumerate(results, 1):
formatted_results += f"{i}. {result['title']}\n"
formatted_results += f" {result['snippet']}\n"
formatted_results += f" URL: {result['link']}\n\n"
return formatted_results
else:
return "No search results found."
except requests.exceptions.RequestException as e:
return f"Error performing search - Network issue: {str(e)}"
except Exception as e:
return f"Error performing search: {str(e)}"
async def _arun(self, query: str) -> str:
"""Async version of search"""
return self._run(query)
# ----------- SERPER SEARCH TOOL ------------
class SerperSearchTool(BaseTool):
name: str = "search_web"
description: str = "Searches the web for real-time information and returns structured results"
def _run(self, query: str) -> str:
"""Search the web using Serper API"""
try:
api_key = os.getenv("SERPER_API_KEY")
if not api_key:
return "Error: SERPER_API_KEY not found in environment variables"
url = "https://google.serper.dev/search"
payload = json.dumps({"q": query})
headers = {
'X-API-KEY': api_key,
'Content-Type': 'application/json'
}
response = requests.post(url, headers=headers, data=payload, timeout=10)
response.raise_for_status()
search_results = response.json()
# Extract and format organic results
results = []
if 'organic' in search_results:
for item in search_results['organic'][:5]: # Limit to top 5 results
results.append({
"title": item.get('title', ''),
"link": item.get('link', ''),
"snippet": item.get('snippet', '')
})
# Format results in a readable way
if results:
formatted_results = "Search Results:\n\n"
for i, result in enumerate(results, 1):
formatted_results += f"{i}. {result['title']}\n"
formatted_results += f" {result['snippet']}\n"
formatted_results += f" URL: {result['link']}\n\n"
return formatted_results
else:
return "No search results found."
except requests.exceptions.RequestException as e:
return f"Error performing search - Network issue: {str(e)}"
except Exception as e:
return f"Error performing search: {str(e)}"
async def _arun(self, query: str) -> str:
"""Async version of search"""
return self._run(query)
UserDataTracker類
功能:在建立任何健身計劃之前獲取所有必要資訊
Required Data Fields (in order):Fitness goal (weight loss, muscle gain, etc.)Age (in range 10-100 validation)Gender (male/female/other)Weight (in units, - kg/lbs)Height (in cm or feet/inches)Activity Level (5 predefined levels)Diet Preferences (vegetarian, vegan, etc.)Diet Restrictions/allergyWorkout-Preferencing & limitations
主要功能:
- 欄位驗證:每個輸入都將使用自定義驗證函式進行驗證。
- 順序流程:任何人都無法跳過任何步驟。
- 錯誤處理:為無效輸入提供具體的錯誤訊息。
# ----------- USER DATA TRACKER CLASS ------------
# Define required fields with their validation functions and question prompts
'question': "What is your primary fitness goal? (e.g., weight loss, muscle gain, general fitness)",
'validate': self._validate_fitness_goal
'question': "How old are you? (Must be between 10-100)",
'validate': self._validate_age
'question': "What is your gender? (male/female/other)",
'validate': self._validate_gender
'question': "What is your current weight? (e.g., 150 lbs or 68 kg)",
'validate': self._validate_weight
'question': "What is your height? (e.g., 5'10\" or 178 cm)",
'validate': self._validate_height
'question': "What is your activity level? (sedentary, lightly active, moderately active, very active, extremely active)",
'validate': self._validate_activity_level
'question': "Do you follow any specific diet? (e.g., vegetarian, vegan, keto, none)",
'validate': self._validate_dietary_preferences
'dietary_restrictions': {
'question': "Any food allergies or dietary restrictions? (e.g., nuts, dairy, gluten, none)",
'validate': self._validate_dietary_restrictions
'question': "What are your workout preferences? (e.g., gym, home workouts, equipment available, any injuries?)",
'validate': self._validate_workout_preferences
# ----------- USER DATA TRACKER CLASS ------------
class UserDataTracker:
def __init__(self):
self.data = {}
# Define required fields with their validation functions and question prompts
self.required_fields = {
'fitness_goal': {
'question': "What is your primary fitness goal? (e.g., weight loss, muscle gain, general fitness)",
'validate': self._validate_fitness_goal
},
'age': {
'question': "How old are you? (Must be between 10-100)",
'validate': self._validate_age
},
'gender': {
'question': "What is your gender? (male/female/other)",
'validate': self._validate_gender
},
'weight': {
'question': "What is your current weight? (e.g., 150 lbs or 68 kg)",
'validate': self._validate_weight
},
'height': {
'question': "What is your height? (e.g., 5'10\" or 178 cm)",
'validate': self._validate_height
},
'activity_level': {
'question': "What is your activity level? (sedentary, lightly active, moderately active, very active, extremely active)",
'validate': self._validate_activity_level
},
'dietary_preferences': {
'question': "Do you follow any specific diet? (e.g., vegetarian, vegan, keto, none)",
'validate': self._validate_dietary_preferences
},
'dietary_restrictions': {
'question': "Any food allergies or dietary restrictions? (e.g., nuts, dairy, gluten, none)",
'validate': self._validate_dietary_restrictions
},
'workout_preferences': {
'question': "What are your workout preferences? (e.g., gym, home workouts, equipment available, any injuries?)",
'validate': self._validate_workout_preferences
},
}
self.current_step = 0
# ----------- USER DATA TRACKER CLASS ------------
class UserDataTracker:
def __init__(self):
self.data = {}
# Define required fields with their validation functions and question prompts
self.required_fields = {
'fitness_goal': {
'question': "What is your primary fitness goal? (e.g., weight loss, muscle gain, general fitness)",
'validate': self._validate_fitness_goal
},
'age': {
'question': "How old are you? (Must be between 10-100)",
'validate': self._validate_age
},
'gender': {
'question': "What is your gender? (male/female/other)",
'validate': self._validate_gender
},
'weight': {
'question': "What is your current weight? (e.g., 150 lbs or 68 kg)",
'validate': self._validate_weight
},
'height': {
'question': "What is your height? (e.g., 5'10\" or 178 cm)",
'validate': self._validate_height
},
'activity_level': {
'question': "What is your activity level? (sedentary, lightly active, moderately active, very active, extremely active)",
'validate': self._validate_activity_level
},
'dietary_preferences': {
'question': "Do you follow any specific diet? (e.g., vegetarian, vegan, keto, none)",
'validate': self._validate_dietary_preferences
},
'dietary_restrictions': {
'question': "Any food allergies or dietary restrictions? (e.g., nuts, dairy, gluten, none)",
'validate': self._validate_dietary_restrictions
},
'workout_preferences': {
'question': "What are your workout preferences? (e.g., gym, home workouts, equipment available, any injuries?)",
'validate': self._validate_workout_preferences
},
}
self.current_step = 0
Langchain代理配置
代理初始化:
- 模型:GPT-4o-mini,溫度設定為 0.3,以確保一致性。
- 記憶體:ConversationBufferMemory,用於上下文一致性。
- 工具:Web 搜尋,用於代理查詢即時資訊。
initialize_fitcoach_agent
函式用於配置 FitCoach,這是一個 Langchain 對話代理,可充當虛擬健身和營養教練。它連線到語言模型 GPT-4o-mini,可以透過 Web 搜尋工具進行增強,並跟蹤對話記憶以獲取上下文。該代理遵循嚴格的基於規則的對話連續性:它會逐個向使用者詢問特定問題,以提取有關健身目標、年齡、身體指標、飲食習慣和病史等所有重要資訊。只有在收集並確認所有需要的資訊後,代理才會承諾不生成任何健身或飲食計劃。透過這種方式,代理可以提供使用者所需的安全、準確和個性化的指導。一旦收集到所有必要的資訊,FitCoach 就會根據使用者生成全面的鍛鍊計劃和膳食計劃,同時提供互動且引人入勝的指導計劃。
# ----------- LANGCHAIN AGENT SETUP ------------
def initialize_fitcoach_agent():
"""Initialize the FitCoach agent with error handling"""
# Check for OpenAI API key
openai_key = os.getenv("OPENAI_API_KEY")
raise ValueError("OPENAI_API_KEY not found in environment variables")
# Initialize the language model with correct model name
openai_api_key=openai_key
if os.getenv("SERPER_API_KEY"):
search_tool = SerperSearchTool()
tools.append(search_tool)
print("✅ Search tool initialized successfully")
print("⚠️ SERPER_API_KEY not found - search functionality will be limited")
print(f"⚠️ Could not initialize search tool: {e}")
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
# ----------- LANGCHAIN AGENT SETUP ------------
def initialize_fitcoach_agent():
"""Initialize the FitCoach agent with error handling"""
try:
# Check for OpenAI API key
openai_key = os.getenv("OPENAI_API_KEY")
if not openai_key:
raise ValueError("OPENAI_API_KEY not found in environment variables")
# Initialize the language model with correct model name
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0.3,
openai_api_key=openai_key
)
# Initialize tools
tools = []
try:
if os.getenv("SERPER_API_KEY"):
search_tool = SerperSearchTool()
tools.append(search_tool)
print("✅ Search tool initialized successfully")
else:
print("⚠️ SERPER_API_KEY not found - search functionality will be limited")
except Exception as e:
print(f"⚠️ Could not initialize search tool: {e}")
# Initialize memory
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
# ----------- LANGCHAIN AGENT SETUP ------------
def initialize_fitcoach_agent():
"""Initialize the FitCoach agent with error handling"""
try:
# Check for OpenAI API key
openai_key = os.getenv("OPENAI_API_KEY")
if not openai_key:
raise ValueError("OPENAI_API_KEY not found in environment variables")
# Initialize the language model with correct model name
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0.3,
openai_api_key=openai_key
)
# Initialize tools
tools = []
try:
if os.getenv("SERPER_API_KEY"):
search_tool = SerperSearchTool()
tools.append(search_tool)
print("✅ Search tool initialized successfully")
else:
print("⚠️ SERPER_API_KEY not found - search functionality will be limited")
except Exception as e:
print(f"⚠️ Could not initialize search tool: {e}")
# Initialize memory
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
Gradio聊天機器人邏輯
- is_plan_content:透過檢查多個關鍵詞(例如星期幾、膳食名稱和鍛鍊對比)來確定給定文字是否包含詳細的健身或營養計劃。有助於將計劃與健身相關的非正式對話區分開來。
- format_plan_for_text:將原始健身計劃文字格式化為更簡潔的部分,同時保留標題、列表和段落,以提高可讀性並適合在聊天或電子郵件中分享。
- chat_function:管理 FitCoach 聊天流程。分步收集使用者資訊(使用者健身目標、膳食偏好),呼叫 AI 代理生成自定義鍛鍊和膳食計劃,並安全處理錯誤以確保聊天流程不中斷。
----------- GRADIO CHATBOT LOGIC ------------
def is_plan_content(text: str) -> bool:
"""Check if the text contains a fitness plan with detailed content"""
if not text or len(text.strip()) < 100: # Too short to be a complete plan
# Check for common plan indicators
'workout plan', 'exercise routine', 'training program',
'meal plan', 'nutrition plan', 'diet plan', 'weekly schedule',
'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday',
'sets x reps', 'rest between sets', 'warm up', 'cool down',
'day 1', 'day 2', 'day 3', 'day 4', 'day 5', 'day 6', 'day 7',
'breakfast', 'lunch', 'dinner', 'snacks', 'meals', 'nutrition',
'exercise', 'workout', 'training', 'routine', 'program', 'plan'
# Check for multiple indicators to reduce false positives
text_lower = text.lower()
matching_indicators = [ind for ind in plan_indicators if ind in text_lower]
# Require at least 3 matching indicators to consider it a plan
return len(matching_indicators) >= 3
----------- GRADIO CHATBOT LOGIC ------------
def is_plan_content(text: str) -> bool:
"""Check if the text contains a fitness plan with detailed content"""
if not text or len(text.strip()) < 100: # Too short to be a complete plan
return False
# Check for common plan indicators
plan_indicators = [
'workout plan', 'exercise routine', 'training program',
'meal plan', 'nutrition plan', 'diet plan', 'weekly schedule',
'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday',
'sets x reps', 'rest between sets', 'warm up', 'cool down',
'day 1', 'day 2', 'day 3', 'day 4', 'day 5', 'day 6', 'day 7',
'breakfast', 'lunch', 'dinner', 'snacks', 'meals', 'nutrition',
'exercise', 'workout', 'training', 'routine', 'program', 'plan'
]
# Check for multiple indicators to reduce false positives
text_lower = text.lower()
matching_indicators = [ind for ind in plan_indicators if ind in text_lower]
# Require at least 3 matching indicators to consider it a plan
return len(matching_indicators) >= 3
----------- GRADIO CHATBOT LOGIC ------------
def is_plan_content(text: str) -> bool:
"""Check if the text contains a fitness plan with detailed content"""
if not text or len(text.strip()) < 100: # Too short to be a complete plan
return False
# Check for common plan indicators
plan_indicators = [
'workout plan', 'exercise routine', 'training program',
'meal plan', 'nutrition plan', 'diet plan', 'weekly schedule',
'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday',
'sets x reps', 'rest between sets', 'warm up', 'cool down',
'day 1', 'day 2', 'day 3', 'day 4', 'day 5', 'day 6', 'day 7',
'breakfast', 'lunch', 'dinner', 'snacks', 'meals', 'nutrition',
'exercise', 'workout', 'training', 'routine', 'program', 'plan'
]
# Check for multiple indicators to reduce false positives
text_lower = text.lower()
matching_indicators = [ind for ind in plan_indicators if ind in text_lower]
# Require at least 3 matching indicators to consider it a plan
return len(matching_indicators) >= 3
注意:我在文章中只展示了部分程式碼。完整的程式碼可以在這裡找到。
使用者介面
說到使用者介面,您可以使用 Streamlit 或 Gradio 等解決方案來簡化操作。我選擇了 Gradio,因為它允許我建立一個精緻的 Web 應用,該應用具有自定義設計、自動更新以及快速響應的介面,非常適合健康和健身應用。點選此處檢視原始碼。

Langchain用例
- 客戶支援機器人:建立一個可以搜尋客戶支援知識庫以查詢客戶問題答案的助手。
- 搜尋輔助聊天機器人:Curse 對映到 Google 和維基百科等即時知識來源。
- 文件問答:允許使用者上傳 PDF 檔案並自動檢索帶有引文的準確答案。
- 資料操作助手:允許使用者上傳和瀏覽電子表格中的資料,同時提出與資料相關的問題。
- 內容生成工具:生成內容,包括部落格、電子郵件或社交媒體帖子。
- 多智慧體系統:建立 AI 智慧體可以協作或專門處理任務的系統。
小結
總而言之,人工智慧不僅僅是技術;它關乎如何利用技術的力量來改善我們日常生活的內在運作!無論是塑造體形、健康飲食還是保持動力,設計專屬於您的私人健身教練都是人工智慧如何支援和激勵我們,同時又讓我們對自己的行為負責,最終實現目標的完美範例。最棒的是,您無需成為技術專家即可開始構建應用程式!市面上有許多工具,例如用於開發的 LangChain、用於 AI 功能的 OpenAI 以及用於部署智慧應用程式的 Gradio,等等,它們可以幫助任何人構建專屬的智慧應用程式。健身的未來,以及生活的其他諸多領域,都已觸手可及!
評論留言