Build an enhanced chat app
A release assistant reads a rollout policy and a security ticket. Asked whether the team can ship, it answers: “The launch is blocked by the security review.” Neither document contains that sentence. The answer combines a rule in one source with a fact in another.
Add an evidence panel to the chat: select that conclusion and inspect which passages influenced it. This is the selection-to-evidence interaction used in Strahl’s chat workspace.
Can we ship?
AssistantThe launch is blocked by the security review.
Selected answer → source evidence“The launch is blocked by the security review.”
Production rollout requires all critical findings to be closed.
SEC-17 remains open at critical severity.
Inspect the original passage before relying on the answer.
The conversation
Section titled “The conversation”The following small, illustrative transcript stands in for an answer saved by your chat backend. In your app, use the exact context and assistant output from the original generation.
| Source | Supplied text |
|---|---|
| Rollout policy | Production rollout requires all critical findings to be closed. |
| Security ticket | SEC-17 remains open at critical severity. |
| Assistant output | The launch is blocked by the security review. |
Searching for the answer verbatim finds neither source. Retrieval can find related documents; the attribution request asks which supplied passages influenced this particular output.
Connect the selection to Prism
Section titled “Connect the selection to Prism”Set up the attribute function from Quickstart
in prism.py. Save this as chat_evidence.py and run python chat_evidence.py:
from prism import attribute
messages = [ {"role": "system", "content": "Answer release questions using the supplied records."}, {"role": "user", "content": ( "Rollout policy: Production rollout requires all critical findings to be closed.\n" "Security ticket: SEC-17 remains open at critical severity.\n" "Can we ship?" )}, {"role": "assistant", "content": "The launch is blocked by the security review."},]
answer = messages[-1]["content"]result = attribute( conversation={"messages": messages}, target={ "ref": {"message_index": 2, "field": "content"}, "span": {"start": 0, "end": len(answer)}, },)
for excerpt in result["excerpts"]: print(excerpt["ref"], excerpt["text"]) for highlight in excerpt["highlights"]: print(" →", highlight["text"])This makes a live, billable attribution request. The mockup illustrates the intended interface; it does not predict the excerpts or scores this call returns.
Put the response in the chat panel
Section titled “Put the response in the chat panel”Store each completed answer with an immutable conversation snapshot. When a reader selects text, send its answer ID and selection to your backend; resolve them against that snapshot before calling Prism. Return the result to the panel associated with that same selection.
Render excerpt.text and shade its highlights. The highlight coordinates are
relative to the original message field, so subtract excerpt.start when drawing
inside an excerpt. This browser helper constructs a highlighted passage without
interpreting source text as HTML:
function renderExcerpt(excerpt) { const passage = document.createElement("p"); const characters = Array.from(excerpt.text); let cursor = 0;
for (const highlight of excerpt.highlights) { const start = highlight.start - excerpt.start; const end = highlight.end - excerpt.start; passage.append(characters.slice(cursor, start).join("")); const mark = document.createElement("mark"); mark.textContent = characters.slice(start, end).join(""); passage.append(mark); cursor = end; } passage.append(characters.slice(cursor).join("")); return passage;}The panel now connects a synthesized conclusion to inspectable source passages.
Use effective_span to show the output actually analyzed. Keep source titles and
“Open original” links in your application’s snapshot mapping.
Selections in rendered Markdown must map back to original text. JavaScript uses
UTF-16 positions, while Prism spans count Unicode code points; Array.from
handles the latter when rendering. Keep attribution associated with the answer
and selection that requested it so a late response cannot overwrite newer evidence.
For measured results from longer conversations, see the legal conclusion and financial synthesis examples.