1. 在 ChatGPT App 情境下,什麼是 workflow
既然你已經搞定授權,那就值得一份獎勵。讓我們進入非常有趣的主題——ChatGPT App 裡的 workflow。很多人一聽到「workflow」就會回想起 BPMN 圖與沉悶的企業軟體。別擔心:在 ChatGPT App 的語境中,我們關心的是輕量得多的版本。
在本課程中,我們把 workflow 理解為多步驟情境,其中:
- 有清晰的目標(例如挑選禮物並完成購買),
- 有連續的步驟(詢問 → 產生選項 → 釐清 → 完成),
- 每個步驟中,GPT、widget 與工具各有其角色。
重點:workflow 不是「MCP 伺服器中的一個方法」。它是一種組合:
- 模型的推理(問什麼問題、何時呼叫哪個工具),
- tools 的呼叫(MCP/Agents),
- widget 裡的 UI 步驟,
- 後端中的狀態。
也就是說,你不會只有一個「萬能工具」solve_everything,而是在不同階段啟用多個簡單工具。你也不會有一個「巨型 widget」,而是少量的畫面/狀態,各自處理其子任務。
workflow 中的「責任三角」
可以把 workflow 想成三個參與者的舞蹈:
| 角色 | 在 workflow 中的任務 | GiftGenius 中的例子 |
|---|---|---|
| GPT | 大腦。理解使用者意圖,決定何時步驟完成、下一步是什麼。可以呼叫 tools。 | 理解「我想找給極客的東西」,並決定呼叫 search_items(category="geek")。 |
| Widget | 門面。渲染目前步驟,只顯示相關內容,收集點擊與輸入。保存 UI 狀態。 | 先顯示表單「送給誰?」,接著是禮物卡片,最後顯示「購買」按鈕。 |
| MCP/Agent | 雙手。執行繁重且結構化的工作,驗證資料,保存商務狀態。 | 保存收禮者的個人檔案,查詢禮物目錄,依預算過濾。 |
這三個角色共同實現同一個情境,但位於不同層次:GPT 決定「接下來怎麼做」,widget 顯示「此刻要做什麼」,MCP 則負責資料實際發生了什麼。
2. 以 GiftGenius 為例的 workflow
以熟悉的 GiftGenius 情境——禮物精選助手——為例。它可以描述成一個簡單的線性精靈。
步驟可能如下:
- 收集收禮者的基本資訊。
- 設定預算與限制。
- 產生並過濾禮物點子。
- 顯示候選項,允許喜歡/隱藏。
- 前往結帳(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 個欄位的表單,幾乎保證會導致 rage‑quit:使用者會放棄並關閉分頁。使用者是來「對話」的,不是來考試。
但如果你把任務切成步驟:
- 「步驟 1/4:請介紹對方」
- 「步驟 2/4:選擇預算」
- 「步驟 3/4:瀏覽選項」
- 「步驟 4/4:確認選擇」
那麼每個時間點看起來都可負擔。進度列或步驟標示會帶來掌控感:知道正在發生什麼、還剩多少。
模型的認知負荷
驚喜的是:模型也有類似問題。LLM 畢竟不是人,但它也有有限的「注意力」與上下文視窗。如果你要 GPT 在一次請求中:
- 了解收禮者的一切,
- 決定預算,
- 考慮運送細節,
- 找出 10 個選項,
- 解釋為什麼選這些,
那模型會在每個子任務上消耗注意力與 token。一次請求中任務越多,某些部分就越可能粗糙或出錯。
若改為構建步驟鏈——本質上是把 chain-of-thought 外顯在介面上——模型先解決「擷取個人檔案」、再「校正預算」、再「挑選候選」。模型在每一步的推理品質通常更好。
可維護性與除錯
當一切被塞進單一工具與單一畫面,除錯就變成解謎:「到底在哪一步變糟了?」
在多步驟 workflow 中,你幾乎自動就擁有:
- 可記錄的節點:step_started、step_completed、step_failed,
- 清楚的轉換量測點(有多少人走到第 3 步),
- 局部化問題:「只在產生點子那一步會炸」。
這些在 workflow 分析模組會派上用場,但現在就該養成以步驟思考的習慣。
4. workflow 中的步驟類型與 UI 呈現方式
我們已討論為何要切步驟。接下來整理常見的「積木」:避免變成雜亂的畫面集合,最好有一個步驟類型的「庫」。在你的 App 中幾乎總會重複幾個典型樣式。
以下是一張基本表:
| 步驟類型 | 目標 | 在 ChatGPT App 中常見的樣貌 | GiftGenius 範例 |
|---|---|---|---|
| 資料蒐集(Wizard) | 把複雜資料分段填寫 | 小型表單、chips、選項選擇、進度指示 | 「送給誰?」「年齡?」「興趣?」 |
| 分支 | 決定接下來走哪一條路 | 聊天中的提問 + UI 中的簡易選項 | 「禮物給小孩 → 兒童分類」 |
| 檢視/確認 | 讓使用者核對結果 | 摘要卡片 + 「返回」/「確認」按鈕 | 「這是我對她的理解,是否正確?」 |
| 最終步驟 | 結束情境並提供下一步行動 | 最終結果畫面 + 聊天中的 follow‑up | 「這是你的禮物,想要下單嗎?」 |
要記得:同一個邏輯步驟可以以 UI 呈現,也可以是純文字對話。例如,「蒐集興趣」這個步驟可以是:
- 表單與標籤:「運動」「桌遊」「烹飪」,
- 或是一段對話,GPT 禮貌地追問:「他平常的興趣是什麼?」。
常見最佳解是混合式:GPT 發問、使用者以文字回答,同時也能在 widget 裡點擊 chips。
5. 誰來「帶」workflow:GPT、widget,還是伺服器?
直覺上你可能會說:「當然是 widget,我們是前端,用 state 控制一切。」但在 ChatGPT App 世界並非如此。Workflow 是三方共同合作。
作為協調者的 GPT
GPT:
- 帶領對話、提出問題,
- 決定什麼時候可以視為步驟完成,
- 選擇何時呼叫 tool(例如「該產生禮物清單了」)。
對 GPT 而言,你的 workflow 是一組子任務。你能在 system‑prompt 中描述有哪些子任務以及通常的順序,但仍保留模型一定的調整空間。
GiftGenius 的 system‑prompt 內部小指令範例(偽代碼,非精確語法):
1. 先釐清收禮者的個人檔案(年齡、關係、興趣)。
2. 接著釐清預算。
3. 當資料足夠時,呼叫工具 suggest_gifts。
4. 拿到候選後,協助使用者做出選擇。
重點:GPT 不知道(也不該知道)你的 React 元件細節。它用目標導向的步驟來思考:「蒐集個人檔案」「挑選點子」。
作為步驟「門面」的 widget
Widget:
- 只顯示當下最相關的步驟,
- 保存 UI 狀態(被選的卡片、開啟中的分頁、表單的本地欄位),
- 可以顯示步驟進度指示。
UI workflow 的最簡表示如下:
type GiftWorkflowStep =
| "profiling"
| "budget"
| "candidates"
| "checkout";
type GiftWidgetState = {
step: GiftWorkflowStep;
selectedGiftId?: string;
};
在 React widget 內,你可以用一般的 useState 儲存這個狀態,或若想綁定到 ChatGPT 中 widget 的生命週期,使用 Apps SDK 的 useWidgetState。
const [widgetState, setWidgetState] = useState<GiftWidgetState>({
step: "profiling",
});
widget 中的處理函式不會直接「購買禮物」,而是切換步驟,並把需要的資料回傳給模型/後端。
MCP-tools 作為 workflow 的「雙手」
MCP‑伺服器:
- 保存商務狀態(個人檔案、選擇歷史),
- 驗證步驟(「沒有選定禮物就不能進入 Checkout」),
- 執行繁重工作:目錄檢索、價格計算、與 ACP 整合。
例如,決定「要顯示哪些禮物」比較適合由 MCP‑tool suggest_gifts 來做,讓模型能在釐清條件時多次呼叫。
於是你得到清楚的分工:
- GPT —— 文字互動與順序,
- widget —— 當前步驟的視覺呈現,
- MCP —— 資料與不變條件。
6. 如何在程式碼中描述 workflow:迷你 state‑machine
還記得開頭的 GiftGenius 狀態圖嗎?現在把同樣的邏輯寫成簡單的型別與函式——程式碼中的迷你 state‑machine。我們不打算把你的 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; // 結尾
}
}
這已經帶來:
- 集中管理的步驟清單,
- 明確的轉移規則,
- 能快速調整順序與邏輯的能力。
在 widget 中使用
最簡版的「精靈」在你的 widget 中可能長這樣:
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‑widget 是個「大表單」:
- 收禮者姓名,
- 年齡,
- 興趣,
- 預算,
- 場合類型,
- 勾選「需要配送」與另外五個欄位,
- 底部只有一個大按鈕「挑選禮物」。
做原型時這常常沒問題,但只要想走向產品化——就該切成步驟。
「之前」長這樣
誇張一點的例子:
// 反樣式:一個巨大的表單
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>
{/* 用於類型與興趣的單選鈕/chips */}
<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>
{/* slider 或 input */}
<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 做成一個龐大的表單,尤其是在內嵌 widget 裡。使用者已經在聊天了;額外的 UI 模組應該降低、而非提高負荷。如果你發現自己在想「這 12 個欄位都很重要」——幾乎可斷定應該切步驟。
第三,別做「為了好看」而存在的步驟。每個步驟都該有明確目標:要不是蒐集資料,要不就是縮小選項,或是讓使用者確認。像「再一下就好了」這種只有「下一步」按鈕的空白畫面,幾乎沒幫助。
最後,不要一開始就想把 App 的所有能力全部曝光。像「進階篩選」「特殊配送條件」之類的細節,可以做成額外步驟,只對真的需要的人顯示。
9. 簡單的 workflow 設計練習
為了更好地鞏固內容,試著在紙上(或 IDE,但先不寫碼)做以下練習。
選一個任務,可能是:
- 挑選禮物(GiftGenius),
- 預訂旅程,
- 制定某項技能的學習計畫。
把它切成 3–5 個步驟。對每個步驟描述:
- 目標:完成該步驟後應該知道/完成什麼,
- 形式:這裡比較適合純 GPT 文字、widget,或兩者結合。
例如,一個簡單的「TypeScript 學習計畫」:
- 步驟「程度評估」——對話(GPT 問幾個問題)+ 簡短自評表單。
- 步驟「目標」——文字討論 + widget 中的目標勾選框。
- 步驟「計畫」——產生計畫(清單)+ 按鈕「加難/變簡」。
- 步驟「確認」——簡短摘要與按鈕「保存計畫」。
接著試著想想,每個步驟可能會用到哪些 tools,但先別陷入細節:工具、它們的啟用/停用與狀態保存——會在本模組後續的講座中討論。
10. 多步驟 workflow 常見錯誤
錯誤 1:試圖用單一步驟與單一 tool 解決所有問題。
很容易想做一個「大型聰明工具」,既能發問、又能分析、還能自己挑選、甚至直接下單。實際上,這會同時惡化 UX(單一沉重畫面)與模型的推理品質(一次承擔太多任務)。把任務拆成 3–5 個簡單步驟更簡單、可靠,維運也更省力。
錯誤 2:隱性步驟只存在於開發者腦中。
有時程式中看似有一連串動作,但它從未被明確描述:沒有步驟型別、沒有設定、沒有圖。結果是,團隊裡誰都說不清「這個 App 從頭到尾到底發生了什麼」。最基本的宣告式步驟/轉移描述,能省下大量除錯時間。
錯誤 3:把 UI 步驟與商務邏輯混在一起。
若步驟轉移邏輯被埋進 React 元件深處(在 if (isValid && hasBudget && !needsShipping) 的 onClick 之類),就很難重用與測試。最好有相對明確的「狀態機」或至少 getNextStep 函式,而 UI 只需呼叫並顯示結果。
錯誤 4:忽視 GPT 作為協調者的角色。
有時開發者想完全用 widget 控制情境:「我要自己問完所有需要的問題,模型只負責挑選。」結果 ChatGPT 看起來不像活生生的助理,而像表單背後的計算引擎。更好的體驗是,GPT 積極溝通、推動下一步,並主動發起 tools 呼叫——你則用步驟設計與指令協助它。
錯誤 5:沒有明確目標的步驟。
有時精靈裡會出現「多餘」的步驟——老實說,可能只是設計看起來比較漂亮。使用者看到「步驟 2/5」,但那一步既不需要他做什麼,也沒有任何進展。這種空白畫面只會增加複雜感。如果一個步驟無法表述為「之後我們確定了 X」或「之後使用者完成了 Y」,那它多半是不需要的。
錯誤 6:忘了進度,讓使用者缺乏路徑感。
沒有視覺化輔助的多步驟,會變成一個黑盒子:使用者不知道自己在哪、還剩多少。即使只是簡單的文字指示「步驟 2/4」,或在 widget 頂部水平列出步驟,都能大幅降低焦慮。忽略這點,是人們「半路離開」的原因之一,即便實際的複雜度可能不高。
GO TO FULL VERSION