Ich möchte vorschlagen, AI-Artefakte zu ermöglichen, Webhooks zu senden, whenever die gespeicherten Daten in einem AI-Artefakt aktualisiert werden. Es gibt viele Gründe, warum dies vorteilhaft wäre, aber ich werde versuchen, meinen Fall kurz zu halten.
Viele Organisationen, die Discourse nutzen, setzen auch verschiedene Automatisierungen ein, und die Tools für Automatisierungen werden zunehmend beliebter, einfacher einzurichten und benutzerfreundlich. AI-Artefakte können wertvolle Daten enthalten, die in solchen Automatisierungen sehr effektiv genutzt werden können, mit dem zusätzlichen Vorteil, dass sie bereits im JSON-Format vorliegen!
In meinem Fall verwende ich n8n in einer Docker Compose-Umgebung neben meinem Discourse-Container, und ich nutze es bereits, um eine Vielzahl von Dingen zu automatisieren, einschließlich Vorgängen auf meinen Discourse-Instanzen über ein Docker-Netzwerk. Allerdings ist eine der Discourse-Instanzen, die ich betreue, für eine Bildungsorganisation/ein Bildungsunternehmen gedacht, und AI-Artefakte werden verwendet, um Lernernotizen, Lektionstagebücher und dergleichen zu verfolgen. Solche Daten wären wirklich nützlich, um die Arbeitsabläufe der Lehrkräfte zu verbessern, Zusammenfassungen zu erstellen und so weiter, und zwar über ein Automatisierungstool wie n8n.
Es wäre technisch möglich, Artefakte auf Updates zu prüfen, aber dies könnte leicht zu einer Belastung der Systemressourcen führen und wäre bestenfalls eine Verschwendung von Ressourcen, zudem wäre eine umfangreiche technische Expertise für die Einrichtung erforderlich, die viele Administratoren nicht besitzen.
Unnötig zu erwähnen, wäre dies auch für die Unternehmenskunden von Discourse höchst vorteilhaft.
Eine Workaround-Lösung könnte darin bestehen, dass das JavaScript des Artefakts nach dem Aufruf von window.discourseArtifact.set(...) einen externen Webhook aufruft.
Dies hätte jedoch einige Einschränkungen:
-
Es ist browserseitig und daher nicht autoritativ.
-
Es wird nur ausgelöst, wenn das Artefakt-JavaScript im Browser des Benutzers erfolgreich ausgeführt wird.
-
Es kann durch CORS, Browser-Privatsphäre-Tools, Netzwerk-Blocker oder Sandbox-Einschränkungen beeinflusst werden.
-
Die Nutzlast kann nicht als von Discourse stammend vertraut werden, es sei denn, es wird eine zusätzliche serverseitige Verifizierung implementiert.
-
Geheimnisse können nicht sicher in das Artefakt-JavaScript eingebettet werden.
Ein serverseitiges Event/Webhook wäre viel zuverlässiger und sicherer.
Ich bin kein Ruby-Entwickler, daher habe ich mit GPT-5.5 darüber gesprochen, und es hat auch einige interessante Einblicke geliefert…
Basierend auf der aktuellen Webhook-Architektur von Discourse ist die saubere Implementierung wahrscheinlich keine maßgeschneiderte „Rufe diese URL auf“-Funktion innerhalb von AI-Artefakten. Sie sollte als normaler Discourse-Webhook-Event-Typ implementiert werden, unterstützt durch ein normales internes DiscourseEvent.
Optimale Implementierungsform
Ich schlage vor, dass die Discourse-Entwickler dies in zwei Schichten implementieren:
-
Interne Events, die ausgelöst werden, wenn sich KV-Einträge von Artefakten ändern.
-
Webhook-Event-Typen, die diese internen Events abonnieren und das bestehende Webhook-Zustellungssystem verwenden.
Dies hält es konsistent mit dem Rest von Discourse. Bestehende Webhooks funktionieren bereits, indem sie interne DiscourseEvents auf WebHook.enqueue_*-Aufrufe in config/initializers/012-web_hook_events.rb abbilden; beispielsweise werden Topic-, Post-, User-, Category-, Tag-, Reviewable-, Notification- und Like-Events auf diese Weise verkabelt.
Der Speicherpfad für Artefakte ist einfach genug: ArtifactKeyValuesController#set findet oder initialisiert einen Key-Value-Eintrag, weist key/value/public zu und speichert ihn; destroy findet den aktuellen Eintrag des Benutzers nach Key und löscht ihn. Das Modell selbst ist AiArtifactKeyValue, gehört zu einem Artefakt und einem Benutzer, hat key, value und public und erzwingt die Eindeutigkeit nach Artefakt/Benutzer/Key.
Vorgeschlagene Event-Namen
Ich würde wahrscheinlich drei spezifische Webhook-Events verwenden:
ai_artifact_key_value_created
ai_artifact_key_value_updated
ai_artifact_key_value_deleted
Alternativ könnte ein einzelnes Event funktionieren:
ai_artifact_key_value_changed
…aber drei Events passen besser zum bestehenden Stil von Discourse. Discourse hat bereits separate Webhook-Event-Namen wie post_created, post_edited, post_destroyed, calendar_event_created, calendar_event_updated und so weiter.
Dateien/Klassen, die sie wahrscheinlich berühren würden
1. Neue Webhook-Event-Typen hinzufügen
WebHookEventType definiert derzeit numerische Konstanten, ein group-Enum und einen TYPES-Hash von Event-Namen zu IDs.
Sie könnten etwas wie Folgendes hinzufügen:
AI_ARTIFACT = 20
enum :group,
{
# bestehende Gruppen…
ai_artifact: 18,
},
scopes: false
TYPES = {
# bestehende Typen…
ai_artifact_key_value_created: 2001,
ai_artifact_key_value_updated: 2002,
ai_artifact_key_value_deleted: 2003,
}
Die genauen IDs liegen bei den Discourse-Maintainern; sie müssen nur Konflikte vermeiden.
Sie würden auch Seed-Einträge in db/fixtures/007_web_hook_event_types.rb hinzufügen, da bestehende Webhook-Event-Typen dort mit IDs, Namen und Gruppen geseedet werden.
Beispiel:
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
Die Admin-Webhook-Oberfläche sollte diese dann automatisch aufnehmen, da Admin::WebHooksController#index bereits gruppierte aktive Event-Typen für die UI serialisiert. Der Event-Typ-Serializer stellt bereits id, name und group zur Verfügung.
2. Die Events ausblenden, wenn Discourse AI deaktiviert ist
WebHookEventType.active blendet bereits plugin-abhängige Webhook-Events aus, wenn deren Funktionen deaktiviert sind, wie z. B. solved, assign, Topic-Voting, Chat und Calendar-Events.
Sie könnten also Folgendes hinzufügen:
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
Sie möchten möglicherweise auch eine artefaktspezifische Einstellung prüfen, falls eine existiert oder hinzugefügt wird.
3. Interne Events vom Modell aus auslösen, nicht nur vom Controller
Obwohl der aktuelle Schreibpfad controllerbasiert ist, ist der robuste Ort wahrscheinlich das Modell, unter Verwendung von Commit-Callbacks:
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
Callbacks auf Modellebene würden auch zukünftige Schreibpfade abfangen, nicht nur ArtifactKeyValuesController#set. Der after_commit-Teil ist wichtig, da die Webhook-Zustellung erst nach dem sicheren Commit der Datenbankänderung in die Warteschlange gestellt werden sollte.
Eine Feinheit: Sie sollten wahrscheinlich kein „updated“-Event auslösen, wenn das Artefakt set() mit demselben Wert aufruft und sich nichts tatsächlich ändert. Die Prüfung von previous_changes vermeidet laute Webhooks.
4. Einen Webhook-Payload-Serializer hinzufügen
Discourse-Webhooks generieren Payloads durch Serializer. Die generische Methode WebHook.enqueue_object_hooks kann einen Serializer übernehmen, und WebHook.generate_payload serialisiert das Objekt mit einem System-Benutzer-Guardian.
Sie könnten etwas wie Folgendes hinzufügen:
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
Ich empfehle dringend, den value-Wert standardmäßig nicht einzuschließen. Artefakt-KV-Daten können pro Benutzer privat sein, und das Modell hat eine public-Flagge. Wenn Discourse Werte unterstützen möchte, könnten sie dies explizit machen:
include_value: false
include_public_values: true
include_private_values: false
Aber die sichere v1 sollte Werte wahrscheinlich vollständig weglassen.
5. Interne Events mit der Webhook-Zustellung verknüpfen
Sie könnten Handler zu config/initializers/012-web_hook_events.rb hinzufügen, die bestehenden Mustern entsprechen.
Etwas wie:
%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
Dies würde die bestehende Webhook-Job-Pipeline wiederverwenden. WebHook.enqueue_hooks findet aktive Webhooks für das Event und stellt Jobs::EmitWebHookEvent in die Warteschlange. Der Job übernimmt bereits Zustellung, Wiederholungen, Protokollierung, Header, Signaturen und Admin-Sichtbarkeit.
Die Einbeziehung von category_id und tag_ids ist ein netter Touch, da bestehende Webhook-Jobs nach Kategorie und Tag filtern können. Der Webhook-Job prüft bereits Kategorie- und Tag-Einschränkungen, bevor er sendet. Da ein AI-Artefakt zu einem Post gehört und das Artefakt im Modell zu einem Post gehört, sollte es möglich sein, den Kategorie/Topic-Kontext abzuleiten.
Wie der ausgelieferte Webhook aussehen könnte
Da der Webhook-Body von Discourse event_type als oberste Wurzel verwendet, würde dies wahrscheinlich so aussehen:
{
"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"
}
}
Der Event-Name würde in den Headern stehen, konsistent mit der bestehenden Webhook-Zustellung:
X-Discourse-Event-Type: ai_artifact_key_value
X-Discourse-Event: ai_artifact_key_value_updated
X-Discourse-Event-Signature: sha256=...
Diese Header entsprechen der Art und Weise, wie EmitWebHookEvent heute Webhook-Header erstellt.
Die Entwickler müssten wahrscheinlich folgende Entscheidungen treffen:
Sollen Werte eingeschlossen werden?
Meine Stimme: nein, standardmäßig. Vielleicht nur öffentliche Werte zulassen oder die Einschließung von Werten zu einer Admin-Einstellung machen. Private pro-Benutzer-Artefaktdaten sollten die Seite nicht stillschweigend verlassen.
Soll es ein Event oder drei geben?
Drei sind für Webhook-Abonnenten sauberer. Ein Event mit einem action-Feld ist intern einfacher. Der bestehende Discourse-Stil neigt zu mehreren Event-Namen.
Sollten Kategorie/Tag-Filterung gelten?
Ich denke ja. Das Artefakt ist an einen Post/Topic angehängt, daher sind Kategorie- und Tag-Filter sinnvoll.
Soll dies im Discourse-Kern oder im Discourse AI-Plugin implementiert werden?
Das Datenmodell lebt im Discourse AI-Plugin, aber die Webhook-Event-Registrierung lebt im Kern. Da gebündelte Plugins wie solved/chat/calendar bereits Webhook-Event-Typen in der gemeinsamen WebHookEventType-Liste haben, könnte Discourse damit einverstanden sein, AI-Artefakt-Events dort ebenfalls hinzuzufügen. Die active-Methode kann sie deaktivieren, wenn AI deaktiviert ist.
Sollte dies auch ein internes DiscourseEvent auslösen, auch wenn kein Webhook existiert?
Ja. Das gibt Plugin-Autoren einen sauberen Hook-Punkt, unabhängig von Webhooks.
Komplexität
Ich würde dies als mäßig, aber sehr begrenzt einstufen.
Die wahrscheinliche Arbeit besteht darin:
-
3 Event-Typ-Konstanten hinzufügen.
-
1 Webhook-Gruppe hinzufügen.
-
3 Seed-Einträge hinzufügen.
-
1 Serializer hinzufügen.
-
3 Modell-Callbacks oder servicelevel-Event-Trigger hinzufügen.
-
Webhook-Initialisierer-Handler hinzufügen.
-
Spezifikationen für Erstellen/Aktualisieren/Löschen hinzufügen.
-
Eine Datenschutzentscheidung treffen, ob
valueeingeschlossen wird.
Das Zustellungssystem, Wiederholungen, Signaturen, Event-Protokoll, Ping/Wiederzustellungs-UI und Admin-Webhook-Konfiguration sind bereits vorhanden. Das Feature dreht sich hauptsächlich darum, Artefakt-KV-Änderungen für dieses bestehende System sichtbar zu machen.