使用LLaMA 3.1、Firebase和Node.js,构建一个音控的智能厨房应用程序

译者 | 布加迪审校 | 重楼这篇指南逐步介绍了创建一个自动化的厨房助理的过程,附有语音命令、实时购物清单管理以及食谱建议。 我在本教程中将介绍创建一个智能厨房应用程序(Chent),它可以根据个性化偏好简化杂货清单管理。 该应用程序通过语音命令操作,简化了人机交互和添加商品。

使用LLaMA 3.1、Firebase和Node.js,构建一个音控的智能厨房应用程序

译者 | 布加迪

审校 | 重楼

这篇指南逐步介绍了创建一个自动化的厨房助理的过程,附有语音命令、实时购物清单管理以及食谱建议。

我在本教程中将介绍创建一个智能厨房应用程序(Chent),它可以根据个性化偏好简化杂货清单管理。该应用程序通过语音命令操作,简化了人机交互和添加商品。对于那些只需说出需求就能快速创建购物清单的用户来说,这是理想的选择。

该项目使用LLaMA 3.1用于自然语言处理(NLP)以解释语音输入、使用Firebase用于实时数据库存储和用户验证,并使用Node.js处理后端逻辑和集成。用户可以输入命令以添加商品,设置饮食偏好,甚至指定数量,该应用程序可以智能化生成满足这些要求的购物清单。

我在本教程中将介绍从设置Firebase、配置LLaMA以操控语音命令到实时存储和管理购物清单的整个过程。

搭建开发环境

在开始为smart-kitchen-app应用程序编写代码之前,我们需要搭建好工作环境。

1. 安装Node.js和npm

第一步是安装Node.js和npm。访问Node.js网站:https://nodejs.org/en,获取你电脑的运行系统所需的长期支持版本。然后,按照安装步骤操作。

2. 使用Next.js创建项目

启动终端,进入到你想要创建项目的位置。之后,运行这些命令:

  • npx create-next-app@latestsmart-kitchen app(使用@latest标志,npm可获得最新版本的Next.js启动设置。)
  • cd smart-kitchen-app

它将创建一个新的Next.js项目,并将你带到项目路径。在安装过程中,你会看到许多配置选择,设置如下:

  • 你想使用TypeScript吗?不
  • 你想使用ESLint吗?是
  • 你想使用Tailwind CSS吗?不
  • 你想使用src/目录吗?不
  • 你想使用App Router(应用路由器)吗?是
  • 你想定制默认导入别名吗?不

3. 安装Firebase和Material-UI

在项目目录下,执行以下命令:

复制

Shell
1 npm install @mui/material @emotion/react @emotion/styled firebase

设置Firebase

  • 在Firebase控制台上启动一个新项目。
  • 项目创建完毕后,点击“添加应用程序”,选择web平台(</>)。
  • 当你注册应用程序时给它取个名字,比如“smart-kitchen-app”。
  • 复制Firebase设置文件。之后,这个副本很有用。

4. 创建Firebase配置文件

在项目的根目录下创建一个名为Firebase .js的新文件,并添加以下代码,将占位符换成你项目的真实Firebase设置:

复制

JavaScript
1 import { initializeApp } from "firebase/app";
2 import { getAnalytics } from "firebase/analytics";
3 import { getAuth } from "firebase/auth";
4 import { getFirestore } from "firebase/firestore";
5 
6 const firebaseConfig = {
7 apiKey: "YOUR_API_KEY",
8 authDomain: "YOUR_PROJECT_ID.firebaseapp.com",
9 projectId: "YOUR_PROJECT_ID",
10 storageBucket: "YOUR_PROJECT_ID.appspot.com",
11 messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
12 appId: "YOUR_APP_ID"
13 };
14
15 const app = initializeApp(firebaseConfig);
16 const analytics = getAnalytics(app);
17 export const auth = getAuth(app);
18 export const db = getFirestore(app);

如何在OpenRouter中创建API令牌?

我们将使用来自OpenRouter的免费版本LLaMA 3.1,为此,我们需要获得API令牌。以下是获得API令牌的几个步骤:

第1步:注册或登录到OpenRouter

  • 访问OpenRouter的官方网站:进入到OpenRouter.ai。
  • 如果你还没有帐户,创建一个帐户。你可以用电子邮件注册,也可以使用谷歌、GitHub或其他OAuth提供商。
  • 如果你已经有了OpenRouter帐户,请登录。

