Skip to main content

Answer feedback

Every finished answer, typed or spoken, has a thumbs up and a thumbs down. The votes tell the people improving the prompts which answers were not good enough and why, broken down by capability and by prompt set.

How it behaves​

  • A thumbs up is recorded at once.
  • A thumbs down is recorded at once too, and opens a card under the answer with reason chips and an optional note. Sending needs at least one reason or a note.
  • Skip closes the card; the thumbs down stands.
  • Pressing the other thumb switches the vote. Pressing the same one again withdraws it.

The reasons are one shared list (FEEDBACK_REASONS), used by the card, the API and the report:

IdShown as
incorrectWrong or made up
missedMissed something on the claim
off_targetDidn’t answer my question
unclearToo long or unclear
unsafeInappropriate or unsafe
otherOther

Why the reasons never reach the model​

They are asked for in a card, not in the chat box, on purpose. Anything typed in the chat box becomes part of the conversation. A complaint about an answer would colour every later answer and could be read as an instruction. The card posts to /api/feedback, and the chat history is untouched. A Vitest test and a Playwright spec both check this.

What is recorded​

The Worker mints a turnId for every text answer and forgets it. Because the Worker keeps no transcript, the browser sends the context with the vote: the claim id, the capability, the channel (text or voice), the surface (app or embed), the locale and brand, the model, the prompt fingerprint and the overridden prompt ids, and the content itself (the question, the answer and the lookups). Voice answers have no server turn, so the browser mints their turn id.

The contract is FeedbackRequest in packages/shared/src/feedback.ts. A null rating withdraws a vote; reasons and the note are dropped on anything but a thumbs down.

Content and the embed​

SurfaceStored with the vote
The demo appEverything, including the question, answer and lookups
An embedded hostThe vote, reasons, note and context, but not the question, answer or lookups, unless the element has feedback-content

In a host system, that text quotes a real claim. The host always gets the full vote, content included, in the claimpilot-feedback DOM event.

Where it goes​

One row per rated answer in a feedback table, keyed by turn id, in the same store as the claims (api/src/feedback/). With DATABASE_PROVIDER=none each vote is written to the Worker log as one JSON line instead. See Data stores.

Reading it back​

The Prompt Lab's Answer feedback panel calls GET /api/feedback and shows:

  • By capability: helpful and unhelpful counts for each capability.
  • By prompt set: the same per capability and fingerprint. The same Prompt Lab versions in any browser give the same fingerprint, so the effect of an edit can be measured without the server ever storing a prompt.
  • Recent: the latest votes (thumbs down by default), with reasons, note, question and the answer behind a disclosure.

Replay in Try It loads the rated question, its claim and its capability into the Prompt Lab runner. Open claim goes to the claim (demo votes only).

Code map​

PartWhere
Contract and reason listpackages/shared/src/feedback.ts
Turn id and fingerprintapi/src/chat/service.ts, api/src/prompts/fingerprint.ts
Route, port, adaptersapi/src/feedback/
Migrationdb/migrations/{d1,neon}/0002_feedback.sql
Thumbs and cardfrontend/src/chat/Feedback.tsx
What a click means and what is sentuseChat().feedback in frontend/src/chat/useChat.ts
Embed event and the content switchfrontend/src/embed/element.tsx, EmbeddedAssistant.tsx
Reportfrontend/src/promptlab/FeedbackPanel.tsx