CodeGym /课程 /ChatGPT Apps /ChatGPT Apps 栈架构

ChatGPT Apps 栈架构

ChatGPT Apps
第 1 级 , 课程 1
可用

1. 引言

如果把 ChatGPT App 仅仅当作“又一个 Web 服务器”,很快就会出现架构混乱:这里是 Next.js,那边是 MCP 服务器,某处是代理,另一个地方是 commerce 后端——这些在脑中混作一个“大服务器”。

更划算的做法是从一开始就承认它是一个分层蛋糕:

  • 最上层——我们无法控制但需要适配的 ChatGPT UI;
  • 其下——在 Apps SDK(Next.js 16,React 19)上的小部件,渲染在聊天中;
  • 再下——带有工具(tools/resources/prompts)的 MCP 服务器;
  • 可选——用于编排复杂场景的代理层;
  • 最底层——你的“落地”服务:数据库、外部 API、commerce/ACP(面向 commerce 场景的协议)等。

在课程笔记中,这条路径可以画成一条链:

User ChatGPT Widget Apps SDK MCP Gateway (Auth) Agent Service ACP / Stripe.

现在我们的任务是把这条链变成清晰的心智模型。

2. 栈的总体示意

先看全图,然后逐层拆解。

flowchart TD
    U[ChatGPT 中的用户] --> C["ChatGPT UI 聊天 + Apps 面板"]
    C --> W["你的 App 小部件 (Apps SDK, Next.js)"]
    W --> M["MCP 服务器 (tools/resources/prompts)"]
    M --> AG["代理(们) (Agents SDK,编排)"]
    AG --> B["后端与 ACP 数据库、服务、支付"]

需要注意几件事。

首先,用户只看得到两层:ChatGPT UI 和你的小部件。下面的一切都是“幕后”。

其次,MCP 不是随意的缩写,而是官方标准,Apps SDK 通过它与你的工具交互:服务器需要能枚举 tools、接收 call_tool 并返回一个可在 ChatGPT 中渲染的 UI 资源链接。

第三,Agents 和 ACP 这两个层从形式上看是可选的,但在真实的商业应用里几乎总会出现:有的地方需要规划多步流程,有的地方需要收款。

现在分别拆解每一层。

Insight: ChatGPT 是一个框架

与 ChatGPT 的集成不在单一点上——它分散在大量的集成点上。对程序员来说,这最像在使用一个框架。框架自行决定何时何地调用你的代码,你只需要在正确的位置补上正确的内容。

在 ChatGPT 中确实如此:

  • 小部件——通过 mcp-resources 注册,GPT 自行决定何时展示
  • mcp-tools——GPT 自行决定何时调用
  • product feed——可以通过 mcp-tool 加入到模型,但标准方式是通过 site register merchant
  • ACP/InstantCheckout——独立的 API
  • 认证——独立的 mcp auth 服务器。

3. 层 1 — ChatGPT UI:我们的“宿主”

ChatGPT UI 是 OpenAI 的浏览器(与移动端)界面,用户在其中进行主要对话。这里有熟悉的输入框、消息历史、模型选择按钮以及应用标签页(Store/Composer)。

这层我们不编写。我们无法访问其代码、DOM 与样式。但它设定了边界:

  • 用户在这里“选择”你的应用:显式(通过 Store/Composer)或隐式(模型主动推荐 App);
  • ChatGPT 在这里决定:直接文本回复、调用你的 tool、渲染小部件,或三者同时;
  • 这里有基础 UX 模式:内嵌小部件、全屏模式、PiP 窗口等(详见模块 8)。

从实务角度记住:ChatGPT UI 是我们的宿主应用。我们嵌入其内,而不是相反。GPT 服务器会把你的小部件代码加载到它自己的服务器,清理无关内容,然后在它的聊天中以它自己的域名加载你的代码。

4. 层 2 — Apps SDK 与小部件(Next.js 16 嵌入聊天)

下一层是你的 UI 代码,用 React/Next.js 编写,并使用 Apps SDK。

