AI SDK – najłatwiejszy sposób tworzenia aplikacji AI #EN257
Adam Michalski
26 sierpnia 2025
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
nastreamText
- 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 odpowiedziresult.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 modelamiuseObject
– 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
zamiaststreamText
- 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 tekstugenerateTranscription
– 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