CodeGym /课程 /ChatGPT Apps /多步骤 workflow 作为降低认知负荷的方法

多步骤 workflow 作为降低认知负荷的方法

ChatGPT Apps
第 11 级 , 课程 0
可用

1. 在 ChatGPT App 背景下,什么是 workflow

如果你已经搞定了授权,那就值得表扬。我们来进入一个非常有趣的话题——ChatGPT App 里的 workflow。说到“workflow”,很多人会被 BPMN 图和沉闷的企业软件触发回忆。别担心:在 ChatGPT App 的语境下,我们关注的是更轻量的版本。

在本课程中,workflow 指的是一个多步骤的场景,其中:

  • 有清晰的目标(例如挑选礼物并推进到购买),
  • 有连续的步骤(问询 → 生成备选 → 细化 → 完成),
  • 在每个步骤里,GPT、小部件和工具各司其职。

关键点:workflow 并不是“MCP 服务器里的一个方法”。它是一个组合:

  • 模型的推理(问哪些问题、在何时调用哪个工具),
  • tools 的调用(MCP/Agents),
  • 小部件中的 UI 步骤,
  • 后端的状态。

也就是说,你不需要一个“万能工具” solve_everything,而是需要若干简单工具,在不同阶段启用。同样,也不是一个“超级小部件”,而是一组小屏/状态,每个解决一个子任务。

workflow 中的“责任三角”

把 workflow 想成三个参与者的配合舞最为方便:

角色 在 workflow 中的职责 GiftGenius 中的示例
GPT 大脑。理解用户意图,决定何时步骤完成、下一步是什么。可以调用 tools。 理解“我想给极客买点什么”,并决定调用 search_items(category="geek")
Widget 界面。渲染当前步骤,只展示相关内容,收集点击与输入。保存 UI 状态。 先显示“送给谁的礼物?”的表单,然后是礼物卡片,最后是“购买”按钮。
MCP/Agent 双手。执行繁重且结构化的工作,校验数据,保存业务状态。 保存收礼人档案,请求礼物目录,并按预算过滤。

这三种角色共同实现同一个场景,但作用层级不同:GPT 决定“下一步是什么”,小部件展示“现在是什么”,MCP 负责数据层面真正发生了什么。

2. 基于 GiftGenius 的 workflow 示例

我们用熟悉的 GiftGenius 场景——礼物挑选助手。它可以被描述为一个简单的线性向导。

步骤顺序可能是:

  1. 收集收礼人的基础信息。
  2. 确定预算与约束。
  3. 生成并过滤礼物创意。
  4. 展示候选项,允许点赞/隐藏。
  5. 进入结算(Checkout)或保存这次挑选。

同一场景也可以看作一个小型“状态机”:

stateDiagram-v2
    [*] --> Profiling
    Profiling --> ProfilingDone: 档案已填写
    ProfilingDone --> Browsing: 已生成创意
    Browsing --> Refining: 用户已细化筛选
    Refining --> Browsing: 已更新列表
    Browsing --> Checkout: 已选择礼物
    Checkout --> Success: 订单已提交
    Success --> [*]

这里:

  • Profiling——收集收礼人回答的步骤,
  • Browsing/Refining——与候选列表互动,
  • Checkout——结算,
  • Success——最终确认。

请注意:图上没有任何按钮,也没有一个 fetch。这是一组逻辑步骤;具体的 UI 屏幕、tools 和 API 调用是你叠加在其上的实现。

3. 为什么要把任务拆解为多步

如果你曾做过“25 个问题塞进一个屏”的问卷,你已经知道原因。但我们还是把它系统化一下。

用户的认知负荷

人的注意力资源有限。心理学里常提米勒定律:短时记忆里大约有 7±2 个块。对 UX 来说,这意味着一个非常实际的原则:同时展示的字段和选项越多,用户卡住、疲惫或直接关页的概率就越高。

在 ChatGPT 的小型内联小部件里把 12 个字段放在一个屏幕上,几乎可以保证“愤然退出”:用户放弃并直接关掉标签页。用户来这里是为了“对话”,不是为了“考试”。

如果你把任务拆成步骤:

  • “第 1 步(共 4 步):请介绍这位收礼人”,
  • “第 2 步(共 4 步):选择预算”,
  • “第 3 步(共 4 步):查看候选项”,
  • “第 4 步(共 4 步):确认选择”,

那么每个时刻都显得可完成。进度条或步骤标签会带来掌控感:清楚发生了什么、还剩多少。

模型的认知负荷

惊喜的是:模型也有类似的问题。LLM 当然不是人,但它也有有限的“注意力”和上下文窗口。如果你让 GPT 在一次请求中同时:

  • 问清关于收礼人的一切,
  • 搞定预算,
  • 考虑配送细节,
  • 挑出 10 个备选,
  • 解释为什么是这些选项,

