Skip to content

Nodo: ai ​

Una domanda a un modello per ogni record, con la risposta che arricchisce il record. È la stessa meccanica di un lookup — batch, cache, retry, dead-letter — con un tipo tutto suo, perché nel grafo e nel DAG si veda dove decide un modello.

0/4
modello — external modelloinference di Noeva documenti — source documentiworkspace Noeva estrai-campi — ai estrai-campinoevaAiExtract fatti — sink fattiPostgreSQL da-rivedere — dead-letter da-rivederedead-letter
Metodo.ai(from, id, config, { deadLetter?, fromPort?, purpose?, recordReport?, writeReport? })
Ingressi1
Uscite≥1 (fan-out libero)
Dead-lettersì, per record
Codicesrc/core/ai.ts (AiConfig è un alias di LookupConfig), configurazioni nei connettori

Quando usarlo ​

Quando il valore che serve non si può calcolare né interrogare: estrarre campi da un documento, classificare un testo, ricavare un dato da un allegato. Se la risposta sta in un database o in un'API è un lookup; se è una regola scrivibile in codice è una transform, che costa nulla ed è deterministica.

Il tipo è una dichiarazione: il core non verifica che il resolver parli davvero con un modello, come non verifica che un'action scriva davvero. Serve a chi legge il DAG.

La configurazione ​

Identica a quella del lookup — resolver, key, merge, onNotFound, batchSize, cache, maxCacheEntries, outputSchema, retry, rateLimit — perché la semantica è la stessa: una lettura senza effetti, ripetibile.

Oggi l'unica configurazione pronta è noevaAiExtract (noeva): estrae campi da uno schema Zod, una chiamata per documento, con regole di aggregazione obbligatorie per campo.

Esempio ​

ts
.ai<Documento, DocumentoEstratto>("testi", "estrai-campi", noevaAiExtract(client, {
  modelId,
  prompt,
  schema: campiSchema,      // i campi attesi, validati alla risposta
  documenti: (d) => d.fileIds,
  regole: { numeroOrdine: "primo-non-vuoto", totale: "somma" },
  merge: (campi, d) => ({ ...d, ...campi }),
}), { deadLetter: daRivedere })
.transform("estrai-campi", "contratto", validate("contratto", documentoEstrattoSchema))

Errori ​

Una risposta che non rispetta lo schema è un LookupError, come una riga che non torna da un database: dead-letter del nodo, ramo catch sotto un try-catch, altrimenti fail-fast. Va sempre usato con uno dei due: un documento storto capita, e non deve fermare gli altri.

Timeout e indisponibilità del servizio sono errori transitori: si ritentano con retry, e rateLimit distanzia le chiamate.

Nel DAG e nel report ​

stepType: "ai" nel piano e kind: "ai" nel report: nell'app il nodo è verde acqua, con l'icona scintilla e il badge «AI». Il descriptor porta il modello e i campi richiesti — mai il prompt né il testo dei documenti. Come nel lookup, outputSchema dà al nodo le colonne del proprio output, e in nodeReports il nodo porta sotto lookup gli stessi contatori della run. Con recordReport il report elenca anche che cosa ha risposto il modello per ogni record: è il modo di rileggere una decisione senza aprire i log.

Dal vivo ​

Come il lookup, il nodo ai ha un in lavorazione: i record della finestra in attesa del modello. Conta record e non domande, quindi tre documenti con due testi distinti valgono tre anche se le domande sono due. Un numero fermo vuol dire che il modello sta rispondendo lento.

Anti-pattern ​

  • .lookup(…, noevaAiExtract(…)): compila (è lo stesso tipo di configurazione), ma nel DAG il modello sparisce dietro un lookup qualunque.
  • .ai(…) con un resolver SQL o REST: il core non lo impedisce, ma il grafo direbbe una cosa falsa.
  • Una chiamata al modello dentro una transform: è IO nascosto, e per giunta il più costoso.
  • Fidarsi della risposta senza contratto: lo schema Zod è ciò che rende l'estrazione verificabile.

Noeva è un marchio registrato di 4D S.R.L.