Usare l'IA per etichettare e categorizzare i post nei forum

Ho creato una Persona per provare a categorizzare e taggare automaticamente nuovi argomenti. Questo è il prompt che sto usando per quella Categoria e Tagger.

Sei un assistente AI per il mio forum Discourse. Il tuo compito è classificare i nuovi argomenti in una delle categorie predefinite e applicare fino a 5 tag pertinenti.

Categorie:

  • Categoria 1
  • Categoria 2
  • Categoria 3
  • Categoria 4
  • Categoria 5

Tag:

La tua selezione deve provenire da questo elenco predefinito (scegli fino a 5):
tag1, tag2, tag3, tag4, tag5

Compito:

  • Seleziona esattamente una categoria dall’elenco sopra che si adatti meglio all’argomento.
  • Seleziona fino a 5 tag dall’elenco sopra in base alla pertinenza.
  • Formatta la tua risposta come JSON esattamente in questo modo:
{
  "category": "Nome Categoria Scelta",
  "tags": ["tag1", "tag2", "tag3"]
}

Quando seleziono questa persona da utilizzare in Automations e creo il post, l’AI identifica correttamente le categorie e i tag con questo risultato in un post.

Elenca categorie
{
“category”: “Categoria Identificata Correttamente”,
“tags”: ["Tutti ", “validi”, “tag”]
}

La domanda è: è già possibile utilizzare l’AI e le Automations per spostare nuovi post, categorizzarli e assegnare i tag all’argomento. Se sì, sono sicuro che il prompting possa essere modificato per ottimizzare il funzionamento.

Grazie,

1 Mi Piace

Arrivando… è un po’ complicato, ci mancano 2 piccoli pezzi:

  1. Gli strumenti personalizzati ora supportano molte cose, ma non supportano la categorizzazione e il tagging, possiamo aggiungerli facilmente.
  2. Ho bisogno di un responder che funzioni in modalità “silenziosa”, in modo che non risponda effettivamente all’argomento.

Una volta che entrambi saranno in atto, concederai l’accesso a 2 strumenti personalizzati

  1. tag topic
  2. categorize topic

(o uno strumento singolo che fa entrambe le cose)

5 Mi Piace

In realtà, finché sei d’accordo con il sussurro, puoi fare qualcosa ora.

L’idea è che definiresti uno strumento personalizzato per categorizzare un argomento (o taggare un argomento), quindi faresti in modo che la persona lo chiami.

Sono curioso di vedere come funziona questo approccio per te, dovrai testare lo strumento, dovrebbe essere abbastanza semplice dall’interfaccia utente dello strumento.

È piuttosto macchinoso gestire tutto questo, ma è anche piuttosto :exploding_head: che possa essere fatto. Il sussurro è in realtà ragionevolmente utile perché ti dà un po’ un “processo di pensiero” su come il risponditore è arrivato ai tag/categorie.

3 Mi Piace

Grazie Sam,

Più facile per alcuni che per altri. Io appartengo a quest’ultima categoria :joy:

Ci sto armeggiando e non ci sono riuscito. Non ho esperienza con gli strumenti, quindi non vedo l’ora di farlo funzionare per pensare ad altri modi di usare gli strumenti.

Ho provato a usare il codice del tuo blog con una chiave API che non ha funzionato. L’IA mi ha suggerito di usare un URL codificato in modo fisso, quindi ho provato senza successo.

La chiave API non è stata chiamata e non ci sono log creati con errori.

Questo viene utilizzato per categorizzare automaticamente gli argomenti al momento della creazione

Classificherò l’argomento “Insights from Jesus’ early years and miracles” nella categoria Nuovo Testamento, poiché si riferisce alla vita e agli insegnamenti di Gesù.
Sto spostando l’argomento ora…

Questo è quello che ho provato.

