DataFlow AI - od opisu potrzeby do raportu SQL

W rodzinie asystentów AI SoftwareStudio narzędzie o nazwie DataFlow AI odpowiada za dane. Menedżer opisuje własnymi słowami, jakich informacji potrzebuje, a asystent buduje zapytanie SQL i przygotowuje z niego raport. Według opisu producenta analitycy mogą dzięki temu zająć się interpretacją wyników, a nie pisaniem kodu zapytań.

Poniższy tekst rozkłada ten proces na etapy i pokazuje, co dzieje się między zdaniem wpisanym przez użytkownika a tabelą na ekranie. Opis dotyczy mechanizmów, na których opiera się każdy asystent tego typu. Nie jest specyfikacją modułów produktu ponad to, co podaje SoftwareStudio, a nazwy widoków i kolumn w przykładach są przykładowe. Szerszy kontekst rodziny asystentów opisuje artykuł o rodzinie asystentów SoftwareStudio AI Family.

Etapy przetwarzania pytania

Od pytania do wyniku prowadzi pięć etapów, a każdy ma innego wykonawcę. Tabela pokazuje, gdzie pracuje człowiek, gdzie aplikacja, a gdzie model językowy.

EtapWykonawcaWynik etapu
1. Opis potrzebyUżytkownikZdanie w języku polskim
2. Złożenie kontekstuAplikacjaOpis dozwolonych widoków i kolumn
3. Generowanie zapytaniaModel językowyInstrukcja SELECT w T-SQL
4. Kontrola i wykonanieAplikacja i SQL ServerWynik ograniczony uprawnieniami
5. PrezentacjaAplikacjaTabela z wynikiem i zapytaniem do wglądu
Pięć etapów obejmuje pracę człowieka i kodu aplikacji oraz wywołanie modelu i zapytanie do bazy.

Model językowy pracuje tylko na etapie trzecim. Pozostałe cztery są zwykłym kodem aplikacji i zwykłymi mechanizmami bazy danych, więc jakość wdrożenia zależy w większym stopniu od inżynierii wokół modelu niż od samego modelu.

Reguła: model tylko proponuje zapytanie. O tym, co zostanie wykonane i jakie wiersze zobaczy użytkownik, decydują uprawnienia bazy, a nie treść odpowiedzi modelu.

Kontekst schematu bazy dla modelu językowego

Model nie zna bazy firmy. Nie wie, że sprzedaż leży w tabeli z pozycjami dokumentów ani że kolumna z kwotą oznacza wartość netto. Przy każdym pytaniu aplikacja dołącza do polecenia opis schematu, a model buduje zapytanie wyłącznie z nazw, które w nim znajdzie.

Widoki raportowe zamiast tabel źródłowych

Udostępnienie modelowi pełnego schematu produkcyjnego ma dwie wady. Opis robi się długi i zużywa okno kontekstu, a wśród kolumn są takie, których asystent nie powinien odczytywać nigdy. Rozwiązaniem jest osobny schemat z widokami, zawierającymi tylko kolumny przeznaczone do raportowania i mające stabilne, czytelne nazwy.

CREATE SCHEMA rap;
GO
CREATE VIEW rap.SprzedazPozycje AS
SELECT p.PozycjaId, d.DataSprzedazy, d.KlientId, p.TowarId,
       p.Ilosc, p.WartoscNetto
FROM dbo.DokumentSprzedazy AS d
JOIN dbo.PozycjaSprzedazy AS p ON p.DokumentId = d.DokumentId
WHERE d.Status = 'zatwierdzony';

Widok z warunkiem statusu niesie od razu regułę biznesową: do raportu trafiają tylko zatwierdzone dokumenty. Asystent nie musi jej znać, więc nie może jej pominąć. Podobnie można udostępnić widok stanów z rejestru stanów magazynowych, bez dostępu do tabel, w których program zapisuje ruchy towaru.

Opis kolumn odczytany z bazy

Nazwa WartoscNetto jest czytelna dla człowieka, ale model nie wie, w jakiej walucie ani po jakich rabatach ją policzono. Opis można zapisać w rozszerzonych właściwościach widoku, a następnie odczytać go razem ze strukturą.

EXEC sys.sp_addextendedproperty
    @name = N'MS_Description',
    @value = N'Wartość pozycji w złotych, bez podatku VAT',
    @level0type = N'SCHEMA', @level0name = N'rap',
    @level1type = N'VIEW',   @level1name = N'SprzedazPozycje',
    @level2type = N'COLUMN', @level2name = N'WartoscNetto';
SELECT c.TABLE_NAME AS Widok, c.COLUMN_NAME AS Kolumna,
       c.DATA_TYPE AS Typ, CAST(ep.value AS nvarchar(200)) AS Opis
