Skip to content
This page has been auto-translated and may contain errors.View in English

在本地运行 Vercel AI SDK 课程

使用本页在你的电脑上运行提取的客服代理。你需要将其 Express 服务器连接到 OpenAI 和课程中创建的 Supabase 数据,然后修改一个启动脚本,使 Node 从 .env 加载这些值。嵌入和向量数据库指南在相同类型的 Supabase 向量设置上运行一个相关项目。

准备工作

安装受支持的 Node.js LTS 版本。推荐使用 Node 24。你还需要:

  • 一个具有账单和模型访问权限的 OpenAI API 密钥;
  • 课程中创建的 Supabase 项目和数据;
  • 一个 Supabase 密钥,或旧版的 service_role 密钥。

密钥和 service_role 密钥绕过行级安全性。它们只能放在这个服务器端的 .env 中,永远不要放在浏览器代码里。参见 Supabase API 密钥

Juno准备工作 安装 Node.js LTS,然后从课程中收集你的 OpenAI 密钥以及 Supabase 项目和服务器端密钥。那个 Supabase 密钥就像整个数据库的主密钥,所以它永远不应该靠近浏览器代码。
Juno准备工作 服务器用提升权限的密钥读取 Supabase,而没有课程中创建的行的话检索会毫无用处。两个服务和这些数据都必须在你首次运行之前存在。Supabase 密钥只能保留在 Express 环境中;它永远不应该在浏览器中。
Juno准备工作 service-role 密钥绕过行级安全性,所以持有它的人可以用服务器的完整数据库权限行动。只在 Express 中保留它;如果把它交付给浏览器,每个打开开发者工具的访问者都会获得那种访问权。我见过这个错误恰好到达过生产环境一次,一次已经足够了。

打开和安装项目

在包含 package.json 的提取文件夹中打开终端,然后安装锁定的包:

bash
$ cd path-to-your-downloaded-project
$ npm ci
Juno打开和安装项目 在包含 package.json 的提取文件夹中运行 npm ci。它安装课程构建时使用的确切包版本,所以这里没有任何需要配置或猜测的。
Juno打开和安装项目npm ci 从包含的锁定文件安装,所以你的版本与提取的客服项目完全匹配。这种匹配很重要,因为 SDK 和模型代码是一起测试的;漂移的依赖项是你没有订购的一个调试会话。
Juno打开和安装项目 在做任何其他事情之前从下载的锁定文件安装。然后只修改启动脚本,这样不相关的依赖项更新就不会被误认为是环境文件的修复;把这两者分开为我节省的晚上比我能数出来的都多。

使 Node 加载 .env

打开 package.json 并将启动脚本从:

json
"start": "node server.js"

改为:

json
"start": "node --env-file=.env server.js"

代码使用旧版变量名 SUPABASE_SERVICE_ROLE_KEY。你可以在该变量中放入当前的 Supabase 密钥,而无需重命名代码。

package.json 旁边创建 .env

dotenv
OPENAI_API_KEY=your-openai-api-key
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your-server-side-secret-key
PORT=3000

创建 .gitignore

txt
.env
node_modules/

Git 手册在忽略文件和良好习惯中涉及这个习惯。

将 Supabase 密钥保留在服务器上

永远不要用 VITE_ 前缀重命名服务密钥或将其移到 client.js 中。它具有提升的数据库访问权限。不要提交 .env 或在 ZIP 中共享它。

Juno使 Node 加载 .env--env-file=.env 添加到启动脚本,用所有四个值创建文件,并将其保留在 Git 之外。Supabase 密钥始终保留在服务器上。一个包含 .env.gitignore 是我在任何项目中创建的第一个文件,因为我用艰难的方式学到了这一点。
Juno使 Node 加载 .env Node 本身不会读取 .env,所以提取的项目启动时没有配置。--env-file 标志用一个内置功能解决这个问题:没有额外的包,值保留在 Express 读取它们的服务器端。
Juno使 Node 加载 .env 内置标志在 server.js 运行之前加载四个现有变量名。因为只有 Express 读取它们,OpenAI 和 Supabase 密钥永远不会出现在发送到浏览器的代码中。没有重命名,没有 dotenv 依赖项,没有新的攻击面。

