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

本地运行 Vercel AI SDK 课程 ​

使用本页面在你的电脑上运行 Vercel AI SDK 课程中提取的客户支持代理,用你自己的 OpenAI 密钥和在课程中构建的 Supabase 项目获取答案。一个 Node 服务器运行整个项目:它既提供聊天页面,也回答问题,所以你的密钥始终保存在服务器上,不会流向浏览器。与 Scrimba 编辑器不同的一点是:Node 不会自动读取 .env 文件,所以启动脚本需要一个标志。

你需要先准备什么 ​

安装受支持的 Node.js LTS 版本。推荐使用 Node 24,它包含 npm。

检查两个命令都能正常工作。你的版本号可能不同:

bash
$ node --version
v24.18.0
$ npm --version
11.18.0

你还需要三个值:

  • 一个 OpenAI API 密钥,来自已设置账单的账户,因为 OpenAI 会为每个答案计费;
  • 你的 Supabase 项目 URL,在项目的连接对话框中显示;
  • 该项目的密钥,以 sb_secret_ 开头,位于设置 > API 密钥中。

使用课程中的 Supabase 项目。代理会通过项目的 match_documents 函数搜索存储其中的 Scrimba 帮助文档。如果你不再拥有该项目,服务器启动时仍需要 Supabase URL 和密钥,使用新项目的值也可以。只有 Scrimba 问题才会用到 Supabase,所以一般性问题仍然能通过网络搜索获得答案,而 Scrimba 问题则找不到任何搜索结果。

旧版 service_role 密钥也可用,直到 Supabase 在 2026 年底停用它。参考 Supabase API 密钥。

Juno你需要先准备什么 检查 node --version 和 npm --version 都能打印出版本。然后收集三样东西:你的 OpenAI 密钥、Supabase 项目 URL 和该项目的密钥。

使用你在课程中构建的 Supabase 项目;新项目没有代理可以搜索的文档。

Juno你需要先准备什么 账单很重要,因为每个问题都会产生 OpenAI 请求费用。一般性问题需要一次请求:模型搜索网络并在同一请求中给出答案。Scrimba 问题需要三次请求:一次选择知识库,一次将问题转换为 Supabase 的嵌入向量,一次生成答案。
Juno你需要先准备什么config.js 使用密钥构建 Supabase 客户端,所以 match_documents 调用完全跳过行级安全检查。这是安全的,因为客户端存在于服务器上;Supabase 也会拒绝从浏览器发送的密钥。

安装依赖包 ​

在提取的文件夹中打开终端,该文件夹包含 package.json 和 server.js,然后安装课程使用的确切版本:

bash
$ cd path-to-your-extracted-project
$ npm ci
added 94 packages, and audited 95 packages in 4s

npm 还会报告这些包中存在的一些漏洞。它们不会阻止项目在你的电脑上运行。不要运行 npm audit fix;它会升级课程代码所针对的包。

Juno安装依赖包 在包含 package.json 的文件夹中运行 npm ci。它会最后报告添加了多少个包。

如果 npm 说需要现有的 package-lock.json 才能安装,说明你在错误的文件夹中。

Juno安装依赖包npm ci 从 package-lock.json 安装,而不是从 package.json 中的版本范围,所以你会得到课程使用的 AI SDK、Supabase 客户端和 Express 版本,而不是这些范围允许的最新版本。
Juno安装依赖包package.json 要求 ai ^5.0.29;lockfile 将其固定为 5.0.43,包含 @ai-sdk/openai 2.0.30。当两个文件不一致时 npm ci 会拒绝运行,所以编辑的依赖会在这里失败,而不是在问题处理到一半时。

修改启动脚本 ​

在 Scrimba 中,编辑器将你的密钥交给服务器。在你的电脑上,Node 只在你要求时才读取 .env,而 config.js 如果 OpenAI 密钥缺失会立即停止服务器。打开 package.json 并将启动脚本从:

json
"start": "node server.js"

改为:

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

这个标志内置于 Node,所以没有额外的东西需要安装。

Juno修改启动脚本 在 package.json 中,按照所示准确添加 --env-file=.env 到启动脚本,然后保存文件。项目中的其他内容都不变。
Juno修改启动脚本 有了这个标志,Node 会在 server.js 运行前将 .env 中的每一行复制到 process.env,这是 config.js 查找密钥的地方。没有它,config.js 中的第一个检查会停止服务器。
Juno修改启动脚本 你的 shell 中已经设置的变量会覆盖 .env 中同名的变量。在你的终端配置文件中导出的旧的 OPENAI_API_KEY 会被使用,而不是文件中的那个。

将你的密钥添加到 .env ​

在 package.json 旁边创建 .env 文件,包含你的三个值:

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

代码仍然使用旧名称 SUPABASE_SERVICE_ROLE_KEY;将你的密钥放在那里,不要重命名任何东西。它必须是密钥或旧版 service_role 密钥,不能是可发布密钥,后者用于浏览器代码,没有服务器需要的访问权限。PORT 是可选的:没有它的话,服务器使用 3000。

在 package.json 旁边创建 .gitignore 并添加:

txt
.env
node_modules/

进入 Git 仓库的密钥即使在你删除文件后也会保留在其历史记录中。Git 手册在忽略文件和好习惯中介绍了这个习惯。

将 Supabase 密钥保存在服务器上

