Configurazione del widget (EDS)
Questa guida spiega come creare un widget EDS end-to-end: dalla configurazione dell’azione nell’interfaccia utente LLM Apps alla configurazione del progetto EDS, alla scrittura del codice di blocco che esegue il rendering dei dati all’interno della piattaforma LLM. Per una panoramica di alto livello, vedi Concetti di base.
Il SDK LLM Apps
Tutto inizia con il pacchetto @adobe/llmapps-sdk npm. SDK è la libreria JavaScript che alimenta il canale di comunicazione bidirezionale tra il widget e l’host LLM.
Il SDK invia anche aem-embed.js, il punto di ingresso specifico per EDS che collega SDK alla pipeline di blocchi EDS standard. Quando npm install @adobe/llmapps-sdk, uno script di post-installazione copia automaticamente due file nel progetto:
scripts/
└── llm-apps/
├── aem-embed.js ← EDS widget entry point, ships with the SDK
└── llmapps-sdk.js ← core SDK, loaded internally by aem-embed.js
Nei progetti EDS, non utilizzi mai SDK direttamente nel codice di blocco. aem-embed.js crea e gestisce la connessione SDK e passa un’istanza LLMApp completamente connessa al blocco come argomento bridge in decorate(block, bridge). L’API SDK completa è disponibile su bridge. Non è necessaria alcuna importazione.
Se si sta creando un widget senza EDS (un bundler standard o un progetto TypeScript), è possibile utilizzare direttamente SDK:
import { LLMApp } from '@adobe/llmapps-sdk';
const app = new LLMApp({ appInfo: { name: 'MyWidget', version: '1.0.0' } });
await app.connect();
const { structuredContent } = await app.toolResult;
Come si combina tutto
Quando l’IA chiama l’azione e il gestore restituisce structuredContent, la piattaforma LLM esegue il rendering di un widget interattivo nella conversazione. Tre fattori rendono possibile il funzionamento congiunto:
Interfaccia utente LLM Apps: quando crei un’azione, immetti un URL script e un URL widget nella scheda Metadati widget. L’URL dello script punta a aem-embed.js, il file che viene fornito con SDK e risiede nell’archivio EDS in scripts/llm-apps/aem-embed.js. Indica alla piattaforma LLM quale script caricare quando viene richiamata l’azione.
aem-embed.js — la piattaforma LLM carica questo script in una superficie di widget in modalità sandbox. aem-embed.js è un elemento HTML personalizzato (<aem-embed>) che funge da punto di ingresso compatibile con EDS per il widget. Esegue l’handshake con l’host LLM utilizzando SDK, sopprime la normale pipeline della pagina EDS (senza intestazione/piè di pagina), recupera il contenuto della pagina EDS dall’URL del widget, esegue la pipeline del blocco EDS e distribuisce un oggetto bridge attivo alla funzione decorate() di ciascun blocco.
Codice blocco: si scrive un blocco EDS standard che esporta una funzione decorate(block, bridge). bridge è l’istanza di SDK connessa. Fornisce il risultato strutturato dell’azione e consente di inviare nuovamente messaggi alla conversazione.
Aggiungi a un progetto EDS esistente
Se disponi già di un progetto EDS, sono disponibili solo due passaggi prima di poter iniziare a scrivere blocchi.
-
Installa
@adobe/llmapps-sdk. Lo script di post-installazione copiaaem-embed.jsellmapps-sdk.jsinscripts/llm-apps/:code language-bash npm install @adobe/llmapps-sdk -
Configura le intestazioni CORS in modo che la piattaforma LLM possa caricare le pagine widget e gli script tra origini diverse. Vedi Configurare le intestazioni CORS di seguito.
Quindi crea il blocco seguendo il contratto decorate(block, bridge), crea la pagina del widget e immetti gli URL nella finestra di dialogo Crea azione.
Imposta un nuovo progetto EDS
Creare l’archivio
-
Crea un nuovo archivio GitHub basato sul modello AEM boilerplate.
-
Aggiungi l’app GitHub di sincronizzazione codice AEM all’archivio.
-
Installare AEM CLI per lo sviluppo locale:
npm install -g @adobe/aem-cli. -
Installa
@adobe/llmapps-sdk. Lo script di post-installazione copiaaem-embed.jsellmapps-sdk.jsinscripts/llm-apps/:code language-bash npm install @adobe/llmapps-sdk
Per una guida completa sui progetti EDS, consulta l’esercitazione per sviluppatori di AEM e l’anatomia del progetto.
Una volta configurato, il sito EDS sarà disponibile all’indirizzo:
- Anteprima:
https://main--<repo>--<owner>.aem.page/ - Live:
https://main--<repo>--<owner>.aem.live/
Struttura dell’archivio
my-brand-eds/
├── scripts/
│ ├── llm-apps/
│ │ ├── aem-embed.js # Widget entry point — copied by post-install
│ │ └── llmapps-sdk.js # Core SDK — copied by post-install
│ ├── aem.js # AEM core library
│ └── scripts.js # Site-level decoration and loading
├── blocks/
│ └── search-products/ # One folder per widget block
│ ├── search-products.js
│ └── search-products.css
├── styles/
│ └── styles.css
├── head.html
└── package.json
Configurare le intestazioni CORS
Le pagine dei widget EDS vengono caricate all’interno di una superficie di widget in modalità sandbox dalla piattaforma LLM. Il sito EDS deve restituire access-control-allow-origin intestazioni corrette in modo che l’host possa recuperare il contenuto del widget da un’origine all’altra.
Le intestazioni vengono configurate tramite il pannello di amministrazione di AEM in admin.hlx.page utilizzando il servizio di configurazione. Aggiungi intestazioni di risposta personalizzate per i percorsi in cui risiedono le pagine dei widget e gli script di SDK:
{
"/<your-widget-pages-path>/**": [
{ "key": "access-control-allow-origin", "value": "*" }
],
"/scripts/**": [
{ "key": "access-control-allow-origin", "value": "*" }
]
}
* come valore di origine è accettabile per il contenuto del widget pubblico nel dominio .aem.live. Se il sito contiene contenuto protetto, limita l’origine a domini specifici.Creare la pagina del widget
Creare una pagina nello strumento di creazione EDS e aggiungervi il blocco. L’URL della pagina diventa l’URL widget configurato nell’azione, ovvero l’unica connessione tra l’azione e il blocco. Non esiste alcun requisito di denominazione tra il blocco e il nome dell’azione.
Immetti gli URL nella finestra di dialogo Crea azione
Dopo aver configurato l’archivio EDS, vai a URL modello → metadati widget durante la creazione dell’azione:
URL script — punta a aem-embed.js nell’archivio EDS. Questo è lo stesso valore per ogni azione nello stesso progetto EDS:
https://main--<repo>--<owner>.aem.live/scripts/llm-apps/aem-embed.js
URL widget: URL della pagina EDS creata per questo widget. Univoco per azione:
https://main--<repo>--<owner>.aem.live/<path-to-your-widget-page>
La piattaforma LLM carica aem-embed.js dall’URL dello script. aem-embed.js recupera quindi .plain.html dall’URL del widget per ottenere il contenuto del blocco.
Flusso di dati
Percorso completo dal gestore a un widget di cui è stato eseguito il rendering:
- Il gestore azioni restituisce
structuredContent:
// actions/search-products/index.js
return {
structuredContent: {
products: [
{ id: 'COF-001', name: 'Single Origin Ethiopian Coffee', price: '$18', rating: 4.7 },
{ id: 'COF-002', name: 'Colombia Huila Natural', price: '$22', rating: 4.5 },
],
total: 2,
category: 'coffee'
}
};
-
La piattaforma LLM apre una superficie di widget e carica
aem-embed.jsdall’URL dello script. -
aem-embed.jssi connette all’host tramite SDK, recupera.plain.htmldall’URL del widget, esegue la pipeline del blocco EDS e chiamadecorate(block, bridge)sul blocco. -
Il blocco legge i dati da
bridge.toolResulted esegue il rendering dell’interfaccia utente. -
L’interazione utente attiva
bridge.sendMessage(...)obridge.callTool(...), inviando un follow-up alla conversazione.
Il contratto decorate(block, bridge)
Ogni blocco di widget EDS deve esportare una funzione decorate predefinita. Questa è la firma di blocco EDS standard, estesa con un secondo argomento, bridge connesso, che è un’istanza SDK LLMApp con l’API completa disponibile:
export default async function decorate(block, bridge) {
// ...
}
bridge è presente solo quando viene eseguito all’interno della superficie del widget della piattaforma LLM. Proteggi sempre le chiamate bridge in modo che il blocco venga riprodotto anche quando viene visualizzata in anteprima direttamente in un browser o nel server di sviluppo locale.
Rendering dei dati dal risultato dell’azione
bridge.toolResult è una promessa che risolve con il risultato completo restituito dal gestore, incluso structuredContent.
const SAMPLE_PRODUCTS = [
{ id: 'COF-001', name: 'Single Origin Ethiopian Coffee', price: '$18', rating: 4.7 },
];
export default async function decorate(block, bridge) {
let products = SAMPLE_PRODUCTS;
if (bridge) {
const result = await bridge.toolResult;
products = result?.structuredContent?.products ?? [];
}
block.innerHTML = products.map(p => `
<div class="product-card">
<h3>${p.name}</h3>
<p class="price">${p.price}</p>
<button data-id="${p.id}">Tell me more</button>
</div>
`).join('');
}
Applicazione del tema host
Chiamare bridge.applyHostStyles() all’inizio di decorate per inserire nel widget le variabili e i font CSS dell’host (tema chiaro/scuro, tipografia). In questo modo il widget rimane visivamente coerente con l’interfaccia utente della piattaforma LLM circostante.
export default async function decorate(block, bridge) {
if (bridge) {
bridge.applyHostStyles();
}
// ...
}
Per reagire alle modifiche del tema in fase di runtime (ad esempio, quando l’utente passa dalla modalità chiara alla modalità scura):
if (bridge) {
bridge.onContextChange(ctx => {
block.dataset.theme = ctx.theme; // 'light' | 'dark'
});
}
Invio di un messaggio di completamento
bridge.sendMessage(text) inserisce un messaggio utente nella conversazione. Questo è il modo principale in cui un widget attiva un’ulteriore interazione di intelligenza artificiale, ad esempio quando un utente fa clic su una scheda del prodotto per richiedere i dettagli.
block.querySelectorAll('button[data-id]').forEach(btn => {
btn.addEventListener('click', () => {
bridge.sendMessage(`Show me details for product ${btn.dataset.id}`);
});
});
Chiamata diretta di un’altra azione
bridge.callTool(name, args) richiama un’altra azione dall’interno del widget senza passare attraverso un messaggio utente. Utile per caricare dati correlati su richiesta.
btn.addEventListener('click', async () => {
const result = await bridge.callTool('get-product-details', { id: product.id });
renderDetails(result.structuredContent);
});
Ridimensionamento automatico del widget
La piattaforma LLM ridimensiona il widget in base a ciò che viene riportato. Utilizza bridge.autoResize(element) per mantenere sincronizzata l’altezza del widget man mano che il contenuto cambia, usa un ResizeObserver internamente. Chiamalo dopo il rendering iniziale:
export default async function decorate(block, bridge) {
// ... render content ...
if (bridge) {
bridge.autoResize(block);
}
}
In alternativa, puoi segnalare manualmente una dimensione fissa:
bridge.reportSize(block.offsetWidth, block.offsetHeight);
Modalità Anteprima e sviluppo locale
Quando si visualizza in anteprima una pagina EDS direttamente in un browser o sul server di sviluppo locale, bridge è undefined. Utilizza il pattern di fallback dei dati di esempio mostrato qui sopra in modo che il blocco venga eseguito immediatamente senza un gestore live.
Per avviare un server di sviluppo locale:
npm install -g @adobe/aem-cli
aem up
Verrà aperto http://localhost:3000, in cui è possibile passare alle pagine del widget e visualizzare i blocchi di rendering con dati di esempio. Le modifiche apportate al blocco JS e CSS vengono applicate immediatamente.