DEV Community

Cover image for How I made AI disclosure part of the chatbot shell
SharpHaw
SharpHaw

Posted on

How I made AI disclosure part of the chatbot shell

If your chatbot’s AI disclosure lives in the welcome message, an ordinary copy edit can remove it.

That was the failure mode I wanted to eliminate in SharpOS Support. The result is simple: the product shell always renders an identity line before the conversation starts.

AI assistant · May make mistakes.
Enter fullscreen mode Exit fullscreen mode

The organisation can configure the assistant name, welcome copy, knowledge and fallback contact. It cannot disable that line.

This article is about the implementation model, not legal advice. Article 50 of the EU AI Act has applied since 2 August 2026, and the European Commission says providers of systems designed for direct two-way interaction should ensure that people are informed they are interacting with AI from the start of the first interaction.

Here is the architecture I use around that requirement.

1. Render identity outside generated content

The assistant should not be responsible for disclosing that it is an assistant.

Generated content can drift. Instructions change. Welcome messages are localised. A retrieval failure can produce a response before the intended introduction. The disclosure belongs in deterministic UI.

Conceptually, the shell owns it:

function AssistantIdentity() { return ( <p aria-label="AI assistant. May make mistakes."> <a href="/ai-transparency">AI assistant</a> <span aria-hidden="true"> · </span> <span>May make mistakes.</span> </p> ) }
Enter fullscreen mode Exit fullscreen mode

The real implementation details will differ. The important boundaries are stable:

  • the text appears before the first response;
  • it is not supplied by the model;
  • organisation-level configuration cannot clear it;
  • the link opens a fuller transparency explanation;
  • the accessible name still makes sense without the visual separator. Do not rely on a bot avatar or a clever product name to make AI “obvious”. Explicit text is cheaper to test.

2. Treat the knowledge base as an allow-list

The disclosure says what the system is. It does not constrain what the system says.

SharpOS Support grounds answers in approved knowledge. I think of that knowledge as an allow-list of claims rather than a folder of helpful documents.

A source should have:

type KnowledgeSource = { id: string status: "draft" | "approved" | "retired" ownerId: string reviewedAt: string content: string }
Enter fullscreen mode Exit fullscreen mode

Only approved, current sources should enter retrieval. A retired pricing page should not remain available because embeddings were generated once and forgotten.

The model instruction is equally important: answer from the supplied evidence, identify missing evidence and avoid filling the gap with general knowledge when the question is about this business.

3. Make uncertainty a typed product state

Do not model fallback as another friendly paragraph the model might choose to write.

The orchestration layer should be able to produce a result that is not an answer:

type SupportResult = | { type: "answer"; text: string; sourceIds: string[] } | { type: "fallback"; reason: "missing_knowledge" | "low_confidence" } | { type: "blocked"; reason: "policy" | "sensitive_request" }
Enter fullscreen mode Exit fullscreen mode

The UI then renders organisation-approved fallback copy and a real contact route.

In SharpOS Support, this is intentionally not a fake live-agent handoff. If the business provides an email, WhatsApp number or phone route, the fallback points there. If no person is waiting in a support queue, the interface should not imply that one is.

Typed states also make the behaviour measurable. You can count fallbacks without attempting to infer them later from prose.

4. Store enough context for review, not everything forever

Conversation review is part of the control loop.

Store the outcome type, source IDs, timestamps and the minimum conversation content needed for the business to diagnose gaps. Apply access controls and a retention policy appropriate to the personal data customers may put into a chat box.

The review queue should answer:

  • Which questions repeatedly fell back?
  • Which approved source produced the answer?
  • Which visitor suggestions recur?
  • Did the contact exit work?
  • Which knowledge source needs an owner or review date? This is more actionable than a total-message chart.

5. Test the shell, not only the model

Model evaluations will not catch a disclosure hidden below a mobile viewport.

I add deterministic product tests around the interaction:

test("shows AI identity before the first message", async ({ page }) => { await page.goto("/support") await expect(page.getByText("AI assistant")).toBeVisible() await expect(page.getByText("May make mistakes.")).toBeVisible() await expect(page.locator("[data-message]")).toHaveCount(0) })
Enter fullscreen mode Exit fullscreen mode

Then test the whole path:

  1. Open as a first-time visitor.
  2. Check the disclosure on desktop, mobile and zoomed layouts.
  3. Ask an answerable question and verify the source boundary.
  4. Ask an unsupported question and verify the typed fallback.
  5. Follow the contact route.
  6. Confirm the conversation appears in the review surface. ## The design rule

Product transparency should be deterministic. Model behaviour should be bounded. Failure should be explicit. Review should produce a change.

That is the difference between adding disclaimer copy and making disclosure part of the system.

Top comments (0)