Widgety — Hanc.AI
5 form1 tag skryptu14 języków

Agent głosowy na Twojej stronie.
Jeden tag, bez backendu.

Pięć komponentów webowych, dzięki którym odwiedzający porozmawia z Twoim agentem — głosem, w przeglądarce, bez instalowania czegokolwiek. Rozmowa idzie po WebRTC, odpowiedź wraca w około sekundę, a agent potrafi otwierać strony i widzieć, co jest na ekranie.

Ta sfera to widget. Naciśnij i mów.

Pięć form

Wybierz formę, której potrzebuje strona

Ten sam agent, pięć sposobów, by do niego dotrzeć. Łącz je swobodnie — dwa tagi z jednym agent-id to zwykły układ.

Sfera w treści

Sfera w treści

370 px domyślnie, stoi w toku strony. Nadaj jej potrzebną szerokość; na wąskim ekranie zmniejsza się sama.

<hanc-ai-inline-call>

Sekcja na landingu, strona kontaktu — tam, gdzie rozmowa jest sensem bloku.

Sfera pływająca

Sfera pływająca

120 px, przypięta 32 px od prawego dolnego rogu. Trzy inne rogi do wyboru albo static, by wstawić ją w tok.

<hanc-ai-floating-call>

Dostępna z każdej strony i nie zajmuje miejsca w układzie.

Pigułka

Pigułka

Poziomy przycisk: po lewej sfera 48 px, po prawej Twój własny napis. Stoi w toku jak każdy inny przycisk.

<hanc-ai-pill-call>

W rzędzie przycisków, w nagłówku, wewnątrz karty.

Pływająca pigułka

Pływająca pigułka

Ten sam przycisk, przypięty 32 px od prawego dolnego rogu. Te same cztery rogi do wyboru.

<hanc-ai-pill-floating-call>

Gdy okrągła sfera zbyt rzuca się w oczy, a chcesz mieć ją zawsze pod ręką.

Formularz oddzwonienia

Formularz oddzwonienia

Wybór kraju, numer formatowany w trakcie wpisywania i jeden przycisk. Ile razy oddzwaniać, ustawia się na agencie, nie w znacznikach.

<hanc-ai-callback>

Dla tych, którzy nie będą rozmawiać przez przeglądarkę — i wszędzie tam, gdzie mikrofon jest nie na miejscu.

Pięć minut

Instalacja

Jeden atrybut obowiązkowy: identyfikator agenta z panelu. Klucze API nie mają czego szukać w znacznikach — wszystko, co trafia do HTML, widzi odwiedzający.

  1. 1

    Weź identyfikator agenta

    Ciąg w rodzaju 69d20781de6244c89509eb08, z panelu.

  2. 2

    Dodaj skrypt

    Jedna linia w <head>. Przez npm to jeden import — tagi rejestrują się same.

  3. 3

    Umieść tag

    Tam, gdzie rozmowa ma sens. Obowiązkowy jest tylko agent-id.

  4. 4

    Serwuj po HTTPS

    Na http:// przeglądarka nie da mikrofonu. Podczas pracy wystarczy localhost.

index.html
<!-- 1. the script, once, in <head> -->
<script src="https://unpkg.com/hanc-webrtc-widgets" async></script>

<!-- 2. the widget, wherever the call belongs -->
<hanc-ai-inline-call
  agent-id="69d20781de6244c89509eb08"
  size="320"
  theme="tangerine"
  button-start-text="Talk to us">
</hanc-ai-inline-call>
1 script tag 0 dependencies WebRTC

React

Poniżej wersji 19 nieznane propsy przekazywane są jako ciągi, więc obiektów tak nie oddasz. Owiń przez @lit/react — oficjalna droga, przy okazji daje typowanie i zdarzenia.

Next.js

Widget potrzebuje przeglądarki: 'use client' i dynamiczny import z ssr: false. Inaczej build wysypie się na renderowaniu serwerowym.

Vue

Działa bez opakowań. Powiedz bundlerowi, że tagi hanc-ai- to elementy niestandardowe.

Prowadzi stronę

Agent potrafi otwierać strony i widzieć, co jest na ekranie

Odpowiedź głosem często jest złym nośnikiem — nikt nie chce, by czytano mu tabelę dostaw. Dlatego agent może wysłać stronie polecenie i zapytać ją, na co właśnie patrzy odwiedzający. Kontrolę zachowuje Twój kod: to on decyduje, co wykona.

Hanc.AI
navigate
page_context
cart_state
Widget
navigate