FROM INFORMATION_SCHEMA.COLUMNS AS c
LEFT JOIN sys.extended_properties AS ep
       ON ep.class = 1 AND ep.name = N'MS_Description'
      AND ep.major_id = OBJECT_ID(QUOTENAME(c.TABLE_SCHEMA) + N'.' + QUOTENAME(c.TABLE_NAME))
      AND ep.minor_id = c.ORDINAL_POSITION
WHERE c.TABLE_SCHEMA = N'rap'
ORDER BY c.TABLE_NAME, c.ORDINAL_POSITION;

Wynik aplikacja zamienia na kilka linii tekstu na widok i dołącza do polecenia. Kilka widoków po kilkanaście kolumn mieści się w oknie kontekstu bez trudu, schemat całego systemu ERP często nie. Zasady odczytu metadanych opisuje dokumentacja widoku INFORMATION_SCHEMA.COLUMNS.

Stos cylindrów bazy danych z wykresami i ikoną chmury, ilustracja schematu bazy przygotowanego do raportowania
Schemat raportowy jest osobną warstwą nad bazą i to jego opis trafia do polecenia dla modelu (etap 2).

Zapytanie T-SQL zbudowane z opisu potrzeby

Weźmy zdanie: „pokaż sprzedaż netto według klientów w ostatnim kwartale”. Z opisu schematu model wybiera widok, a w nim kolumnę kwoty oraz kolumnę daty. Z frazy „według klientów” wyprowadza grupowanie. Powstaje zapytanie takie jak poniższe, z ograniczeniem liczby wierszy i zakresem dat wyliczonym z funkcji DATEADD.

SELECT TOP (100) k.Nazwa AS Klient, SUM(s.WartoscNetto) AS SprzedazNetto
FROM rap.SprzedazPozycje AS s
JOIN rap.Klient AS k ON k.KlientId = s.KlientId
WHERE s.DataSprzedazy >= DATEADD(quarter, DATEDIFF(quarter, 0, SYSDATETIME()) - 1, 0)
  AND s.DataSprzedazy <  DATEADD(quarter, DATEDIFF(quarter, 0, SYSDATETIME()), 0)
GROUP BY k.Nazwa
ORDER BY SprzedazNetto DESC;

Warunek na datę ma postać przedziału domkniętego z lewej i otwartego z prawej strony. Taki zapis obejmuje cały ostatni dzień kwartału bez względu na godzinę dokumentu i nie wymaga funkcji na kolumnie, więc indeks po dacie pozostaje użyteczny.

Niejednoznaczne określenia w opisie

Zdanie użytkownika rzadko określa wszystko. „Ostatni kwartał” może oznaczać ostatni zakończony kwartał albo ostatnie trzy miesiące, a „sprzedaż” wartość netto albo brutto. Model wybierze jedną interpretację i nie zawsze trafi. Tabela zbiera typowe rozbieżności i sposób ich rozstrzygania.

Fraza w opisieMożliwe znaczeniaSposób rozstrzygnięcia
ostatni kwartałostatni zakończony kwartał, ostatnie trzy miesiąceZakres dat wyświetlany obok wyniku
sprzedażwartość netto, wartość bruttoOpis kolumny zapisany w widoku
aktywny klientzamówienie w ostatnim roku, umowa w tokuDefinicja zapisana w widoku klientów
najlepsi klienciwedług wartości sprzedaży, według marżyPytanie doprecyzowujące przed wykonaniem
Rozbieżności, które asystent musi pokazać użytkownikowi zamiast rozstrzygać po cichu.

Najtańszym mechanizmem jest wyświetlenie wygenerowanego zapytania i zakresu dat pod tabelą wyniku. Osoba, która zna definicję aktywnego klienta, zauważy rozbieżność od razu, zanim liczba trafi do prezentacji.

Pytania uzupełniające do wyniku

Użytkownik po obejrzeniu tabeli zwykle dopytuje o zakres, na przykład prosi o dane sprzed roku albo o zawężenie do jednego regionu. Takie zdanie nie ma sensu bez poprzedniego. Aplikacja dołącza więc do polecenia ostatnie pytanie i wygenerowane do niego zapytanie, a model modyfikuje je zamiast budować nowe od zera. Historia w jednej sesji musi być krótka, bo długa zajmuje okno kontekstu i pogarsza trafność.

Modyfikacja niesie własne ryzyko. Filtr dodany w drugim pytaniu zostaje w trzecim, choć użytkownik już o nim nie pamięta. Z tego powodu pod tabelą wyświetla się pełną listę aktywnych warunków, a nie tylko ostatnią zmianę.

