1. 什么是 workflow 上下文,它为何重要
在普通的 Web 应用中,你通常很清楚状态存在哪里:数据库、缓存,以及前端的 Redux 或本地 React 状态。到了 ChatGPT App 就更有意思了:状态同时分布在三个世界里——模型内部(对话历史)、小部件内部(UI 状态)以及你的服务器/MCP(业务数据)。
这里所说的workflow 上下文,指的是回答“我们处于哪一步”和“目前已知信息有哪些”所需的全部数据。以我们的教学示例 GiftGenius 而言,上下文包括:
- 收礼人的资料:年龄、性别、兴趣;
- 预算以及可能的货币;
- 生成的创意清单,以及用户点赞或隐藏了哪些;
- 技术性信息:会话或 workflow 的标识、状态(“profile_collected”、“ideas_shown”、“checkout_started”)。
这个上下文不仅对后端开发者重要,对模型同样重要,它需要知道哪些问题已经问过、哪些工具已经调用、当前在讨论什么。对用户也重要,这样当他回到聊天时不必从零开始。
用户直觉上会以为“ChatGPT 会记住一切”。实际上模型只“记住”对话的文本,而且仅限于上下文窗口能容纳的部分。类似 order_id、cart_id 或“点赞的创意列表”这类结构化信息应该保存在你的服务器上,否则你会得到一台自信但经常给出错误结论的机器。
2. 三层状态:UI、LLM、业务
理解上下文保存最方便的方法,是采用三层状态模型,也称为 “State Triad”。
层级表
先看一张小表:
| 层级 | 存储位置 | 生命周期 | 职责 | GiftGenius 示例 |
|---|---|---|---|---|
| UI State | 小部件(React,widgetState) | 只要包含小部件的聊天/消息处于打开状态 | 视觉状态,本地输入 | 哪些卡片被高亮、表单状态 |
| LLM Context | OpenAI 中的聊天历史 | 只要消息还能“放得下”上下文窗口 | 对话理解与推理 | “给妈妈找礼物,预算 $50” |
| Business State | MCP/你的后端(数据库/Redis) | 由你决定(可持久化) | 事实:经验证的数据与状态 | { step: "ideas", budget: 50, liked: [42, 51] } |
UI 层快速且响应灵敏,但很脆弱:当你滚回历史记录上方时,ChatGPT 可能会“卸载”带有小部件的 iframe,随后再重新挂载。为此才有 widgetState,它的生命期略长于 React 组件,并与 ChatGPT 宿主客户端保持同步。
LLM 层让模型拥有连续对话的感觉,但它只保存文本和工具调用。你可以把购物车的 JSON 塞进去,但本质上只是把 JSON 插入了文本——模型并不会把它当作数据库。
业务层则是你作为工程师可以完全掌控的地方:在那里存着经过校验的数据、索引和订单状态。一旦你的场景变得认真起来(送礼、预订、学习),这一层就应该成为状态的权威来源。
最大的工程问题在于确保这三层不要各自“跑偏”。用户在小部件里改了预算,模型还在考虑旧预算,而数据库里又是第三个值——这正是各种怪异行为的配方。
3. 我们具体保存什么:WorkflowContext 的结构
为了更具体一些,用 TypeScript 为 GiftGenius 定义上下文接口。假设我们已有几个步骤:收集资料、选择预算、生成创意以及浏览/点赞。
从一个简单结构开始:
// backend/types/workflow.ts export type GiftWorkflowStep = | "profile" | "budget" | "ideas" | "checkout"; export interface GiftWorkflowContext { id: string; // workflowId — 场景的标识符 userId?: string; // 如果已经配置了认证 currentStep: GiftWorkflowStep; profile?: { age?: number; gender?: string; interests?: string[]; }; budget?: { min?: number; max?: number; currency: string; }; ideas?: { id: string; title: string; }[]; likedIdeaIds: string[]; hiddenIdeaIds: string[]; updatedAt: number; // 用于 TTL/清理的时间戳 }这不是最终的 schema,但关键元素已就位,包括:
- workflow 标识符,用它来查找该上下文;
- 当前步骤,帮助小部件和模型理解我们已经走到哪里;
- 在各个步骤