UXAIRFORCE

AI SDK – najłatwiejszy sposób tworzenia aplikacji AI #EN257

A

Adam Michalski

26 sierpnia 2025

Notatki z prezentacji wideo pokazującej praktyczne zastosowanie AI SDK w TypeScript. Wszystkie przemyślenia, obserwacje i wnioski pochodzą od autora prezentacji.

TL;DR

  • AI SDK unifikuje integrację z różnymi modelami AI (OpenAI, Anthropic, Google) w jednym narzędziu
  • Kompletna aplikacja czatu powstaje w zaledwie ~10 linijkach kodu ze strumieniowaniem tekstu
  • Trzy typy narzędzi: manual tools, provider-specific i MCP servers dla maksymalnej elastyczności
  • Gotowe komponenty UI dla React, Svelte i Angular z hookami useChat, useObject
  • Przesyłanie plików – obrazy i PDF działają od razu po instalacji z kompatybilnymi modelami
  • Structured outputs z walidacją schema Zod dla uporządkowanych danych
  • Płynne strumieniowanie poprawia UX poprzez sztuczne spowolnienie wyświetlania tekstu

Dlaczego AI SDK zmienia reguły gry

Według prezentacji, tradycyjny sposób integracji z modelami AI tworzy szereg problemów dla programistów:

  • Osobne SDK dla każdego providera (OpenAI, Anthropic, Google)
  • Różne API i sposoby implementacji
  • Osobne klucze oraz konfiguracje
  • Trudność w przełączaniu między modelami

AI SDK rewolucjonizuje ten proces poprzez standaryzację. Prezenter podkreśla, że jeden pakiet zastępuje wszystkie biblioteki providerów. Co więcej, oferuje zaawansowane funkcje, które znacznie ułatwiają implementację takich rozwiązań jak strumieniowanie, tool calling czy structured outputs.

Niezależnie od tego, czy planujesz dodać małą funkcję AI do istniejącej aplikacji, czy stworzyć w pełni opartą na AI platformę – AI SDK dostarcza odpowiednie narzędzia.

Od prostej generacji do profesjonalnego strumieniowania

Podstawy – generateText w 5 linijkach

Najprostsza implementacja wymaga zaledwie kilku linijek kodu:

import { generateText } from 'ai';

const result = await generateText({
  model: 'gpt-5',
  prompt: 'Your prompt here'
});

Funkcja zwraca właściwość text z odpowiedzią od modelu. Prezenter używa OpenAI GPT-5 poprzez Vercel AI Gateway – co jest kluczowe. Jak wyjaśnia, ten mechanizm pozwala korzystać z wielu modeli za pomocą jednego klucza API i wspólnego rachunku, ujednolicając rozliczenia i zarządzanie dostępem do różnych providerów.

Alternatywnie można używać bezpośrednio konkretnego providera (jak Anthropic dla Claude Sonnet 4) lub OpenRouter, którego prezenter często wykorzystuje. Wszystko jednak bez zmian w kodzie aplikacji.

Problem z czasem odpowiedzi

GenerateText sprawdza się w zastosowaniach nieinteraktywnych. Dla aplikacji skierowanych do użytkownika ma jednak istotną wadę – może zabierać ponad 20 sekund na odpowiedź. Dlatego rozwiązaniem jest strumieniowanie tekstu w czasie rzeczywistym.

StreamText – strumieniowanie w czasie rzeczywistym

📋 Lista kontrolna implementacji strumieniowania:

  • Zamień generateText na streamText
  • Usuń await przed funkcją
  • Dodaj convertToModelMessages() dla UI messages
  • Użyj result.toTextStreamResponse() w endpoint
  • Skonfiguruj interfejs użytkownika z useChat hook
  • Przetestuj strumieniowanie w przeglądarce

Funkcja streamText działa inaczej niż jej statyczny odpowiednik. Jak wyjaśnia autor, nie czeka się już na pełną odpowiedź. Zamiast tego otrzymuje się obiekt result z dostępem do różnych strumieni:

  • result.textStream – tylko tekst odpowiedzi
  • result.fullStream – procesy pośrednie: kroki rozumowania (reasoning), wywołania narzędzi i metadata

