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: klíč a tenant
- Krok 1: definice polí
- Krok 2: kontext, historie, jazyk
- Krok 3: vlastní hledání
- Krok 4: zdroje a vykreslení
- Příklady požadavků
- curl a C#
- Kdy přeskočit interpretaci
- Chyby
- Rotace klíčů a spotřeba
- Kontrolní seznam
- Referenční adaptéry
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).
| Vlastnost | Pravidlo |
|---|---|
name | unikátní, ^[A-Za-z][A-Za-z0-9_]{0,49}$ |
type | string, integer, boolean, date, stringArray |
description | povinný, nejvýše 500 znaků |
required | boolean |
allowedValues | volitelné, 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
questionje 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, roleuserneboassistant, obsah do 2000 znaků.locale:cs-CZ(výchozí) neboen-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:
| Vlastnost | Limit |
|---|---|
id | unikátní, do 100 znaků |
title | do 200 znaků |
text | do 4000 znaků; součet textů všech zdrojů nejvýše 40 000 znaků |
url | absolutní HTTPS, do 2048 znaků, bez přihlašovacích údajů |
updatedAt | volitelné |
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 textKdy 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íč.
| Status | Význam | Co udělat |
|---|---|---|
| 400 | validation_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. |
| 401 | unauthorized: neznámý, zrušený nebo vypnutý klíč (jedna obecná odpověď) | Zkontrolovat klíč a tenanta, neopakovat ve smyčce. |
| 413 | request_too_large: tělo přes 128 KiB | Poslat méně nebo kratší zdroje. |
| 429 | concurrency_limit nebo quota_exceeded | Počkat na hlavičku Retry-After (u kvóty do dalšího měsíce UTC), pak opakovat. |
| 502 | Chyba poskytovatele nebo neplatný výstup modelu (provider_error, provider_invalid_response) | Zobrazit náhradní obsah. Pokus mohl být započítán. |
| 503 | provider_unavailable nebo service_unavailable (chyba databáze) | Zobrazit náhradní obsah, později zkusit znovu s odstupem. |
| 504 | provider_timeout | Neopakovat 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
- Vytvořte druhý klíč (
POST /admin/tenants/{id}/keys). - Přepněte volající backend na nový klíč.
- 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_clarificationainsufficient_sourcesmají 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