Agent otwiera stronę u Ciebie — produkt, sekcję, wcześniej wypełniony formularz.

page_context

Strona odpowiada, gdzie jest odwiedzający i co jest pokazane, żeby agent mówił o tym produkcie, a nie ogólnie.

cart_state

Strona odpowiada, co leży w koszyku i ile kosztuje, żeby agent podał sumę na głos.

Każde polecenie przychodzi jako anulowalne zdarzenie DOM agent-command. Wywołanie preventDefault() znaczy «obsłużone»; respond(data) odsyła odpowiedź do agenta. Zignorujesz zdarzenie — agent dowie się, że nie zostało obsłużone, i wróci do mówienia.

Polecenie to prośba, nie rozkaz: sprawdzaj każdą ścieżkę, zanim nią pójdziesz. Przykład odrzuca wszystko, co nie jest ścieżką wewnątrz witryny.

widget.js
widget.addEventListener('agent-command', (event) => {
  const command = event.detail;

  if (command.type === 'navigate') {
    const path = command.payload?.path;
    // Only same-site paths. Never follow an absolute URL.
    if (!path?.startsWith('/') || path.includes('://')) return;
    event.preventDefault();          // "handled" — the ACK protocol
    router.push(path);
  }

  if (command.type === 'cart_state') {
    event.preventDefault();
    command.respond({                // answer travels back to the agent
      items: cart.map(i => ({ name: i.name, price: i.price })),
      total: cart.total,
      currency: 'UAH',
    });
  }
});

Działa na

milotec.com.ua

Agent doprowadza odwiedzającego do produktu, mówi, co jest na bieżącej stronie, i odczytuje koszyk.

Aplikacje mobilne

Ten sam widget w Twojej aplikacji

iOS i Android pokazują go w widoku webowym. Zbuduj małą stronę HTML w kodzie i wczytaj ją z bazą HTTPS — bez bezpiecznego źródła silnik nie odda stronie mikrofonu.

Agenta nie słychać

Widok webowy nie odtworzy dźwięku WebRTC, dopóki sesja audio aplikacji stoi w trybie odtwarzania. Przełącz ją na tryb rozmowy z wyjściem na głośnik na czas połączenia i przywróć po zakończeniu.

Agent odpowiada w złym języku

Widget bierze język z navigator.language, a w widoku webowym to język systemu, nie ten wybrany w aplikacji. Nadpisz go na starcie dokumentu — i załataj fetch, dopisując browser_language do żądania pokoju, którego wbudowany widget sam nie wysyła.

Mikrofon wymaga obu połówek

Uprawnienie systemowe dla aplikacji i uprawnienie dla strony w widoku webowym. Na Androidzie zapytaj o systemowe przed otwarciem ekranu; na iOS przyznaj żądanie strony w delegacie.

Ten sam widget w Twojej aplikacji
Działa w aplikacji RunOrJog
SupportView.swift
let html = """
<hanc-ai-inline-call
    agent-id="\(agentId)"
    support-session-token="\(sessionToken)"></hanc-ai-inline-call>
<script src="https://unpkg.com/hanc-webrtc-widgets" async></script>
"""

// An HTTPS base is required — without a secure origin
// the engine will not hand the page a microphone.
webView.loadHTMLString(html, baseURL: URL(string: "https://hanc.ai"))
Kto jest na rozmowie

Pozwól agentowi zobaczyć zalogowanego użytkownika — i tylko jego

Przekaż token w support-session-token. Widget dołącza go do rozmowy bez zmian, my przekazujemy go Twojemu systemowi w nagłówku X-Hanc-Support-Token, a Twój serwer MCP decyduje, kto to jest. Token jest dla nas nieprzezroczysty: nie rozbieramy go, nie weryfikujemy i nie przechowujemy.

Twoja aplikacja
Widget
Hanc.AI
Twój MCP
Twoje dane

Nigdy nie ufaj identyfikatorowi, który przychodzi wewnątrz tokena jako twierdzenie. Zapytaj wystawcę tokena, kto to jest, i pracuj z odpowiedzią.

PodejścieDo podrobieniaOcena
Jawny identyfikatorTrywialnieNie do przyjęcia
Zaszyfrowany identyfikatorNie, ale można go użyć ponowniePołowa rozwiązania
Token Twojego dostawcy logowaniaNie: podpis stawia dostawcaDziała
Jednorazowy talon serwerowyNie, i ponownie też nieNajlepszy