Rezultat można przekształcić w HTTP response za pomocą toTextStreamResponse(). Dla większości zastosowań wystarczy textStream, jednak fullStream daje pełny wgląd w proces myślenia modelu i wykonywane przez niego operacje.

W rezultacie: Cała funkcjonalność strumieniowania mieści się w około 10 linijkach kodu.

Komponenty UI i architektura SDK

AI SDK dzieli się na dwie części:

  • AI SDK Core – funkcje serwerowe (generateText, streamText)
  • AI SDK UI – komponenty interfejsu użytkownika dla popularnych środowisk

Wsparcie środowisk: React, Vue, Svelte, Angular

Trzy kluczowe hooki React

Prezenter koncentruje się na trzech głównych hookach:

  • useChat – obsługa konwersacji z modelami
  • useObject – structured outputs z walidacją
  • useCompletion – completion pojedynczych zadań

Hook useChat zwraca kompletny zestaw narzędzi do zbudowania interfejsu czatu. Messages to typ UIMessage zawierający dodatkowe informacje dla aplikacji – reasoning steps, informacje o tool calling, liczbę tokenów.

Struktura UIMessage:

  • Role – czy to wiadomość user, system czy assistant
  • Parts – reasoning, tool calling, i finalna odpowiedź
  • Metadata – tokeny, timing, inne dane dla UI

Każda część renderuje się inaczej – tekst w markdown, reasoning w osobnym komponencie, tool calls w dedykowanych komponentach. Przed wysłaniem zapytania do modelu, dane te są konwertowane na lżejszy format ModelMessage za pomocą convertToModelMessages(), co optymalizuje komunikację i nie zajmuje niepotrzebnie miejsca w kontekście.

Bonus tip: Prezenter wspomina o AI Elements – to komponenty Shad CN współpracujące z AI SDK, które znacznie przyspieszają tworzenie UI dla aplikacji AI.

Zaawansowane funkcje dla lepszego UX

Przesyłanie plików – obrazy i PDF bez wysiłku

📋 Lista kontrolna dodania przesyłania plików:

  • Utwórz state dla listy plików (useState<FileList>)
  • Dodaj input type=”file” z akceptowanymi formatami
  • Przekaż pliki do sendMessage() w parametrze
  • Sprawdź czy model obsługuje pliki (OpenAI, Google, Anthropic ✅)
  • Przetestuj z obrazem i dokumentem PDF

Dodanie obsługi plików wymaga minimalnych zmian. Modele OpenAI, Google i Anthropic obsługują to natywnie. W demonstracji model poprawnie opisał załączony obraz przycisku „Subscribe” oraz potrafił analizować dokumenty PDF.

Strumieniowanie rozumowania z GPT-5

GPT-5 oferuje reasoning steps, ale wymaga specjalnej konfiguracji poprzez providerOptions. Prezenter pokazuje jak aktywować:

  • Reasoning summary – concise, detailed lub auto
  • Reasoning effort – wpływa na czas i głębokość analizy
providerOptions: {
  reasoning: { 
    summaryMode: 'detailed',
    reasoningEffort: 'medium' 
  }
}

Kluczowa obserwacja: GPT-5 używa multiple reasoning steps. W demonstracji model myślał przez 3 sekundy, 2 sekundy, 2 sekundy – każdy krok to osobny komponent w UI. Z reasoning effort ustawionym na 'medium’ może myśleć jeszcze dłużej, ale w rezultacie daje bardziej przemyślane odpowiedzi.

Płynne strumieniowanie dla lepszej czytelności

Szybkie modele jak GPT-5 Nano wyświetlają tekst w dużych kawałkach, co pogarsza UX w porównaniu do ChatGPT czy Claude. AI SDK oferuje rozwiązanie w postaci smoothStream:

experimental_transform: smoothStream({
  delayInMs: 10,
  chunkingStrategy: 'word' // lub 'line', 'custom'
})

Dostępne strategie:

  • Word – dzieli tekst po słowach (domyślna)
  • Line – dzieli po liniach
  • Custom – własny regex lub chunk detector