心智模型很简单:它像一个迷你 SPA,在聊天中作为嵌入小部件渲染。但有几个注意点:

  • 你的代码运行在沙箱中: 受限的 DOM、特别的网络请求规则、用于与 ChatGPT 通信的特殊对象 window.openai (我们会有专门一讲介绍);
  • 小部件不控制对话流程: 用户在公共聊天中输入,模型决定何时调用你的 App,你只能在自己的“框”里回应;
  • Apps SDK 包办了很多工作: 同步小部件状态与对话历史、处理 tool 结果、对接 MCP 等。

对 Next.js 开发者来说,这看起来也很熟悉:有页面/组件、hooks、props。但你会更少地使用传统的 fetch('/api/...') ,而更多依赖于 MCP 服务器定义的工具(tools)和 Apps SDK 的特殊 hooks(后续课程介绍)。

为了更具体一点,回到我们的项目——一个假想的 GiftGenius。这个 App 根据参数帮你挑礼物:送谁、预算多少、什么场合等。

未来 UI 的一个小片段(暂时不涉及 SDK 细节,只是一个想法):

// GiftSummary.tsx — 我们的 App 的一个简单 React 组件
type GiftIdea = {
  id: string;
  title: string;
  price: number;
};

interface GiftSummaryProps {
  ideas: GiftIdea[];
}

export function GiftSummary({ ideas }: GiftSummaryProps) {
  return (
    <ul>
      {ideas.map((idea) => (
        <li key={idea.id}>
          {idea.title} — ${idea.price}
        </li>
      ))}
    </ul>
  );
}

之后该组件不会凭空获得 ideas,而是来自 MCP 服务器工具的结果(ToolOutput)。但在架构层面更重要的是:这些代码都处于“第二层”,并且只负责状态的呈现

5. 层 3 — MCP 服务器:工具与数据的世界

现在继续向下,进入服务端部分。

Model Context Protocol(MCP)是一个标准,描述 LLM 客户端(ChatGPT、Apps SDK、Agents)如何与你的服务器通信。它定义了可以提供哪些工具、它们的输入/输出模式、如何调用,以及还能加载哪些资源/提示词等。

面向 Apps SDK 的最小 MCP 服务器需要会三件事:

  • 返回工具列表(List tools),包括其 JSON Schema 与元数据;
  • 处理工具调用(Call tools)——接收 call_tool 请求,执行业务逻辑并返回结构化结果;
  • 返回 html、js、css、……——可选,如果某个 tool 关联到需要在 ChatGPT 中显示的特定小部件。

一个重要点:MCP 是与传输协议无关的。对 ChatGPT Apps 来说,我们关心它的 HTTP 变体以及可流式/streamable 的实现,但传输细节与消息格式是 MCP 模块(第 6 层)的主题。现在你只需要理解,Apps SDK 在“底层”访问的是 MCP 服务器,而不是任意 REST 端点。

架构上 MCP 层通常是一个独立微服务:

flowchart LR
    subgraph App["你的 ChatGPT App"]
      W["小部件(Next.js + Apps SDK)"]
      M["MCP 服务器 (@modelcontextprotocol/sdk)"]
    end

    W <-- JSON-RPC over HTTP/SSE --> M
    M --> DB[(礼物目录)]
    M --> EXT[外部 API]

在 MCP 服务器内部,你写常规的 TypeScript/Node 代码,使用数据库、队列、第三方 API 等。官方的 MCP TypeScript SDK 会处理 JSON-RPC 的序列化、模式校验与路由。

对我们的 GiftGenius,某个 MCP 工具可以叫做 search_gifts。在 TypeScript 层面,它可以看起来像一个普通函数:

// 伪代码:MCP 服务器内的业务逻辑
export async function searchGifts(params: {
  recipient: string;
  budget: number;
}) {
  // 在这里你会访问数据库/目录
  const items = await findGiftsInCatalog(params);
  return items.slice(0, 10);
}

之后我们会把它包装成带模式描述的 MCP tool,但关键在于:这一层就是你的“正常”后端,只是通过 MCP 对外说话。

6. 层 4 — Agents SDK:复杂场景的大脑

并非所有应用都需要代理,但一旦场景不再是“调用一次工具——返回一次答复”,代理层就非常有用。