Włącz na połączeniu «wymagany rozpoznany rozmówca», a przy anonimowej rozmowie Twoje narzędzia w ogóle się nie podniosą — agent nie opowie o cudzym koncie, nawet jeśli kiedyś w Twoim kodzie pojawi się błąd.

Rozmowa bez tokena nie dostaje żadnego dostępu do konta. To zachowanie domyślne, nie opcja.

your_mcp_server.py
async def verified_user_id(ctx) -> str:
    token = ctx.request.headers.get("x-hanc-support-token", "").strip()
    if not token:
        raise ValueError("No signed-in user on this call.")

    # Ask the issuer who this is. Never read the id out of the token.
    resp = await http.get(f"{AUTH_URL}/auth/v1/user",
                          headers={"Authorization": f"Bearer {token}"})
    if resp.status_code != 200:
        raise ValueError("Token invalid or expired.")

    return resp.json()["id"]        # ← the trusted identity
Wygląd

Ma wyglądać jak Twoja strona, nie jak nasza

Jedenaście motywów, jasny i ciemny wedle ustawienia systemu lub wymuszone, i jeszcze około piętnastu atrybutów samej sfery.

default
emerald
rose
amber
cyan
purple
blue
white
black
tangerine
ember
AtrybutCo ustalaDomyślnie
agent-id Który agent odpowiada. Jedyny obowiązkowy
theme Jeden z jedenastu motywów kolorystycznych default
size Rozmiar w pikselach; na wąskich ekranach zmniejsza się sam 370
button-start-text Napis na przycisku Try to call
position Róg dla form pływających bottom-right
glow-intensity Siła poświaty, 0–2 0.8
audio-reactivity Jak mocno sfera reaguje na głos 3.0
terms-enabled Okno zgody przed rozmową false
sound-enabled Dźwięki początku i końca true
support-session-token Token zalogowanego użytkownika

Zdarzenia dla Twojego kodu

Żadne ze zdarzeń nie niesie danych: oba to zwykły Event, dostajesz więc sam fakt i moment. Treści rozmowy nie ma w żadnym z nich — transkrypcje i nagrania żyją w panelu i w API.

Bez owijania

Co chroni, a co ochroną nie jest

Kanał jest szyfrowany

Żądania sterujące po HTTPS, dźwięk po WSS i SRTP. Bez HTTPS przeglądarka i tak nie da mikrofonu.

Agent jest sprawdzany

Połączenie podnosi się tylko dla istniejącego identyfikatora agenta.

Częstotliwość jest ograniczona

Wydawanie dostępu do rozmowy jest dławione — przeciw atakom siłowym i nadużyciom.

Token sesji nie osiada

Czytany na nowo przy każdej rozmowie i nigdy nie trafia do naszej bazy.

Lista dozwolonych domen nie jest granicą bezpieczeństwa

Ta kontrola działa w przeglądarce. Przydaje się, żeby widget nie wystartował przez nieuwagę na cudzej kopii Twojej strony, ale nie przeszkodzi nikomu zwrócić się do usługi bezpośrednio. Traktuj jako publiczne wszystko, co agent powie anonimowemu rozmówcy, a resztę zamykaj tokenem sesji.

Co i gdzie zostaje

W przeglądarce

Dwa wpisy w localStorage: znacznik odwiedzającego i fakt zgody

Dźwięk

Strumieniem. Nagrywany tylko, jeśli nagrywanie jest włączone dla agenta

Transkrypcja

Zapisywana razem z nagraniem, do odczytu w panelu

Lokalizacja

Przetwarzanie i przechowywanie w UE

Ograniczenia, o których lepiej wiedzieć wcześniej

Bez HTTPS nie będzie mikrofonu

Testuj na localhoście albo bezpiecznej domenie

Safari chce gestu przed dźwiękiem

Rozmowa musi zaczynać się od kliknięcia

Blokery i proxy potrafią ciąć WebRTC

Zostaw formularz oddzwonienia jako drogę zapasową

W oknie prywatnym znacznik nie zostaje

Każda rozmowa wygląda jak pierwsza — to normalne

React poniżej 19 nie przekaże obiektów atrybutem

Owiń przez @lit/react

Renderowanie serwerowe psuje się przy bezpośrednim imporcie

Dynamiczny import z ssr: false

Twój telefon, który w końcu
pracuje dla Ciebie.

Zacznij za darmo w 60 sekund. Bez karty kredytowej. Anuluj w każdej chwili. Telefon sam odbiera połączenia.

Zacznij za darmo
Book a Meeting