Prezenter pokazuje równoległe porównanie: strumień bez wygładzania, z domyślnymi opcjami, i z line strategy. Domyślna opcja daje najlepszy rezultat wizualny – tekst przepływa naturalnie, jak w profesjonalnych interfejsach AI.

Tool calling – trzy sposoby rozszerzania możliwości

📋 Lista kontrolna wyboru typu tool calling:

Manual tools – wybierz gdy:

  • Potrzebujesz pełnej kontroli nad logiką
  • Integracja z własnym API/serwisem
  • Custom business logic
  • Chcesz działać z dowolnym modelem

Provider-specific tools – wybierz gdy:

  • Potrzebujesz szybkiej implementacji
  • Używasz tylko jednego providera
  • Wystarczają Ci gotowe funkcje (web search, code execution)

MCP servers – wybierz gdy:

  • Eksperymentalna funkcjonalność jest OK
  • Potrzebujesz integracji z GitHub/external services

Manual tools – pełna kontrola

Manual tools dają programiście pełną kontrolę nad logiką. Przykład z generowaniem obrazów pokazuje, jak zdefiniować tool z schema w Zod, opisami parametrów i custom logic w funkcji execute.

Kluczowa wskazówka: Schema w Zod z funkcją .describe() to klucz do skutecznego tool calling. Prezenter podkreśla, że opisy parametrów pomagają modelowi zrozumieć nie tylko typ danych, ale także kontekst i oczekiwania co do zawartości. To znacznie poprawia precyzję wywołań narzędzi.

const generateImageTool = tool({
  description: 'Generate an image based on a prompt',
  parameters: z.object({
    prompt: z.string().describe('Detailed description of the image to generate')
  }),
  execute: async ({ prompt }) => {
    return await experimental_generateImage({
      model: 'gpt-image-1',
      prompt
    });
  }
});

W demonstracji model wygenerował „cute Corgis in space” po przeanalizowaniu request, poprawnie wykorzystując zarówno opis narzędzia, jak i schema parametrów.

Provider-specific tools – gotowe rozwiązania

Dostawcy oferują gotowe narzędzia jak computer use, code execution czy web search. Google dostarcza szczególnie użyteczne tools:

  • Google Search
  • URL Context
  • Code Execution
// Przełączenie na Gemini 2.5 Flash
model: google('gemini-2.0-flash-exp'),
tools: {
  webSearch: google.tools.googleSearch(),
  urlContext: google.tools.urlContext()
}

Dodatkowa funkcja: W UIMessageStreamResponse można włączyć opcję wysyłania źródeł używanych w Google Search, co pozwala na wyświetlanie ich w UI.

Prezenter testuje to pytaniem o najnowsze wiadomości z F1. Model zwraca aktualne informacje wraz ze źródłami, które można renderować jako osobne komponenty React. To działa od razu po instalacji.

Ograniczenia i alternatywy

Provider tools mają istotne wady. Krytyczne ograniczenie: Jak zauważa prezenter, Google Search „actually locks it down so you can’t use any other tool calls” – używanie Google tools uniemożliwia jednoczesne korzystanie z innych narzędzi.

To znacznie ogranicza funkcjonalność aplikacji. Dodatkowo wiążą z konkretnym dostawcą, co utrudnia przełączanie między modelami.

Autor preferuje zewnętrzne serwisy jak Tavily, Firecrawl czy Brave Search API. Dają one możliwość używania dowolnego modelu, kombinowania tools i większą kontrolę nad implementacją. Demonstracja z Tavily pokazuje GPT-5 scrapeujący Formula1.com i zwracający najnowsze nagłówki.

MCP servers – eksperymentalna przyszłość

MCP (Model Context Protocol) servers to najnowsza opcja, obecnie w fazie eksperymentalnej. Prezenter pokazuje integrację z Grep MCP server dla wyszukiwania w GitHub.

// Różne metody transportu
const transport = createHttpClientTransport({
  url: 'http://localhost:3000/mcp'
});
// Alternatywnie: standard I/O lub SSE methods

const client = experimental_createMCPClient({ transport });
const tools = await client.tools();

MCP oferuje różne metody transportu – oprócz HTTP client transport można używać standard I/O i SSE methods, w zależności od konfiguracji serwera.