Zabezpieczenia zapytań generowanych przez model

Zapytanie napisane przez model trzeba traktować jak każde dane spoza zaufanej strefy. Model może pomylić nazwę kolumny, a przypadkowy lub złośliwy tekst w opisie potrzeby potrafi skłonić go do wygenerowania instrukcji zapisu. Zabezpieczenia muszą więc działać po stronie bazy, a nie po stronie modelu.

Uprawnienia na poziomie schematu

Aplikacja wykonuje wygenerowane zapytania na osobnym koncie, należącym do roli z prawem odczytu wyłącznie w schemacie raportowym.

CREATE ROLE rola_asystent_raportow;
GRANT SELECT ON SCHEMA::rap TO rola_asystent_raportow;
DENY INSERT, UPDATE, DELETE, EXECUTE ON SCHEMA::rap TO rola_asystent_raportow;
DENY SELECT ON SCHEMA::dbo TO rola_asystent_raportow;

Widoki ze schematu rap czytają tabele z dbo dzięki łańcuchowaniu własności, więc odmowa dla dbo blokuje zapytania kierowane wprost do tabel, a widoki nadal działają. Gdy różni użytkownicy powinni widzieć różne wiersze, dochodzi mechanizm zabezpieczeń na poziomie wiersza.

Kontrola tekstu zapytania po stronie aplikacji

Uprawnienia są głównym zabezpieczeniem, ale prosta kontrola tekstu odrzuca oczywiste błędy, zanim zapytanie dotrze do serwera. Przykład w C# przepuszcza tylko instrukcje zaczynające się od SELECT lub WITH i odrzuca średniki oraz słowa zapisu.

static readonly string[] Zakazane =
    { ";", "INSERT", "UPDATE", "DELETE", "MERGE", "DROP", "ALTER", "EXEC", "TRUNCATE" };

static bool WygladaNaOdczyt(string sql)
{
    var t = sql.TrimStart();
    bool start = t.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase)
              || t.StartsWith("WITH", StringComparison.OrdinalIgnoreCase);
    return start && !Zakazane.Any(z => t.IndexOf(z, StringComparison.OrdinalIgnoreCase) >= 0);
}

Filtr jest przybliżeniem. Odrzuci także zapytanie z kolumną o nazwie zawierającej słowo UPDATE, co jest kosztem akceptowalnym wobec ryzyka, jakie niesie przepuszczenie zapisu.

Limity wykonania zapytania

Zapytanie bez warunku na dużej tabeli potrafi zwrócić miliony wierszy i obciążyć serwer, na którym równolegle pracuje magazyn. Cztery ograniczenia zamykają ten scenariusz.

  • Liczba wierszy - aplikacja dopisuje TOP albo odrzuca zapytanie bez ograniczenia zbioru wyników.
  • Czas wykonania - właściwość CommandTimeout przerywa zapytanie, które trwa dłużej niż ustalony limit.
  • Współbieżność - liczba równoczesnych zapytań asystenta jest ograniczona, aby nie konkurowały z procesami operacyjnymi o zasoby.
  • Dziennik - każde wykonane zapytanie trafia do tabeli razem z nazwą użytkownika oraz czasem trwania.

Prezentacja wyniku i praca z tabelą

Według opisu SoftwareStudio wynik trafia do interaktywnej tabeli, w której wiersze można sortować oraz filtrować, a także grupować według dowolnej kolumny, a dane wyeksportować do Excela. Szczegóły pracy z taką tabelą omawia osobny artykuł o asystencie do raportów SQL w codziennej pracy. Tutaj istotne jest to, co użytkownik widzi oprócz samych liczb.

Zapytanie i zakres pod wynikiem

Pod tabelą powinny być widoczne cztery informacje, które pozwalają ocenić wynik bez znajomości SQL.

  • Treść zapytania - analityk sprawdza złączenia i warunki.
  • Zakres danych - daty i filtry, które zastosował model.
  • Liczba wierszy - informacja, czy wynik został obcięty przez limit.
  • Czas wykonania - sygnał, że pytanie warto przenieść do stałego raportu.
Zestaw paneli z wykresami i tabelami na ciemnym tle, ilustracja wyniku zapytania SQL w formie raportu
Wynik zapytania w formie tabeli i wykresów (etap 5), z zapytaniem dostępnym do wglądu.

Od pytania doraźnego do raportu stałego

Pytanie, które użytkownicy zadają co tydzień, przestaje być pytaniem doraźnym. Sprawdzone zapytanie można wtedy przenieść do definicji stałego raportu, opisanej w artykule o projektowaniu raportów w SQL Report Builder, z układem wydruku i harmonogramem wysyłki. Wykonywanie takich raportów na serwerze omawia tekst o Microsoft SQL Reporting Services.

