1. Dlaczego same instrukcje nie wystarczą bez dobrych tools i metadanych
Warto uświadomić sobie jedną nieprzyjemną prawdę: model nie widzi waszego kodu. Nie wie, jakie macie kontrolery w Next.js, jakie funkcje w TypeScript i jakie cudowne heurystyki zebraliście w serwisie rekomendacji.
Widzi wasz App przez kilka interfejsów:
- System‑prompt (kontrakt roli).
- Opisy narzędzi: nazwa, description, inputSchema, outputSchema, adnotacje itp.
- Metadane samej aplikacji: nazwa, ikona, krótki i długi opis, kategorie, conversation starters itp.
Podczas obsługi zapytania model patrzy na kontekst dialogu i te metadane, aby zdecydować:
- czy w ogóle proponować jakiś App;
- jeśli tak — który z dostępnych;
- a jeśli App został wybrany — które narzędzie tego App pasuje do bieżącego zapytania.
W poprzedniej części Modułu 5 zajmowaliśmy się tym, co można „opowiedzieć” modelowi słowami — system‑prompt i instrukcje UX. Teraz przechodzimy do tego, co widzi poza tekstem: tools i metadane.
Dlatego zadanie Modułu 5 jest w istocie podwójne. Najpierw w system‑prompt formułujecie „co ten App ma robić i jak się zachowywać”, a następnie w projekcie tools i metadanych pakujecie to w formę, którą model faktycznie potrafi wykorzystać — również do discovery i routowania.
Można to sobie ująć tak: system‑prompt to konstytucja, a tools i metadane — to już prawo i cała biurokracja wokół: formularze wniosków, schematy baz danych itd. Jeśli ograniczymy się tylko do konstytucji, daleko nie zajedziemy.
2. Dekompozycja: „jedno zadanie — jeden tool”, ale z głową
Zacznijmy od najbardziej bolesnego: ile w ogóle narzędzi robić i jak je pociąć.
Intuicyjna zasada: jedno narzędzie — jedno zrozumiałe zadanie. To bardzo ułatwia modelowi wybór: nie ma jednej monstrualnej funkcji do_everything, tylko kilka schludnych akcji z dobrymi nazwami.
Dla GiftGenius możemy mieć takie bazowe narzędzia:
- profile_to_segments — zamienić swobodny opis odbiorcy (wiek, zainteresowania, relacja, kontekst) na znormalizowane segmenty typu "tech", "fitness", "gamer".
- recommend_gifts — dobrać listę id prezentów na podstawie segmentów, budżetu, lokalizacji i okazji.
- get_gift — pobrać pełną kartę wybranego prezentu (opis, media, SKU/warianty) po jego id.
- (opcjonalnie) similar_gifts — na bazie wybranego prezentu zaproponować jeszcze 3–5 podobnych wariantów.
Teoretycznie można by zrobić jeden gift_tool z parametrem mode: "profile_to_segments" | "recommend" | "details" | "similar", ale wtedy utrudniacie życie i sobie, i modelowi: opis zamienia się w tasiemiec, inputSchema puchnie, a przy wyborze narzędzia model ma mniej wyraźnych kotwic.
Antywzorzec: God Tool
Wyobraźcie sobie taki schemat:
server.registerTool(
"gift_tool",
{
description: "Różne operacje z prezentami.",
inputSchema: { /* 50 pól i flag */ },
},
async ({ input }) => { /* ogromny switch po mode */ }
);
W głowie modelu wygląda to jak „jest jakieś abstrakcyjne narzędzie od prezentów, a dalej się zobaczy”. To pogarsza trafność wyboru, szkodzi discovery i utrudnia wam utrzymanie.
Ale wpadnięcie w drugą skrajność — zrobienie 50 mikroskopijnych narzędzi na każdy drobiazg — też jest złe. Każde dodatkowe narzędzie trafia do kontekstu, obciąża uwagę modelu i zwiększa ryzyko błędów routowania. Dokumentacja wprost ostrzega: zbyt wiele drobnych narzędzi to minus dla jakości, zwłaszcza gdy ich opisy się pokrywają.
Praktyczna zasada, z której wygodnie korzystać:
- wszystko, co użytkownik postrzega jako jeden „krok” w scenariuszu (np. pierwszy dobór prezentów na podstawie profilu), to dobry kandydat na osobny tool;
- to, co zawsze wykonuje się ściśle w ramach tego kroku i nie ma samodzielnego sensu (np. wyliczenie scoringu czy zalogowanie podglądu kart), lepiej zostawić wewnątrz implementacji narzędzia.
Załóżmy, że według tej zasady pocięliście już scenariusze na 2–4 narzędzia. Kolejne ważne pytanie — jak opisać wejścia tych tools tak, aby model mógł z nich korzystać bez zgadywania. Od tego zaczniemy.
3. Rzutujemy use‑cases na Input Schema
Bierzemy teraz jeden konkretny use‑case i szczerze sprawdzamy, jakie dane naprawdę są potrzebne narzędziu.
Weźmy scenariusz: „Darczyńca na deadlinie: dobrać 5–7 pomysłów dla przyjaciela, 25 lat, kocha piłkę nożną i gry planszowe, budżet do 50 $”.
Ze spojrzenia jobs‑to‑be‑done wynika, że zadaniem jądra rekomendacyjnego GiftGenius jest zawęzić wybór do małej listy i obniżyć niepokój „a może wybiorę coś słabego”. Na poziomie rozmowy w czacie asystent potrzebuje:
- podstawowych informacji o odbiorcy (wiek, płeć, relacja z darczyńcą);
- zainteresowań/hobby;
- budżetu i waluty;
- okazji (urodziny, jubileusz, Nowy Rok itd.);
- opcjonalnie — kraju/miasta do filtrowania po dostawie.
W architekturze GiftGenius rozkłada się to na dwa kroki:
- profile_to_segments(input) przyjmuje „surowe” dane (wiek, zainteresowania, opis tekstowy) i przekształca je w znormalizowane segmenty, z którymi wygodniej dalej pracować.
- recommend_gifts(segments, budget, locale, occasion) na podstawie segmentów i budżetu dobiera konkretne id prezentów z katalogu.
Z punktu widzenia kontraktu ChatGPT ↔ MCP ważne jest opisanie właśnie drugiego kroku — schematu recommend_gifts, ponieważ to to narzędzie będzie wykorzystywane w większości scenariuszy doboru.
Nie trzeba przy tym od razu wymagać od użytkownika wszystkiego: model może dopytać część przez follow‑up („a jaki mniej więcej budżet?”). Zatem część pól w profilu może być opcjonalna; ale gdy dochodzimy do recommend_gifts, powinien już istnieć znormalizowany zestaw parametrów.
Przykład: TypeScript + JSON Schema dla recommend_gifts
W serwerze MCP w TypeScript może to wyglądać tak:
// apps/mcp/server.ts
import { McpServer } from "@openai/mcp-server";
const server = new McpServer();
server.registerTool(
"recommend_gifts",
{
title: "Rekomendacje prezentów",
description:
"Użyj tego narzędzia, gdy trzeba dobrać prezenty na podstawie segmentów odbiorcy, budżetu, locale i okazji.",
inputSchema: {
type: "object",
properties: {
segments: {
type: "array",
description:
"Lista segmentów odbiorcy, np. ['tech', 'football_fan']. Zwykle pochodzi z profile_to_segments.",
items: { type: "string" },
minItems: 1
},
budget: {
type: "object",
description:
"Zakres budżetu na prezent w walucie użytkownika (minimum/maksimum).",
properties: {
min: {
type: "number",
minimum: 0,
description: "Minimalna kwota, którą użytkownik jest gotów wydać."
},
max: {
type: "number",
minimum: 0,
description: "Maksymalna kwota, którą użytkownik jest gotów wydać."
},
currency: {
type: "string",
minLength: 3,
maxLength: 3,
description: "Trzyliterowy kod waluty (np. USD, EUR, RUB)."
}
},
required: ["min", "max", "currency"]
},
locale: {
type: "string",
description:
"Locale użytkownika w formacie BCP‑47 (np. 'ru-RU' lub 'en-US')."
},
occasion: {
type: "string",
description:
"Okazja na prezent, np. 'birthday', 'new_year', 'anniversary'."
}
},
required: ["segments", "budget", "locale", "occasion"]
}
},
async ({ input }) => {
// Na razie bez kombinowania — zwrócimy prostą odpowiedź
return {
content: [
{
type: "text",
text: `Dobieram prezenty dla segmentów ${input.segments?.join(
", "
)} w budżecie ${input.budget?.min}–${input.budget?.max} ${input.budget?.currency}...`
}
],
structuredContent: {}
};
}
);
Zwróćcie uwagę na kilka kwestii.
Po pierwsze, aktywnie używamy ograniczeń w stylu enum i zrozumiałych opisów. Nawet jeśli formalnie to tylko stringi, description podpowiada modelowi, jakich wartości oczekujemy, co istotnie zwiększa szansę, że poprawnie wypełni argumenty. Zamiast rozmytej frazy "powód": "coś jak urodziny" mamy uporządkowane occasion: "birthday".
Po drugie, opisy pól piszemy nie „dla ludzi z zespołu”, lecz dosłownie jako podpowiedzi dla modelu: co to za pole, jakie typowe wartości, czy jest przykład. Autorzy dokumentacji Apps SDK wprost zalecają dodawać zrozumiałe opisy i przykłady dla każdego parametru.
Czego nie powinno być w schemacie wejściowym
Typowe pasożytnicze pola, które często próbuje się tam wcisnąć:
- wewnętrzne identyfikatory (tenantId, internalSegment), które i tak można dodać po stronie serwera;
- rzeczy, których model nie może znać (np. deploymentRegion) — to już wasza odpowiedzialność;
- pola‑duplikaty historii czatu (np. userPrompt): model i tak widzi wiadomość źródłową, nie każcie mu jej kopiować.
Input Schema to dokładnie to, co model ma ustalić i wypełnić, a nie ogólny worek wszystkiego.
4. Output Schema: nie tylko dane, lecz także sens
W Apps SDK rezultat narzędzia wraca do dialogu jako wiadomość o role: tool. Dalej model decyduje, co z tym zrobić: jak sformatować odpowiedź, jakie follow‑up zadać, czy otwierać widżet itd. Dlatego projekt schematu wyjściowego jest równie ważny jak wejściowego.
Są dwa podejścia.
Wariant „surowe dane” wygląda tak:
{
"items": [
{ "id": "GIFT_1" },
{ "id": "GIFT_2" }
]
}
Model widzi po prostu listę id, bez zrozumienia, dlaczego te warianty tu trafiły, ilu było kandydatów i które są najlepsze. Może coś dorobić, ale ryzyko dziwactw jest większe.
Wariant semantycznie bogaty:
{
"items": [
{
"id": "GIFT_1",
"score": 0.92,
"reason": "Silnie dopasowany do segmentu 'football_fan' i mieści się w budżecie."
},
{
"id": "GIFT_2",
"score": 0.81,
"reason": "Pasuje dla miłośnika gier planszowych, nieco bliżej górnej granicy budżetu."
}
],
"meta": {
"totalCandidates": 27,
"returned": 5,
"segmentsUsed": ["football_fan", "board_games"],
"budget": { "min": 20, "max": 50, "currency": "USD" },
"advice": "Lepiej zacząć od wariantów z najwyższym score i czytelnym uzasadnieniem."
}
}
Teraz model może uczciwie wyjaśnić, dlaczego właśnie te prezenty, i budować follow‑up: „Znalazłem 27 wariantów, pokazuję 5 najlepszych, oto dlaczego właśnie one”.
Przykład: opisujemy Output Schema dla recommend_gifts
Dodajmy do opisu narzędzia schemat rezultatu (nawet jeśli technicznie można go nie podawać, lepiej to zrobić — to część kontraktu z modelem):
const recommendGiftsOutputSchema = {
type: "object",
properties: {
items: {
type: "array",
items: {
type: "object",
properties: {
id: { type: "string", description: "ID prezentu w katalogu." },
score: {
type: "number",
description: "Ocena dopasowania do profilu (0..1)."
},
reason: {
type: "string",
description:
"Krótkie wyjaśnienie, dlaczego prezent pasuje (może być generowane na backendzie)."
}
},
required: ["id", "score"]
},
description: "Lista rekomendowanych prezentów z ocenami trafności."
},
meta: {
type: "object",
properties: {
totalCandidates: {
type: "integer",
description: "Ilu kandydatów łącznie znaleziono w katalogu."
},
returned: {
type: "integer",
description: "Ile prezentów zwróciło to wywołanie."
},
advice: {
type: "string",
description:
"Ogólna wskazówka: np. od jakiego typu prezentów warto zacząć."
}
}
}
},
required: ["items"]
};
I użyjmy tego schematu wewnątrz implementacji:
server.registerTool(
"recommend_gifts",
{
title: "Rekomendacje prezentów",
description:
"Użyj, gdy trzeba dobrać 3–7 prezentów na podstawie segmentów i budżetu. Zwraca id prezentów i oceny dopasowania; szczegółowe karty pobieraj przez get_gift.",
inputSchema: /* jak wyżej */,
// Nie zawsze formalnie podaje się outputSchema, ale do dokumentacji to przydatne:
// outputSchema: recommendGiftsOutputSchema
},
async ({ input }) => {
const recommendations = await recommendFromCatalog(input); // nasza logika biznesowa
return {
content: [
{
type: "text",
text: `Znalazłem ${recommendations.items.length} pasujących pomysłów. Zaraz pokażę najlepsze.`
}
],
structuredContent: {
items: recommendations.items,
meta: {
totalCandidates: recommendations.meta.totalCandidates,
returned: recommendations.items.length,
advice: recommendations.meta.advice
}
}
};
}
);
Robimy dwie rzeczy: dajemy modelowi minimalny tekst dla użytkownika i jednocześnie dokładamy semantyczny JSON, na podstawie którego może dalej prowadzić dialog i follow‑up.
Jednocześnie get_gift na podstawie id dociągnie pełne karty (nazwa, media, SKU itd.), a widżet GiftGenius wyrenderuje je jako karty prezentów.
5. Nazewnictwo i opisy narzędzi jako podstawa discovery
Teraz najciekawsze: jak nazwy i opisy tools wpływają na to, czy model je wywoła, czy nie.
Dokumentacja i best practice dotyczące metadanych zalecają:
- używać nazw zorientowanych na działanie: profile_to_segments, recommend_gifts, get_gift, similar_gifts, a nie tool1, search, do_stuff;
- zaczynać opis w stylu „Use this when… / Użyj tego narzędzia, gdy…”, opisując scenariusze wyzwalające i ograniczenia („nie używaj do…”).
To bezpośrednio wiąże się z waszym golden prompt set. Sformułowania w opisie powinny pokrywać się z realnymi zapytaniami użytkowników. Jeśli w opisie jest „Użyj, gdy użytkownik prosi o dobór prezentu według budżetu i zainteresowań odbiorcy”, a w golden prompt macie „dobierz prezent dla przyjaciela‑gracza do 50 $”, model o wiele łatwiej skojarzy zapytanie z narzędziem.
Przykład dobrego opisu narzędzia
Przeanalizujmy dodatkowe narzędzie GiftGenius — similar_gifts, które pomaga rozszerzyć wybór o podobne pomysły na podstawie konkretnego prezentu:
server.registerTool(
"similar_gifts",
{
title: "Podobne prezenty",
description:
"Użyj tego narzędzia, gdy użytkownik wybrał konkretny prezent i chce zobaczyć jeszcze kilka podobnych wariantów. Nie używaj do pierwszego doboru od zera — do tego służy recommend_gifts.",
inputSchema: {
type: "object",
properties: {
giftId: {
type: "string",
description:
"Identyfikator prezentu z poprzedniego doboru, dla którego trzeba znaleźć podobne warianty."
},
limit: {
type: "integer",
description:
"Ile podobnych prezentów zwrócić (domyślnie 3–5).",
minimum: 1,
default: 5
}
},
required: ["giftId"]
}
},
async () => {
/* ... */
}
);
Ważne punkty:
- Wyraźnie mówimy, kiedy narzędzie trzeba użyć, a kiedy nie.
- W opisie pojawiają się słowa „podobne warianty”, „wybrał konkretny prezent” — właśnie te, które często występują w realnych zapytaniach użytkowników.
- Unikamy nakładania się z obszarem recommend_gifts — to zmniejsza konkurencję między narzędziami przy wyborze.
Przykład złego opisu
description: "Praca z prezentami."
Model z takiego opisu niewiele rozumie. Takie narzędzie może zadziałać tylko wtedy, gdy GPT desperacko próbuje coś wywołać „na chybił trafił”.
6. Adnotacje i hints: jak zasugerować modelowi wagę działania
Narzędzie to nie tylko nazwa i schemat, lecz także adnotacje, które podpowiadają ChatGPT, jak niebezpieczna/istotna jest akcja i czy trzeba prosić użytkownika o potwierdzenie. W specyfikacji Apps SDK są do tego różne hints, jak readOnlyHint, destructiveHint, openWorldHint i inne.
- readOnlyHint: true mówi, że narzędzie tylko czyta dane i nie zmienia stanu. Wtedy asystent może pominąć zbędne potwierdzenia i wywoływać je swobodniej.
- destructiveHint: true sygnalizuje, że narzędzie może coś usunąć lub nieodwracalnie zmienić, więc trzeba pokazać użytkownikowi wyraźne „Czy na pewno?”.
- openWorldHint: true wskazuje, że działanie dotyka świata zewnętrznego (postowanie w mediach społecznościowych, tworzenie wpisu poza kontem itd.), o czym także warto uprzedzić.
Minimalny poziom — bez potwierdzeń
Jeśli macie public readonly tools, warto oznaczać je jako readOnlyHint: true. Przykład:
"annotations": {
"readOnlyHint": true,
"destructiveHint": false,
"openWorldHint": false
}
Takie narzędzia można wywoływać bez zbędnych dialogowych potwierdzeń po stronie GPT.
Jedno potwierdzenie
Jeśli macie tools, które coś zmieniają na serwerze, logiczne jest oznaczenie ich jako readOnlyHint: false:
"annotations": {
"readOnlyHint": false,
"destructiveHint": false,
"openWorldHint": false
}
Model, widząc takie narzędzie, najpewniej poprosi użytkownika o potwierdzenie jeden raz (zwykle to modalne okno dialogowe w UI ChatGPT).
Niebezpieczne działanie
Jeśli macie tool, który coś usuwa na serwerze, oznaczcie go jako destructiveHint: true:
"annotations": {
"readOnlyHint": false,
"destructiveHint": true,
"openWorldHint": false
}
Model będzie bardzo ostrożnie wywoływać taki tool i dwukrotnie dopyta:
- najpierw poprosi o potwierdzenie w tekście,
- potem platforma pokaże standardowe okno dialogowe.
Dla naszego GiftGenius w ramach tego modułu nie piszemy jeszcze narzędzi commerce, ale można naszkicować, jak wyglądałby przyszły create_gift_order:
server.registerTool(
"create_gift_order",
{
title: "Utworzenie zamówienia na prezent",
description:
"Używaj tylko po wyraźnej zgodzie użytkownika na zakup wybranego prezentu. Tworzy zamówienie w systemie i zwraca status.",
inputSchema: {
type: "object",
properties: {
giftId: {
type: "string",
description: "ID prezentu, który użytkownik wybrał."
},
deliveryEmail: {
type: "string",
description: "Email, na który trzeba wysłać prezent cyfrowy."
}
},
required: ["giftId", "deliveryEmail"]
},
annotations: {
destructiveHint: true,
openWorldHint: true
}
},
async () => {
/* ... */
}
);
Adnotacje nie zastępują waszych kontroli uprawnień po stronie serwera, one jedynie pomagają ChatGPT ułożyć UX: zapytać o potwierdzenie, pokazać ostrzeżenie i nie wykonywać takich narzędzi „po cichu”.
7. Metadane App i dwa poziomy discovery
Narzędzia to połowa historii. Druga połowa to: jak użytkownik w ogóle znajduje i uruchamia wasz App.
W ekosystemie ChatGPT są dwa kluczowe poziomy discovery.
Pierwszy — in‑conversation discovery. Gdy użytkownik pisze coś na czacie (nawet bez jawnego wspomnienia App), model patrzy:
- na tekst wiadomości i historię dialogu;
- na opisy dostępnych aplikacji i ich narzędzi;
- na wzmianki o marce, tematykę i słowa kluczowe.
Na tej podstawie decyduje, czy warto zaproponować jakiś App, a jeśli tak — który i z jakim scenariuszem. Tu szczególnie ważne są opisy narzędzi i samego App. Jeśli zawierają „triggery” typu „dobór prezentów”, „pomysł na prezent”, „budżet prezentu”, szansa, że model wybierze wasz App, rośnie znacząco.
Drugi poziom — globalne discovery: katalog i launcher. Tam rolę odgrywa człowiek: wybiera App wzrokiem po nazwie, ikonie, krótkim opisie i tagach. Ważne, abyście uczciwie i jasno wyjaśnili, co robi wasza aplikacja, dla kogo jest i gdzie leży jej podstawowa wartość.
Można to streścić w krótkiej tabeli:
| Warstwa | Co widzi model/użytkownik | Co jest ważne w metadanych |
|---|---|---|
| In‑conversation | Tekst dialogu, opisy tools i App | Formuły wyzwalające, nazwy akcyjne, ograniczenia |
| Katalog/launcher | Nazwa, ikona, short/long description, tagi | Jasne pozycjonowanie, czytelny value‑props |
Dla GiftGenius można np. sformułować:
- Nazwa: GiftGenius — dobór prezentów w 60 sekund.
- Krótki opis: Zbiera profil odbiorcy i proponuje 5–7 pomysłów na prezenty z możliwością natychmiastowego zakupu w ChatGPT.
- Opis dla in‑conversation: Używaj tej aplikacji, gdy użytkownik prosi o pomoc w wyborze prezentu, nie wie, co podarować, podaje budżet, zainteresowania odbiorcy lub okazję.
Te sformułowania warto zsynchronizować z tym, co już napisaliście w system‑prompt i w opisach narzędzia recommend_gifts. Wtedy model widzi spójny obraz, a nie zbiór sprzecznych tekstów.
8. Jak routowanie działa „w głowie” ChatGPT
Zbierzmy wszystko i spójrzmy na typową ścieżkę zapytania — bez wchodzenia w protokół MCP, to będzie w kolejnych modułach.
Załóżmy, że użytkownik pisze:
„Pomóż wymyślić prezent dla brata, uwielbia piłkę nożną i planszówki, budżet do 50 dolarów.”
W dużym uproszczeniu algorytm:
- Model analizuje wiadomość i historię. Widzi słowa „prezent”, „brat”, „piłka nożna”, „planszówki”, „budżet 50”.
- Porównuje to z opisami dostępnych App i ich narzędzi. Dla GiftGenius opisy jednoznacznie zawierają „dobór prezentów według zainteresowań i budżetu”, więc prawdopodobieństwo, że App jest relewantny, jest wysokie.
- Jeśli App nie jest jeszcze aktywny w tej sesji, model formułuje zapowiedź: „Mogę otworzyć aplikację GiftGenius, która pomoże dobrać prezent według twoich parametrów. Otworzyć?” — to z góry ujęliśmy w instrukcjach UX.
- Po zgodzie użytkownika model wybiera wewnątrz App narzędzie recommend_gifts, ponieważ to jego opis najlepiej odpowiada bieżącej intencji. Tutaj i nazwa, i description, i struktura inputSchema działają jako sygnały wejściowe.
- Model uzupełnia argumenty narzędzia na podstawie zapytania: najpierw (w razie potrzeby) wywołuje profile_to_segments, aby z tekstu „brat, lubi piłkę nożną i planszówki” uzyskać segmenty ["football_fan", "board_games"], następnie wywołuje recommend_gifts z segments, budget: {min: 0, max: 50, currency: "USD"}, locale, occasion: "birthday".
- Serwer MCP wykonuje narzędzie, formuje structured output z items i meta i zwraca go.
- Model czyta JSON, który opisaliście w outputSchema, i buduje odpowiedź: wyjaśnia, co znalazł, dlaczego właśnie te prezenty, i proponuje follow‑up („chcesz zawęzić po kategorii?”, „pokazać podobne do tego prezentu?” lub „sfinalizować zakup tego prezentu?”).
Oto prosta blok‑schemat tego procesu:
flowchart TD A[User: zapytanie o prezent] --> B[ChatGPT analizuje kontekst] B --> C[Porównanie z metadanymi App i tools] C -->|relewantne| D[Zapowiedź GiftGenius] D -->|użytkownik się zgadza| E["Wywołanie recommend_gifts (+ profile_to_segments)"] E --> F[Serwer MCP GiftGenius] F --> G[Wynik JSON z items/meta] G --> H[Model formułuje odpowiedź i follow‑up]
Im lepiej opisaliście narzędzia i use‑cases, tym mniej tu przypadkowości i tym stabilniejsze routowanie.
Insight: Tool Call SEO
W ekosystemie Apps wkrótce będziecie konkurować nie tylko o uwagę ludzi w katalogu, ale i o uwagę samego modelu. Na to samo zapytanie użytkownika ChatGPT może przywołać kilkanaście różnych aplikacji, a wybór zajdzie nie w czyjej prezentacji będzie ładniejszy design, lecz w „wynikach wyszukiwania” wewnątrz głowy modelu. Ta niewidzialna warstwa coraz bardziej przypomina SEO, tylko zamiast stron macie tools i serwery MCP.
Model w istocie rangruje kandydatów: najpierw na poziomie App, potem na poziomie poszczególnych narzędzi. Patrzy na nazwę, descriptions, schematy, adnotacje i zestawia je z sformułowaniami zapytania. Jeśli w opisie recommend_gifts jest „dobór prezentów według budżetu i zainteresowań odbiorcy”, a w zapytaniu brzmi „dobierz prezent przyjacielowi‑graczowi za 50 $”, to to narzędzie ma większe szanse „trafić do topu” niż abstrakcyjne search z opisem „praca z prezentami”.
Stąd rodzi się praktyczna idea Tool Call SEO: traktować nazwy, descriptions, wartości enum i metadane jak słowa kluczowe i snippety. Nie opisujecie tylko kontraktu dla developerów — optymalizujecie go pod realny ruch zapytań ze swojego golden prompt set. Zbyt ogólne sformułowania, nachodzące na siebie obszary kilku narzędzi, God‑narzędzia bez wyraźnej niszy — to wszystko obniża „CTR” waszego App w głowie modelu.
9. Małe ćwiczenie praktyczne
Spróbujcie w myślach (albo w swoim repozytorium) wykonać następujące kroki.
Najpierw wybierzcie jeden z kluczowych scenariuszy GiftGenius — np. „Dobrać prezent dla kolegi z pracy z ograniczonym budżetem”.
Sformułujcie dla niego:
- Jakie osobne narzędzie jest potrzebne pod ten scenariusz: czy to czyste recommend_gifts, czy potrzebujecie jeszcze wyspecjalizowanego narzędzia dla case’u B2B, czy może wystarczy po recommend_gifts użyć similar_gifts do wariacji?
- Jakie pola naprawdę są niezbędne w schemacie wejściowym recommend_gifts. Jakie pola można dopytać użytkownika osobno (przez follow‑up), zamiast kazać modelowi zgadywać.
- Jak powinien wyglądać outputSchema, aby model mógł uczciwie wyjaśnić wybór i zaproponować następne kroki (np. przełączyć się na tryb B2B, pokazać tylko prezenty cyfrowe, zawęzić po przedziale cenowym).
A potem spójrzcie na swój golden prompt set z poprzedniego wykładu i sprawdźcie:
- czy dla każdego wzorcowego zapytania istnieje oczywiste narzędzie (recommend_gifts, get_gift, similar_gifts itd.);
- czy nie wyszło tak, że dwa narzędzia równie „pasują” do jednego i tego samego zapytania (overlapping tools);
- czy trzeba wzmocnić opisy albo zmienić nazwę któregoś narzędzia, aby model mylił się rzadziej.
To dokładnie ten proces, który będziecie powtarzać przed każdą poważniejszą zmianą promptu, schematów czy logiki — w istocie mini‑eval jakości discovery.
Jeśli streścić wszystko powyżej w checkliście, na tym etapie potrzebujecie:
- uczciwie pociąć scenariusze na 2–4 sensowne narzędzia;
- porządnie opisać inputSchema/outputSchema z przykładami i enum‑ami;
- uporządkować nazwy, descriptions i adnotacje;
- zsynchronizować to z system‑prompt i metadanymi App.
W kolejnych modułach zobaczymy już, jak to wszystko działa przez MCP oraz jak diagnozować dziwne zachowania discovery/routowania.
10. Typowe błędy przy projektowaniu tools i metadanych
Błąd nr 1: „Wszystko opisaliśmy w system‑prompt, narzędzia jakoś sobie poradzą”.
Jeśli świetnie rozpisaliście rolę App, granice odpowiedzialności i zachowania UX, ale zostawiliście narzędzia z nazwami tool1, search, do_stuff i schematami bez opisów, model po prostu nie zwiąże waszego ładnego tekstu z realnymi wywołaniami. Dla ChatGPT narzędzia to główny interfejs; bez porządnych metadanych żaden system‑prompt nie uratuje sytuacji.
Błąd nr 2: God Tool, który robi wszystko naraz.
Chęć „optymalizacji” i zrobienia jednej funkcji z parametrem mode jest zrozumiała, ale prowadzi do monstrualnych schematów JSON, zamieszania w opisach i gorszego routowania. Model zaczyna zgadywać, którego trybu użyć, a wy — utrzymujecie ogromny switch na serwerze. Lepiej mieć kilka wyraźnych narzędzi do konkretnych kroków scenariusza niż jedno „zrób wszystko”.
Błąd nr 3: Schemat wejściowy przepełniony polami „na wszelki wypadek”.
Często developerzy próbują od razu przepchnąć przez inputSchema wszystkie parametry, które kiedykolwiek mogą się przydać, plus parę pól wewnętrznych. W efekcie model próbuje zgadywać to, czego nie może znać (np. tenantId), a wy potem dziwicie się dziwnym wartościom. Input Schema powinien zawierać tylko to, co model realnie może wywnioskować z dialogu lub dopytać. Szczegóły wewnętrzne dodawajcie po stronie serwera.
Błąd nr 4: „Niemie” dane wyjściowe bez metainformacji.
Zwracanie z narzędzia po prostu tablicy obiektów bywa kuszące. Ale wtedy odbieracie modelowi zrozumienie, dlaczego te wyniki się pojawiły. Bez pól takich jak score, reason, searchCriteria, totalCandidates trudniej mu budować uczciwe wyjaśnienia i follow‑up. Dodanie niewielkiej otoczki meta z kryteriami wyszukiwania i wskazówkami często radykalnie poprawia jakość odpowiedzi.
Błąd nr 5: Deski w opisach: „Praca z prezentami”, „Wyszukiwanie kursów”, „Przetwarzanie danych”.
Takie opisy są złe, bo nie dają modelowi ani triggerów, ani ograniczeń. Nie wie, kiedy dokładnie trzeba wywołać narzędzie i w jakim obszarze jest ono stosowne. Dobry opis zaczyna się od „Użyj tego narzędzia, gdy…” i zawiera konkretne scenariusze oraz zakazy typu „Nie używaj do…”. Idealnie, jeśli te sformułowania pokrywają się ze złotymi zapytaniami z waszego golden prompt set.
Błąd nr 6: Ignorowanie adnotacji i mieszanie działań read‑only z modyfikującymi.
Jeśli nie oznaczacie narzędzi, które tylko czytają dane (readOnlyHint), i tych, które wykonują akcje (destructiveHint, openWorldHint), model nie może zbudować właściwego UX potwierdzeń. W efekcie albo pojawiają się zbędne „Czy na pewno?” na każdym kroku, albo przeciwnie — ciche zakupy i zmiany bez zgody użytkownika. Adnotacje to tani i skuteczny sposób podpowiedzenia modelowi wagi operacji.
Błąd nr 7: Metadane App do katalogu i metadane do in‑conversation żyją w różnych światach.
Bywa, że krótki opis w katalogu napisał marketing („Rewolucyjny asystent AI, który zmienia twoje życie”), a descriptions tools i system‑prompt — developer („dobór prezentów według budżetu”). W rezultacie w katalogu nie wiadomo, o co chodzi w App, a model w czacie nie może skojarzyć pytań typu „co to za serwis?” z realnymi możliwościami App. Piszcie metadane jako jedną specyfikację, a nie dwa niezależne teksty marketingowe.
GO TO FULL VERSION