代理本质上是一个可控的 LLM 过程,它会:

  • 读取用户请求与对话历史中的事实;
  • 规划步骤序列:调用哪些工具、按什么顺序、携带哪些参数;
  • 分析结果,并可决定“重新调用工具”“向用户索取澄清”“构建更复杂的回答”;
  • 有时在步骤间保存状态(记忆、会话、检查点——这是第 12 层的主题)。

Agents SDK提供了描述这类场景的结构化方式:代理可用哪些工具、如何保存/恢复状态、如何限制循环等。代理在你的后端中运行,让你能按需使用 OpenAI 的能力:不受 ChatGPT Apps 小部件的限制。

在我们的栈中,代理通常位于 MCP 层与领域 API 之间。它可以把外部 API、内部函数和 MCP 工具当作“手”,自己负责“脑”。

例如,GiftGenius 的一个场景可能是:

  1. 用户写道:“为妈妈挑一个不超过 50 美元的礼物”。
  2. ChatGPT 调用你应用的 search_gifts 工具。
  3. 在后端,search_gifts 工具背后是一个代理,它决定先澄清若干细节(兴趣、场合)。
  4. 用户补充了更多偏好。
  5. ChatGPT 再次调用你应用的 search_gifts 工具,并附带更多参数。
  6. 服务器端的代理可调用其他工具(比如库存校验)。
  7. 然后返回给 ChatGPT 已准备好的选项,并可能附带用于可视化的小部件链接。

后续我们会详细拆解代理的运行周期、幂等与安全性。但在总体架构上要认识到:代理层是可选却强大的“大脑”,能替你吸收一部分复杂的编排。

7. 层 5 — ACP/后端:金钱、数据与落地事务

最底层是你的常规服务:

  • 数据库(商品目录、用户、订单);
  • 外部 API(支付服务商、物流、第三方 SaaS);
  • 专用协议,如面向 commerce 场景的 ACP(Agentic Commerce Protocol)与 Instant Checkout。

ACP 描述了 ChatGPT 与代理如何与你的 commerce 后端通信:SKU 选品、创建购物车、下单、退款、成功/失败操作的 webhooks 等。

对 GiftGenius,大致会是这样:

  • MCP 工具 search_gifts 从产品 feed/数据库读取;
  • 代理选定具体商品后,通过 ACP 发起 commerce 意图;
  • 你的 ACP 兼容后端告诉 PaymentService:“扣款”,并告知 ChatGPT 状态;
  • 用户在 ChatGPT 中看到订单已完成,无需跳转外站。

有了对各层的概览,接下来看看一个端到端的具体流程。

8. 端到端场景:用户请求如何穿越所有层

以这个请求为例:“为妈妈挑选不超过 50 美元的礼物,她喜欢读书和茶”。

分解为以下步骤:

  1. 用户在 ChatGPT 中输入文本。这是第一层——ChatGPT UI。对用户来说就像普通聊天。
  2. 模型读取对话历史、你的 App 元数据(描述、分类、权限)并判断 GiftGenius 是合适候选。根据 Apps SDK 的发现规则,模型会结合 tools 的文本描述、以往使用、上下文,甚至品牌提及。
  3. ChatGPT 要么:
    • 直接调用你应用的工具而不显示 UI(tool-first 场景);
    • 要么在回复中建议:“我可以使用 GiftGenius 帮你挑选礼物”,并调用你的 tool。
  4. ChatGPT 向 MCP 服务器发送 call_tool 请求,调用 search_gifts 工具。 MCP 服务器执行业务逻辑:访问数据库/产品 feed,按预算与偏好过滤,并返回包含合适商品列表的 JSON。
  5. 工具结果回到 ChatGPT。它可以:
    • 仅把结果作为文本回复的数据来源(“这里有 3 个礼物创意...”),不展示小部件;
    • 或者渲染小部件,把 ToolOutput 传入你的组件以渲染商品卡片。
  6. 这时你的小部件 GiftGenius(Apps SDK)才启动,你的 Next.js 代码在聊天中渲染。小部件可以展示带澄清字段的表单:“送给谁?”“预算”“兴趣”。用户也可以继续在聊天里输入——模型会把这些与 App 同步。
  7. 当小部件需要真实数据(礼物目录)时,它不会直接 fetch('https://my-backend/gifts') 。相反,它会发起 MCP 工具调用:ChatGPT 再次向 MCP 服务器发送 call_tool 请求,调用 search_gifts
  8. 如果场景是多步(需要澄清、排序、做额外库存校验、提供替代方案),代理层会承担规划、工作流管理与代理编排。
  9. 当用户决定“购买”某个商品时,ChatGPT 按 ACP 协议发起购买。commerce 后端通过 ACP 与 Instant Checkout 处理交易、返回状态、触发 webhooks,ChatGPT 显示最终状态(“订单已完成,这是收据”)。