那么模型会把注意力与 token 分散在每个子任务上。在一个请求里任务越多、越不相干,就越可能出现浅尝辄止或出错。

而如果你把流程做成步骤链——本质上就是把 chain-of-thought 显式地铺在界面里——模型先解决“抽取档案”的窄任务,再“校准预算”,然后“挑选候选项”。模型在每个环节的推理质量会明显更高。

可维护性与调试

当一切都塞进一个工具和一个屏时,调试就变成了“到底哪里出了问题”的寻宝游戏。

在多步骤的 workflow 中,你几乎自带:

  • 日志点:step_startedstep_completedstep_failed
  • 清晰的转化测点(有多少人走到了第 3 步),
  • 定位问题的能力:“只在生成创意这一步会失败”。

这些会在 workflow 分析模块中继续用到,但从现在开始就请养成“按步骤思考”的习惯。

4. workflow 中的步骤类型以及它们在 UI 中的样子

我们已经讨论了为什么要拆步。现在来梳理步骤本身,看看在 ChatGPT App 中最常见的“基石”有哪些。为了不陷入杂乱的屏幕堆,建立一套“步骤类型库”很有用。你的 App 里经常会反复出现几个模式。

这里是一张基础表:

步骤类型 目标 在 ChatGPT App 中通常的样子 GiftGenius 中的示例
数据采集(Wizard) 分步填充一个复杂对象 小表单、标签(chips)、选项选择、进度指示 “送给谁?”、“年龄?”、“兴趣?”
分支 决定接下来走哪条路径 聊天中的一个问题 + UI 中的简单选项 “给孩子的礼物 → 儿童分类”
查看/确认 让用户核对最终结果 汇总卡片 + “返回”/“确认”按钮 “我对她的理解如下,是否正确?”
最终步骤 完成场景,并提出后续动作 带有结果的最终屏 + 聊天里的 follow‑ups “这是你的礼物列表,要去下单吗?”

需要记住:同一个逻辑步骤既可以在 UI 中体现,也可以只是纯文本对话。例如,“收集兴趣”这一步可以是:

  • 一个带“运动”“桌游”“烹饪”等标签的表单,
  • 或者一段对话,GPT 温和地追问:“他/她平时有哪些爱好?”。

很多时候,最佳方案是混合式:GPT 提问,用户用文本回答,同时也可以在小部件里点击标签。

5. 谁来“驱动” workflow:GPT、小部件还是服务器?

直觉可能会说:“当然是小部件,我们做前端的,一切都用 state 控制。”但在 ChatGPT App 世界里并非如此。Workflow 是三方协作的结果。

GPT 作为编排者

GPT:

  • 进行对话,提出问题,
  • 决定何时可以认为步骤完成,
  • 选择何时调用 tool(例如,“该生成礼物建议了”)。

对它而言,你的 workflow 是一组子任务。在 system‑prompt 里,你可以描述有哪些子任务、通常以什么顺序执行,但要保留一定自由度让模型做些微调整。

GiftGenius 的 system‑prompt 中的一个迷你指令示例(伪代码,非精确语法):

1. 先澄清收礼人的档案(年龄、关系、兴趣)。
2. 再确认预算。
3. 当数据足够时——调用工具 suggest_gifts。
4. 拿到候选项后——帮助用户做选择。

重点:GPT 不知道(也不需要知道)你的 React 组件细节。它用“收集档案”“生成创意”这样的目标语义来思考步骤。

小部件作为步骤的“界面”

小部件:

  • 只展示当前相关的步骤,
  • 保存 UI 状态(选中的卡片、打开的标签、本地表单字段),
  • 可以展示步骤进度指示器。

在代码中对 UI‑workflow 的最简单表达:

type GiftWorkflowStep =
  | "profiling"
  | "budget"
  | "candidates"
  | "checkout";

type GiftWidgetState = {
  step: GiftWorkflowStep;
  selectedGiftId?: string;
};

在 React 小部件内部,你可以用普通的 useState 保存这个状态,或者如果你想绑定到 ChatGPT 中小部件的生命周期,可以用 Apps SDK 的 useWidgetState

const [widgetState, setWidgetState] = useState<GiftWidgetState>({
  step: "profiling",
});

小部件中的处理函数不会直接“购买礼物”,而是修改步骤并把所需数据回传给模型/后端。

MCP-tools 作为 workflow 的“双手”

MCP 服务器:

  • 保存业务状态(档案、选择历史),
  • 校验步骤(“没有选中礼物就不能进入 Checkout”),
  • 执行重活:目录搜索、价格计算、与 ACP 集成。

例如,“展示哪些礼物”的决定逻辑更适合放在 MCP‑tool suggest_gifts 中,这样模型在细化条件时可以多次调用它。

