Gemini Live API 实时健身指导应用
开发者用 Gemini 实时语音 API 构建无需手动输入的健身助手。演示了实时多模态 API 的实际应用。
开发者用 Gemini 实时语音 API 构建无需手动输入的健身助手。演示了实时多模态 API 的实际应用。
本文是对谷歌 AI Studio 多模态挑战赛的参赛作品
AI Personal Trainer 是一款实验性健身应用,采用"语音优先"的设计理念,能将你的智能手机变成一个交互式的运动伙伴。该应用主要通过语音命令进行控制,让你可以专注于锻炼而不是盯着屏幕。
我要解决的问题:
已实现的功能:
🎤 语音程序创建:与 AI 对话以创建个性化的锻炼计划
🧠 实时音频交互:锻炼中进行双向语音通信
📊 完整的数据库系统:用于存储计划、训练课程和进度的系统
📈 分析仪表板:可视化进度追踪和性能洞察
📅 Google Calendar 集成:自动将锻炼添加到日历中
🎯 混合架构:结合对话速度和分析准确性
💻 GitHub 仓库:ai-personal-trainer
感谢 @aquascript-team 帮助制作视频!
开发工作直接在 Google AI Studio 中开始,我在那里尝试了不同的多模态交互方法。
// Connection to live audio dialog
sessionRef.current = await clientRef.current.live.connect({
model: 'gemini-2.5-flash-preview-native-audio-dialog',
callbacks: {
onopen: () => setConnectionStatus('connected'),
onmessage: async (message) => {
// Processing user speech
if (message.serverContent?.inputTranscription) {
const userText = message.serverContent.inputTranscription.text;
onTranscript(userText);
}
// Playing AI response
const audio = message.serverContent?.modelTurn?.parts[0]?.inlineData;
if (audio && outputAudioContextRef.current) {
await playAudioResponse(audio);
}
}
},
config: {
systemInstruction: createDynamicPrompt(),
responseModalities: [Modality.AUDIO],
outputAudioTranscription: {}, // <--- Enable LLM transcription
inputAudioTranscription: {}, // <--- Enable user transcription
speechConfig: {
voiceConfig: { prebuiltVoiceConfig: { voiceName: 'Orus' } }
}
}
});
// Precise interpretation of user commands
export const interpretWorkoutCommand = async (transcript: string): Promise<{ command: 'log_set' | 'get_form_tip' | 'chat_message', data: { reps?: number, weight?: number, text?: string } | null }> => {
const prompt = `You are an AI assistant interpreting voice commands from a user during a workout. The user's voice transcript is: "${transcript}".
Your task is to analyze the transcript and classify it into one of the following commands, extracting relevant data.
POSSIBLE COMMANDS:
1. 'log_set': The user is reporting the completion of a set. They might mention repetitions (reps) and/or weight.
- Keywords: "done", "finished", "log it", "reps", "weight", "kilos", numbers.
- Example Transcripts: "Okay, 12 reps at 50 kilos", "I'm done", "8 reps", "log 90 pounds".
2. 'get_form_tip': The user is asking for advice on their exercise form.
- Keywords: "form", "technique", "how do I do this", "am I doing it right".
- Example Transcripts: "check my form", "what's the technique for this".
3. 'chat_message': The user is saying something else, likely a question or comment for the AI coach. This is the default if no other command fits.
- Example Transcripts: "how many sets left", "I'm feeling tired", "what's the next exercise".
Respond in JSON format with "command" and optional "data".
- For 'log_set', 'data' should be an object with optional 'reps' and 'weight' numbers.
- For 'get_form_tip', 'data' should be null.
- For 'chat_message', 'data' should be an object with the original transcript as 'text'.
Return ONLY the JSON object.
Example Responses:
- Transcript: "10 reps at 80 kg" -> { "command": "log_set", "data": { "reps": 10, "weight": 80 } }
- Transcript: "how do I do this right?" -> { "command": "get_form_tip", "data": null }
- Transcript: "what's the next exercise?" -> { "command": "chat_message", "data": { "text": "what's the next exercise?" } }
`;
try {
const result = await ai.models.generateContent({
model: "gemini-2.5-flash",
contents: prompt,
...
已实现:使用 Gemini Live API SDK 的 client.live.connect 建立持续的双向流传输
独特性:就像打电话一样运作——你可以随时打断并获得即时响应
User: "Did eight reps with sixty kilos, felt pretty easy"
Dialog Model → "Great! Logged 8 reps with 60 kg. Should we increase the weight?"
Analysis Model →
{
"command": "log_set",
"data": {
"reps": 8,
"weight": 60
}
}
已实现的架构:
锻炼计划 (/programs/{programId}):
{
"name": "Strength program, 12 weeks",
"createdBy": "userId",
"workouts": {
"day1": {
"dayName": "Chest and triceps",
"exercises": [
{
"exerciseId": "bench_press",
"name": "Barbell bench press",
"sets": [{"reps": 8, "weight": 60}],
"rest": 120
}
]
}
}
}
详细的训练课程 (/sessions/{sessionId}):
{
"userId": "user123",
"date": "2024-01-15T10:00:00Z",
"programId": "strength_program_001",
"workoutId": "day1_chest",
"duration": 5400, // seconds
"voiceTranscript": "Complete log of conversation with AI...",
"performedSets": {
"set001": {
"exerciseId": "bench_press",
"setNumber": 1,
"reps": 8,
"weight": 62.5,
"timestamp": "2024-01-15T10:15:30Z"
}
}
}
已实现:直接与 Google Calendar API 集成
export const scheduleWorkouts = async (workouts: Workout[], accessToken: string): Promise<void> => {
if (!workouts || workouts.length === 0) {
throw new Error("No workouts to schedule.");
}
const schedulePromises = workouts.map(workout => {
const startTime = getNextWorkoutDate(workout.dayOfWeek);
const endTime = new Date(startTime.getTime() + 60 * 60 * 1000); // Assume 1-hour duration
const event = {
'summary': `Workout: ${workout.dayName}`,
'description': `Your scheduled workout session.\n\nExercises:\n- ${workout.exercises.map(e => e.name).join('\n- ')}`,
'start': {
'dateTime': startTime.toISOString(),
'timeZone': Intl.DateTimeFormat().resolvedOptions().timeZone,
},
'end': {
'dateTime': endTime.toISOString(),
'timeZone': Intl.DateTimeFormat().resolvedOptions().timeZone,
},
};
return fetch('https://www.googleapis.com/calendar/v3/calendars/primary/events', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(event),
});
});
await Promise.all(schedulePromises);
};
AI 理解特定的词汇:
集合日志识别 (log_set):AI 在用户语音中搜索数字和关键字(如"reps"、"times"、"weight"、"pounds")以自动填充已完成集合的数据。
处理请求和评论 (get_form_tip, chat_message):不包含直接日志数据的短语被处理为教练请求或简单评论。
已实现:一个专门的分析部分,提供对锻炼性能和进度追踪的详细洞察。
语音识别准确性:AI 在实时对话中不总是能正确解释命令,尤其是在背景噪音的环境下
命令执行:模型有时会"忘记"在收到响应后在应用中执行特定操作
传统健身应用强制你做出选择:要么进行数据追踪,要么专注于锻炼。多模态方法解决了这个两难困境:
其结果是一个理解自然语音并适应每个用户独特风格的健身伙伴。
注意:尽管我的 AI 教练相当聪明和激励人心,我们强烈建议在健康问题上保持常识。💪
我对谷歌 AI Studio 多模态挑战赛的组织者表示深深的感谢,他们为我提供了与尖端人工智能技术进行实验的独特机会。
感谢对象:
这个项目得以实现,得益于尖端工具的生态系统和谷歌 AI 创建的支持性开发者社区。
MVP 开发工具:React、TypeScript、Firebase、Google Calendar API 和 Google Gemini 多模态能力
由 Premananda 用 ❤️ 打造