你的 Supabase 密钥可以读取和改变数据库中的一切。在这个项目中只有服务器读取它,这是唯一安全的地方:永远不要将它复制到 client.js,也不要提交或分享 .env。

Juno将你的密钥添加到 .env 在 package.json 旁边创建一个名为 .env 的文件,在前三行输入你的值,然后在 .gitignore 中列出 .env,这样 Git 就永远不会保存它。
Juno将你的密钥添加到 .env Node 在服务器启动时读取 .env 一次。在对它进行任何更改后,用 Ctrl+C 停止服务器并重新启动它。
Juno将你的密钥添加到 .env 浏览器只与你自己的服务器通信:client.js 向 /api/ask 发送问题并获得答案及其来源。浏览器下载的任何内容中都没有密钥。

启动代理并提问 ​

启动服务器:

bash
$ npm start

> start
> node --env-file=.env server.js

Server listening at http://localhost:3000

打开 http://localhost:3000,或者你在 .env 中设置的端口。Support AI Chatbot 页面有一个问题输入框和一个提问按钮。提一个关于 Scrimba 的问题,例如如何重置你的密码。当代理工作时,页面显示思考中…,然后显示答案。当答案来自你的 Supabase 文档时,答案下方的查看来源会列出它使用的段落,每个都有相似度分数:OpenAI 和 Supabase 都有效。一般性问题,例如关于新闻的问题,会通过网络搜索得到答案,并列出网页;这只需要 OpenAI。RAG 章节解释了代理为什么在回答前会查找信息。

如果答案显示 I encountered an error while processing your request using tool calling. Please try again later.,页面无法告诉你原因。原因在服务器运行的终端中,在 [ToolBased] Error in RAG process: 这一行下面。

用 Ctrl+C 停止服务器。

Juno启动代理并提问 运行 npm start,等待 Server listening at http://localhost:3000,然后打开那个地址。提一个关于 Scrimba 的问题并等待答案。

如果答案说遇到了错误,看终端而不是页面。

Juno启动代理并提问 页面打开只表示 Express 在提供文件。对于每个问题,模型首先选择一个工具:知识库(需要 Supabase)或网络搜索。查看来源告诉你它选择了哪一个。
Juno启动代理并提问webSearchRetrievalAgent 会捕获问题处理过程中的每个错误并返回该固定的道歉信息,状态码为 200,所以页面永远看不到原因。Supabase 错误会作为工具的结果传给模型,所以你会得到一个没有来源的答案和终端中的 [Tool:KB] 行。

故障排除 ​

服务器立即停止并显示 Missing OPENAI_API_KEY environment variable: Node 没有加载 .env。检查启动脚本是否说 node --env-file=.env server.js 以及 .env 中的名称是否正确是 OPENAI_API_KEY。SUPABASE_SERVICE_ROLE_KEY is missing or invalid 和 SUPABASE_URL is missing or invalid 对这些名称的意思相同。

node: .env: not found: 启动脚本没问题,但 package.json 旁边没有 .env。检查文件是否名称正确是 .env,而不是 .env.txt。

Invalid supabaseUrl: Must be a valid HTTP or HTTPS URL.: SUPABASE_URL 必须是从连接对话框获取的完整地址,以 https:// 开头。

Error: listen EADDRINUSE: address already in use :::3000: 另一个程序正在使用 3000 端口,通常是这个服务器的另一个终端中的早期运行。用 Ctrl+C 停止它,或在 .env 中设置 PORT=3001,重新启动并打开 http://localhost:3001。

答案说它遇到了错误,终端显示 Incorrect API key provided: OpenAI 拒绝了密钥并返回 401。从你的 OpenAI 账户重新复制密钥,检查它没有被删除,然后重启服务器。

终端显示 You exceeded your current quota: 密钥有效,但 OpenAI 账户没有信用。在你的 OpenAI 账户中添加账单,然后再试一次。

Scrimba 问题得到没有来源的答案,终端显示以 [Tool:KB] Error 开头的一行: OpenAI 有效,但 Supabase 没有返回课程文档。检查 URL 和密钥来自同一项目,密钥是密钥或 service_role 密钥而不是可发布密钥,项目仍然有课程文档和其 match_documents 函数。

终端显示模型 gpt-4o 不可用: OpenAI 在 2027 年 1 月 14 日关闭 gpt-4o,并将 gpt-6-luna 命名为其替代品。将 constants.js 中的 ANSWERING_MODEL 改为 OpenAI 弃用页面上的替代品。保持 EMBEDDING_MODEL_NAME 为 text-embedding-3-small:Supabase 中的文档是用它嵌入的,不同的嵌入模型会找到很差的匹配而不会产生任何错误。

Juno故障排除 注意错误出现的位置。如果服务器在 npm start 后立即停止,问题在 package.json 或 .env 中。如果页面用道歉回应,原因在服务器的终端中。
Juno故障排除 故障按顺序出现:Node 加载 .env、Supabase URL 检查、端口,然后每个问题的 OpenAI,然后知识库工具中的 Supabase。修复你遇到的第一个。
Juno故障排除 启动错误来自 config.js,它检查每个变量并在 server.js 加载它时立即构建两个客户端,所以它们停止进程。问题期间的错误被捕获并记录,所以服务器在你修复 .env 时继续运行;重启它以加载更改。