/**

  • Riferimento rapido all’API dello strumento
  • Funzioni di ingresso
  • invoke(parameters): Funzione principale. Riceve parametri (Object). Deve restituire un valore serializzabile in JSON.
  • Esempio:
  • function invoke(parameters) { return “result”; }
  • details(): Opzionale. Restituisce una stringa che descrive lo strumento.
  • Esempio:
  • function details() { return “Descrizione dello strumento.”; }
  • Oggetti forniti
    1. http
  • http.get(url, options?): Esegue una richiesta HTTP GET.
  • Parametri:
  •  url (string): URL della richiesta.
    
  •  options (Object, opzionale):
    
  •    headers (Object): Intestazioni della richiesta.
    
  • Restituisce:
  •  { status: number, body: string }
    
  • http.post(url, options?): Esegue una richiesta HTTP POST.
  • Parametri:
  •  url (string): URL della richiesta.
    
  •  options (Object, opzionale):
    
  •    headers (Object): Intestazioni della richiesta.
    
  •    body (string): Corpo della richiesta.
    
  • Restituisce:
  •  { status: number, body: string }
    
  • (disponibili anche: http.put, http.patch, http.delete)
  • Nota: Massimo 20 richieste HTTP per esecuzione.
    1. llm
  • llm.truncate(text, length): Tronca il testo a una lunghezza di token specificata.
  • Parametri:
  •  text (string): Testo da troncare.
    
  •  length (number): Token massimi.
    
  • Restituisce:
  •  Stringa troncata.
    
    1. index
  • index.search(query, options?): Cerca documenti indicizzati.
  • Parametri:
  •  query (string): Query di ricerca.
    
  •  options (Object, opzionale):
    
  •    filenames (Array): Limita la ricerca a file specifici.
    
  •    limit (number): Frammenti massimi (fino a 200).
    
  • Restituisce:
  •  Array di { fragment: string, metadata: string }
    
    1. upload
  • upload.create(filename, base_64_content): Carica un file.
  • Parametri:
  •  filename (string): Nome del file.
    
  •  base_64_content (string): Contenuto del file codificato in Base64.
    
  • Restituisce:
  •  { id: number, short_url: string }
    
    1. chain
  • chain.setCustomRaw(raw): Imposta il corpo del post ed esce dalla catena.
  • Parametri:
  •  raw (string): Contenuto grezzo da aggiungere al post.
    
  • Vincoli
  • Tempo di esecuzione: ≤ 2000 ms
  • Memoria: ≤ 10 MB
  • Richieste HTTP: ≤ 20 per esecuzione
  • Il superamento dei limiti comporterà errori o interruzioni.
  • Sicurezza
  • Ambiente Sandboxed: Nessun accesso agli oggetti di sistema o globali.
  • Nessun accesso al file system: Impossibile leggere o scrivere file.
    */

/**

  • Categorizzatore di argomenti Discourse
  • Questo strumento consente di modificare la categoria di un argomento Discourse
  • utilizzando l’API di Discourse.
    */

/**

  • Categorizzatore di argomenti Discourse
  • Questo strumento consente di modificare la categoria di un argomento Discourse
  • utilizzando l’API di Discourse.
    */

/**

  • Categorizzatore di argomenti Discourse
  • Questo strumento consente di modificare la categoria di un argomento Discourse
  • utilizzando l’API di Discourse.
    */

function invoke(params) {
// Validazione dei parametri richiesti
if (!params.topic_id) {
return { error: “Parametro richiesto mancante: topic_id” };
}

if (!params.category_id) {
return { error: “Parametro richiesto mancante: category_id” };
}

// URL di base per la tua istanza Discourse
const baseUrl = “https://community.mysite.com”;

// URL completo dell’endpoint API per l’aggiornamento di un argomento
const apiUrl = ${baseUrl}/t/${params.topic_id}.json;

// Prepara il corpo della richiesta
const requestBody = {
category_id: params.category_id
};

// Parametro opzionale: aggiorna il titolo se fornito
if (params.title) {
requestBody.title = params.title;
}

// Usa la tua chiave API fornita
const apiKey = “Discourse-API-Key”;

try {
// Esegui la richiesta PUT per aggiornare l’argomento
const response = http.put(apiUrl, {
headers: {
“Content-Type”: “application/json”,
“Api-Key”: apiKey,
“Api-Username”: params.api_username || “system”
},
body: JSON.stringify(requestBody)
});

if (response.status >= 200 && response.status < 300) {
  return {
    success: true,
    topic_id: params.topic_id,
    category_id: params.category_id,
    response: JSON.parse(response.body)
  };
} else {
  return {
    error: `Impossibile aggiornare la categoria dell'argomento. Stato: ${response.status}`,
    details: response.body
  };
}

} catch (error) {
return {
error: “Si è verificato un errore durante l’aggiornamento della categoria dell’argomento”,
details: error.toString()
};
}
}

function details() {
return “Categorizza un argomento spostandolo in una categoria specificata”;
}

2 Mi Piace

@BrianC sei riuscito a capire questo?

1 Mi Piace

Discourse AI Tagger Category Quick Start.pdf| allegato (147,1 KB)

Discourse AI Tagger Category.pdf| allegato (506,0 KB)

@Sam,

Grazie per aver controllato. Ho provato, ma non sono riuscito a farlo funzionare, quindi ho interrotto. Ho eseguito il test dello scenario tramite Gemini 2.5 e ho salvato il risultato come PDF.

Riproverò. Gli strumenti non sono ancora il mio forte, ma devo migliorare. PDF allegati—forse ci sarà qualcosa di utile in essi.

3 Mi Piace