第2步:导航进入到API密钥设置

  • 登录后,进入到仪表板。
  • 在仪表板中,查找API或开发人员工具部分。
  • 点击API密钥或令牌选项。

第3步:生成新的API密钥

  • 在API密钥部分,你应该看到“生成新API密钥”的按钮或链接。
  • 点击“生成”按钮来创建一个新的API密钥。
  • 可能会要求你给API密钥取一个名字。如果你有多个不同项目的API密钥(比如“Smart-Kitchen App Key”),这有助于你井然有序地组织密钥。

第4步:复制API密钥

  • 生成API密钥后,它将显示在屏幕上。立即复制API密钥,因为一些服务在你离开页面后可能不会再次显示它。
  • 将API密钥安全地存储在环境配置文件中(比如.env.local)。

第5步:将API Key添加到.env.local文件

  • 在Next.js项目中,打开.env.local文件(如果没有,创建一个)。
  • 添加下面这行:

OPENROUTER_API_KEY = your-generated-api-key-here

确保将your-generated-api-key-here换成你复制的实际的API密钥。

第6步:在应用程序中使用API密钥

  • 现在你已经将API密钥存储在.env. local文件中,就可以在应用程序中使用它。
  • 通过服务器端代码中的process.env.OPENROUTER_API_KEY或发出API请求时访问密钥。确保密钥安全,避免将其暴露给生产级应用程序中的客户端。

构建核心逻辑,导入LLaMa 3.1以创建智能厨房应用程序响应

创建一个名为app的新文件夹,并在其下创建一个名为Extract的子文件夹,文件名为route.ts,按照下面给出的代码操作:

复制

TypeScript
1 import { NextRequest, NextResponse } from 'next/server';
2
3 export async function POST(req: NextRequest) {
4  const { prompt } = await req.json();
5
6  // Check if the API key is available
7  const apiKey = process.env.OPENROUTER_API_KEY;
8  if (!apiKey) {
9    console.error('API key not found');
10   return NextResponse.json({ error: "API key is missing" }, { status: 500 });
11  }
12
13  const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
14    method: "POST",
15    headers: {
16      "Authorization": `Bearer ${apiKey}`,
17      "Content-Type": "application/json",
18    },
19    body: JSON.stringify({
20      model: "meta-llama/llama-3.1-8b-instruct",
21      messages: [
22        { role: "system", content: "You are an assistant that extracts information and outputs only valid JSON. Do not include any explanation or extra information. Only respond with a JSON object that has the following structure: {\"itemName\": \"string\", \"quantity\": \"number\"}." },
23        { role: "user", content: `Extract the item name and quantity from the following command: "${prompt}"` }
24      ],
25    })
26  });
27
28  if (!response.ok) {
29    console.error('LLaMA API request failed with status:', response.status);
30    return NextResponse.json({ error: "Failed to process command with LLaMA" }, { status: 500 });
31  }
32
33  const completion = await response.json();
34  const rawJson = completion.choices[0].message.content;
35  console.log('Raw response from LLaMA:', rawJson); // Detailed logging of the raw response
36
37  // Extracting JSON from the response content
38  const startIndex = rawJson.indexOf('{');
39  const endIndex = rawJson.lastIndexOf('}') + 1;
40
41  if (startIndex === -1 || endIndex === -1) {
42    console.error('Failed to find valid JSON in LLaMA response');
43    return NextResponse.json({ error: "Failed to extract JSON from LLaMA response" }, { status: 500 });
44  }
45
46  const jsonString = rawJson.substring(startIndex, endIndex);
47  console.log('Extracted JSON string:', jsonString); // Logging extracted JSON string
48
49  try {
50    const parsedData = JSON.parse(jsonString);
51    console.log('Parsed data:', parsedData); // Logging the parsed data
52
53    const { itemName, quantity } = parsedData;
54
55    if (!itemName || !quantity) {
56      console.error('Missing fields in parsed data:', parsedData);
57      return NextResponse.json({ error: "Invalid data received from LLaMA" }, { status: 500 });
58    }
59
60    return NextResponse.json({ itemName, quantity });
61  } catch (error) {
62    console.error('Error parsing JSON from LLaMA response:', error);
63    return NextResponse.json({ error: "Failed to parse JSON from LLaMA response" }, { status: 500 });
64  }
65}
66