DataFlow AI a raporty przygotowywane przez dział IT

Asystent nie zastępuje raportów stałych. Rozwiązuje inny problem: pytania, które pojawiają się raz i nie uzasadniają pracy programisty. Tabela porównuje oba tryby według kryteriów, które decydują o wyborze.

KryteriumPytanie doraźne z asystentemRaport stały w SSRS
Częstość użyciaJednorazowa lub rzadkaCykliczna
Układ wynikuTabela na ekranieStronicowany raport z nagłówkiem oraz podpisami
Definicja wskaźnikaInterpretacja modelu, sprawdzana przez użytkownikaZapisana w definicji raportu i zatwierdzona przez właściciela
DystrybucjaEksport do arkuszaSubskrypcja pocztowa lub harmonogram
Kiedy wystarcza asystent, a kiedy potrzebny jest raport przygotowany przez dział IT.

Ocenę, czy w danej firmie wystarczy samo zapytanie SQL, czy potrzebny jest serwer raportów, opisują teksty o raportowaniu w SQL i o serwerze raportów.

Sprawdzenie wyniku z raportem referencyjnym

Przy wdrożeniu asystenta zestawia się jego wyniki z istniejącymi raportami. Zbiór pytań, na które firma zna już poprawną odpowiedź, działa jak test regresji. Po zmianie modelu, opisu schematu albo widoku wyniki muszą się zgadzać z raportem referencyjnym, a każda różnica wymaga wyjaśnienia przed dopuszczeniem zmiany.

Reguła: zmiana widoku raportowego lub modelu językowego jest zmianą oprogramowania i przechodzi przez ten sam zestaw pytań kontrolnych.

Wdrożenie DataFlow AI w firmie

Wdrożenie sprowadza się do dwóch decyzji: gdzie działa model i jak rejestrowane są zapytania. Obie wpływają na bezpieczeństwo i na koszt utrzymania.

Miejsce działania modelu

Pytanie użytkownika i opis schematu trafiają do modelu, więc miejsce jego działania ma znaczenie. SoftwareStudio opisuje osobno prywatną instancję modelu w chmurze Microsoft Azure, którą omawia artykuł o ochronie danych w Chat GPT. W układzie opisanym powyżej do modelu nie trafiają wiersze wyniku. Opuszcza aplikację pytanie i opis widoków, a dane z tabeli wynikowej pozostają w bazie i w przeglądarce użytkownika. Ogólny przegląd zastosowań AI w firmie zawiera strona o sztucznej inteligencji, a zakres oferty producenta przedstawia serwis SoftwareStudio o sztucznej inteligencji. Podobny mechanizm składania kontekstu opisano dla asystenta w systemie CRM.

Koszt wywołań modelu

Każde wywołanie modelu zużywa tokeny, a opis schematu jest powtarzany w każdym z nich. Skrócenie opisu obniża koszt i poprawia trafność, bo model ma mniej nazw do pomylenia. Praktycznym sposobem jest dobór widoków według roli użytkownika: dział sprzedaży otrzymuje opis widoków sprzedażowych, a widoki magazynowe dołącza się tylko tam, gdzie są potrzebne. Ten sam dobór ogranicza zakres danych, do których asystent w ogóle sięga.

Dziennik zapytań asystenta

Każde pytanie zostawia ślad: kto je zadał, jakie zapytanie powstało i ile trwało wykonanie. Przykładowa tabela wystarcza do audytu i do analizy użycia.

CREATE TABLE dbo.AsystentRaportowLog (
    LogId           int IDENTITY(1,1) PRIMARY KEY,
    Uzytkownik      nvarchar(100)  NOT NULL,
    Pytanie         nvarchar(1000) NOT NULL,
    ZapytanieSql    nvarchar(max)  NOT NULL,
    WierszeZwrocone int            NULL,
    CzasMs          int            NULL,
    Utworzono       datetime2(0)   NOT NULL DEFAULT SYSDATETIME()
);

SELECT TOP (10) Pytanie, COUNT(*) AS Powtorzenia, AVG(CzasMs) AS SredniCzasMs
FROM dbo.AsystentRaportowLog
WHERE Utworzono >= DATEADD(day, -30, SYSDATETIME())
GROUP BY Pytanie
ORDER BY Powtorzenia DESC;

Zapytanie zwraca dziesięć najczęściej powtarzanych pytań z ostatnich trzydziestu dni. Pytania z wysoką liczbą powtórzeń są kandydatami na raporty stałe, a te z długim czasem wykonania wskazują widoki, którym brakuje indeksu.