对开发者而言,好在每一层都边界清晰。同时各层通过新的标准化协议(MCP、ACP)连接,而不是陈旧的 REST 请求。

以上是逻辑视图:有哪些层,以及请求如何穿过它们。接下来我们关心物理视图:这些层究竟如何以代码与基础设施形式部署——一个 Next 单体,还是若干服务(这里不是在讨论单体 vs 微服务的架构学派)。

9. Next.js 单体 vs 拆分架构

现在的合乎逻辑的问题是:“这些一定要变成一堆独立服务吗?我能不能就做一个 Next.js 单体就结束?”

答案:可以。课程会由浅入深。一开始完全可以把“几乎所有东西”放进一个仓库甚至同一个运行时:

flowchart LR
    U[ChatGPT] --> W["Next.js App (Apps SDK)"]
    W --> M["MCP 端点 (就在同一个 Next.js 中)"]
    M --> DB[(数据库/目录)]

也就是说,你的 Next.js 服务器(API 路由或独立服务器)同时:

  • 提供 UI 小部件(Apps SDK 的页面/组件),
  • 实现 MCP 端点(基于 HTTP 的 JSON-RPC),
  • 访问数据库/外部 API。

这在开发模式与应用早期版本中非常方便:活动部件更少,部署更简单。

但随着功能增长,会出现拆分的理由:

  • MCP 服务器需要单独扩展(很多重型工具);
  • 金融/支付后端跑在自有域上,由其他团队维护,对安全性有特殊要求;
  • 代理逻辑可拆为独立应用,拥有自己的监控与 SLA。

那时图就更接近我们之前看到的样子:

flowchart TD
    U[ChatGPT] --> W[Next.js + Apps SDK]
    W --> MG[MCP Gateway]
    MG --> M1[MCP Gifts Server]
    MG --> M2[MCP Analytics Server]
    M1 --> AG[Agent Service]
    AG --> ACP[Commerce/ACP Backend]

这里增加了 MCP Gateway 的概念——面向 ChatGPT 的统一入口。它把请求路由到不同的 MCP 服务器,连接 REST API,管理授权与限流(rate limiting)等。

我们会先从更单体的方案写例子,但从一开始就组织好代码结构,以便后续较为平滑地拆分。

10. 你究竟会在哪里写代码(以及交给谁)

既然已经勾勒了把各层收拢为单体或拆分为分布式架构的方式,接下来明确你具体会在哪些地方写代码,哪些交给其他服务/团队。

从 TypeScript/Next.js 开发者的角度,直接标注出你所控制的区域很有帮助。

在小部件(Apps SDK + Next.js)中,你将:

  • 编写 React 组件,用于展示工具状态与用户输入;
  • 使用 Apps SDK 的 hooks 读取 ToolInput/ToolOutput 与小部件状态(widget state);
  • 配置显示模式(内嵌/全屏/PiP、主题、尺寸——见第 8 层);
  • 通过 window.openai 与 ChatGPT 进行更进阶的交互(课程后续模块)。

在 MCP 服务器中,你将:

  • 使用 MCP SDK 描述 tools/resources/prompts;
  • 实现工具的业务逻辑(本质上是常规的 TypeScript 函数,访问数据库、API 等);
  • 优化模式与响应,让模型更易读(更少幻觉,更多结构化)。

