Představ si agenta, který má za úkol „projdi objednávky a najdi neobvyklé vzory". Připojí se k databázi pod účtem, který zdědil po nějakém interním nástroji z roku 2023, a do deseti minut přečte celou tabulku zákazníků včetně telefonních čísel a firemních slev, ke kterým neměl nikdy mít přístup. Nikdo to nerozbil — jen přesunul bordel z aplikace o vrstvu níž. Snowflake to loni na konci srpna pojmenovalo napříč: access control postavený pro lidi a dashboardy neumí zachytit agenty, kteří jednají sami za sebe. Řešení přitom není další vrstva v agentovi, ale oprávnění nastavená přímo na datech.
V tomto návodu ti ukážu, proč klasické role přestávají stačit, co přesně znamená „governance v datové vrstvě" a jak ji nastavíš v Postgresu za odpoledne — včetně konkrétních SQL příkazů, které zkopíruješ a upravíš.
Proč role pro lidi selhávají u agentů
Access control většiny firem vznikal pro lidi, kteří se dívají na dashboardy. Člověk se přihlásí, pod svým jménem spustí report, něco si přečte a zavře záložku. Na tento model jsou nastavené role, scope i audit. Agent funguje úplně jinak a rozbíjí to třemi způsoby:
1. Nejedná pod svou identitou. Agent běží typicky pod service účtem nebo přebírá identitu uživatele, který ho spustil. V obou případech se oprávnění agenta a člověka míchají dohromady. Když agent čte data, které by jeho spouštěč vidět neměl (ale service účet ano), audit log ukazuje jen „nástroj", ne skutečného aktéra.
2. Čte objemy, ne obrazovky. Člověk v dashboardu vidí agregát za svůj region. Agent ve stejném nastavení stáhne milion řádků po jednotlivých zákaznících, protože ho o to nikdo nezablokoval — row-level omezení prostě chybí. Rychlost mění charakter rizika z „nekdo si něco přečte" na „někdo vystáhne celou tabulku".
3. Řetězí akce bez pauzy. Agent, kterému povolíš čtení faktur, si za chvíli logicky řekne, že potřebuje i dodavatele, pak kontakty na dodavatele a nakonec smlouvy v přílohách. U člověka mezi těmito kroky sedí povolovací proces. U agenta mezi nimi sedí dvě sekundy a další iterace smyčky.
Společnosti jako Snowflake (Cortex AI Gateway) nebo BigID na to reagují stejně: pokud agent jedná autonomně, musí být hranice jeho přístupu vynucená tam, kde data leží. Ne v promptu, ne v system promptu, ne v „agentovi to zakážeme" — to všechno agent oběhne, pokud mu to vyhovuje, a hlavně to není vynutitelné. Prompt injection a obrana na úrovni modelu jsme si rozebrali zvlášť — dnes jdeme o vrstvu níž, kde si agent nemůže nic namluvit.
Co governance v datové vrstvě znamená
Princip je jednoduchý: databáze (nebo warehouse) je poslední instance, která rozhoduje, co agent vidí. Nezáleží na tom, co agentovi řekl prompt, jaký má plán úkolu nebo co si myslí jeho orchestrace. Když přijde s dotazem, databáze mu vrátí jen to, k čemu má jeho role oprávnění, v rozsahu, který mu dovolují policy.
V praxi to znamená čtyři stavební kameny:
- Dedikovaná role pro agenta — žádné sdílení s jinými nástroji.
- Přístup jen k explicitně povoleným objektům — default deny, ne „vše kromě".
- Row-level security — agent vidí řádky, ke kterým má jeho úloha vztah, ne celé tabulky.
- Audit na úrovni dotazů — každý SELECT s rolí agenta v logu, ideálně s tagem úlohy.
Nastavení v Postgresu: konkrétní kroky
Ukážu si to na reálném scénáři: analytický agent, který má najít neobvyklé objednávky. Následující příkazy fungují v Postgresu 15+, ale stejný vzorec aplikuješ v MySQL 8, Snowflake nebo BigQuery — jen se změní syntaxe.
Krok 1: Vlastní role, žádné dědičné nároky.
CREATE ROLE agent_orders_analytics LOGIN PASSWORD '...';
REVOKE ALL ON SCHEMA public FROM agent_orders_analytics;
-- agent dostane přístup explicitně, objekt po objektu:
GRANT USAGE ON SCHEMA analytics TO agent_orders_analytics;
GRANT SELECT ON analytics.orders_enriched TO agent_orders_analytics;
Klíčové je REVOKE na začátku. Výchozí stav „nová role vidí, co mají ostatní" je přesně ten, který agenti zneužijí (nebo zneužije jejich chyba). Default deny otoč logiku: vše je zakázané, povoluješ jen to, co agent skutečně potřebuje pro svou úlohu.
Krok 2: View místo přímých tabulek.
Nikdy nedávej agentovi SELECT na surovou tabulku orders, která obsahuje jména, adresy a telefonní čísla. Vytvoř view, který citlivé sloupce odstraní nebo zamaskuje:
CREATE VIEW analytics.orders_enriched AS
SELECT
o.id,
o.created_at,
o.total_amount,
o.region,
c.segment -- agregovaný segment místo jména
FROM public.orders o
JOIN public.customers c ON c.id = o.customer_id;
-- žádný sloupec s PII, žádný contact_email, žádná adresa
Data, která agent k úloze nepotřebuje, prostě do view nedostaneš. Je to nejlevnější maskování, jaké existuje: sloupec, který ve view není, agent nikdy nevidí — a na rozdíl od pokynů v promptu to není možné obejít.
Krok 3: Row-level security pro rozsah.
Když agent analyzuje objednávky jen pro jeden region nebo jen pro B2B segment, řekni to databázi:
ALTER TABLE analytics.orders_internal ENABLE ROW LEVEL SECURITY;
CREATE POLICY agent_b2b_only ON analytics.orders_internal
FOR SELECT
TO agent_orders_analytics
USING (segment = 'b2b');
Od této chvíle agent i přes SELECT * dostane jen b2b řádky — databáze podmínku přidá do každého dotazu sama. Bez ohledu na to, jak si agent dotaz postaví nebo co mu prompt injection napoví.
Krok 4: Audit, který něco znamená.
Zapni logování a nauč se ho číst:
ALTER SYSTEM SET log_statements = 'all';
ALTER SYSTEM SET log_connections = on;
V logu pak hledáš dotazy pod rolí agent_orders_analytics. Pokud agent používá víc úloh, rozliš je application name (SET application_name = 'agent-anomalie-objednavky'), ať víš, která úloha který dotaz pustila. Tím se připojíš k observability, kterou jsme řešili u monitorování agentů v produkci — teď máš na konci řetězce tvrdá data z databáze, ne jen výstupy modelu.
Časté chyby, které uvidíš
Sdílený service účet pro tři agenty. Až jeden z nich udělá problém, nedohledáš, který to byl. Jedna role = jeden agent = jedna úloha. Ano, je to víc administrativy. To je cena za to, že audit log mluví pravdu.
Přístup „na později". „Dej mu zatím všechno, až pak to omezíme" je věta, kterou uslyšíš v každé firmě, která pak řeší incident. Omezování po nasazení nikdy nepřijde, protože agent mezitím někdo zapojí do produkce.
Testování na produkčních datech. Agent s plnou mocí necháváš běžet nad reálnými daty zákazníků? Vytvoř mu pohled na anonymizovanou kopii a plný přístup dej až po otestování chování. Tady je výhodou, pokud už používáš Postgres jako hlavní databázi aplikace — schéma a role máš k dispozici bez nové infrastruktury.
Spoléhání se na prompt. „Do systémového promptu jsme napsali, že má číst jen anonymizovaná data" není kontrola, je to doporučení. Prompt je instrukce pro model, oprávnění jsou instrukce pro databázi. Agent může prompt ignorovat (nebo být donucen ho ignorovat), ale GRANT a policy platí vždycky.
Shrnutí: kontrolovat agenta zespodu
Governance pro AI agenty nefunguje jako další nastavování v agentovi samotném. Funguje jako prostředí, do kterého agent vstupuje: vlastní role, explicitní granty, pohledy bez citlivých sloupců, row-level security a dotazový audit. Většina z toho je práce na jedno odpoledne a funguje to pro každého agenta, kterého kdy pustíš — Cursor, MCP nástroj, vlastní orchestraci, cokoli.
Nejlepší na tom je, že se nemusíš rozhodovat mezi bezpečností a rychlostí nasazení. Default-deny role s view a RLS ti umožní agenta pustit rychle, protože hranice, které překročit nemůže, jsou postavené dřív, než se poprvé připojí.
A pokud agenta provozuješ napříč větší organizací, stejný princip tě čeká o vrstvu výš: Central AI Gateway, tags pro jednotlivé úlohy a centralizovaný přehled o tom, kdo co četl. Ale pořadí je důležité — nejdřív databáze, pak gateway. Gateway bez datových oprávnění je jen hezčí dashboard.