AI Asistent by ZCLOUD

Implementační návod

Připojení systému (městský portál, e-shop, helpdesk) je adaptér ve vašem backendu. Nikdy nevyžaduje změnu v AI Asistentovi. Příklady používají doménu produktů.

Stav: vývoj, zatím bez veřejného provozu. Přístup na vyžádání.

Obsah

Krok 0: poskytnutí přístupu

Přístup zřizuje provozovatel, ne integrovaná aplikace, pomocí admin klíče (hlavička X-Admin-Key, nastavený v konfiguraci AI Asistenta, alespoň 32 znaků).

POST /admin/tenants
{"name":"Acme Shop","monthlyRequestLimit":20000}

POST /admin/tenants/{id}/keys
{"name":"backend production"}

Limit je 1 až 1 000 000 požadavků měsíčně. Odpověď na vytvoření klíče obsahuje surový key jen jednou, ukládá se pouze jeho SHA-256 hash.

Klíč uložte do správce tajemství vašeho backendu a používejte ho jen na serveru jako Authorization: Bearer <key>. Nikdy ho nedávejte do prohlížeče ani mobilní aplikace. Admin klíč z aplikace nepoužívejte vůbec.

Krok 1: definice polí

Filtry vašeho vyhledávání popište jako fields (1 až 20 položek).

VlastnostPravidlo
nameunikátní, ^[A-Za-z][A-Za-z0-9_]{0,49}$
typestring, integer, boolean, date, stringArray
descriptionpovinný, nejvýše 500 znaků
requiredboolean
allowedValuesvolitelné, 1 až 100 různých řetězců po nejvýše 100 znacích, jen pro string a stringArray

Data se vracejí ve tvaru yyyy-MM-dd. Pro uzavřené množiny hodnot vždy použijte allowedValues.

Krok 2: kontext, historie, jazyk

  • question je povinná, neprázdná, nejvýše 2000 znaků.
  • context: JSON objekt, nejvýše 8000 serializovaných znaků. Pošlete fakta, která model nemůže znát (měna, časové pásmo, povolená data, město uživatele). Bere se jako data, ne jako instrukce.
  • history (volitelné): nejvýše 10 položek, role user nebo assistant, obsah do 2000 znaků.
  • locale: cs-CZ (výchozí) nebo en-US.
  • Celé tělo požadavku je omezeno na 128 KiB.

Krok 3: vlastní hledání s autorizací

Vrácené parametry nejdřív zvalidujte (rozsahy, vlastnictví, obchodní pravidla), pak dotazujte vlastní databázi. Použijte vlastní pravidla viditelnosti (publikováno, skladem, patří tomuto uživateli). Odpověď modelu nikdy není autorizační rozhodnutí. Neodesílejte nepublikované, skryté ani zrušené záznamy.

Krok 4: záznamy jako zdroje a vykreslení

Pošlete nejvýše 20 záznamů jako sources:

VlastnostLimit
idunikátní, do 100 znaků
titledo 200 znaků
textdo 4000 znaků; součet textů všech zdrojů nejvýše 40 000 znaků
urlabsolutní HTTPS, do 2048 znaků, bez přihlašovacích údajů
updatedAtvolitelné

Pole answer vykreslete jako escapovaný prostý text a nikdy ho neberte jako HTML. Vrácené sources zobrazte jako karty s odkazy na vaše vlastní stránky. Samotná odpověď neobsahuje odkazy.

Úplné příklady (doména produktů)

POST /v1/interpretations

Požadavek:

POST /v1/interpretations
Authorization: Bearer <tenant key>
Content-Type: application/json

{
  "question": "Do you have a tent under 3000 CZK in stock?",
  "context": { "currency": "CZK" },
  "fields": [
    { "name": "category", "type": "string", "description": "Product category.", "required": true, "allowedValues": ["outdoor", "books"] },
    { "name": "maxPrice", "type": "integer", "description": "Maximum price in CZK.", "required": false },
    { "name": "inStock", "type": "boolean", "description": "Only items in stock.", "required": false }
  ]
}

Odpověď. status je ready nebo needs_clarification; usage nese počty tokenů od poskytovatele, known je false, pokud je nenahlásil.

{
  "requestId": "...",
  "status": "ready",
  "parameters": { "category": "outdoor", "maxPrice": 3000, "inStock": true },
  "clarification": null,
  "usage": { "inputTokens": 812, "outputTokens": 64, "known": true }
}

U needs_clarification obsahuje clarification otázku pro uživatele a povinné parametry mohou chybět. Pole se vrací s deklarovanými typy, backend je přesto musí znovu zvalidovat a vynutit.

POST /v1/answers

Požadavek se záznamy, které vrátilo vaše hledání pro dané parametry:

POST /v1/answers
Authorization: Bearer <tenant key>
Content-Type: application/json

{
  "question": "Do you have a tent under 3000 CZK in stock?",
  "sources": [
    { "id": "tent", "title": "Family tent", "text": "Family tent: Four-person tent for family trips. Price 2490 CZK.", "url": "https://shop.example/products/tent", "updatedAt": "2026-09-30T08:00:00+00:00" }
  ]
}

Odpověď. status je answered nebo insufficient_sources; sources jsou jen vstupní záznamy, které model citoval.

{
  "requestId": "...",
  "status": "answered",
  "answer": "Yes, the Family tent is available for 2490 CZK.",
  "sources": [
    { "id": "tent", "title": "Family tent", "url": "https://shop.example/products/tent", "updatedAt": "2026-09-30T08:00:00+00:00" }
  ],
  "usage": { "inputTokens": 640, "outputTokens": 48, "known": true }
}

