Prompt packs 如何協助 AI Agent 補齊介面的空、錯誤與載入狀態
Prompt packs for empty / error / loading states — stop shipping happy-path only
Sherotree 分享一套為 AI Agent 編寫介面提示詞包的方法,要求每個介面至少涵蓋正常、空、錯誤和載入四種狀態。提示詞包會區分空狀態的不同原因,並訂明錯誤重試及載入時避免版面跳動的要求。作者亦建議以 Storybook 故事或 Playwright 案例驗收,不要只檢查正常狀態。
Agents are weirdly good at the happy path. Give them a Figma frame of a filled dashboard and you get a filled dashboard. Give them a “build a projects list” prompt and you get rows, avatars, and a neat toolbar.
What you usually don’t get: the empty list on day one, the 500 that lands mid-scroll, or the skeleton that doesn’t jump when real data arrives. Those states are where users decide whether the product feels finished — and they’re the first thing agents skip unless you ask for them on purpose.
So I stopped describing “the screen” and started shipping prompt packs for the three non-happy states every surface needs.
The pattern: one pack per surface, four stories minimum
For any list / detail / settings panel the agent touches, I keep a tiny markdown pack next to the feature (or in the PR description). Four required stories:
- Happy — data present, primary actions enabled
- Empty — zero rows / first-run / filtered-to-nothing (these are different)
- Error — failed fetch, partial failure, retry affordance
- Loading — initial load and refetch / pagination (also different)
If the agent only returns #1, the PR is incomplete. Same bar as “missing tests.”
Example pack stub I paste into the agent chat:
## Surface: Projects list (`ProjectsPage`)
Implement ALL four states before considering this done.
### Happy
- 3–12 projects; name, updatedAt, owner avatar
- Primary CTA: New project (top-right)
- Row click → project detail
### Empty (three variants — do all)
- First-run: zero projects ever → illustration + “Create your first project”
- Filtered empty: filters active, zero matches → “No projects match” + Clear filters
- Archived-only: toggle shows archived but none → short copy, no big empty art
### Error
- List fetch failed → inline error in list region, Retry button, keep chrome
- Do NOT full-page crash the app shell
- Preserve last-known list if refetch fails (stale-while-error)
### Loading
- Initial: skeleton rows matching final layout (same columns / gutters)
- Refetch: subtle top progress or opacity — do NOT remount full-page spinner
- No layout shift when data lands (reserve avatar + title width)
That pack is boring on purpose. Agents love inventing a clever empty illustration and forgetting Clear filters.
Why “empty” needs variants
“Empty state” is not one design. First-run is marketing-adjacent. Filtered-empty is a recovery path. Permission-empty (“you can’t see any projects yet”) is a different CTA entirely.
If you only say “add an empty state,” the model picks the prettiest one and ships it for every case. Then support gets “I filtered by tag and the whole page turned into onboarding.”
Name the variants in the pack. Force the agent to emit separate components or clear branches:
type ListPhase =
| { kind: 'loading'; mode: 'initial' | 'refetch' }
| { kind: 'error'; error: Error; stale: Project[] | null }
| { kind: 'empty'; reason: 'first-run' | 'filtered' | 'archived' | 'forbidden' }
| { kind: 'ready'; items: Project[] };
I ask the agent to type the phase union first. The UI has somewhere to hang after that.
Error states agents keep getting wrong
Common agent mistakes I reject in review:
- Full-page error that unmounts the nav
- Toast-only failure with an infinite spinner underneath
- Retry that re-runs the whole route loader and wipes scroll
- Copy that blames the user (“Something went wrong. Try again later.”) with no Retry
In the pack I spell the contract:
### Error contract
- Error UI lives *inside* the list landmark (`role="region"` / labelled heading)
- Retry calls the same query fn; disable button while in flight
- If `stale` exists, show dimmed list + banner — don’t blank the page
- Log `error.message` for support; never render raw stack to the user
Agents follow contracts better than vibes.
Loading without the layout jump
Happy-path demos hide CLS. Real users notice when the toolbar drops 40px after load.
I put explicit anti-shift rules in the loading section:
### Loading anti-shift
- Skeleton row height === real row height (measure once, hardcode)
- Keep page title + primary CTA mounted during initial load
- Prefer `content-visibility` / fixed skeleton count over “spinner in a void”
- Refetch must not replace the list with a centered spinner
If the design system already has <Skeleton>, tell the agent which one — otherwise it invents a new shimmer.
How I wire this into the agent workflow
-
Before codegen: paste the pack (or
@-mentiondocs/prompt-packs/projects-list.md). - Acceptance: “PR must include Storybook stories or Playwright cases for empty/error/loading, not only happy.”
-
Review checklist: open the four states in Storybook before reading the diff. If empty looks like happy with
items=[]and no CTA, bounce it.
A one-liner that saves me hours:
Don’t merge agent UI that only has a happy path Storybook story.
Minimal Storybook set I ask for
export default { title: 'Projects/List', component: ProjectsList };
export const Happy = () => <ProjectsList phase={{ kind: 'ready', items: mockProjects }} />;
export const FirstRun = () => <ProjectsList phase={{ kind: 'empty', reason: 'first-run' }} />;
export const FilteredEmpty = () => <ProjectsList phase={{ kind: 'empty', reason: 'filtered' }} />;
export const LoadError = () => (
<ProjectsList phase={{ kind: 'error', error: new Error('503'), stale: null }} />
);
export const InitialLoading = () => (
<ProjectsList phase={{ kind: 'loading', mode: 'initial' }} />
);
Five stories beats a paragraph of “please handle edge cases.”
What changed after we did this
- Fewer “looks done in staging, broken for new orgs” launches
- Reviewers stop arguing about taste and point at a missing story
- Agents stop optimizing only for the screenshot we pasted from Figma
Happy path is still the demo. Empty / error / loading are the product. Put them in the prompt pack up front, or you’ll keep shipping demos.
來源:DEV Community · dev.to