W demonstracji model wyszukuje funkcję experimental_createMCPClient na GitHub i zwraca snippety kodu z dynamicznymi tool calls. To pokazuje potencjał protokołu MCP do łączenia LLM z dowolnymi zewnętrznymi serwisami.

Stop conditions – kontrola nad narzędziami

Tool calling wymaga kontroli nad liczbą kroków. Domyślnie jest ustawiony step count = 1, jak zauważa prezenter, ale dla złożonych scenariuszy warto zwiększyć do 20.

streamText({
  maxSteps: 20 // lub stepCount(20)
});

Dlaczego to jest kluczowe: Prezenter podkreśla znaczenie warunku stopWhen, który zapobiega tworzeniu się nieskończonych pętli wywołań narzędzi. To pozwala modelowi na przykład wyszukać URLs w pierwszym kroku, przeanalizować ich zawartość w kolejnych, ale nigdy nie przekroczyć ustalonego limitu. Można również definiować custom conditions oprócz liczby kroków.

Structured outputs i walidacja danych

📋 Lista kontrolna implementacji structured outputs:

  • Zdefiniuj schema w Zod z opisami pól
  • Użyj streamObject zamiast streamText
  • Przekaż schema do funkcji
  • W interfejsie użytkownika użyj useObject hook
  • Obsłuż partial object podczas strumieniowania
  • Dodaj walidację i error handling

Zamiast wolnego tekstu, model może zwracać uporządkowane dane według schema. Schema approach daje walidację, pełne typowanie TypeScript i dokumentację – opisy pól pomagają modelowi zrozumieć wymagania.

Implementacja z Zod schema

const breakdownSchema = z.object({
  originalTask: z.string(),
  subtasks: z.array(z.object({
    description: z.string().describe('What needs to be done'),
    timeEstimate: z.string().describe('Estimated time in minutes')
  })),
  totalTimeEstimate: z.string()
});

streamObject({
  model: 'gpt-5',
  schema: breakdownSchema,
  prompt: taskPrompt
});

Korzyści schema approach:

  • Walidacja – automatyczne sprawdzanie poprawności danych
  • TypeScript support – pełne typowanie w IDE
  • Dokumentacja – opisy pól pomagają modelowi zrozumieć wymagania
  • Konsystencja – schema ewoluuje razem z kodem

UI z useObject hook

Interfejs użytkownika używa useObject hook:

const { object, submit } = useObject({
  api: '/api/breakdown',
  schema: breakdownSchema
});

Kluczowa różnica: Obiekt jest partial podczas strumieniowania – poszczególne części pojawiają się stopniowo. Jak pokazuje demo z „clean the kitchen”, każde pole jest strumieniowane osobno w czasie rzeczywistym.

Dodatkowe możliwości poza podstawami

Prezenter podkreśla, że pokazane funkcje to tylko wierzchołek góry lodowej. AI SDK oferuje pełny toolkit dla AI development:

  • generateSpeech – synteza mowy z tekstu
  • generateTranscription – transkrypcja plików audio
  • Embeddings – obsługa wektorowych reprezentacji tekstu
  • Multi-modal – praca z różnymi typami contentu
  • Caching – optymalizacja kosztów i wydajności

Ciekawostka: Pakiet ai na NPM był opublikowany 12 lat temu jako „Abandon Issues”. Vercel przejął go i przekształcił w dzisiejszy AI SDK.

Podsumowanie – dlaczego warto wybrać AI SDK

AI SDK eliminuje główne bariery w tworzeniu aplikacji AI. Unifikacja różnych providerów w jednym API oszczędza czas i upraszcza kod. Gotowe komponenty UI dla popularnych środowisk przyspieszają development.

Szczególnie wartościowe są zaawansowane funkcje jak płynne strumieniowanie, tool calling czy structured outputs. Możliwość łączenia manual tools z provider-specific i MCP servers daje nieograniczoną elastyczność w architekturze aplikacji.

Jak pokazuje prezentacja, pełnofunkcyjna aplikacja chat z przesyłaniem plików, web searchem i generowaniem obrazów powstaje w czasie rzeczywistym. To demonstruje jak daleko zaszły współczesne narzędzia AI development – od żmudnej integracji do plug-and-play solutions.