保留提供的模型

下载的 constants.js 使用 gpt-4o 进行答案生成和分类,使用 text-embedding-3-small 进行嵌入。本地设置不需要更改任何一个模型。除非 OpenAI 对你的项目拒绝了某个模型,否则保持它们不变。如果你后来替换了某个模型,测试完整的代理流程,并确认新的查询嵌入与存储的 Supabase 向量保持兼容。嵌入交换可能会悄悄失败:具有相同维数但不同向量空间的模型会返回较差的匹配,且没有错误,所以在更改嵌入模型后重新生成存储的向量。

Juno保留提供的模型 对于本地设置,两个提供的模型名称都保持不变;它们不是本页上任何东西失败的原因。如果你的 OpenAI 项目拒绝了一个,在编辑代码之前检查当前的模型列表,并在之后重新测试整个代理。
Juno保留提供的模型 答案、分类和嵌入角色有不同的兼容性需求,所以模型交换永远不是一行改动。替换品必须与此 SDK 配合工作,并保持与已存储在 Supabase 中的向量兼容。
Juno保留提供的模型 具有相同维数但不同向量空间的嵌入模型会悄悄破坏检索:查询返回较差的匹配,且完全没有错误。这就是为什么嵌入交换意味着重新生成存储的向量,而不仅仅是编辑 constants.js。无声降级是我最尊重的失败模式,因为没有任何东西会提醒你。

运行代理

bash
$ npm start

打开 http://localhost:3000,或使用你在 .env 中设置的端口。用 Ctrl+C 停止服务器。

页面加载表明本地服务器正在运行。提出一个由课程数据涵盖的问题:有用的答案表明 Supabase 检索和 OpenAI 生成也在工作。如果答案失败,服务器终端会在链中打印第一个失败的调用:嵌入、检索或生成。RAG 章节解释了为什么代理首先在检索的课程数据中基础其答案。

Juno运行代理 运行 npm start 并在浏览器中打开本地地址。页面加载证明服务器运行;完整答案还需要 OpenAI 和课程的 Supabase 数据,所以把那些视为两个单独的胜利。
Juno运行代理 把服务器启动和答案生成视为单独的检查:运行的服务器但答案失败是服务问题,不是代码问题。用 Ctrl+C 停止,如果默认地址忙碌,在 .env 中设置 PORT
Juno运行代理 当答案失败时,服务器终端会在链中打印第一个失败的调用:嵌入、检索或生成。读取那个错误并在触及任何配置之前归因于失败。当有三个外部调用在场时猜测配置是如何晚上消失的;我从经验中说话。

故障排除

Missing OPENAI_API_KEY 确认启动脚本包含 --env-file=.env.envpackage.json 旁边,变量名完全匹配。

Supabase 身份验证或关系错误: 确认 URL 和服务器端密钥属于同一项目,然后完成课程的模式和数据步骤。不要为此服务器操作用可发布密钥替换。

端口 3000 已在使用:.env 中更改 PORT,重启服务器,并打开新端口。

页面加载但答案失败: 检查服务器终端的第一个提供商或数据库错误。本地启动本身不会验证任何外部服务。

Juno故障排除 缺失的密钥指向启动脚本或 .env,Supabase 错误指向项目或其数据,加载的页面且答案失败指向外部服务。先将症状与层匹配,修复通常会自己说出来。
Juno故障排除 读取第一个服务器终端错误,不是最后一个;后面的失败通常是从它引发的。那第一个错误将环境加载、数据库授权、端口绑定和 OpenAI 访问分离成四个不同的修复。
Juno故障排除 按顺序遍历服务器错误:Node 是否加载了 .env,Supabase 是否返回了课程数据,OpenAI 是否生成了答案。并且永远不要交换进一个浏览器安全的 Supabase 密钥来平静错误;那会隐藏缺失的表或策略问题而不是修复它。