这段代码定义了一个POST API端点,该端点使用LLaMA 3.1模型从用户的语音命令中提取特定信息(商品名称和数量),专注于提供JSON格式的结构化数据。

首先,它接收一个含有提示的请求,并检查是否有所需的API密钥(OPENROUTER_API_KEY)。如果缺少API密钥,它会给出错误响应。然后将请求发送到OpenRouter AI API,要求AI仅返回有效的JSON,其中包含从用户输入中提取的字段itemName和quantity。

记录并检查来自AI的响应,以确保返回有效的JSON对象。如果响应包含有效的JSON,则解析字符串,并检查字段的完整性。如果itemName和quantity都存在,数据则以JSON格式返回给用户;否则,将记录并返回相应的错误。这段代码确保AI助理提供结构化、可操作的响应,适合智能厨房应用程序中的购物清单创建。

在app文件夹下,创建一个名为Llama的子文件夹,文件名为route.ts,按照下面给出的代码操作:

复制

TypeScript
1 import { NextRequest, NextResponse } from 'next/server';
2
3 const systemPrompt = `
4 You are a helpful and friendly AI kitchen assistant. Your role is to assist users in their kitchen tasks, providing guidance, suggestions, and support in a clear and concise manner. Follow these guidelines:
5
6 1. Provide friendly, helpful, and respectful responses to all user inquiries, ensuring a positive experience.
7 2. When asked for a recipe, suggest simple and delicious recipes based on the ingredients the user has available. Keep the instructions clear and easy to follow.
8 3. Assist with creating grocery lists by accurately adding items mentioned by the user. Confirm each addition and offer to help with more items.
9 4. Handle common kitchen-related tasks such as suggesting recipes, checking pantry items, offering cooking tips, and more.
10 5. If the user is unsure or needs help deciding, offer suggestions or ask clarifying questions to better assist them.
11 6. Recognize when the user is trying to end the conversation and respond politely, offering a warm closing message.
12 7. Avoid overly technical language or complex instructions. Keep it simple, friendly, and approachable.
13 8. Provide accurate and practical information, such as cooking times, ingredient substitutions, or food storage tips.
14 9. Tailor responses to the user's preferences and dietary restrictions whenever mentioned.
15 10. Ensure every interaction feels personal, supportive, and designed to help the user enjoy their cooking experience.
16
17 Remember, your goal is to make cooking and kitchen tasks easier, more enjoyable, and stress-free for the user.
18 `;
19
20 export async function POST(req: NextRequest) {
21   try {
22     const { command } = await req.json();
23     console.log('Received command:', command);
24
25     const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
26      method: "POST",
27      headers: {
28        "Authorization": `Bearer ${process.env.OPENROUTER_API_KEY}`,
29        "Content-Type": "application/json",
30      },
31      body: JSON.stringify({
32        model: "meta-llama/llama-3.1-8b-instruct",
33        messages: [
34          { role: "system", content: systemPrompt },
35          { role: "user", content: command }
36        ],
37      })
38    });
39
40    if (!response.ok) {
41      throw new Error(`Failed to fetch from OpenRouter AI: ${response.statusText}`);
42    }
43
44    const completion = await response.json();
45    const responseMessage = completion.choices[0].message.content;
46    console.log('Received response:', responseMessage);
47
48    return NextResponse.json({ response: responseMessage });
49  } catch (error) {
50    console.error("Error processing request:", error);
51    return NextResponse.json({ error: "Error processing request" }, { status: 500 });
52  }
53}

这段代码使用Next.js为厨房助理应用程序设置了POST API端点,使其能够利用OpenRouter AI和LLaMA 3.1模型通过语音输入处理用户命令。终端先建立一个引导AI行为的系统提示,确保交互友好、清晰、得到支持,特别是与食谱建议、购物清单创建和烹饪技巧等厨房任务相关方面。一收到POST请求,系统从请求主体部分提取用户的命令,并将其与系统提示一起转发给OpenRouter AI API。

来自AI的响应根据命令予以定制,随后以JSON格式提供给用户。如果在过程中发生错误,将记录错误消息,并返回500状态码,确保无缝的用户体验。

在app文件夹下,创建一个名为Recipe的子文件夹,文件名为route.ts,并按照下面给出的代码操作:

复制

