In un paio d’anni, l’intelligenza artificiale è passata da una tendenza emergente a una componente essenziale del software moderno. ChatGPT, Grok, Gemini e altri assistenti AI svolgono ormai un ruolo fondamentale nella vita quotidiana di tutti, sia a livello professionale che personale.
Ecco perché 4D 21 introduce 4D.Vectors e 4D AI Kit: per dare agli sviluppatori 4D strumenti semplici ed efficaci per aggiungere funzionalità AI alle loro applicazioni.
Abbiamo già condiviso molti esempi, tutorial e webinar sull’IA, ma di recente mi sono chiesto: cosa servirebbe per portare l’IA in un’applicazione 4D di 30 anni fa?
Potreste semplicemente chiedere a un’applicazione di questo tipo i vostri 10 migliori clienti e ottenere immediatamente un bel grafico?
Ebbene, indovinate un po’? È risultato così semplice da meritare un post tutto suo.
Iniziamo con il link ai sorgenti del progetto su Github e con un rapido video dimostrativo.
L’applicazione 4D Invoice
L’applicazione 4D Invoice è disponibile da tempo nel 4D Depot. È un solido esempio di come gestire prodotti, clienti e fatture in modo pulito e strutturato. Sebbene utilizzi già la modalità progetto (un requisito per sfruttare le ultime funzionalità di 4D AI), non sfrutta ancora le DataClass e i suoi moduli sono precedenti alla più recente classe Form. La base di codice è piuttosto ampia, utilizza modelli di progettazione generici e include diverse regole di business non banali.
Per questo esperimento, il mio obiettivo era aggiungere un nuovo modulo che permettesse di interagire con l’applicazione grazie all’intelligenza artificiale. Volevo che gli utenti potessero conversare con un assistente AI e porre domande semplici o più complesse. Nell’uso reale, questo avrebbe dato immediatamente un’impronta moderna a un’applicazione piuttosto “vecchia scuola”, in linea con le attuali aspettative degli utenti guidate dall’intelligenza artificiale. Davvero entusiasmante!
Un altro obiettivo era quello di non apportare modifiche alla struttura dei dati e di non alterare i meccanismi dell’interfaccia utente esistente. In altre parole, l’aggiornamento dell’intelligenza artificiale doveva essere altamente flessibile, poco invasivo e idealmente riutilizzabile in altre applicazioni legacy.
Di conseguenza, questo aggiornamento non si basa su 4D.Vectors o embeddings. Torneremo su questo punto più avanti, ma vale la pena di sottolinearlo subito: l’introduzione dell’intelligenza artificiale nella vostra applicazione 4D non richiede necessariamente embeddings o ricerca semantica. Si tratta di concetti separati.
Preparazione
In 4D abbiamo già condiviso diversi esempi e webinar basati sull’IA, tra cui le recenti dimostrazioni sulla costruzione di un sistema RAG utilizzando il tool calling. Per questo progetto, ho deciso di estrarre il modulo dell’interfaccia utente della chat dalla demoPeople & Skills e di renderlo più generico e riutilizzabile.
L’interfaccia utente della chat è costituita dai seguenti componenti:
- Il modulo AIChat, insieme alla sua classe formAIChat.4dm. Questo modulo fornisce:
- un semplice input di testo per la richiesta dell’utente
- un’area web che visualizza la conversazione con il modello
- La classe AI_ChatWithTools, responsabile di:
- istanziare un bot AI utilizzando l’helper di chat del 4D AI Kit, sulla base del server di inferenza e del modello configurato in Resources/AIProvider.json
- caricare gli strumenti definiti in Resources/AITools.json
- ospitare l’implementazione di tali strumenti
La classe viene eseguita interamente in modalità streaming, in modo che l’utente abbia un’esperienza simile a quella di ChatGPT, con risposte che appaiono progressivamente.
- La resa delle conversazioni è gestita dal singleton ChatHTMLRenderer.
Prende l’insieme dei messaggi memorizzati nell’AIBot e li trasforma in HTML utilizzando i modelli presenti nella cartella Resources:
-
- chat-template.html
- chat-template.css
- icona-strumento.svg
Sarò onesto: il rendering HTML è stato essenzialmente “codificato in modo vibrante” con l’aiuto dell’agente GitHub di VS Code e di Claude Sonnet 4.5. La parte più impegnativa è stata garantire un rendering corretto durante lo streaming, soprattutto per elementi come tabelle, chiamate agli strumenti e grafici.
Chiamata allo strumento AI
Come probabilmente sapete, un modello di intelligenza artificiale non conosce intrinsecamente nulla dei vostri dati. Può accedervi solo attraverso gli strumenti forniti esplicitamente dall’utente. In questa sezione spiegherò quali strumenti ho implementato e come funzionano.
tool_getProducts, tool_getClients, tool_getInvoices, tool_getInvoiceLines
Tutti questi strumenti sono responsabili dell’interrogazione del database. Ognuno di essi esegue una semplice query ORDA basata sui campi richiesti dal modello.
Naturalmente queste funzioni possono essere migliorate. Grazie alle funzioni native di 4D 21 AI, potremmo immaginare una ricerca semantica che utilizzi 4D.Vector e gli embeddings per potenziare il recupero dei clienti. Poiché ciò comporterebbe cambiamenti più profondi nella struttura, ho scelto una strada più semplice.
Analizziamo lo strumento_getClients, poiché lo stesso schema si applica a tutti gli altri strumenti “getter”:
Function tool_getClients($input : Object) : Object
Questa funzione riceve un oggetto come input e restituisce un oggetto come output. L’input descrive i parametri di ricerca; l’output contiene i dati risultanti.
Convalida dell’input
var $validation; $returnObject : Object
var $entities : cs.CLIENTSSelection:=ds.CLIENTS.all()
$validation:=JSON Validate($input; This._getToolArgumentsSchema(This._functionName(Call chain)))
If (Not($validation.success))
return {error: "Could not validate input parameters against JSON Schema, call the tool again with proper input parameters"}
End if
Il primo passo consiste nel verificare che l’input corrisponda allo schema definito in AITools.json.
La maggior parte dei modelli moderni produce chiamate allo strumento ben strutturate, ma a seconda del modello, la validazione è comunque una buona pratica di sicurezza.
Se la validazione fallisce, lo strumento restituisce un semplice oggetto contenente una proprietà di errore. È importante sapere che il modello legge questo valore, quindi è bene essere espliciti su cosa è andato storto.
Impostazione dei valori di input predefiniti
$input.ID:=($input.ID) || "any"
$input.Name:=($input.Name) || "@"
$input.Contact:=($input.Contact) || "@"
$input.Total_Sales:=$input.Total_Sales || {}
$input.Total_Sales.min:=$input.Total_Sales.min || 0
$input.Total_Sales.max:=$input.Total_Sales.max || 9999999
$input.orderBy:=($input.orderBy) || {}
$input.orderBy.field:=($input.orderBy.field) || "Name"
$input.orderBy.order:=($input.orderBy.order) || "asc"
$input.top:=($input.top) || This.defaultTop
$input.countOnly:=($input.countOnly) || False
I valori predefiniti garantiscono la sicurezza delle query ORDA, ma hanno anche un interessante vantaggio secondario: scrivere uno schema JSON completo in AITools.json può essere noioso. Invece, ho lasciato che GitHub Copilot deducesse lo schema dal codice e dalla struttura predefinita e il risultato è stato eccellente. Copilot ha prodotto una definizione accurata dello schema basata esclusivamente sulla logica di cui sopra.
Parametri da notare
- orderBy e top riducono la dimensione dell’output (e quindi l’uso di token).
- countOnly consente al modello di richiedere solo i conteggi dei record senza recuperare i dati.
Esempi pratici:
- Se il modello ha bisogno del numero di clienti → countOnly = true
- Se vuole i primi 5 clienti → top = 5 e orderBy: {field: “Total_Sales”, order: “desc”}
Inizializzazione dell’oggetto di output
$returnObject:={}
$returnObject.form:="Clients"
$returnObject.dataClass:="CLIENTS"
$returnObject.counts:={}
$returnObject.counts.total:=$entities.length
- form indica il modulo dell’interfaccia utente che può essere utilizzato per visualizzare i risultati – lo vedremo più avanti.
- dataClass identifica la classe di dati coinvolta, importante per le interrogazioni su più tabelle – vedremo anche questo più avanti.
- counts.total è il numero di record prima dell’applicazione di qualsiasi filtro.
Esecuzione della query
If ($input.ID#"any")
$entities:=$entities.query("ID = :1"; $input.ID)
End if
$entities:=$entities.query("Name = :1 and Contact = :2 and Total_Sales >= :3 and Total_Sales <= :4 order by "+$input.orderBy.field+" "+$input.orderBy.order; $input.Name; $input.Contact; $input.Total_Sales.min; $input.Total_Sales.max)
Questa parte è semplice e potrebbe essere resa più generica, ma non era questo il mio obiettivo.
Finalizzazione del risultato
$returnObject.counts.totalFiltered:=$entities.length
If ($returnObject.counts.totalFiltered>$input.top)
$entities:=$entities.slice(0; $input.top)
End if
$returnObject.counts.totalSent:=$entities.length
$returnObject.entities:=($input.countOnly) ? [] : $entities.toCollection("ID, Name, Contact, City, State, Country, Discount_Rate, Total_Sales, Comments")
return $returnObject
Ripartizione
- counts.totalFiltered: numero di record dopo il filtro
- counts.totalSent: numero di record effettivamente restituiti (dopo l’applicazione del top)
- entities: la raccolta finale dei dati (o vuota se countOnly = true)
Si noterà che lo strumento controlla esattamente quali attributi vengono inviati al modello. È importante trovare il giusto equilibrio.
Cosa considerare
- Qualità delle conversazioni: più dati → migliori approfondimenti
- Prestazioni: più dati → elaborazione più lunga
- Finestra di contesto: i modelli locali possono degradarsi soprattutto in caso di sovraccarico.
- Costi: più token → maggiore utilizzo dei server di inferenza nel cloud
- Riservatezza: considerare sempre il GDPR e la sensibilità dei dati quando si scelgono i campi
Il risultato finale
Qualsiasi oggetto restituito dallo strumento, 4D AI Kit lo serializza in modo trasparente e lo consegna al modello. Piuttosto comodo, no?
Interrogazione di tabelle incrociate e il primo prompt di sistema
Un’altra scoperta interessante fatta durante questo progetto è stata che il modello non comprendeva automaticamente le relazioni tra le tabelle. Ad esempio, richieste come:
Dammi i primi 5 clienti, la loro fattura più alta e il prodotto più ordinato
non sempre producevano risultati affidabili.
Il problema era semplice: il modello non si rendeva conto che l’ID della tabella Clienti doveva essere usato per interrogare la tabella Fatture, o che le righe delle fatture sono collegate ai prodotti e così via.
In altre parole, dovevo dare al modello una certa conoscenza della struttura, ma senza andare troppo lontano. I miei requisiti erano:
- Nessun prompt di sistema codificato: Non voglio aggiornare manualmente il prompt ogni volta che la struttura cambia.
- Nessun catalogo completo della struttura: Il modello non ha bisogno dell’intero schema, ma solo delle relazioni rilevanti.
Per ottenere questo risultato, ho riutilizzato una classe molto utile di Thomas Maul: StructureInfo.4dm, che estrae solo le relazioni tra le tabelle.
Da qui ho generato il mio prompt di sistema iniziale:
var $relations : Collection:=This._relationsInfos()
$systemPrompt:="You are a helpful assistant. I need your help to answer questions data stored in my application.\n"+\
"**CONTEXT**\n"+\
"The application stores data about invoices, products and clients"+\
"In some cases, you'll need to cross query several tables (dataClasses) in order to answer."+\
"To help you, here are the application relations between tables (dataClasses):\n"+\
JSON Stringify($relations)+"\n"+\
"**INSTRUCTIONS**\n"+\
"Analyze questions and answer step by step.\n"+\
"Use the tools at your disposal to answer everytime you think they are relevant.\n"+\
"**FORMATING**\n"+\
"Use HTML everytime.\n"+\
"Use bullet lists and Tables everytime everytime necessary\n"+\
"**IMPORTANT**\n"+\
"When calling tools, always include all required arguments in valid JSON.\n"+\
"Do not call a tool with empty arguments. If a value is missing, choose a reasonable default.\n"+\
"Always double check tools results before answering. Especially when they rely on vector search. \n"+\
"Indeed they may return results not matching with your search intention.\n"+\
"When tool calling returns data not related with the initial question, or that you cannot use to answer,\n"+\
"avoid detailing such results too much and stay short.\n"
Combinando questi metadati sulle relazioni con le informazioni sulle classi di dati restituite da ogni strumento, il modello era finalmente in grado di capire come concatenare le chiamate agli strumenti tra le tabelle.
Questo ha migliorato notevolmente la qualità e l’accuratezza delle risposte del modello, soprattutto per le analisi a più tabelle, come ad esempio:
primi clienti per fatturato
o
prodotti ordinati più frequentemente
o
le maggiori fatture per cliente
Esperienza utente: aprire un modulo 4D direttamente dall’interfaccia utente conversazionale
Nei miei precedenti esperimenti con RAG, ho spesso visualizzato i risultati in una casella di riepilogo separata. Questo approccio funzionava, ma presentava diversi svantaggi:
- Avevo bisogno che il modello fornisse, in una sezione nascosta separata, l’elenco degli ID delle entità da visualizzare nella casella di riepilogo. Ciò significava più token, risposte più lente e trucchi HTML aggiuntivi (come nascondere l’elenco degli ID all’interno di un blocco commentato).
- Ha richiesto ulteriore lavoro per rendere la casella di riepilogo abbastanza generica da gestire diversi tipi di entità (clienti, prodotti, fatture, …).
- Limitava l’interfaccia utente e sembrava meno integrata nell’applicazione.
Per questo progetto, volevo qualcosa di più semplice, più elegante e più naturale per l’utente.
Ho quindi adottato una nuova strategia: ogni volta che l’AI menziona un’entità, genera un collegamento ipertestuale personalizzato che apre direttamente il modulo 4D corrispondente, filtrato sull’entità selezionata.
La cosa si è rivelata sorprendentemente facile. Ho semplicemente aggiunto le seguenti istruzioni al prompt del sistema:
"**CUSTOM URL HANDLING**\n"+\
"Tools responses give information about the form to open when available, the dataClass and entities ID\n"+\
"When you display any element coming from a tool response, you must use a custom url so that the user can open the corresponding form\n"+\
"Such custom url must follow the following syntax examples:\n"+\
"<a href=\"myapp://openform?form=Products&dataClass=PRODUCTS&entities=989511\">A single product</a>\n"+\
"<a href=\"myapp://openform?form=Invoices&dataClass=INVOICESS&entities=654KJY,6467HGS,79864JSD\">A list of invoices</a>\n"
Poiché i miei strumenti restituiscono sempre il modulo appropriato, la classe di dati e l’elenco delle entità, il modello ha tutto ciò che gli serve per generare correttamente questi collegamenti. I modelli AI sono molto bravi in questo tipo di output strutturato.
Gestione degli URL personalizzati in 4D
Tutto ciò che mi serviva in 4D era un piccolo meccanismo di filtraggio degli URL nell’area web per intercettare i link myapp://link e aprire il modulo giusto con la giusta selezione di entità:
Function webAreaEventHandler($formEventCode : Integer)
var $formToOpen : Text
var $entitySelectionToShow : 4D.EntitySelection
var $queryObject : Object
Case of
: ($formEventCode=On Load)
ARRAY TEXT($filters; 0)
ARRAY BOOLEAN($allowDeny; 0)
APPEND TO ARRAY($filters; "myapp://*") // Intercept all URLs starting with myapp://
APPEND TO ARRAY($AllowDeny; False) //Allow
WA SET URL FILTERS(*; "Web Area"; $filters; $allowDeny)
: ($formEventCode=On URL Filtering)
$url:=WA Get last filtered URL(*; "Web Area")
// Parse the URL to determine what to do
Case of
: ($url="myapp://openform?@")
$queryObject:=This.queryObjectFromUrl($url)
If ($queryObject=Null)
return
End if
If ($queryObject.entitiesCollection.length>0)
$entitySelectionToShow:=ds[$queryObject.dataClass].query("ID in :1"; $queryObject.entitiesCollection)
CALL WORKER("Generic"; "W_Generic"; $queryObject.form; True; $entitySelectionToShow)
Else
CALL WORKER("Generic"; "W_Generic"; $queryObject.form; False)
End if
End case
End case
Il risultato
Il risultato finale è un’esperienza utente molto fluida, in cui l’interfaccia utente conversazionale si fonde perfettamente con il resto dell’applicazione 4D.
Gli utenti possono chattare con l’intelligenza artificiale, esplorare i dati e aprire istantaneamente i moduli 4D nativi, il tutto senza alterare la logica o la struttura principale dell’applicazione.
Un modello di interazione moderno, che si colloca sopra un’applicazione 4D classica… senza quasi alcuna intrusione.
Uno strumento aggiuntivo: tool_createInvoice
Il database di esempio fornito nel progetto GitHub comprendeva solo una manciata di fatture, non abbastanza per rendere interessanti le interazioni con l’IA. Avevo bisogno di più dati.
Creare un modulo personalizzato o scrivere un’altra routine di generazione di fatture sembrava noioso e irrilevante per lo scopo di questa demo. E poi ho capito: Avevo già tutto quello che mi serviva.
- Un’interfaccia utente conversazionale.
- Un agente AI consapevole del mio modello di dati.
- Conoscenza di clienti, prodotti, prezzi, relazioni…
Quindi perché non lasciare che l’IA crei le fatture direttamente dall’interfaccia della chat?
Tutto ciò che dovevo fare era aggiungere un nuovo strumento: tool_createInvoice.
Il mio approccio è stato semplice:
- Implementare una semplice funzione di creazione di fatture all’interno di AI_ChatWithTools.
- Avvolgere l’intero processo in una transazione:
- In caso di errore: rollback e invio di un messaggio di errore chiaro al modello.
- In caso di successo: eseguire il commit della fattura e restituirne i dettagli.
- Documentare attentamente lo schema dello strumento, in particolare i parametri di input.
- Poi… lasciare che GitHub Copilot generi lo schema JSON all’interno di AITools.json.
Il risultato?
Ora posso chiedere all’intelligenza artificiale di generare da sola una nuova fattura. Trova un cliente pertinente, seleziona i prodotti, calcola i totali e chiama lo strumento, il tutto attraverso la stessa UI conversazionale che ho costruito in precedenza.
Si è rivelato un modo sorprendentemente elegante per generare dati di test realistici.
Un’osservazione interessante: alcuni modelli (in particolare OpenAI GPT-4.1) tendono a chiedere conferma, o almeno sufficienti dettagli di input, prima di creare effettivamente una fattura… a meno che non gli si dia esplicitamente istruzioni di non farlo.
Rendering HTML dei messaggi OpenAI
Come accennato in precedenza, questa parte è stata interamente codificata in modo vibratile. Riutilizzatela, adattatela o ignoratela, a seconda delle vostre esigenze. L’idea è semplice: prendere una collezione di messaggi OpenAI e renderli in HTML.
Ma, naturalmente, i nostri utenti finali meritano sempre qualcosa di più di un semplice dump di testo. Ho quindi aggiunto alcune caratteristiche di qualità della vita:
- Un layout pulito e strutturato per le chiamate agli strumenti, compresa un’indicazione visiva quando uno strumento è in esecuzione.
- Un pulsante “Copia” con un solo clic per copiare facilmente qualsiasi messaggio dell’assistente e incollarlo in un documento, sia esso Microsoft Word, Google Docs o un’e-mail.
Questo rende l’intera esperienza di conversazione raffinata e professionale, pur essendo veloce e leggera da implementare.
- Un pulsante di copia per ogni tabella, che consente all’utente di estrarre istantaneamente i dati tabellari grezzi e di incollarli direttamente in un foglio di calcolo simile a Excel.
Questo è estremamente comodo quando gli utenti vogliono manipolare o confrontare i dati al di fuori dell’applicazione senza esportare nulla.
Funzionalità di creazione di grafici direttamente all’interno dell’interfaccia utente conversazionale
Quando si conversa con un’intelligenza artificiale su fatture, clienti o prodotti, gli utenti si aspettano naturalmente dei grafici. Classifiche, confronti, tendenze… gli approfondimenti visivi fanno ormai parte di qualsiasi esperienza moderna.
Ho quindi deciso di portare il rendering dei grafici in linea nell’interfaccia utente della chat e si è rivelato sorprendentemente facile, grazie a un po’ di vibe-coding con GitHub Copilot.
L’ho implementato in tre fasi:
1) Importare Chart.js all’interno di chat-template.html ed estendere il mio motore di rendering HTML per riconoscere i blocchi <chart>…</chart>.
Mentre l’intelligenza artificiale trasmette la sua risposta, una piccola animazione CSS visualizza un segnaposto del grafico, dando un’impressione UX raffinata.
2) Estendere il prompt del sistema per insegnare esplicitamente all’IA come richiedere i grafici. Ho aggiunto istruzioni come:
"**CHARTS**\n"+\
"Create charts for rankings, comparisons, trends, or distributions. Format: <chart>{...JSON...}</chart>\n"+\
"Available types: bar, line, pie, doughnut, radar, polarArea. Always include:\n"+\
"- \"type\": chart type\n"+\
"- \"data.labels\": array of x-axis labels\n"+\
"- \"data.datasets\": array with \"label\", \"data\" (numeric array), \"backgroundColor\" (color array)\n"+\
"- \"options.responsive\": true\n"+\
"- \"options.plugins.title\": {\"display\": true, \"text\": \"Chart Title\"}\n"+\
"- \"options.scales.y.beginAtZero\": true (for bar/line charts)\n"+\
"Use distinct vibrant colors (e.g., #4caf50, #2196f3, #ff9800, #e91e63, #9c27b0). Set \"legend.display\" to false for single datasets, true for multiple.\n"
3) Lasciare che l’intelligenza artificiale generi definizioni di grafici in JSON, che l’area web renderizza istantaneamente utilizzando Chart.js.
Il risultato è davvero impressionante: il modello produce immagini significative direttamente all’interno del flusso di conversazione, arricchendo notevolmente l’esperienza.
Detto questo, non mi affiderei completamente al modello per i calcoli complessi. Per l’utilizzo in produzione, sarebbe opportuno aggiungere una serie di strumenti dedicati:
- totali e cumuli
- raggruppamenti e aggregazioni
- riepiloghi basati sul tempo
- confronti tra periodi
- estrazione di KPI
Il 4D esegue i calcoli difficili e accurati, mentre l’IA si limita a richiederli e a disegnare i grafici sulla base di dati numerici precisi e convalidati.
Una combinazione perfetta tra l’affidabilità del 4D e l’intelligenza artificiale dell’interfaccia utente.
Conclusione
Quindi… e adesso?
In sostanza, abbiamo preso un’applicazione aziendale un po’ antiquata e l’abbiamo dotata di un’esperienza moderna, conversazionale e basata sull’intelligenza artificiale.
Ma vederla semplicemente come “un altro modo di recuperare dati” significherebbe perdere di vista il punto.
Certo, non serve un’interfaccia utente conversazionale per ottenere “i primi 5 clienti per fatturato totale nel 2025”.
Una schermata di base con un paio di filtri può farlo.
Ma molto rapidamente ci si rende conto che si tratta di qualcosa di diverso.
Si tratta di un nuovo modo di esplorare i dati.
Con un unico modulo, l’interfaccia di chat, gli utenti possono chiedere qualsiasi cosa: fatture, prodotti, clienti… se vogliono una tabella, un grafico o entrambi.
Questo elimina la necessità di creare innumerevoli schermate specializzate per esigenze di reportistica leggermente diverse.
Una volta capito questo, si capisce anche che la cosa va ben oltre l’interrogazione e la visualizzazione dei dati.
Il vero valore non sta nel rispondere “mostrami questo”.
Il vero valore sta nel rispondere “aiutami a capire questo”.
La vostra richiesta non dovrebbe più essere:
Disegna un grafico che confronta il 2024 e il 2025
ma piuttosto:
Fornite un rapporto dettagliato sulle vendite per il 2025, che includa le vendite totali, i primi 5 prodotti per fatturato, i primi 5 clienti per vendite, l’andamento delle vendite nel corso dell’anno e le fatture non pagate. Mostrate grafici e tabelle pertinenti e spiegate eventuali tendenze, opportunità e rischi degni di nota. Inoltre, confrontate le prestazioni dei prodotti rispetto al 2024 e suggerite spunti d’azione per migliorare le vendite e il flusso di cassa. Includete un’analisi delle prestazioni per cliente e per prodotto con le metriche chiave. Voglio sia le cifre che le spiegazioni e gli spunti d’azione. Il vostro rapporto includerà confronti tra il 2024 e il 2025. Gli obiettivi di tale rapporto sono: come incrementare le vendite, quali clienti indirizzare, come aumentare le prestazioni dei prodotti.
E questo è il tipo di risposta che voi – e i vostri utenti – otterrete:
Un’unica interfaccia conversazionale, perfettamente integrata nella vostra applicazione 4D, in grado di fornire approfondimenti, ragionamenti, visualizzazioni, creazioni…
Non si tratta di una funzione.
È un passo avanti verso una nuova generazione di applicazioni aziendali.
Come riutilizzare tutto questo nel proprio progetto?
Se la lettura di questo post e l’utilizzo dell’applicazione hanno suscitato un interesse, potete semplicemente:
- Copiare i seguenti file nel vostro progetto:
- Classi
- AI_ChatWithTools.4dm
- ChatHTMLRenderer.4dm
- formAIChat.4dm
- StructureInfo.4dm
- Forma
- AIChat
- Risorse
- AIprovider.json
- AITools.json
- chat-template.html
- chat-template.css
- icona-strumento.svg
- Effettuare gli adattamenti necessari
- Richiesta di sistema e implementazione degli strumenti in AI_ChatWithTools.4dm
- Definizione degli strumenti successivi in AITools.json (usare solo AI per questo!)
- Adattare l’apertura del modulo alla propria applicazione in formAIChat.webAreaEventHandler()
- Impostazioni del provider AI in AIprovider.json
- Classi
E questo è tutto!
Concludo con un paio di osservazioni aggiuntive:
In questo esempio, tutta la logica degli strumenti è implementata nella classe AI_ChatWithTools. L’ho fatto perché il mio obiettivo era quello di essere il meno invasivo possibile e perché il progetto non implementa le DataClass. A seconda dell’architettura della vostra base di codice, una pratica migliore potrebbe essere quella di far puntare tali strumenti sulle funzioni delle DataClasses.
Tutto questo progetto funziona perfettamente con OpenAI e il modello gpt-4.1 o superiore. Lavorare con un modello locale solleva ulteriori problemi che possono essere discussi nel forum 4D!