于是你得到清晰的分工:

  • GPT——文本与顺序,
  • 小部件——当前步骤的可视化呈现,
  • MCP——数据与不变条件。

6. 如何在代码中描述 workflow:迷你 state‑machine

还记得开头 GiftGenius 的状态图吗?现在把同样的逻辑写成简单的类型与函数——代码里的迷你状态机。我们不会把你的 App 变成理论课程,但这点小抽象会让开发轻松很多。

步骤类型与配置

从声明式的步骤描述开始。使用我们已经见过的 GiftWorkflowStep(为了直观在此重复),并为它写个配置:

type GiftWorkflowStep =
  | "profiling"
  | "budget"
  | "candidates"
  | "checkout";

type StepConfig = {
  label: string;
  isFinal?: boolean;
};

export const GIFT_WORKFLOW_STEPS: Record<GiftWorkflowStep, StepConfig> = {
  profiling: { label: "收礼人" },
  budget: { label: "预算" },
  candidates: { label: "候选项" },
  checkout: { label: "结算", isFinal: true },
};

现在添加一个简单的跳转函数:

export function getNextStep(
  current: GiftWorkflowStep
): GiftWorkflowStep | null {
  switch (current) {
    case "profiling":
      return "budget";
    case "budget":
      return "candidates";
    case "candidates":
      return "checkout";
    default:
      return null; // 结束
  }
}

这已经带来:

  • 集中化的步骤清单,
  • 显式的跳转规则,
  • 快速修改顺序与逻辑的能力。

在小部件中使用

一个最简版的“向导”在你的小部件里可以是这样:

function GiftWizard() {
  const [step, setStep] = useState<GiftWorkflowStep>("profiling");

  const handleStepComplete = () => {
    const next = getNextStep(step);
    if (next) setStep(next);
  };

  return (
    <div>
      <ProgressBar step={step} />
      <StepContent step={step} onComplete={handleStepComplete} />
    </div>
  );
}

组件 StepContent 会根据步骤渲染不同的子表单:

function StepContent(props: {
  step: GiftWorkflowStep;
  onComplete: () => void;
}) {
  const { step, onComplete } = props;

  if (step === "profiling") {
    return <ProfilingStep onNext={onComplete} />;
  }
  if (step === "budget") {
    return <BudgetStep onNext={onComplete} />;
  }
  if (step === "candidates") {
    return <CandidatesStep onNext={onComplete} />;
  }
  return <CheckoutStep />;
}

注意:这里我们还没有涉及 GPT 如何选择步骤——这只是本地的 UI 逻辑。之后你可以把这个 step 与服务器状态或 tools 消息同步,但理解多步骤性到这就够用了。

7. 迭代教学应用:从“超级表单”到向导

设想在本讲之前,你的 GiftGenius 小部件是一份“超大表单”:

  • 收礼人姓名,
  • 年龄,
  • 兴趣,
  • 预算,
  • 事件类型,
  • 勾选“需要配送”和另外五个字段,
  • 底部一个大按钮“挑选礼物”。

做原型时这经常没问题,但一旦你想做成产品流程——就该拆成步骤了。

“之前”的样子

夸张点的例子:

// 反模式:一个巨大的表单
function GiftFormAllInOne() {
  return (
    <form>
      {/* 10+ 个字段混在一起 */}
      {/* ... */}
      <button type="submit">挑选礼物</button>
    </form>
  );
}

典型问题:

  • 用户搞不清哪些是必填项,
  • 不知道这要花多少时间,
  • GPT 更难向用户解释发生了什么,也难做后续跟进。

“之后”怎么做:三个屏的向导

步骤 1——把档案与预算拆开:

function ProfilingStep(props: { onNext: () => void }) {
  const [recipientType, setRecipientType] = useState("");
  const [interests, setInterests] = useState<string[]>([]);

  const handleSubmit = () => {
    // 这里可以调用用于保存档案的 tool
    props.onNext();
  };

  return (
    <div>
      <h3>我们要给谁挑礼物?</h3>
      {/* 一组单选/标签用于类型与兴趣 */}
      <button onClick={handleSubmit}>下一步</button>
    </div>
  );
}

步骤 2——预算:

function BudgetStep(props: { onNext: () => void }) {
  const [budget, setBudget] = useState<number | null>(null);

  const handleSubmit = () => {
    // 可以调用校验预算的 tool
    props.onNext();
  };

  return (
    <div>
      <h3>你的预算是多少?</h3>
      {/* 滑块或输入框 */}
      <button onClick={handleSubmit} disabled={!budget}>
        生成候选项
      </button>
    </div>
  );
}

步骤 3——候选列表:

function CandidatesStep(props: { onNext: () => void }) {
  const [selectedId, setSelectedId] = useState<string | null>(null);

  // 此处展示礼物卡片
  // 并允许选择一个

  return (
    <div>
      <h3>请选择合适的选项</h3>
      {/* 卡片 onClick = setSelectedId */}
      <button onClick={props.onNext} disabled={!selectedId}>
        进入结算
      </button>
    </div>
  );
}

没错,代码稍微多了点,但逻辑更简单了:

  • 每一步只解决一个小任务,
  • 模型可以单独对各步骤的切换进行说明,
  • 你可以分别记录/度量每一步。

8. 反模式:别把 workflow 养成怪兽

在类似 App 的实践与观察中,有几类常见错误需要尽量避免。

首先,不要试图用一张复杂的 BPMN 图把一切“画完”,里面 30 个状态、40 条箭头、一整张 A0。对 ChatGPT App 而言,直观的台阶式步骤比形式化符号更重要。像我们给 GiftGenius 画的那种小图就足够了。

其次,别把 App 变成一个巨型表单,尤其是在内联小部件里。用户已经在聊天里;加入的 UI 区块应该降低而不是增加负担。如果你发现自己在想“这 12 个字段都很重要”——这几乎总是需要拆分任务的信号。

第三,别做“为了好看”的步骤。每一步都应有清晰目标:要么收集数据,要么缩小范围,要么让用户确认。像“再等等就好了”且只有一个“下一步”按钮的空屏通常没有帮助。

最后,别在初始步骤就把 App 的所有能力都展示出来。诸如“高级筛选”“特殊配送条件”等细节,应该只对确实需要的人作为额外步骤按需开启。

9. 设计 workflow 的简单练习

为了更好地巩固内容,试着在纸上(或 IDE 中,但先别写代码)做下面的练习。

取一个任务。比如:

  • 挑选礼物(GiftGenius),
  • 预订旅行,
  • 制订某项技能的学习计划。

把它拆成 3–5 个步骤。对每一步,写下:

  • 目标:在这一步之后应该知道/完成什么,
  • 形式:这里更适合纯 GPT 文本、小部件,还是二者结合。

例如,一个简单的“TypeScript 学习计划”:

  1. 步骤“水平评估”——对话(GPT 提几个问题)+ 一个自评的短表单。
  2. 步骤“目标”——文本讨论 + 小部件里的目标复选框。
  3. 步骤“计划”——生成计划(列表)+ “更难/更易”的按钮。
  4. 步骤“确认”——简短总结与“保存计划”按钮。

然后再想想各步骤可能会用到哪些 tools,但先别陷入细节:工具、启用/禁用与状态持久化会在本模块的后续讲里展开。

10. 处理多步骤 workflow 时的常见错误

错误 1:尝试用一个步骤和一个 tool 解决所有问题。
做一个“万能大工具”很诱人:它既提问、也分析、还自己挑选并下单。实际上这会同时降低 UX(一个沉重的大屏)和模型的推理质量(reasoning)——一次调用里职责太多。把任务拆成 3–5 个简单步骤的链路更易用、更可靠、维护成本更低。

错误 2:步骤是隐性的,只存在于开发者脑中。
有时代码里似乎有一串动作,但并没有显式描述:没有步骤类型、没有配置、没有图。结果团队里没人能清晰回答“这个 App 从头到尾发生了什么”。最小化的声明式步骤与跳转描述能省下大量调试时间。

错误 3:把 UI 步骤与业务逻辑混在一起。
如果步骤跳转逻辑深埋在 React 组件里(比如在按钮 onClick 里写一堆 if (isValid && hasBudget && !needsShipping)),就很难复用和测试。最好有一个相对显式的“状态机”甚至只是一组 getNextStep 函数,UI 只负责调用与展示结果。

错误 4:忽视 GPT 的编排者角色。
有时开发者试图完全从小部件控制流程:“我自己把该问的都问了,让模型只做挑选”。结果 ChatGPT 不再像一个活的助手,而像一个表单下的计算引擎。更好的体验是:GPT 主动交流、推动下一步并主动发起 tools 调用——而你通过步骤设计与指令来帮助它。

错误 5:没有清晰目标的步骤。
有时向导里会出现“多余”的步骤——说白了只是因为“看起来更美”。用户看到“第 2 步(共 5 步)”,但这一步并不需要他做什么,也没有发生什么。这类空屏只会增加复杂感。如果一个步骤无法表述为“之后我们明确知道了 X”或“之后用户完成了 Y”,那它很可能不需要。

错误 6:忘了进度,缺乏路径感。
没有可视化支撑的多步骤会变成黑盒:用户不知道自己在哪、还剩多少。即便只是简单的“第 2 步(共 4 步)”文本指示,或小部件顶部的水平步骤列表,也会明显降低焦虑。忽视这一点是用户在流程中途“流失”的原因之一,尽管那里的实际难度可能并不大。

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