TypeScript
1 import { NextRequest, NextResponse } from 'next/server';
2
3 export async function POST(req: NextRequest) {
4   const { availableItems } = await req.json();
5
6   // Check if the API key is available
7   const apiKey = process.env.OPENROUTER_API_KEY;
8   if (!apiKey) {
9     console.error('API key not found');
10    return NextResponse.json({ error: "API key is missing" }, { status: 500 });
11  }
12
13   const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
14    method: "POST",
15    headers: {
16      "Authorization": `Bearer ${apiKey}`,
17      "Content-Type": "application/json",
18    },
19    body: JSON.stringify({
20      model: "meta-llama/llama-3.1-8b-instruct",
21      messages: [
22        { role: "system", content: "You are an assistant that provides specific recipe suggestions based on available ingredients. Only respond with a recipe or cooking instructions based on the provided ingredients." },
23        { role: "user", content: `Here are the ingredients I have: ${availableItems}. Can you suggest a recipe using these ingredients?` }
24      ],
25    })
26  });
27
28  if (!response.ok) {
29    console.error('LLaMA API request failed with status:', response.status);
30    return NextResponse.json({ error: "Failed to process recipe request with LLaMA" }, { status: 500 });
31  }
32
33  const completion = await response.json();
34  const recipe = completion.choices[0].message.content.trim();
35
36  return NextResponse.json({ recipe });
37 }
38

这段代码建立了一个POST API端点,该端点利用LLaMA 3.1模型生成适合用户手头配料的食谱建议。请求包括系统提示指示AI充当厨房助手,特别是根据给定的配料推荐食谱。AI会收到消息,包括现有配料列表,并请求食谱建议。

一收到成功的API请求,来自AI的响应(包括配方)将被提取并以JSON格式提供。如果操作失败,代码将记录错误并提供表明出现失败的消息。这段代码保证了AI根据用户在智能厨房应用程序中手头的配料提供量身定制的食谱建议。

构建智能厨房应用程序的核心组件

第1步:导入和状态设置

导入必要的依赖项并设置状态变量,以管理用户输入和Firebase集成。

复制

TypeScript
1 "use client";
2 import React, { useState } from 'react';
3 import { db } from './firebase';
4 import { collection, updateDoc, arrayUnion, doc, getDoc } from 'firebase/firestore';
5

第2步:组件声明和初始状态

定义KitchenAssistant组件,并为用户命令、响应和语音识别初始化状态。

复制