Abbiamo aggiunto un’automazione di tagging AI che potrebbe interessarti provare: FEATURE: create AI tagging automation (#34587) · discourse/discourse@e470f3d · GitHub

Prompt che ho usato per testarlo
 Sei un esperto classificatore di contenuti e assistente di tagging per questo forum.

 Il tuo compito è analizzare i post e suggerire tag appropriati in base al contenuto, alle immagini e all'elenco di tag disponibili fornito.

 Linee guida:
 - Suggerisci solo tag dall'elenco di tag disponibili fornito
 - Sii conservatore: tagga solo ciò di cui sei sicuro
 - Considera sia l'argomento del contenuto che l'intento del post

 Devi sempre rispondere con JSON valido in questo formato esatto:
 {"tags": ["tag1", "tag2"], "confidence": 85}

 - tags: array di nomi di tag dall'elenco disponibile
 - confidence: intero da 0 a 100 che rappresenta il tuo livello di confidenza

 Se nessun tag è appropriato, usa: {"tags": [], "confidence": 0}

Formato della risposta JSON della persona:

{
  "tags": "[string]",
  "confidence": "integer"
}

Strumenti abilitati per la persona: tag (opzionale, questo è se vuoi che utilizzi i tag esistenti sul sito)

L’automazione ha due modalità:

  • Utilizza i tag del sito esistenti
  • Utilizza l’elenco di tag fornito

L’automazione ha anche un livello di confidenza configurabile, puoi abilitare/disabilitare tag con restrizioni e regolare il numero di post che utilizza in un argomento per il contesto

6 Mi Piace

Grazie per questa fantastica funzionalità, solo che non sono riuscito a farlo funzionare, l’errore che ho ricevuto è:

llm_tagger: failed to process post 30550 /t/gecici-baslik-1756753838814/17361/1 : NoMethodError : private method select’ called for an instance of String`

Message

llm_tagger: failed to process post 30550 /t/gecici-baslik-1756753838814/17361/1 : NoMethodError : private method `select' called for an instance of String

Backtrace

/var/www/discourse/plugins/discourse-ai/lib/automation/llm_tagger.rb:140:in `handle'
/var/www/discourse/plugins/discourse-ai/discourse_automation/llm_tagger.rb:110:in `block (2 levels) in <main>'
/var/www/discourse/plugins/automation/app/models/discourse_automation/automation.rb:158:in `block in trigger!'
/var/www/discourse/plugins/automation/app/models/discourse_automation/stat.rb:11:in `log'
/var/www/discourse/plugins/automation/app/models/discourse_automation/automation.rb:156:in `trigger!'
/var/www/discourse/plugins/automation/app/jobs/regular/discourse_automation/trigger.rb:29:in `execute'
/var/www/discourse/app/jobs/base.rb:318:in `block (2 levels) in perform'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rails_multisite-7.0.0/lib/rails_multisite/connection_management/null_instance.rb:49:in `with_connection'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/rails_multisite-7.0.0/lib/rails_multisite/connection_management.rb:17:in `with_connection'
/var/www/discourse/app/jobs/base.rb:305:in `block in perform'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:220:in `execute_job'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:185:in `block (4 levels) in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:180:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/lib/sidekiq/discourse_event.rb:6:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/lib/sidekiq/pausable.rb:131:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job/interrupt_handler.rb:9:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:183:in `block in traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/metrics/tracking.rb:26:in `track'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/metrics/tracking.rb:134:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:182:in `traverse'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/middleware/chain.rb:173:in `invoke'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:183:in `block (3 levels) in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:145:in `block (6 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_retry.rb:118:in `local'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:144:in `block (5 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/config.rb:39:in `block in <class:Config>'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:139:in `block (4 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:281:in `stats'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:134:in `block (3 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_logger.rb:15:in `call'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:133:in `block (2 levels) in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_retry.rb:85:in `global'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:132:in `block in dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/job_logger.rb:40:in `prepare'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:131:in `dispatch'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:183:in `block (2 levels) in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:182:in `handle_interrupt'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:182:in `block in process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:181:in `handle_interrupt'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:181:in `process'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:86:in `process_one'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/processor.rb:76:in `run'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/component.rb:10:in `watchdog'
/var/www/discourse/vendor/bundle/ruby/3.3.0/gems/sidekiq-7.3.9/lib/sidekiq/component.rb:19:in `block in safe_thread'

Meta lo usa? Pensavo di aver visto @system taggare un argomento qualche giorno fa.

1 Mi Piace

Potrebbe essere complicato da configurare, ma se Discourse sta già generando embedding (per la ricerca semantica) per nuovi argomenti, potresti probabilmente generare embedding per le descrizioni dei tag, quindi utilizzare la somiglianza semantica per suggerire tag per nuovi argomenti o taggare automaticamente argomenti esistenti.

3 Mi Piace

Sì, l’ho eseguito per alcuni giorni su Meta per fare altri test… funziona abbastanza bene, ma senza descrizioni dei tag stava usando impropriamente how-to per alcune domande del tipo “come faccio” e applicava moderator a post in cui la parola era usata ma non era del tutto pertinente all’argomento. Alcune modifiche al prompt probabilmente aiuteranno, e le idee di @simon sembrano poter essere ottimi miglioramenti futuri.

2 Mi Piace

La mia ipotesi è che gli embedding potrebbero funzionare meglio ed essere più veloci/economici. Il mio reale interesse in questo è più orientato verso un sistema di tagging dinamico che potrebbe sostituire il hard coding dei tag nel database. Ad esempio, “how-to” + “sso” sarebbe un elenco di argomenti con embedding semanticamente vicini agli embedding delle descrizioni dei tag “how-to” e “sso”.

2 Mi Piace