1. 为什么要关心环境
在常规 Web 开发中,迟早会出现三件套:本地开发、测试服务器和 production 环境。在 ChatGPT Apps 的世界里也一样,不过有一个额外的转折:客户端(ChatGPT)始终在云端,即使你在“本地”开发。
如果一切只在你的笔记本上通过一个临时隧道地址运行,会出现一些恼人的问题。首先,URL 不断变化,你很难记住此刻 Dev Mode 具体指向哪个 endpoint。其次,性能和网络情况与真实生产环境不一样。再次,本地环境往往使用不同的密钥、不同的服务,整体像是“平行世界”。
另一方面,长期“在生产上活着”也不好。任何改动都可能突然影响真实用户的使用场景,尤其当你已经接了 Stripe、OAuth 或 ACP 支付等集成。从法律与政策角度看也有问题:在真实用户身上做实验,并不是走向 Store 的最佳道路。
因此本讲的目标是:在脑海中形成一个简单但严格的方案——有 local dev,有 staging,有 production,并且有 Dev Mode 作为把 ChatGPT 引导到目标环境的方式。不要把一切混在一起,变成“我的带隧道的笔记本,它有时突然变身生产环境”。
2. ChatGPT Apps 的特殊之处:客户端始终在云端
在传统的 SPA 应用里,你常常会在本地同时跑起前端和后端:浏览器连 localhost,后端也在 localhost,一切都在同一台机器内愉快通信。
在 ChatGPT Apps 里并非如此。客户端(ChatGPT + 你的小部件)始终运行在 OpenAI 的基础设施中。即使你的应用代码运行在笔记本上,请求路径也大致如下:
sequenceDiagram
participant User as 用户
participant ChatGPT as ChatGPT(云端)
participant Tunnel as HTTPS 隧道
participant App as 你的 Next.js + MCP
User->>ChatGPT: 消息 / 点击小部件
ChatGPT->>Tunnel: 向 App URL 发送 HTTPS 请求
Tunnel->>App: 代理到 localhost
App-->>Tunnel: 响应(UI/JSON)
Tunnel-->>ChatGPT: 响应
ChatGPT-->>User: 更新后的聊天 + 小部件
即使“只是在本地测试”,你也已经身处一个分布式系统:有云端客户端、有网络、有隧道,也有你的本地服务器。
这点很重要,因为:
- 本地环境不是“全部都在我本机”。它其实是“云端 → 隧道 → 本地服务器”。
- 当你后来加入 staging 和 production 时,唯一的差别只是 ChatGPT 把请求发往哪里:隧道、staging 域名,还是生产域名。
3. Local dev:你当前的拓扑长什么样
我们来看看,这个通用拓扑在你现在的项目里可能是怎样的。
在完成第 2–6 模块之后,你很可能是这样的:
- 通过命令 npm run dev 启动的 Next.js 开发服务器(通常是 http://localhost:3000)。
- 本地 MCP 服务器(通常是独立进程,例如 http://localhost:2091)。
- HTTPS 隧道(ngrok、Cloudflare Tunnel 等),把你的 Next.js/HTTP endpoint 发布到公网地址,例如 https://abc123.ngrok.app。
在 ChatGPT 里通过 Dev Mode 指定这个公网 URL,ChatGPT 就会访问你的应用。这一切共同构成了local dev 环境。
local dev 的主要特性:
- 本地环境提供非常快的反馈回路。你在 VS Code 里改代码,Next.js 进行热重载,小部件几秒内就能刷新。
- 这里你可以大胆破坏、使用 mock 数据、测试用密钥和“奇怪”配置。
- 没有真实用户,除了你几乎没人知道这个 URL。
通常拓扑大致如下:
graph LR
subgraph 开发者笔记本
Next[Next.js 开发服务器]
MCP[MCP 服务器]
end
ChatGPT((ChatGPT(云端)))
Tunnel[[HTTPS 隧道]]
ChatGPT --> Tunnel --> Next
Next --> MCP
为了不在 local/staging/production 之间混淆,最好让应用本身“知道”自己当前所处的环境。就代码而言,明确标识当前是 dev 环境会很有用。最简单的一步,就是加一个小的环境配置模块。
例如,新建文件 app/config/env.ts:
// app/config/env.ts
export type AppEnv = 'local' | 'staging' | 'production';
export const APP_ENV: AppEnv =
(process.env.NEXT_PUBLIC_APP_ENV as AppEnv) ?? 'local';
export const isProd = APP_ENV === 'production';
这里我们:
- 定义了一个带类型的环境枚举。
- 读取变量 NEXT_PUBLIC_APP_ENV(稍后你会在 dev/staging/prod 中设置不同的值)。
- 默认视为 'local',以便本地开发“开箱即用”。
这还不会触发部署,但已经提供了一个锚点:你的代码知道自己运行在哪个环境。
接着,你可以在小部件里显示当前环境,避免混淆。
// app/components/EnvBadge.tsx
import { APP_ENV } from '../config/env';
export function EnvBadge() {
return <span>ENV: {APP_ENV}</span>;
}
这样一个小徽章能显著减少“我现在在 staging 还是在 prod?”的误判,尤其在小部件外观一致的情况下。
4. Staging:production 的总彩排
Staging 环境就是“production 的彩排”。它不再是你的笔记本和 dev 服务器,而是远程服务器或 Vercel 上的一个构建部署。
对 ChatGPT 来说,staging 看起来几乎和 production 一样:一个稳定、好用的 HTTPS endpoint,域名类似 https://staging.giftgenius.app,并且:
- 代码已经构建完成(npm run build 成功);
- 使用与生产相似的环境变量(相同的名称与格式),但用测试密钥;
- 接入相同的外部服务(Stripe sandbox、测试 OAuth 账号等);
- 网络拓扑更接近生产(例如同类型数据库与相同区域)。
在 ChatGPT Apps 语境下,staging 的价值在于:
首先,staging 适合跑端到端场景。例如:用户在 ChatGPT 中 → ChatGPT 启动你的应用 → 小部件向用户提问 → 调用 MCP tool 访问外部 API → 返回推荐结果 → 小部件展示结果。这种流程在本地通过随机隧道时可能是一种表现,而在 staging 环境——又是另一种:延迟、网络与资源都更接近真实。
其次,staging 便于测试那类在本地跑起来“心里发怵”的集成。例如支付:Stripe、ACP/Instant Checkout 等。在 staging 你配置测试密钥、测试 webhook,用“成年人的方式”走全流程,但不涉及真金白银。
再次,staging 是团队联调之地。如果你有多位开发、设计、QA、产品,他们需要一个与任何人的笔记本无关、也不依赖谁的隧道在线的公共 URL。
可以把 staging 想成这样:
graph LR
ChatGPT((ChatGPT Cloud))
AppStaging["GiftGenius Staging https://staging.giftgenius.app"]
ChatGPT --> AppStaging
在 https://staging.giftgenius.app 背后,你可以运行 Next.js、MCP 服务器、staging 数据库等。
本讲不展开 Vercel 部署的细节,那是后续主题的内容。此刻只需接受一个事实:staging 是一个独立环境,在配置与 ChatGPT 的访问方式上尽量与 production 保持一致。
5. Production:面向真实用户与真实交易
Production 环境是现实用户和真金白银到来的地方。这里不再存在“我先在 main 里改一把,看看会怎样”——任何修改都应当是经过深思熟虑、测试充分,且最好具备回滚能力的。
Production 域名必须稳定。它不是随机的 ngrok URL,而是像 https://giftgenius.app 这样的正规域名。你会在 App 的 Store 设置里填写这个地址:当用户在 ChatGPT Store 里找到并启动你的应用时,ChatGPT 就会访问这个 endpoint。
对 production 通常会有更高要求:
- 稳定性。 低错误率、可预期的响应时间、在负载下正确工作。我们在后续模块会谈到 SLO/SLI,但直觉上就是“应用应当几乎总是可用,并且几乎总是响应迅速”。
- 安全性。 只使用必要的密钥、最小化权限、谨慎处理 PII 与资金。
- 限制实验方式。 不要在工作时间里“又把 dev 服务器重启了”;通过特性开关、A/B 或独立的 dev/staging 环境做实验,而不是直接改动生产服务器。
在 ChatGPT 的语境中,production 不再是 Dev Mode 的故事,而是已发布的 App:它对用户可见,经过审核,需要足够可靠,不能在审核和用户面前“出丑”。
6. Dev Mode 与发布版 App:各自绑定关系
现在说最常见的混淆点:ChatGPT 的 Dev Mode 并不是“一个单独的环境”。它更像是路由开关:在你测试应用时,ChatGPT 此刻应当访问哪个 URL。
在 Dev Mode 里你可以:
- 通过隧道连接本地应用;
- 连接到 staging 环境;
- 甚至临时指向 production(通常不建议)。
形式上,Dev Mode 会告诉 ChatGPT:“这是我 App 的清单(manifest),这是我的 MCP/Apps SDK endpoint 的 URL。请在我启动这个应用时使用它。”而你可以更改这个 URL。
当你将应用发布到 Store 后,App 会有一个官方的 production endpoint。真实用户只能使用它,想要更换则需要新版本、重新审核,等等。
在实践中,一个合理的教学项目方案可能如下:
graph TD
subgraph Dev Mode
DevApp["GiftGenius Dev App
(Dev Mode)"]
end
subgraph Store
ProdApp["GiftGenius
(Store App)"]
end
UserDev[你 / 团队] --> DevApp
UserProd[真实用户] --> ProdApp
DevApp -->|URL 隧道| LocalEnv[Local dev
https://abc123.ngrok.app]
DevApp -->|staging URL| StagingEnv[Staging
https://staging.giftgenius.app]
ProdApp -->|prod URL| ProdEnv[Production
https://giftgenius.app]
你可以把 Dev Mode 下的 GiftGenius Dev 配置为通常指向本地(通过隧道),需要时切换到 staging。Store 里的 GiftGenius 则严格绑定 production URL。
有时团队还会单独做一个用于 QA 的 App,例如 GiftGenius Staging,它只指向 staging URL。如果有较大的测试团队,这样很方便;在本课程中,一个 dev App 已足够。
请习惯这样思考:Dev Mode 是给你和团队的私人沙盒,可以改 URL、改元数据、重启隧道;而 Store 里的 production App 只看 production,并遵循更严格的规则。
7. Git 分支、域名与 ChatGPT App 的关联
环境不仅是服务器,还是代码分支和ChatGPT 中 App 的配置。迟早你会希望仅凭一个 URL 或 App 名称,就能判断当前跑的是哪一版代码。
一个简单的最小化做法如下。
在开发每个功能时,使用 feature/* 分支,例如 feature/new-recommendation-algo。代码在本地跑 + 隧道。ChatGPT 的 Dev Mode 通常指向同一个 dev endpoint,你们轮流在上面跑本地版本。给每个 feature 分支建一个单独的 App 没必要。
在发布前做集成时,可以建立 develop 或 staging 分支。该分支中的内容自动部署到 staging 环境,例如 Vercel 的预览 URL:https://giftgenius-staging.vercel.app。你可以为它创建一个单独的 Dev Mode App,或定期把公共的 Dev App 切到这个 URL。
main(或 master)分支仅包含已测试的代码。它会部署到 production URL,并绑定到 Store 中的 GiftGenius 应用。
整体关系大致如下:
| 环境 | Git 分支 | URL | ChatGPT App |
|---|---|---|---|
| Local dev | |
|
GiftGenius Dev(Dev Mode) |
| Staging | |
|
GiftGenius Dev 或 GiftGenius Staging |
| Prod | |
|
GiftGenius(Store) |
还记得 APP_ENV(位于 app/config/env.ts)吗?这里的 'local'/'staging'/'production' 就直接对应“环境”这一列:local dev 以 APP_ENV=local 运行;staging 部署以 APP_ENV=staging 运行;production 则以 APP_ENV=production 运行。
这张小表并不是官僚主义,而是为了杜绝“现在这个域名到底跑的是哪版代码?”之类的盲调。
在代码里也可以加强这种绑定。例如,在调试模式下显示不止 ENV,还显示提交/分支信息:
// app/config/buildInfo.ts
export const BUILD_COMMIT = process.env.NEXT_PUBLIC_BUILD_COMMIT ?? 'dev';
export const BUILD_ENV = process.env.NEXT_PUBLIC_APP_ENV ?? 'local';
// app/components/BuildInfo.tsx
import { BUILD_COMMIT, BUILD_ENV } from '../config/buildInfo';
export function BuildInfo() {
return <small>Build: {BUILD_ENV}@{BUILD_COMMIT}</small>;
}
如果你在部署时把提交的 SHA 写入 NEXT_PUBLIC_BUILD_COMMIT,小部件就会如实显示当前运行的是哪次提交。在 staging/prod 中,这有时能节省数小时的排查。
8. 迷你实践:画出你自己的环境拓扑
在进入 Vercel 与日志之前,很有必要“在餐巾纸上”画一下你的环境拓扑。可以是 README.md 里的 mermaid 图、白板草图,或笔记本上的一张图。
对于我们的教学项目 GiftGenius,拓扑可以是这样:
graph TD
subgraph ChatGPT
DevMode["Dev Mode
(你和团队)"]
Store["Store
(真实用户)"]
end
subgraph Servers
Local[Local dev
隧道 → localhost]
Staging[Staging
staging.giftgenius.app]
Prod[Production
giftgenius.app]
end
DevMode --> Local
DevMode --> Staging
Store --> Prod
课后对你很有帮助的小练习:
- 列出你已经拥有的所有环境:带隧道的本地、也许还有早期的 Vercel 部署等。
- 在旁边标注对应会部署到哪里的 Git 分支。
- 再旁边标注有哪些 ChatGPT Apps(或连接器)指向了哪里。
- 用箭头标出 ChatGPT 到各服务器的访问路径。
如果你不是单兵作战,建议在仓库里加一个 architecture/environments.md 文件。这样能显著降低“我们的 staging 掉了,但没人知道它的 URL 到底是啥”的概率。
为了把这些和你的应用连起来,你现在就可以在 Dev Mode 里创建一个 App——GiftGenius Dev,并约定:默认指向本地环境的隧道;当你想测试一个完整版本时,临时把它切到 staging URL。后续课程你会学会把 staging/prod 部署到 Vercel,并用环境变量把它们串起来。
把一切归纳成一句话:把环境与 Dev Mode 当作你的 App 的坐标系。本地用于快速开发;staging 用于总彩排;production 面向真实用户;Dev Mode 是你在它们之间的开关,而不是一个独立的神奇环境。
9. 使用环境与 Dev Mode 时的常见错误
错误 1:只靠 localhost + 隧道就当作生产。
这种做法看似方便:“要什么 staging 和 prod,我的隧道能用,ChatGPT 也连得上。”但隧道 URL 不稳定,网络特性也不同,而且整个方案依赖一台笔记本。一旦你需要 OAuth 回调、Stripe webhook 或 MCP Gateway,缺少正规的 staging/prod 会带来很多痛苦。
错误 2:把 Dev Mode 误当成一个独立环境。
很多人认为“我有 Dev Mode,就等于我有 dev 环境”。实际上 Dev Mode 只是在告诉 ChatGPT 去哪里:隧道、staging,甚至 production。Dev Mode 是客户端设置,不是服务器。服务器端的环境(local/staging/prod)需要你自己创建:部署代码、配置域名与环境变量。
错误 3:把 Dev Mode 指向 production,顺手“测一测”。
技术上可行:你可以在 Dev Mode 里填 production URL,然后像在本地一样“玩一玩”App。问题是,你开始在真实用户、真实数据,甚至真实交易上做测试。小部件或工具的任何错误都可能影响生产用户,而且你一开始甚至难以定位来源。更好的做法是让 Dev Mode 指向 dev/staging,把 Store 里的 App 用于 production。
错误 4:缺少“分支 ↔ 环境 ↔ URL ↔ App”的清晰地图。
如果团队里没人能立刻答出哪个分支部署到 staging、staging 的 URL 是什么、哪个 ChatGPT App 指向了它,这几乎注定会混乱。于是有人说“我这边能跑”,staging 上不行,prod 上又是第三种情况。一张简单的表或 markdown 文档能多次拯救你。
错误 5:低估 local dev 与 staging 的差异。
在本地你跑的是 dev 服务器,你有一套密钥、一套服务与一种网络。在 staging 中,代码已经构建,运行在不同的环境里,限制、超时与路由也不同。如果只在本地测试、staging 只是“摆设”,关键问题往往会在生产中爆发。要习惯这条链路:先本地开发,再 staging 验证,最后才发布到 production。
错误 6:出了问题就只“问 ChatGPT”,忽略了环境拓扑。
有时出问题后,开发者开始“问 ChatGPT 发生了什么”,而不是先看那张图:哪个 App 绑定了哪个 URL、是哪一个环境挂了、日志在哪里。我们今天的环境拓扑,是下一讲“系统化调试”的基石:先看日志、用 MCP 检查器,再考虑模型本身。
GO TO FULL VERSION