TL;DR
- The chat can render UI, not just prose. Four tools:
play_track,show_portfolio,show_skills,show_resume. - Tools fire on overview intent only. “What do you build” renders cards; “your Scripps work” stays prose.
- At most one tool per turn, with at most one short lead-in sentence. The UI carries the answer.
- The model picks; the server executes; the client renders. Each tool returns a typed payload that maps to one React component.
- Same grounding still applies. Generative UI is the shape of the answer, not an escape hatch from the retrieved facts.
Why show instead of tell
Some answers are worse as paragraphs. “What have you built?” in prose is a run-on list of six product names. As a grid of cards — each with its accent, status, and a link into the case study — it’s scannable, and every card is an actual affordance: tap one, you’re reading the case study.
That’s the bet behind generative UI. The model doesn’t just write text; it decides, for the right kind of question, to hand back a component. A music player you can press play on beats a sentence that says “I produce deep house.”
The four tools
Each tool renders one component and fires on one kind of intent. The interesting part is the right-hand column — the near-miss that deliberately stays prose.
play_trackRenders
An inline player for Peter's Side B deep-house track — play/pause, a seekable waveform, title and artist.
Fires when
Asks to play, hear, or listen to your track / Side B / deep house / music.
show_portfolioRenders
Mini portfolio cards — one per product, each a link into its case study.
Fires when
Wants an overview (“what do you build”) or names a Frenti product: BonVivant, Pico, AskBahia, VisitMaraú, Vaifredo, BrainCell.
Stays prose
“Tell me about your healthcare work” / “your Scripps work” — a domain of work, not a product. Answered in prose.
show_skillsRenders
A grid of top skill cards — the broad stack at a glance.
Fires when
Wants the broad overview of skills / stack / tech / how Peter builds.
Stays prose
“Do you use Postgres?” — one specific tool. Answered in prose.
show_resumeRenders
A structured career-history card — roles, dates, highlights, a link to /about.
Fires when
Asks for the resume / CV / career history / “where have you worked.”
Stays prose
“What did you do at Scripps?” — one employer. Answered in prose from the about page.
The rule that keeps tools from over-firing
The failure mode of generative UI isn’t the tool breaking — it’s the tool firing when it shouldn’t. Ask about one employer and get a full resume card; ask about one library and get the whole skills grid. The fix is a single distinction, written into the system prompt:
Overview intent renders UI. A single, specific question is answered in prose. One product by name, or “what do you build” → portfolio cards. One employer, one tool, one role → a grounded paragraph.
Two more constraints keep turns clean: at most one tool per turn, and at most one short lead-in sentence before it — no prose answer after the tool, because the component already is the answer. A hard step cap (stopWhen: stepCountIs(3)) stops the model from chaining tool calls into a runaway turn.
How it works, end to end
- 1.Tool definitions go to the model. The chat route registers the four tools (peterwd scope only) alongside the system prompt, each with a description and the firing rules above.
- 2.The model picks at most one. Based on intent, it either answers in prose or calls a single tool — e.g.
show_portfoliowithslugsto scope to named products, or no slugs for all six. - 3.The server executes itinto a typed payload —
{ kind: "side-b" },{ slugs: [...] }, and so on. No prose is generated for the body; the payload is the message part. - 4.The client renders the match. A branch keyed on the tool name and an
output-availablestate swaps the payload for its React component —tool-show_portfoliorenders the portfolio cards, and so on.
// one branch per tool, keyed on name + state
if (part.type === 'tool-show_portfolio' &&
part.state === 'output-available') {
const { slugs } = part.output;
return <ChatPortfolioCards slugs={slugs} />;
}What generative UI buys you
- Scannable answers.A grid beats a comma-separated list the moment there’s more than two of anything.
- Real affordances. Cards link into case studies; the player actually plays. The answer is a thing you can use, not just read.
- Restraint is the feature.Because a specific question stays prose, the cards mean something when they do appear — they signal “here’s the overview,” not noise.
- Still grounded.Tools change the shape of the answer, not its truth. The retrieved facts — and the honesty rules — apply exactly as they do to prose.
Generative UI is how the chat answers; the companion guides cover where those answers come from. See How the chat works: RAG, grounded in my own writing for how the chat retrieves and grounds its answers. Try it yourself — ask the chat on the home page “what do you build?” and watch the cards render. Or reach me at .