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.

Dotknij, aby mówić

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

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.

Rozmowa idzie po WebRTC, odpowiedź wraca w około sekundę, a agent potrafi otwierać strony i widzieć, co jest na ekranie.

  • Sfera w treści

    Sfera w treści

    <hanc-ai-inline-call>

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

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

  • Sfera pływająca

    Sfera pływająca

    <hanc-ai-floating-call>

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

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

  • Pigułka

    Pigułka

    <hanc-ai-pill-call>

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

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

  • Pływająca pigułka

    Pływająca pigułka

    <hanc-ai-pill-floating-call>

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

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

  • Formularz oddzwonienia

    Formularz oddzwonienia

    <hanc-ai-callback>

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

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

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. Weź identyfikator agenta Ciąg w rodzaju 69d20781de6244c89509eb08, z panelu.
  2. Dodaj skrypt Jedna linia w <head>. Przez npm to jeden import, tagi rejestrują się same.
  3. Umieść tag Tam, gdzie rozmowa ma sens. Obowiązkowy jest tylko agent-id.
  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>
  • 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.

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.

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.

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.

  1. 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.

  2. 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.

  3. 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.

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"))
Działa w aplikacji RunOrJog
Działa w aplikacji RunOrJog

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.

  1. Twoja aplikacja
  2. Widget
  3. Hanc.AI
  4. Twój MCP
  5. Twoje dane

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

Pozwól agentowi zobaczyć zalogowanego użytkownika, i tylko jego
Podejście Do podrobienia Ocena
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
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

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.

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

11 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
Ma wyglądać jak Twoja strona, nie jak nasza
Atrybut Co ustala Domyślnie
agent-idKtóry agent odpowiada. Jedyny obowiązkowy
themeJeden z 11 motywów kolorystycznychdefault
sizeRozmiar w pikselach; na wąskich ekranach zmniejsza się sam370
button-start-textNapis na przyciskuTry to call
positionRóg dla form pływającychbottom-right
glow-intensitySiła poświaty, 0-20.8
audio-reactivityJak mocno sfera reaguje na głos3.0
terms-enabledOkno zgody przed rozmowąfalse
sound-enabledDźwięki początku i końcatrue
support-session-tokenToken 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.

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.

Sześciu pracowników AI Hanc w zestawach słuchawkowych stoi razem pod pomarańczowo-różowym pierścieniem światła