Prázdné pole sources vrátí deterministickou lokalizovanou odpověď insufficient_sources, bez volání modelu a bez čerpání kvóty. Členství zdroje se ověřuje, ale to neprokazuje, že každá věta je podložena citovaným textem.

curl a C#

Příklady jsou serverové. Adresu https://ai.example.invalid nahraďte adresou, kterou dostanete při zřízení přístupu.

curl -sS https://ai.example.invalid/v1/answers \
  -H "Authorization: Bearer $AIA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"question":"Do you have a tent?","sources":[]}'
using System.Net.Http.Headers;
using System.Net.Http.Json;

// Server-side only: the key comes from a secret store, never from a client app.
var http = new HttpClient
{
    BaseAddress = new Uri("https://ai.example.invalid/"),
    // Must exceed the service's own 30 s model timeout.
    Timeout = TimeSpan.FromSeconds(40)
};
http.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AIA_KEY"));

var response = await http.PostAsJsonAsync("v1/answers", new
{
    question = "Do you have a tent under 3000 CZK in stock?",
    sources = new[]
    {
        new { id = "tent", title = "Family tent",
              text = "Family tent: Four-person tent. Price 2490 CZK.",
              url = "https://shop.example/products/tent" }
    }
});

if (!response.IsSuccessStatusCode)
{
    // ProblemDetails: log "code" and "traceId", never the key.
    Console.Error.WriteLine(await response.Content.ReadAsStringAsync());
    return;
}
var json = await response.Content.ReadAsStringAsync(); // render "answer" as escaped plain text

Kdy přeskočit interpretaci

Pokud váš systém už ví, co hledat (formulář s filtry, textové hledání v FAQ), nevolejte POST /v1/interpretations. Spusťte hledání a zavolejte rovnou POST /v1/answers. Ušetříte jednu placenou operaci na otázku. Doména faq v ukázce takto funguje.

Chyby

Chyby jsou ProblemDetails se stabilním code a traceId. Logujte traceId, nikdy klíč.

StatusVýznamCo udělat
400validation_error nebo invalid_request: neplatný vstup (limity, duplicitní názvy polí, špatná URL zdroje, null položky, vadný JSON)Opravit požadavek. Neopakovat beze změny.
401unauthorized: neznámý, zrušený nebo vypnutý klíč (jedna obecná odpověď)Zkontrolovat klíč a tenanta, neopakovat ve smyčce.
413request_too_large: tělo přes 128 KiBPoslat méně nebo kratší zdroje.
429concurrency_limit nebo quota_exceededPočkat na hlavičku Retry-After (u kvóty do dalšího měsíce UTC), pak opakovat.
502Chyba poskytovatele nebo neplatný výstup modelu (provider_error, provider_invalid_response)Zobrazit náhradní obsah. Pokus mohl být započítán.
503provider_unavailable nebo service_unavailable (chyba databáze)Zobrazit náhradní obsah, později zkusit znovu s odstupem.
504provider_timeoutNeopakovat naslepo.

AI Asistent placené operace sám neopakuje a nenabízí idempotenci. Timeout je nejednoznačný: poskytovatel mohl požadavek zpracovat a zpoplatnit a neúspěšná volání si drží rezervaci kvóty. 502 a 504 opakujte jen záměrně, s limitem, nikdy v automatické smyčce. Váš vlastní HTTP timeout má být delší než 30sekundový timeout modelu v službě.

Rotace klíčů a spotřeba

  1. Vytvořte druhý klíč (POST /admin/tenants/{id}/keys).
  2. Přepněte volající backend na nový klíč.
  3. Starý klíč zrušte: DELETE /admin/tenants/{id}/keys/{keyId}. Zrušení platí okamžitě.

Tenanta lze vypnout přes PATCH /admin/tenants/{id} s {"enabled":false}. Aktuální měsíční (UTC) počítadla vrací GET /v1/usage.

Kontrolní seznam integrace

  • Tenant vytvořen, klíč vydán jednou a uložen na serveru ve správci tajemství.
  • Pole definována v rámci limitů, pro uzavřené množiny použito allowedValues.
  • Kontext nese časové pásmo, údaje závislé na jazyku a vše, co model nemůže znát.
  • Interpretace přeskočena, pokud jsou filtry už známé.
  • Interpretované parametry validuje backend a vynucuje obchodní pravidla.
  • Hledání uplatňuje autorizaci a viditelnost (nepublikované, skryté a zrušené záznamy se nikdy neposílají).
  • Nejvýše 20 zdrojů, text do 4000 znaků, absolutní HTTPS URL na vaší doméně.
  • Odpověď vykreslena jako escapovaný prostý text, karty zdrojů vytvořeny z vrácených sources.
  • Prázdný výsledek, needs_clarification a insufficient_sources mají cestu pro uživatele.
  • Tabulka chyb implementována, placené timeouty se nezkouší naslepo znovu.
  • Ruční kontrola kvality provedena nad evaluační sadou (nikdy se nespouští automaticky).

Referenční adaptéry

Ukázka samples/IntegrationDemo předvádí tři různé domény nad stejným nezměněným API: městské akce (data, časové pásmo, zrušení), produkty (výčty a dostupnost) a FAQ (volný text, bez interpretace, nepublikovaný článek se nikdy neposílá). Offline spuštění:

dotnet run --project samples/IntegrationDemo -- --offline --domain faq
dotnet run --project samples/IntegrationDemo -- --offline --domain events
dotnet run --project samples/IntegrationDemo -- --offline --domain products