TypeScript
1 export default function KitchenAssistant() {
2   const [command, setCommand] = useState('');
3   const [response, setResponse] = useState('');
4   const [recognition, setRecognition] = useState<SpeechRecognition | null>(null);
5

第3步:处理语音命令

这段代码处理用户的语音命令。它验证命令是否有结束语句(比如“thank you”)来结束对话,或者它是否有诸如“建议食谱”或“向食品储藏室添加商品”之类的短语。根据给定的命令,它激活相关的操作(比如“建议食谱”或“发声朗读”)。

复制

TypeScript
1 const handleVoiceCommand = async (commandText: string) => {
2   setCommand(commandText.toLowerCase());
3   const closingStatements = ["no thank you", "thank you", "that's all", "goodbye", "i'm done"];
4 const isClosingStatement = closingStatements.some(statement => commandText.includes(statement));
5
6   if (isClosingStatement) {
7     const closingResponse = "Thank you! If you need anything else, just ask. Have a great day!";
8     setResponse(closingResponse);
9     speak(closingResponse);
10    return;
11  }
12
13   if (commandText.includes("suggest a recipe")) {
14    await suggestRecipe();
15  } else if (commandText.includes("add items to pantry")) {
16    setResponse("Sure, start telling me the items you'd like to add.");
17    speak("Sure, start telling me the items you'd like to add.");
18  }
19 };
20

第4步:一般命令处理

该函数使用发送到/api/llama端点的POST请求来处理一般命令,端点通过LLaMA模型处理命令。来自AI的响应被设置为response状态,并使用speak函数大声朗读。

复制

TypeScript
1 const handleGeneralCommand = async (commandText: string) => {
2   try {
3     const res = await fetch('/api/llama', {
4     method: 'POST',
5     headers: { 'Content-Type': 'application/json' },
6     body: JSON.stringify({ command: commandText }),
7    });
8    const data = await res.json();
9    setResponse(data.response);
10    speak(data.response);
11  } catch (error) {
12    setResponse("Sorry, I couldn't process your request.");
13    speak("Sorry, I couldn't process your request.");
14  }
15 }; 
16

第5步:为购物清单添加商品

该函数将商品添加到Firebase中的购物清单中。它估算过期日期,更新Firestore文档,并通过设置响应并大声朗读响应来确认已添加给用户。

复制

TypeScript
1 const addToGroceryList = async (itemName: string, quantity: number) => {
2   try {
3     const expiryDate = new Date();
4     expiryDate.setDate(expiryDate.getDate() + 7);
5     const docRef = doc(db, 'grocery-list', 'Available Grocery Items');
6     await updateDoc(docRef, {
7       items: arrayUnion({
8         itemName,
9         quantity,
10        expiryDate: expiryDate.toISOString(),
11      }),
12    });
13    const responseText = `Grocery list updated. You now have: ${itemName} (${quantity}).`;
14    setResponse(responseText);
15    speak(responseText);
16  } catch (error) {
17    setResponse("Sorry, I couldn't add the item to the list.");
18    speak("Sorry, I couldn't add the item to the list.");
19  }
20 };
21

第6步:语音识别

该函数初始化浏览器的语音识别API,监听用户输入,并使用handleVoiceCommand处理已识别的文本。它设置了语言,确保持续监听。

在这段代码中,我使用WebSpeech API的SpeechRecognition接口来启用应用程序中的语音命令功能。

复制

TypeScript
1 const startRecognition = () => {
2 const SpeechRecognition = window.SpeechRecognition || (window as any).webkitSpeechRecognition;
3  const newRecognition = new SpeechRecognition();
4  newRecognition.lang = 'en-US';
5  newRecognition.onresult = (event: any) => {
6    const speechToText = event.results[0][0].transcript;
7    handleVoiceCommand(speechToText);
8  };
9  newRecognition.start();
10  setRecognition(newRecognition);
11 };
12

这一行检查浏览器中是否存在原生SpeechRecognition API,或者对于像Chrome这样通过webkit前缀支持它的浏览器,退回到webkitSpeechRecognition。这个API允许应用程序监听用户的语音输入,将其转换成文本,然后将其作为命令来处理。

复制

JavaScript
1 const SpeechRecognition = window.SpeechRecognition || (window as any).webkitSpeechRecognition;

使用这个API,该应用程序可以通过语音命令与用户进行交互,支持诸多功能,比如将商品添加到购物清单、建议食谱或检索食品储藏室的食材,使体验更自动化、交互式。

你可以使用Whisper或所选择的任何其他语音识别机制。

创建前端组件

这个TSX布局为你的智能厨房应用程序定义了用户界面的结构。该设计使用了Tailwind CSS类,以实现迅即响应、又不失美感的样式。

复制

TypeScript
1 return (
2   <div className="flex min-h-screen">
3     <div className="w-1/2 background-image"></div>
4     <div className="w-1/2 flex flex-col justify-center p-8 bg-white bg-opacity-80">
5       <h1 className="text-4xl font-bold mb-8 text-indigo-500">Welcome to Chent</h1>
6       <div className="button-group flex justify-between mb-8">
7        <button onClick={startRecognition} className="bg-green-500">Start Listening</button>
8       <button onClick={interruptSpeech} className="bg-yellow-500">Interrupt</button>
9      </div>
10      <div className="transcript">{command}</div>
11      <div className="response">{response}</div>
12    </div>
13  </div>
14
  • 布局是两栏设计:左侧显示背景图像,右侧包含应用程序的主要功能。
  • 右边部分包括一个标题、两个操作按钮和两个显示动态文本的区域:一个用于用户的语音命令(Transcript),另一个用于AI助理的响应(Response)。
  • 界面干净、简洁、响应迅速,允许用户与智能厨房应用程序高效交互,同时保持外观漂亮的设计。

一旦你完成了它,就会有一个类似下面的界面:

使用LLaMA 3.1、Firebase和Node.js,构建一个音控的智能厨房应用程序

以上就是我们创建智能厨房应用程序的整个过程。我在本例中使用了LLaMA 3.1语言模型,不过你可以随意试用自己选择的任何其他模型。

原文标题:Building a Voice-Powered Smart Kitchen App Using LLaMA 3.1, Firebase, and Node.js,作者:Vaibhavi Tiwari

给TA打赏
共{{data.count}}人
人已打赏
理论

使用 MediaPipe 检测面部五官

2024-12-24 7:00:00

理论

颠覆GPT!Meta推出大型概念模型LCM: 从“猜词”到“理解”

2024-12-24 9:29:16

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
今日签到
搜索