Vorrei proporre di consentire agli artefatti AI di inviare webhook ogni volta che i dati archiviati in un artefatto AI vengono aggiornati. Ci sono molte ragioni per cui ciò sarebbe vantaggioso, ma cercherò di mantenere breve la mia argomentazione.
Molte organizzazioni che utilizzano Discourse fanno anche uso di varie automazioni, e gli strumenti per le automazioni stanno diventando sempre più popolari, facili da configurare e adatti anche ai principianti. Gli artefatti AI possono contenere dati preziosi che possono essere utilizzati in modo molto efficace in tali automazioni, con il vantaggio aggiuntivo che sono già in formato JSON!
Nel mio caso, utilizzo n8n in una configurazione Docker Compose accanto al mio container Discourse e lo uso già per automatizzare una vasta gamma di cose, inclusi elementi sulle mie istanze di Discourse tramite una rete Docker. Tuttavia, una delle istanze di Discourse che amministro è per un’organizzazione educativa/aziendale e gli artefatti AI vengono utilizzati per tracciare note degli studenti, diari delle lezioni e così via. Questo tipo di dati sarebbe davvero utile per migliorare i flussi di lavoro degli insegnanti, creare riassunti e altro ancora tramite uno strumento di automazione come n8n.
Sarebbe tecnicamente possibile interrogare gli artefatti per gli aggiornamenti, ma ciò potrebbe facilmente iniziare a sollecitare le risorse di sistema e sarebbe, nel migliore dei casi, uno spreco di risorse, oltre a richiedere competenze tecniche avanzate per la configurazione, che molti amministratori non possiedono.
Va da sé che ciò sarebbe anche altamente vantaggioso per i clienti enterprise di Discourse.
Una soluzione alternativa potrebbe essere che il JavaScript dell’artefatto chiami un webhook esterno dopo aver chiamato window.discourseArtifact.set(...).
Tuttavia, questo avrebbe alcune limitazioni:
-
È lato browser e quindi non autorevole.
-
Si attiva solo se il JavaScript dell’artefatto viene eseguito correttamente nel browser dell’utente.
-
Può essere influenzato da CORS, strumenti di privacy del browser, blocchi di rete o vincoli di sandbox.
-
Il payload non può essere considerato attendibile come proveniente da Discourse a meno che non venga implementata una verifica aggiuntiva lato server.
-
I segreti non possono essere inseriti in modo sicuro nel JavaScript dell’artefatto.
Un evento/webhook lato server sarebbe molto più affidabile e sicuro.
Non sono uno sviluppatore Ruby, quindi ho parlato di questo con GPT-5.5, che ha offerto anche alcuni spunti interessanti…
In base all’attuale architettura dei webhook di Discourse, l’implementazione più pulita probabilmente non è una funzionalità personalizzata di „chiama questo URL“ all’interno degli artefatti AI. Dovrebbe essere implementata come un normale tipo di evento webhook di Discourse, supportata da un normale DiscourseEvent interno.
Forma migliore dell’implementazione
Suggerirei agli sviluppatori di Discourse di implementarlo in due livelli:
-
Eventi interni emessi quando cambiano i record KV degli artefatti.
-
Tipi di evento webhook che si sottoscrivono a tali eventi interni e utilizzano il sistema di consegna webhook esistente.
Ciò lo mantiene coerente con il resto di Discourse. I webhook esistenti funzionano già mappando gli DiscourseEvent interni alle chiamate WebHook.enqueue_* in config/initializers/012-web_hook_events.rb; ad esempio, gli eventi relativi a topic, post, utenti, categorie, tag, reviewable, notifiche e like sono collegati in questo modo.
Il percorso di archiviazione degli artefatti è abbastanza semplice: ArtifactKeyValuesController#set trova o inizializza un record chiave-valore, assegna key/value/public e salva; destroy trova il record dell’utente corrente per chiave e lo distrugge. Il modello stesso è AiArtifactKeyValue, appartiene a un artefatto e a un utente, ha key, value e public, e impone l’unicità per artefatto/utente/chiave.
Nomi degli eventi proposti
Probabilmente utilizzerei tre specifici eventi webhook:
ai_artifact_key_value_created
ai_artifact_key_value_updated
ai_artifact_key_value_deleted
In alternativa, potrebbe funzionare un singolo evento:
ai_artifact_key_value_changed
…ma tre eventi si adattano meglio allo stile esistente di Discourse. Discourse ha già nomi di eventi webhook separati come post_created, post_edited, post_destroyed, calendar_event_created, calendar_event_updated e così via.
File/classi che probabilmente toccherebbero
1. Aggiungere nuovi tipi di evento webhook
WebHookEventType definisce attualmente costanti numeriche, un enum group e un hash TYPES di nomi di eventi agli ID.
Potrebbero aggiungere qualcosa del genere:
AI_ARTIFACT = 20
enum :group,
{
# gruppi esistenti...
ai_artifact: 18,
},
scopes: false
TYPES = {
# tipi esistenti...
ai_artifact_key_value_created: 2001,
ai_artifact_key_value_updated: 2002,
ai_artifact_key_value_deleted: 2003,
}
Gli ID esatti sarebbero a discrezione dei manutentori di Discourse; devono solo evitare conflitti.
Aggiungerebbero anche voci di seed in db/fixtures/007_web_hook_event_types.rb, poiché i tipi di evento webhook esistenti vengono seminati lì con ID, nomi e gruppi.
Esempio:
WebHookEventType.seed do |b|
b.id = WebHookEventType::TYPES[:ai_artifact_key_value_created]
b.name = "ai_artifact_key_value_created"
b.group = WebHookEventType.groups[:ai_artifact]
end
WebHookEventType.seed do |b|
b.id = WebHookEventType::TYPES[:ai_artifact_key_value_updated]
b.name = "ai_artifact_key_value_updated"
b.group = WebHookEventType.groups[:ai_artifact]
end
WebHookEventType.seed do |b|
b.id = WebHookEventType::TYPES[:ai_artifact_key_value_deleted]
b.name = "ai_artifact_key_value_deleted"
b.group = WebHookEventType.groups[:ai_artifact]
end
L’interfaccia utente dei webhook per amministratori dovrebbe quindi rilevarli automaticamente, poiché Admin::WebHooksController#index serializza già i tipi di evento attivi raggruppati per l’interfaccia utente. Il serializzatore del tipo di evento espone già id, name e group.
2. Nascondere gli eventi quando Discourse AI è disabilitato
WebHookEventType.active nasconde già gli eventi webhook dipendenti dai plugin quando le loro funzionalità sono disabilitate, come gli eventi relativi a solved, assign, votazione dei topic, chat e calendario.
Quindi potrebbero aggiungere:
unless defined?(SiteSetting.discourse_ai_enabled) && SiteSetting.discourse_ai_enabled
ids_to_exclude.concat(
[
TYPES[:ai_artifact_key_value_created],
TYPES[:ai_artifact_key_value_updated],
TYPES[:ai_artifact_key_value_deleted],
],
)
end
Potrebbero anche voler verificare un’impostazione specifica per gli artefatti se esiste o viene aggiunta.
3. Emettere eventi interni dal modello, non solo dal controller
Sebbene il percorso di scrittura attuale sia basato sul controller, il luogo più robusto è probabilmente il modello, utilizzando callback di commit:
class AiArtifactKeyValue < ActiveRecord::Base
after_create_commit :trigger_created_event
after_update_commit :trigger_updated_event
after_destroy_commit :trigger_deleted_event
private
def trigger_created_event
DiscourseEvent.trigger(:ai_artifact_key_value_created, self)
end
def trigger_updated_event
return if previous_changes.slice("value", "public").blank?
DiscourseEvent.trigger(:ai_artifact_key_value_updated, self)
end
def trigger_deleted_event
DiscourseEvent.trigger(:ai_artifact_key_value_deleted, self)
end
end
Le callback a livello di modello intercetterebbero anche i percorsi di scrittura futuri, non solo ArtifactKeyValuesController#set. La parte after_commit è importante perché la consegna del webhook dovrebbe essere accodata solo dopo che la modifica al database è stata committed in modo sicuro.
Una sottigliezza: probabilmente non dovrebbero attivare un evento „aggiornato“ quando l’artefatto chiama set() con lo stesso valore e nulla cambia effettivamente. Controllare previous_changes evita webhook rumorosi.
4. Aggiungere un serializzatore del payload webhook
I webhook di Discourse generano payload tramite serializzatori. Il metodo generico WebHook.enqueue_object_hooks può accettare un serializzatore e WebHook.generate_payload serializza l’oggetto con un guardiano utente di sistema.
Potrebbero aggiungere qualcosa del genere:
class WebHookAiArtifactKeyValueSerializer < ApplicationSerializer
attributes :id,
:ai_artifact_id,
:post_id,
:topic_id,
:user_id,
:key,
:public,
:value_included,
:created_at,
:updated_at
def post_id
object.ai_artifact.post_id
end
def topic_id
object.ai_artifact.post.topic_id
end
def value_included
false
end
end
Consiglierei vivamente di non includere value per impostazione predefinita. I dati KV degli artefatti possono essere privati per utente e il modello ha un flag public. Se Discourse vuole supportare i valori, potrebbero renderlo esplicito:
include_value: false
include_public_values: true
include_private_values: false
Ma la versione sicura v1 dovrebbe probabilmente omettere completamente i valori.
5. Collegare gli eventi interni alla consegna webhook
Potrebbero aggiungere gestori a config/initializers/012-web_hook_events.rb, rispecchiando i modelli esistenti.
Qualcosa del genere:
%i[
ai_artifact_key_value_created
ai_artifact_key_value_updated
ai_artifact_key_value_deleted
].each do |event|
DiscourseEvent.on(event) do |key_value|
artifact = key_value.ai_artifact
post = artifact.post
topic = post.topic
payload =
WebHook.generate_payload(
:ai_artifact_key_value,
key_value,
WebHookAiArtifactKeyValueSerializer
)
WebHook.enqueue_hooks(
:ai_artifact_key_value,
event,
id: key_value.id,
category_id: topic&.category_id,
tag_ids: topic&.tags&.pluck(:id),
payload: payload
)
end
end
Ciò riutilizzerebbe la pipeline di lavoro webhook esistente. WebHook.enqueue_hooks trova i webhook attivi per l’evento e accoda Jobs::EmitWebHookEvent. Il lavoro gestisce già la consegna, i tentativi di riprova, la registrazione, le intestazioni, le firme e la visibilità per gli amministratori.
Includere category_id e tag_ids è un tocco carino perché i lavori webhook esistenti possono filtrare per categoria e tag. Il lavoro webhook controlla già i vincoli di categoria e tag prima di inviare. Poiché un artefatto AI appartiene a un post e l’artefatto appartiene a un post nel modello, derivare il contesto categoria/topic dovrebbe essere possibile.
Come potrebbe apparire il webhook consegnato
Poiché il corpo del webhook di Discourse utilizza event_type come radice di livello superiore, questo probabilmente assomiglierebbe a:
{
"ai_artifact_key_value": {
"id": 456,
"ai_artifact_id": 123,
"post_id": 789,
"topic_id": 321,
"user_id": 42,
"key": "score",
"public": true,
"value_included": false,
"created_at": "2026-05-26T12:00:00Z",
"updated_at": "2026-05-26T12:05:00Z"
}
}
Il nome dell’evento sarebbe nelle intestazioni, coerente con la consegna webhook esistente:
X-Discourse-Event-Type: ai_artifact_key_value
X-Discourse-Event: ai_artifact_key_value_updated
X-Discourse-Event-Signature: sha256=...
Queste intestazioni si allineano con il modo in cui EmitWebHookEvent costruisce le intestazioni dei webhook oggi.
Gli sviluppatori dovrebbero probabilmente decidere queste cose:
I valori dovrebbero essere inclusi?
Il mio voto: no per impostazione predefinita. Forse consentire solo valori pubblici o rendere l’inclusione del valore un’impostazione per gli amministratori. I dati privati per utente degli artefatti non dovrebbero lasciare il sito in modo silenzioso.
Dovrebbe esserci un evento o tre?
Tre è più pulito per gli abbonati ai webhook. Un singolo evento con un campo action è più semplice internamente. Lo stile esistente di Discourse tende verso nomi di eventi multipli.
Dovrebbero applicarsi i filtri per categoria/tag?
Penso di sì. L’artefatto è allegato a un post/topic, quindi i filtri per categoria e tag sono significativi.
Questo dovrebbe essere implementato nel core di Discourse o nel plugin Discourse AI?
Il modello di dati risiede nel plugin Discourse AI, ma il registro degli eventi webhook risiede nel core. Poiché i plugin inclusi come solved/chat/calendar hanno già tipi di evento webhook nell’elenco condiviso WebHookEventType, Discourse potrebbe sentirsi a suo agio ad aggiungere anche gli eventi degli artefatti AI lì. Il metodo active può nasconderli quando AI è disabilitato.
Dovrebbe anche emettere un DiscourseEvent interno anche se non esiste un webhook?
Sì. Questo offre agli autori di plugin un punto di aggancio pulito anche a parte i webhook.
Complessità
Lo inquadrerei come moderata ma molto contenuta.
Il lavoro probabile è:
-
Aggiungere 3 costanti di tipo evento.
-
Aggiungere 1 gruppo webhook.
-
Aggiungere 3 record di seed.
-
Aggiungere 1 serializzatore.
-
Aggiungere 3 callback del modello o attivatori di eventi a livello di servizio.
-
Aggiungere gestori di inizializzazione webhook.
-
Aggiungere spec per creazione/aggiornamento/eliminazione.
-
Aggiungere una decisione sulla privacy riguardo all’inclusione di
value.
Il sistema di consegna, i tentativi di riprova, le firme, il registro eventi, l’interfaccia utente ping/ritrasmissione e la configurazione webhook per amministratori sono già presenti. La funzionalità riguarda principalmente il rendere visibili le mutazioni KV degli artefatti a quel sistema esistente.