From the field #08 - AX: agent experience, którego twoja strona nie ma
Część 2: sześć portali, siedemdziesiąt minut, warstwa dla agentów
Część 1 postawiła tezę, że twoja strona ma UX i nie ma AX. Warstwa agent-discovery to to, co domyka tę lukę. Ta część to sama warstwa: dwa manifesty, co deklarują, wzorzec, który położył je na flocie zamiast na jednej stronie, i jeden portal, który dostał celowo inną odpowiedź.
Będę konkretny, bo cały sens From the Field jest taki, że pliki są prawdziwe i żywe. Wszystko tu jest na produkcji w tej chwili. Możesz to scurlować w trakcie czytania.
Dwa manifesty, jedna robota każdy
Warstwa agent-discovery to dwa małe publiczne pliki, każdy wymierzony w jeden ze standardów z Części 1.
/.well-known/agent.json to A2A Agent Card. Odpowiada na “czym jest ten agent i co potrafi”. Nazwa, opis, wersja, URL dokumentacji, capabilities, które wspiera, jak się uwierzytelnić, i lista skilli. Skille są częścią, która się liczy: każdy to nazwana rzecz, którą agent potrafi zrobić, z opisem, który LLM czyta dosłownie, żeby zdecydować, czy go użyć, i z przykładowymi zapytaniami, które pokazują, do czego on jest.
/.well-known/mcp.json to MCP discovery manifest. Odpowiada na “gdzie jest wywoływalny endpoint i jak z nim gadać”. Wskazuje na serwer /mcp, deklaruje transport, auth i które prymitywy MCP są dostępne - tools, resources, prompts.
Razem są AX-owym odpowiednikiem dobrej strony głównej. Jeden mówi, czym agent jest, drugi mówi, jak się podłączyć i działać. Agent, który uderza w /.well-known/, znajduje oba i wie, bez skrobania jednej strony, dokładnie co ten portal oferuje i jak go użyć.
Custom na portal, bo portfolio to nie strona dokumentacji
Pułapką jest generyczny template. Kusi, żeby napisać jeden Agent Card, zmienić nazwę i odbić go na każdy portal. To produkuje agenta, który kłamie. Agent Card, którego skille nie zgadzają się z realnym tool surface, jest gorszy niż brak karty, bo agent spróbuje wywołać skill, którego tam nie ma.
Więc każdy portal dostał custom kartę, której skille zgadzają się z jego realnymi toolami /mcp dosłownie. Dowodem jest to, że karty są widocznie różne. Agent portfolio deklaruje siedem skilli - list_articles, read_article, search, list_projects, list_ecosystem, get_about, get_metrics - bo to właśnie eksponuje serwer MCP portfolio. Strona dokumentacji mojego patternu testowego deklaruje zupełnie inne pięć - list_docs, read_doc, list_examples, read_example, search - bo to inny agent robiący inną robotę. Ten sam kształt, inny surface. Agent portfolio i agent dokumentacji to nie ten sam agent, a ich karty to mówią.
Każdy opis skilla jest napisany tak, jak w ogóle piszę opisy MCP toolów, bo to jest agent-SEO: model czyta opis, żeby zdecydować, czy wywołać skill. Więc każdy ma pięćdziesiąt słów albo więcej, mówi, co zwraca, i mówi, kiedy go nie używać. Skill search portfolio mówi agentowi, żeby użył go do zapytań tematycznych, a nie wtedy, gdy ma już dokładny slug, bo zły wybór toola to zmarnowane wywołanie. Ta dezambiguacja to różnica między agentem, który używa cię dobrze, a takim, który się miota.
Wzorzec, który zrobił z tego flotę, a nie harówkę
Tu jest część inżynierska i to powód, dla którego sześć portali kosztowało siedemdziesiąt minut, a nie dzień.
Te portale są dwujęzyczne. Dwa języki, budowane z jednego źródła w dwa deploye o różnych domenach. Statyczny public/.well-known/agent.json wysłałby ten sam zahardkodowany url do obu buildów, więc jeden język reklamowałby złą domenę. Lekarstwem jest nic nie hardkodować. Czytaj tożsamość deploya build-time:
// src/pages/.well-known/agent.json.ts
import type { APIRoute } from 'astro';
export const GET: APIRoute = ({ site }) => {
const baseUrl = site?.toString().replace(/\/$/, '') ?? '';
const agentCard = {
name: 'CDAT Documentation Agent',
description: '...custom, specyficzny dla portalu, 50+ słów...',
url: baseUrl,
version: '1.0.0',
documentationUrl: `${baseUrl}/llms.txt`,
capabilities: {
streaming: false,
pushNotifications: false,
stateTransitionHistory: false,
},
authentication: { schemes: ['public'], credentials: null },
defaultInputModes: ['text/plain', 'application/json'],
defaultOutputModes: ['text/plain', 'application/json'],
skills: [
/* zmatchowane z realnymi toolami /mcp, custom per portal */
],
contact: { name: 'Dariusz Kowalski', url: 'https://portfolio.sdet.it/contact' },
};
return new Response(JSON.stringify(agentCard, null, 2), {
headers: { 'Content-Type': 'application/json; charset=utf-8' },
});
};
Astro.site jest czytane build-time, więc build każdej domeny wpieka własny url i własny link do dokumentacji. Jeden plik źródłowy, poprawny na obu deployach, nic do synchronizowania. Endpoint mcp.json ma ten sam kształt. To też dokładnie wzorzec, którego istniejące endpointy llms.txt.ts i llms-full.txt.ts już używały, więc nowe manifesty pasują do repo, zamiast z nim walczyć.
Zauważ, co blok capabilities uczciwie mówi: streaming off, push off, state history off. Agent portfolio jest read-only i publiczny. Nie zadeklarowałem capability, której endpoint nie pokrywa. Agent Card twierdzący streaming, którego nie potrafi, to to samo kłamstwo co skill, którego nie ma.
Portal, który dostał inną odpowiedź celowo
Pięć z sześciu portali to publiczny read - portfolio, strona dokumentacji, strony brandowe. Szósty to prywatna baza wiedzy i potrzebował ostrożniejszej odpowiedzi, bo właściwym ruchem dla prywatnego API nie jest ukrycie go przed warstwą discovery i nie jest też reklamowanie jego toolów każdemu, kto zapyta.
Więc ten portal też deklaruje Agent Card, ale celowo niejasny. Jego schemat uwierzytelniania to bearer, nie public. Jego skille są opisane wysokopoziomowo i nie wyliczają realnego tool surface. Komunikat manifestu jest precyzyjny: ten brand asset istnieje, oto do kogo należy, a API za nim jest bramkowane. Agent dowiaduje się, że asset jest realny, dowiaduje się, że potrzebuje poświadczeń, i nie dowiaduje się niczego, czego nie powinien. Obecny w warstwie discovery, uczciwy co do bycia zamkniętym. To jest odpowiedź auth-gated discovery i to inna decyzja niż pięć publicznych, podjęta celowo.
Czym te siedemdziesiąt minut realnie było
Liczba, dokładnie, bo precyzja to brand.
Odpalam sześć brand portali. Te siedemdziesiąt minut to batch, który doprowadził wszystkie sześć do tego samego agent-ready baseline - jeden sprint, cztery repozytoria, każdy portal dostał swój custom Agent Card i MCP discovery manifest, wobec budżetu osiemdziesiąt do stu dziesięciu minut.
Czym ta liczba nie jest: nie jest wymyśleniem całej warstwy agentowej z zera w siedemdziesiąt minut. Warstwa read już istniała. Portfolio shipuje /mcp, /llms.txt, /llms-full.txt, /rss.xml i OG images per artykuł od MVP w kwietniu. To, co sprint dodał, to warstwa discovery na wierzchu - A2A Agent Cards i MCP discovery manifesty - i robota zestandaryzowania jej na całej flocie do jednego baseline. To zrównanie floty to swój własny rodzaj roboty, ten, którego większość ludzi nigdy nie budżetuje, a siedemdziesiąt minut to uczciwy koszt tego. Twierdzenie to twierdzenie o flocie i trzymam je dokładnie.
Jedna decyzja architektoniczna podpiera tę szybkość: per-portal, nie hub. Rozważałem jeden agent.sdet.it, który namespace’uje tools każdego portalu za jednym serwisem. Nie zbudowałem go. Hub to jeden serwis więcej do utrzymania i jeden punkt więcej do awarii, a przy sześciu portalach nie kupuje niczego, czego karta per-portal nie kupuje. Każdy portal niesie własne manifesty i jest samowystarczalny. Decyzję rewiduje się przy więcej niż dziesięciu portalach, nie wcześniej.
Jedna uczciwa aktualizacja, bo spec się ruszył
Wszystko to shipnąłem w maju, a agentic web nie stoi w miejscu. Między tym sprintem a pisaniem tego A2A osiągnął v1.0 pod Linux Foundation i ustandaryzował dokładnie te rzeczy, o których jest ten odcinek. Oficjalna ścieżka Agent Card przeniosła się na /.well-known/agent-card.json, z agent.json jako backward-compat aliasem. Auth przeszedł z authentication.schemes na securitySchemes. Pojawiły się signed cards jako trust model. To, co jest żywe na moich sześciu portalach, to snapshot z maja, starszy kształt agent.json. Clone-and-run pack, na który wskazuję w Części 3, jest pod current v1.0 spec, z aliasem. Portale to snapshot w czasie; pack jest current. Ta luka to nie coś do ukrycia, to realna tekstura budowania na standardzie, gdy on twardnieje, i w Części 3 staje się jedną z pięciu lekcji.
Shipnięcie to nie dowód. Manifest, który zwraca 200, to nie to samo co manifest, który agent faktycznie potrafi przeczytać i na nim działać. Część 3 to weryfikacja: dwadzieścia smoke próbek, które odpaliłem na flocie, pięć lekcji, które kosztowały mnie czas, żeby nie kosztowały twojego, i pack, który możesz sklonować, żeby shipnąć tę samą warstwę pod current spec.
From the Field to jest to, co realnie buduję, co się psuje i czego się uczę. Realne projekty, realne liczby, realne bugi. Bez tutoriali.