在本地运行向量嵌入和向量数据库
在这个页面上,你可以在自己的电脑上运行从向量嵌入和向量数据库课程中提取出来的 ReelRecs 电影聊天机器人,使用你自己的 OpenAI 密钥和课程中构建的 Supabase 数据库。与 Scrimba 编辑器有三处不同:需要按名称安装两个包,config.js 中的读取操作需要 Vite 的语法,还有项目使用的聊天模型已经停用了。
你需要先准备的东西
安装受支持的 Node.js LTS 版本。推荐使用 Node 24,它已经包含了 npm。
检查这两个命令都能工作。你的版本号可能不同:
$ node --version
v24.18.0
$ npm --version
11.18.0你还需要三个值:
- **一个 OpenAI API 密钥。**创建一个你完成后会删除的密钥:这个项目直接从浏览器调用 OpenAI,所以密钥会显示在页面上。
- **你的 Supabase 项目 URL,**来自保存电影和你在课程中创建的
match_movies函数的 Supabase 项目。 - **该项目的可发布密钥,**以
sb_publishable_开头。如果你的项目只显示较早的anon密钥,就用那个。千万不要在这里使用密钥或service_role密钥。
node --version 和 npm --version 都能打印出版本号。 然后收集你的 OpenAI 密钥、Supabase 项目 URL 和它的可发布密钥。这个 Supabase 项目必须是你在课程中填充电影的那个。
match_movies。如果那个 Supabase 项目是空的,聊天机器人能运行,但会回答"抱歉,我不知道答案"。 安装包
在提取的文件夹中打开终端。这是包含 package.json、config.js 和 index.js 的文件夹:
$ cd path-to-your-extracted-projectpackage.json 只列出了 Vite,但 config.js 也导入了 OpenAI 和 Supabase 包。Scrimba 的编辑器为你安装这些;在你的电脑上,需要按名称指定它们,npm 就会把它们和 Vite 一起安装:
$ npm install openai @supabase/supabase-js
added 25 packages, and audited 26 packages in 9snpm 最后会报告它添加了多少个包。
package.json 的文件夹中打开终端,然后运行 npm install openai @supabase/supabase-js。npm 最后会说它添加了多少个包。 如果 npm 说找不到 package.json,说明你在错误的文件夹里。
package.json 列出的内容,指定两个包会把它们加到列表里,所以一条命令会安装全部三个。单纯运行 npm install 只会给你 Vite。 npm start 仍然会提供页面:Vite 的启动扫描只会打印一个警告,当浏览器请求 config.js 时导入会失败,显示为页面上的一个错误。 修改 config.js 和 index.js
Scrimba 的编辑器为你填入 process.env.OPENAI_API_KEY、process.env.SUPABASE_API_KEY 和 process.env.SUPABASE_URL。浏览器没有 process,Vite 也不会创建一个,所以 config.js 中的全部四处读取都必须改变;OpenAI 密钥被读取两次。Vite 只把名字以 VITE_ 开头的变量交给浏览器代码,这就是为什么每个名字都要加上那个前缀。
用以下内容替换 config.js 的全部内容:
import OpenAI from 'openai';
import { createClient } from "@supabase/supabase-js";
/** OpenAI config */
if (!import.meta.env.VITE_OPENAI_API_KEY) throw new Error("OpenAI API key is missing or invalid.");
export const openai = new OpenAI({
apiKey: import.meta.env.VITE_OPENAI_API_KEY,
dangerouslyAllowBrowser: true
});
/** Supabase config */
const privateKey = import.meta.env.VITE_SUPABASE_API_KEY;
if (!privateKey) throw new Error(`Expected env var SUPABASE_API_KEY`);
const url = import.meta.env.VITE_SUPABASE_URL;
if (!url) throw new Error(`Expected env var SUPABASE_URL`);
export const supabase = createClient(url, privateKey);只有四处读取与你下载的文件不同。两个 Supabase 的错误信息仍然使用旧的变量名;让它们保持原样。
然后打开 index.js。聊天请求要求 gpt-4,OpenAI 已经停用了它。在 getChatCompletion 中,将:
model: 'gpt-4',改为:
model: 'gpt-4o-mini',保留 index.js 中的另一个模型 text-embedding-ada-002 不变。你数据库中的电影就是用那个模型转换成数字的,你的问题也必须用同样的方法转换成数字,才能和它们进行比较。
config.js 粘贴覆盖旧的,然后在 index.js 中把 gpt-4 改成 gpt-4o-mini。 不要动 text-embedding-ada-002;你的数据库依赖于它。
config.js 时,它会把每个 import.meta.env.VITE_... 读取替换为来自 .env 的值。没有前缀的名字会对浏览器代码保持隐藏,作为 undefined 到达,并触发 config.js 中的检查。 config.js 在加载时抛出异常,早于 index.js 附加其表单处理程序。所以一个坏的读取会留下一个看起来正常的页面,但按 Enter 会重新加载它,你的问题就消失了。 将你的密钥加到 .env
在 package.json 旁边创建 .env 文件,包含你的三个值:
VITE_OPENAI_API_KEY=your-openai-api-key
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_API_KEY=your-publishable-key在 package.json 旁边创建 .gitignore 文件并添加:
.env
node_modules/到达 Git 仓库的密钥即使在你删除文件后仍会留在其历史记录中。Git 手册在忽略文件和良好习惯中讲解了这个习惯。
这些密钥在浏览器中是可见的
Vite 会把每个 VITE_ 值写进它发送给浏览器的代码中,所以打开这个页面的任何人都能读到你的 OpenAI 密钥。把这个版本保留在你自己的电脑上:千万不要部署或分享它,也不要提交 .env。你发布的版本需要一个后端来在服务器上保留 OpenAI 密钥,就像Vercel AI SDK 项目那样。
package.json 旁边创建一个叫 .env 的文件,包含你的三个值,每行一个,然后在 .gitignore 中列出 .env,这样 Git 永远不会保存它。 .env 然后监视它:保存一个改动会打印 .env changed, restarting server...,打开的页面会用新值重新加载。 VITE_ 前缀是一个暴露白名单,不是秘密存储:OpenAI 密钥位于 Vite 提供的模块中以及浏览器发送给 OpenAI 的每个请求的头部。 启动聊天机器人并提问
启动开发服务器:
$ npm startVite 可能首先打印一个以 (!) Your Vite config uses features 开头的警告。它不会停止应用。另一个警告 (!) Failed to run dependency scan 表示两个包丢失了:回到安装步骤。在警告下方,Vite 会打印要打开的地址:
VITE v8.3.4 ready in 218 ms
➜ Local: http://localhost:5173/打开确切的 Local 地址。页面显示 ReelRecs 和一个说"How can I help?"的框。问一个你数据库中的电影,然后按 Enter。Thinking... 出现,几秒后答案替代它。关于你的电影的答案表示 OpenAI 和 Supabase 都能工作。
如果你的问题消失了,没有其他事情发生,页面没有收到你的密钥;下面的第一个故障排除项与终端行相匹配。
如果页面显示"Sorry, something went wrong. Please try again.",查看运行 npm start 的终端。Vite 会把浏览器的错误复制到那里,在包含 Error in main function. 的行上;下面的故障排除与每条信息相匹配。同一行也在你的浏览器开发者工具中(F12,或在 Mac 上 Chrome 中的 Cmd+Option+J),在控制台标签上。
用 Ctrl+C 停止项目。如果在它运行时保存 .env,Vite 会重新启动自己(.env changed, restarting server...),页面用新值重新加载。
npm start,如果出现的话忽略配置警告,然后打开 Local 地址。问一个你的电影之一,等待答案。 如果你得到"Sorry, something went wrong",读终端中最新的一行。
match_movies 找到最近的电影,OpenAI 写出回复。任何一个失败都显示相同的抱歉信息,所以错误行告诉你要修复哪一个。 main 在一个 try/catch 中包装全部三个请求,只记录错误的信息。Vite 8 把浏览器的那个 console.error 转发到终端,这就是为什么终端显示浏览器失败。 故障排除
**终端显示 (!) Failed to run dependency scan,浏览器显示 Failed to resolve import "openai" from "config.js":**两个包没有安装。停止项目,在包含 package.json 的文件夹中运行 npm install openai @supabase/supabase-js,然后再次启动项目。
**你按 Enter 时问题消失,终端显示 ReferenceError: process is not defined:**一个 process.env 读取仍在 config.js 中。用上面显示的版本替换文件并保存它。
**你的问题消失,终端显示 OpenAI API key is missing or invalid 或 Expected env var:**Vite 没有找到值。检查 .env 是否在 package.json 旁边,每个名字是否以 VITE_ 开头,然后保存它;Vite 重新启动自己,页面重新加载。这些信息使用旧的变量名,但 .env 中的名字是 VITE_ 那些。
**Error in main function. 401 Incorrect API key provided:**OpenAI 没有接受这个密钥。从你的 OpenAI 账户再次复制它,检查它没有被删除。
**Error in main function. 429 You exceeded your current quota:**这个密钥能用,但账户没有额度可花。在你的 OpenAI 账户的 Billing 下添加额度,然后再次提问。
Error in main function. Cannot read properties of null (reading 'map'):OpenAI 能工作,但 Supabase 返回了错误而不是电影。检查项目 URL,使用可发布密钥,检查 match_movies 在同一个项目中存在。要了解确切的原因,打开开发者工具的网络标签,再次提问,然后点击名为 match_movies 的请求。
**聊天机器人回答"Sorry, I don't know the answer.":**一切都运行了,但 Supabase 找不到足够接近你的问题的电影。问一个在你的表中的电影。如果每个问题都得到这个答案,检查 Supabase 表是否有行,行级安全是否让可发布密钥读取它们。
**Error in main function. 后跟一条命名模型的信息(gpt-4 或 gpt-4o-mini):**检查 index.js 要求的是 gpt-4o-mini 而不是 gpt-4。在 2027 年 1 月 14 日之后,OpenAI 也会停止提供 gpt-4o-mini;从那时起,使用它的弃用页面命名的替代品。
Error in main function. 开头的行,把它后面的词与上面的粗体标题相匹配。如果你的问题只是消失,首先查看 config.js 和 .env。 config.js 中的读取、OpenAI 密钥、Supabase,然后你的数据。修复你遇到的第一个;下一个只在它消失后才显示。 findNearestMatch 忽略 Supabase 返回的 error,并在它旁边的 null 上调用 data.map。所以错误的 URL、被拒绝的密钥和丢失的函数都读作相同的 null;网络标签保存真正的原因。 