在代理层(若使用 Agents SDK)中,你将:

  • 描述代理可用的工具以及它的目标;
  • 配置运行周期、记忆、循环控制;
  • 确保代理不胡乱操作,也不陷入无休止的规划。

在 ACP/后端中,你将:

  • 要么集成现有的 commerce 服务(Stripe、自有商店的 product feed 等);
  • 要么设计一个理解 ACP 并能接收/返回订单的新后端。

重要提示:在成熟产品中,同一个人很少能完全掌控所有层。但在原型阶段(以及本课程中),我们希望你至少能理解每段代码住在哪一层。

11. 架构如何影响 UX 与平台政策

尽管 UX 与政策是独立的模块,但在架构层面已经需要理解层次划分会如何影响 UX 与平台要求。这里先做几条提示。

首先,沙盒。小部件不能随意上网与收集用户数据——一切通过受控的工具与在 MCP/Store 中描述的权限进行。平台期望你如实描述 App 需要的数据与动作,并据此进行 App 的发现/推荐。

其次,UX 流程。由于模型可能暂时“忘记”你的 App,或相反过于积极地推荐它,架构要对中断友好:如果代理没完成长流程,用户改了话题,应用也应从容应对。课程中的多步场景与工作流编排,会建立在 MCP 工具与代理层之上。

第三,销售。一旦你的 App 开始收款,就会有更多安全、日志、ACP 合同等要求。你如何划分各层(UI、MCP、Agents、ACP/Backend)会极大影响你通过Store 审核安全审计的难易程度。

小结

希望你已经在脑中构建起一幅总览地图:

  • 顶层(ChatGPT UI + Apps SDK)决定用户如何看到与感知你的 App;
  • 中层(MCP)是为模型提供工具与数据的标准方式;
  • 代理与 commerce 层让你的 App 不再只是“数据浏览器”,而是具备逻辑与交易能力的完整产品。

第二阶段我们将从最有意思的部分开始:下载基于 Next.js 的官方 Apps SDK 模板,在本地跑起来,并在 Dev Mode 连接 ChatGPT。也就是说,首先上手 Apps SDK/小部件这一层,而 MCP/代理暂时作为桩或内置后端存在。

但现在就把当前这张图记在脑中很重要:就像看一个 monorepo 并理解,文件夹 apps/ ——是 UI, services/mcp ——是协议, services/agent ——是编排器, 而 services/commerce ——是交易。

12. 对栈架构理解中的常见错误

错误 №1:认为 ChatGPT App = 只是“我的 REST API 的一个 webhook”。
这源自“机器人”世界的习惯:模型直接向我的 URL 发 POST,然后随它去。事实上,模型与代码之间有 Apps SDK 与 MCP。你需要描述工具、它们的模式与行为,而不是“监听”任意 HTTP 请求。

错误 №2:混淆 UI 与业务逻辑层。
常见反模式是把复杂的领域逻辑塞进小部件,让 MCP 层变成薄薄的垫片。结果是 UI 变得臃肿、难测,也难以在 ChatGPT 之外复用。更稳妥的做法是把规则与数据访问放在 MCP/代理层,而小部件专注呈现与简单交互。

错误 №3:无视 MCP,自行造“协议”。
有时会想:“我为什么要 MCP?直接返回 JSON,模型自己会懂。”短演示上它可能“看似可用”,但你会立即失去 MCP 与 Apps SDK“开箱即用”的发现、检视、授权、多客户端支持等能力。

错误 №4:把整个 App 围绕一层来构建。
有人把“一切都放到代理里”,让其承担大量职责;有人相反把一切塞进 MCP 工具;也有人做成一个巨大的 Next.js 单体。更正确的做法是接受每层的职责边界:UI——呈现;MCP——数据/动作的访问;代理——编排;ACP/后端——领域不变量与交易。

错误 №5:低估架构对 Store 审核与安全性的影响。
如果你只有一个服务器,同时是 MCP 端点、ACP 资源、存放密钥并“原样”写日志——安全与内容政策的审核可能会很漫长。具有清晰边界与协议的拆分架构,会极大简化后期流程。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION