Jak poskytovat data jazykovým modelům aneb RAG v praxi

Velké jazykové modely mají (obecně) schopnost napojit se na externí zdroje dat, která pak používají při získávání odpovědí na dotaz uživatele. Možná tuto schopnost znáte pod zkratkou RAG (Retrieval-Augmented Generation).

Využitím externích dat se velké jazykové modely snaží svou odpověď zlepšit, tedy ji např. zpřesnit, mít ji aktuální či důvěryhodnější. Díky externím datům může velký jazykový model automatizovaně pracovat i s takovými informacemi, na kterých nebyl trénovaný.

Technika RAG se typicky využívá při tvorbě agentů nebo v aplikacích, které používají modely pro analýzu specifických uživatelských dat, jež vznikají až v průběhu života aplikace (např. aplikace pro vyhodnocení vyváženosti stravy poskytneme seznam všeho, co si uživatel zaznamenal, že daný den snědl).

Cílem článku je ukázat programový přístup z aplikace k modelům OpenAI pomocí služby OpenAI API včetně využití techniky RAG. Je použit C# a .NET 9. Typickým čtenářem je IT analytikIT solution architekt. Dále to jsou vývojáři, kteří chtějí ukázat cestu.

Motivací k napsání byl nedávný běh mého školení Umělá inteligence v praxi: Od nápadu k reálnému nasazení, které se na AI a LLM dívá z vyšší perspektivy (odpovídá na otázky PROČ a CO). Byl jsem však často dotazován JAK. JAK to ten model dělá? JAK zavolá API? JAK získá data pro své vyhodnocování? Protože to bylo nad rámec obsahu školení, rozhodl jsem se, že odpovím alespoň tímto způsobem.

Pojmová poznámka 1: RAG jako takový ve svém primárním významu pracuje s vektorovou reprezentací dat. V praxi se však každý mechanismus, který externí data doplní do promptu, bere jako forma RAG. V tomto článku budu RAG chápat právě tímto volnějším způsobem.

Pojmová poznámka 2: Data a informace tady budu chápat ve volnějším, synonymickém smyslu. Velké jazykové modely též budu nazývat pouze modely nebo zkratkou LLM (Large Language Model).

Krok 1: Stavíme kulisy

Řekněme, že chceme aplikaci, která bude uživateli vyhodnocovat jeho poslední sportovní výkon. K tomu samozřejmě potřebuje zaznamená data.

Tyto záznamy dělají „krabičky“ typu Garmin či aplikace jako je Strava. My samozřejmě můžeme nechat uživatele, ať si data někde exportuje a pak to sám ručně poskytne modelu, ale s tímto přístupem nás uživatel pošle hodně brzy k šípku. Naším cílem je poskytnout modelu data sice automaticky, ale pouze tehdy, když si o ně explicitně řekne.

Krok 2: Výběr platforem

Abychom mohli využít některý z modelů, potřebujeme si říct, který to bude. Já v tomto případě použiji modely od OpenAI. OpenAI je společnost, která nabízí modely jako jsou např. GPT-5 nebo GPT-3.5.

K modelům od OpenAI lze přistupovat různými způsoby. Nejznámější je přístup pomocí aplikace ChatGPT. Ta je dostupná na webu, ale i jako např. aplikace na telefonu či na počítači. Touto cestou dnes nepůjdeme.

Druhá možnost je využít služby OpenAI API, která zpřístupňuje modely pomocí RestAPI. A přesně tudy se vydáme.

Dále je nutné vytvořit nějakou aplikaci, které k modelu bude přistupovat. Pro co nejjednodušší ukázku použiji obyčejnou konzolovou aplikaci. Tu budu psát v C# pro .NET 9. Z externích balíčků využiji pouze RestSharp (jednoduchý .NET REST klient). Obalení JSON si pak tvořím sám (vy můžete použít oficiální knihovnu).

Upozornění: Aplikaci vytvářím tak, abych pomocí ní snadněji ukazoval princip poskytování dat pro model. Není robustní, nebudu příliš refaktorovat, rozdělovat aplikaci do různých modulů, nebudu ošetřovat všechny chybové stavy apod. Stejně tak definice agentů bude primitivní. Cílem je opravdu ukázat funkční příklad, na které se naučíte poskytovat modelu data, nikoliv tvorbu robustních enterprise aplikací. Celý zdrojový kód najdete na GitHubu.

Krok 3: Nastavení OpenAI API

Abychom mohli využívat OpenAI API, musíme tam samozřejmě mít účet, ve kterém si založíme nový projekt a k němu si vytvoříme odpovídající API klíč (a, bohužel, musíme také přidat platební kartu, ze které si bude OpenAI strhávat peníze). Nezapomeňte nastavit i limit, abyste se pak při placení nedivili.

Krok 4: Kostra aplikace

Základní kód, který pošle dotaz a zobrazí výsledek, pak může vypadat takto:

Po spuštění se do OpenAI API pošle takovéto tělo:

A OpenAI API může poskytnout takovýto výsledek:

Krok 5: Úprava aplikace tak, abychom mohli komunikovat s modelem dle potřeby včetně uchovávání historie komunikace

Nyní kód trošku upravíme, abychom si mohli s modelem povídat, dokud nás to bude bavit. Současně budeme uchovávat historii komunikace, aby model věděl, o čem jsme se v dané komunikaci dosud bavili a mohl s tím pracovat. Stále však zatím bez RAG.

Taková komunikace pak může vypadat např. takto:

Příklad dotazu:

Krok 6: Konečně RAG

Výborně. Základ bychom měli, můžeme konečně přistoupit k tomu, abychom v případě potřeby (tedy když si model řekne) mohli dát modelu potřebná data.

Jak to (ne)funguje v případě OpenAI API

Předně si je třeba na rovinu přiznat, že OpenAI API (resp. OpenAI model) žádné vaše vlastní RestAPI nezavolá. Stejně tak se nepřipojí do vaší databáze nebo neprohlídne vaše sdílené síťové úložiště, a to ani v případě, že to máte veřejně dostupné.

Takto určitě ne!

Důvodem je bezpečnost a ochrana společnosti OpenAI sebe sama před různými nařčeními. Představte si situaci, kdy do vyhledávání odpovědi chcete poslat zákaznická data. Už jenom tohle zavání průšvihem. Opravdu byste někomu naprosto cizímu a z nitra internetu řekli: Hele, tady máš napojení na moji databázi zákazníků, tak si zjisti, co potřebuješ? Co když se data cestou „odkloní“ k vaší konkurenci? Tudy cesta určitě nepovede.

Role při komunikaci s modelem

Když se podíváte na způsob komunikace mezi naší aplikací a modelem, jsou v rámci ní používané různé role:

  • Role system odpovídá asistentovi tak, jak to známe např. při použití ChatGPT (tam se nazývají GPT).
  • Role user je text, který modelu zasílá sám uživatel.
  • Role assistant je pak odpověď od modelu, kterou uživateli typicky zobrazujeme.

V našem dosavadním příkladě je tedy roli system nastaven text: „Jsi specialista na vyhodnocování sportovních výkonů amatérských sportovců a hobíků.“

V příkladu komunikace výše je pak v roli user např. text začínající „Ahoj, dneska se mi fakt nechce…“. Konečně roli assistant odpovídá text začínající „Ahoj, pokud už máš najeto…“.

Uvedené tři role však nejsou jediné, které se v rámci komunikace používají.

A právě zde to začíná být zajímavé. V případě, že model vyhodnotí, že potřebuje další data (informace), dá vám to na vědomí tím, že v odpovědi jako roli uvede tools. Jinými slovy, než aby model sám někde něco shromažďoval, přenechá tento úkol vám.

Takto je to lepší

Role tools

Pomocí role tools vám model jasně dává najevo, že začal zpracovávat výsledek, ale aby mohl pokračovat, musíte mu dodat další vstup. To, kde a jak seženete data, je teď vás. A až je budete mít, v rámci komunikace je pošlete modelu zase zpět.

Postup zjednodušeně může vypadat takto:

OpenAI umí pracovat s různými typy nástrojů, nás bude zajímat požadavek na zavolání funkce. Pokud se chcete do problematiky ponořit hlouběji, rozhodně se začtěte do dokumentace včetně vysvětlení principu používání funkcí.

Požadavek na volání funkce znamená, že model pozastavil zpracování vašeho požadavku, protože vyhodnotil, že pro lepší odpověď potřebuje další data. A tato data lze získat tím, že vám řekne, ať zavoláte nějakou operaci. To, zda a kdy vyhodnotí, že potřebuje další data, příliš neovlivníte.

Rozhodně ale ovlivníte dostupné funkce. Ty totiž dáváte jako součást požadavku a může jich být více. Nejdůležitější je uvést u každé funkce užitečný popis, aby model věděl, k čemu funkce slouží.

Příklad požadavku s definicí jedné funkce (nazvané getLastWorkout):

Model požadavek přijme a zjistí, že neví nic o datech z poslední jízdy, ale z definici funkcí zjistí, že si o ta data může říct. Tedy vám odpoví např. takto:

Nyní je na vás, abyste data sehnali. Ta mohou být v nějakém souboru, v databázi nebo jsou ve výsledku volání nějaké jiné služby. Zde je kreativita čistě na vaší straně. Jakmile data seženete, pošlete je zpět modelu (do historie musíte samozřejmě vložit to, že se model na data ptal a pak i vlastní data). Příklad může vypadat takto:

Díky tomu model už má vše, co potřebuje, a tedy vám dá odpověď s vyhodnocením.

Úprava dosavadního kódu

Ve stávajícím kódu musíme provést několik zásadních změn.

  1. Definovat funkce, které může po nás chtít model vykonat.
  2. Při odpověti typu tool_calls:
    • Získat data.
    • Znovu zavolat model se získanými daty.

Definování funkcí provedeme v inicializaci požadavku (metodu InitRequest). Dále upravíme metodu RunOpenAiExample, abychom mohli zavolat model znovu a nakonec, to nejdůležitější, je metoda ProcessResponse, kde kromě jiného získáváme data.

Právě v ProcessResponse nyní můžeme zavolat cokoliv jiného (v příkladu zde pouze načtu soubor, ale klidně bych mohl do nějaké databáze či provolat právě třeba portál Strava).

Celý zdrojový kód najdete na GitHub.

Ukázka komunikace uživatele s naší aplikací:

Závěr

A to je vlastně celé. Článek včetně kompletního kódu ukazuje, JAK je možné implementovat principy RAG při komunikaci s konkrétními modely od OpenAI. Je zřejmé, že v případě jiných modelů může být postup odlišný.

Buďte první kdo přidá komentář

Napište komentář

Vaše e-mailová adresa nebude zveřejněna.


*