Biblioteka promptów z demonstracji

Prezenter używał konkretnych promptów do testowania różnych funkcjonalności AI SDK. Oto lista z kontekstem zastosowania:

Podstawowe testowanie

„hello world”

  • Kiedy stosować: Pierwszy test po implementacji strumieniowania
  • Cel: Sprawdzenie czy pipeline komunikacji działa poprawnie
  • Oczekiwany rezultat: Krótka, grzeczna odpowiedź z widocznym strumieniowaniem

Przesyłanie plików

„describe the image”

  • Kiedy stosować: Po dodaniu obsługi plików, test z załączonym obrazem
  • Cel: Weryfikacja czy model poprawnie analizuje załączone pliki
  • Oczekiwany rezultat: Szczegółowy opis zawartości obrazu

Tool calling – generowanie obrazów

„generate me an image of Corgis in space”

  • Kiedy stosować: Test manual tools z image generation
  • Cel: Sprawdzenie czy model prawidłowo wywołuje tool i zwraca obraz
  • Oczekiwany rezultat: Reasoning steps + tool call + wygenerowany obraz

Web search i aktualności

„what are the latest news stories in F1”

  • Kiedy stosować: Test provider-specific web search tools
  • Cel: Weryfikacja dostępu do aktualnych informacji z internetu
  • Oczekiwany rezultat: Aktualne wiadomości + źródła

„what are the Latest headlines from Formula1.com”

  • Kiedy stosować: Test URL context tools z konkretnej strony
  • Cel: Sprawdzenie scraping konkretnego URL
  • Oczekiwany rezultat: Nagłówki z danej strony web

MCP servers

„search GitHub for that experimental Create MCP client function”

  • Kiedy stosować: Test integracji z MCP servers (GitHub)
  • Cel: Weryfikacja dynamic tool calls z zewnętrznych serwisów
  • Oczekiwany rezultat: Code snippets z GitHub + linki do repositories

Structured outputs

„clean the kitchen”

  • Kiedy stosować: Test schema validation z breakdown tasks
  • Cel: Sprawdzenie czy model zwraca poprawnie ustrukturyzowane dane
  • Oczekiwany rezultat: JSON z original task, subtasks array, time estimates

Wskazówka: Te prompty są celowo proste i bezpośrednie. W prawdziwych aplikacjach warto dodawać więcej kontekstu i instrukcji, jednak do testowania funkcjonalności AI SDK wystarczą podstawowe polecenia.

Kluczowy insight

Kontrola przez narzędzia, nie prompt

Standardowo myślimy: Aby uzyskać od AI pożądany rezultat, musimy perfekcyjnie skonstruować prompt, dodając instrukcje, reguły i przykłady.

W praktyce okazuje się, że: Większą kontrolę i niezawodność osiągamy, gdy zamiast rozbudowywać prompt, definiujemy precyzyjnie narzędzia (tools) i schematy danych (schemas), które AI ma wykorzystać. Prompt staje się wtedy prostym wyzwalaczem, a nie skomplikowaną instrukcją.

Dlaczego to jest istotne: Zmienia to model pracy z AI ze „sztuki” pisania promptów na „inżynierię” budowania systemów. Takie podejście jest skalowalne, testowalne i znacznie mniej podatne na nieprzewidywalne zmiany w zachowaniu modelu.

Test na jutro: Następnym razem gdy będziesz chciał uzyskać od AI dane w formacie JSON, zamiast pisać w prompcie „odpowiedz w formacie JSON z polami X, Y, Z…” spróbuj zdefiniować te dane jako schemat Zod i użyć funkcji streamObject. Sprawdź, o ile bardziej niezawodny i łatwiejszy w obsłudze będzie rezultat.


Ten wpis jest częścią mojej kolekcji notatek z ciekawych podcastów, webinarów i innych treści, które uważam za wartościowe i do których sam chcę wracać. Jeśli chcesz sprawdzić oryginalne źródło, znajdziesz je tutaj: The EASIEST way to build AI apps – AI SDK Overview

More from the blog