Scrydon

Ontologies

Créer des packs d'ontologie — types d'objets, types de liens, types d'actions, règles d'identité et liaisons

Cet artefact est distribué dans un Pack. Pour le cycle de vie partagé — installation, pack build, upload — voir Packs & SDK d'authoring.

Une Ontologie est le système de types sur lequel votre tenant fonctionne : Customer, Asset, Risk, Incident, les relations entre eux (owns, affects, mitigates), et les actions typées que vous pouvez effectuer sur eux (AssignAsset, AssessRisk). Le SDK d'authoring vous permet de distribuer cela sous forme d'un pack d'ontologie versionné et validé.

Une ontologie est le système de types. Une base de connaissances est le contenu qui l'utilise. Ce sont des choses différentes — une ontologie, de nombreuses bases de connaissances. Plusieurs ontologies peuvent coexister dans un même tenant (par défaut + par domaine + importé via pack).

Installation

bun add -d @scrydon/sdk-authoring zod
npm install --save-dev @scrydon/sdk-authoring zod
import {
  defineOntology,
  defineObjectType,
  defineLinkType,
  defineActionType,
  defineIdentityRule,
  defineBinding,
  defineProperty,
} from '@scrydon/sdk-authoring/ontologies'

Ce que contient un pack

ÉlémentRôle
Object TypeUne entité typée — Customer, Asset, RiskFinding. A des properties, un kind (analytical / transactional / binding_only).
Link TypeUne relation typée — affects(RiskFinding → Customer). Cardinalité + nom de traversée inverse optionnel.
Action TypeUne mutation côté serveur contrôlée par OPA. Porte un schéma d'entrée Zod et un descripteur d'effet secondaire (object_edit / link_edit / outbox / webhook).
Identity RuleComment les instances sont dédupliquées. Clés de correspondance déterministes, ER probabiliste optionnel (signaux + seuils).
BindingRelie une source en amont (silver_table, memex_page, process_flow_task, process_flow_action, extraction, stream, manual) à des instances d'objet ou de lien typées.

Un exemple complet

import {
  defineOntology,
  defineObjectType,
  defineLinkType,
  defineActionType,
  defineIdentityRule,
  defineProperty,
} from '@scrydon/sdk-authoring/ontologies'
import { z } from 'zod'

export default defineOntology({
  id: 'acme-crm',
  version: '1.0.0',
  displayName: 'ACME CRM',
  description: 'Salesforce-style CRM ontology',
  maturity: 'preview',
  author: { name: 'Acme Inc.' },

  objectTypes: [
    defineObjectType({
      slug: 'Account',
      displayName: 'Account',
      kind: 'transactional',
      properties: [
        defineProperty({
          slug: 'name',
          displayName: 'Legal name',
          scalarType: 'string',
          cardinality: 'required',
        }),
        defineProperty({
          slug: 'jurisdiction',
          displayName: 'Jurisdiction',
          scalarType: 'string',
          classification: 'internal',
        }),
        defineProperty({
          slug: 'riskTier',
          displayName: 'Risk tier',
          scalarType: 'string',
          piiFlags: ['regulatory'],
        }),
      ],
    }),
    defineObjectType({
      slug: 'Asset',
      displayName: 'Asset',
      kind: 'transactional',
      properties: [
        defineProperty({ slug: 'name', displayName: 'Name', scalarType: 'string' }),
        defineProperty({ slug: 'value', displayName: 'Value', scalarType: 'float' }),
      ],
    }),
  ],

  linkTypes: [
    defineLinkType({
      slug: 'owns',
      displayName: 'Owns',
      sourceTypes: ['Account'],
      targetTypes: ['Asset'],
      cardinality: '1-N',
      reverseName: 'ownedBy',
    }),
  ],

  actionTypes: [
    defineActionType({
      slug: 'AssignAsset',
      displayName: 'Assign asset to account',
      subjectTypeSlug: 'Asset',
      inputZod: z.object({ accountId: z.string().uuid() }),
      sideEffectDescriptor: { kind: 'link_edit' },
    }),
  ],

  identityRules: [
    defineIdentityRule({
      objectTypeSlug: 'Account',
      deterministicKeys: ['name', 'jurisdiction'],
    }),
  ],

  bindings: [],
})

Modèle de propriété

ChampNotes
scalarTypeL'un de string, int, float, bool, uuid, timestamp, date, json, bytes
cardinalityrequired, optional ou many
classificationpublic, internal, confidential, restricted — alimente le DLP et le RBAC
piiFlagsTags libres exposés à la rédaction DLP au moment de la récupération

Types de kinds d'Object Type

  • analytical — faits en masse, lecture intensive. Supporté par une liaison silver_table.
  • transactional — entités modifiables (saisie manuelle, sortie de process-flow). Natif Postgres.
  • binding_only — projection en lecture seule uniquement (ex. memex_page, extraction). Jamais directement modifié.

Liaisons — les sept kinds

KindRelie
silver_tableUne table analytique bronze/silver → un type d'objet
memex_pageUn nœud de contenu KB Memex (correspondance slug/alias) → une instance d'objet
process_flow_taskUne ligne process_instance_task → une instance d'objet typée
process_flow_actionUn process_instance_task_action de actionType=entity_link → une instance de lien typée
extractionLe pipeline d'extraction analytics 5-WF → une instance d'objet
streamUne source de streaming (Kafka, webhook fanout) → une instance d'objet
manualSaisie directe dans le workbench → une instance d'objet
defineBinding({
  objectTypeSlug: 'Account',
  kind: 'silver_table',
  source: { table: 'silver_account' },
  identitySpec: { keys: ['external_id'] },
  columnMap: { name: 'legal_name', jurisdiction: 'country_code' },
})

Réduction en entités — identitySpec.entityColumns

Les sources riches en observations (trajectoires de position, relevés de capteurs, événements de journal) comptent souvent des millions de lignes pour quelques milliers d'objets réels. Déclarer entityColumns — un sous-ensemble strict de identityColumns désignant l'entité persistante observée — indique au graphe d'afficher un nœud par entité avec un décompte exact d'observations, au lieu d'un nœud par ligne :

defineBinding({
  objectTypeSlug: 'AircraftPosition',
  kind: 'silver_table',
  source: { table: 'adsb_positions' },
  identitySpec: {
    identityColumns: ['icao24', 'seenAt'],  // une observation (relevé de position)
    entityColumns: ['icao24'],              // l'objet persistant (l'aéronef)
  },
  columnMap: { icao24: 'icao24', seenAt: 'seenAt', latitude: 'lat', longitude: 'lon' },
})

Effets sur la page graphe :

  • Un nœud par aéronef, étiqueté avec un décompte exact N observations calculé au moment de la lecture sous vos permissions — jamais un échantillon présenté comme la donnée.
  • Les jointures de relations sur des colonnes de niveau entité (p. ex. icao24) se connectent au niveau entité.
  • Chaque réponse du graphe rapporte affichés / total par type ; tout ce que le budget de lisibilité masque est représenté par un nœud agrégé portant le reste exact.
  • La vue d'ensemble affiche le tissu connecté : les instances participant à au moins une relation. Les instances sans relation calculée ne sont pas éparpillées sur le canevas en points isolés — elles sont repliées dans le nœud agrégé de leur type (avec le décompte exact) et restent consultables via l'expansion de type et la recherche. Une ontologie sans aucune relation calculable retombe sur un échantillon par type afin que la page reste navigable.
  • Les nœuds agrégés s'étendent au clic : chaque clic amène une page bornée des membres du type sur le canevas (rattachés autour de l'agrégat) et le décompte restant décroît exactement ; quand il ne reste rien, l'agrégat disparaît.
  • Étendre les voisins montre d'abord ce qui est connecté — les voisins groupés par relation et type cible avec leurs décomptes — pour ajouter les groupes pertinents au lieu d'inonder la disposition.
  • La vue Carte (bascule en bas de la page graphe) affiche les types portant des coordonnées sous forme de cellules avec des décomptes exacts en vue, selon vos permissions. Le zoom subdivise les cellules ; quand le total en vue d'un type est assez petit, les instances réelles s'affichent à la place. Les listes de cellules tronquées sont étiquetées comme bornes inférieures — jamais présentées comme des nombres définitifs.
  • Les relations s'affichent aussi sur la vue Carte : les types de liens de référence avec une règle de jointure explicite tracent des arcs entre cellules, chacun portant le nombre exact de paires liées entre ces deux zones (survolez pour le voir) ; les liens de proximité tracent de fins connecteurs entre les instances actuellement affichées, et la vue précise qu'ils ne couvrent que les instances affichées — zoomez pour matérialiser le reste. Chaque type de lien rapporte son total de paires en vue aux côtés des totaux par type.

Validation : entityColumns doit être un sous-ensemble strict non vide de identityColumns (les ensembles égaux sont rejetés — omettez entityColumns dans ce cas). L'installation de pack rejette les manifestes qui violent cette règle.

Visualisation du graphe

Les packs denses peuvent inclure des indications optionnelles de visualisation de graphe. Ces indications ne modifient pas le schéma d'ontologie ; elles indiquent aux surfaces de graphe comment restituer efficacement une vue d'instance.

Utilisez mode: 'compact' quand un pack a un type d'objet racine de haute valeur et de nombreuses liaisons enfant de haute cardinalité. Le point de terminaison du graphe ne projette que la liaison racine, affiche des nœuds agrégés par instance racine, et ignore la projection de lignes pour les liaisons d'objet et de lien réduites.

defineOntology({
  id: 'acme-docs',
  version: '1.0.0',
  displayName: 'ACME Documents',
  description: 'Document intelligence ontology',
  maturity: 'preview',
  author: { name: 'Acme Inc.' },

  visualization: {
    graph: {
      mode: 'compact',
      rootObjectType: 'DocumentCorpus',
      rootLimit: 100,
      rootGroup: {
        slug: 'DocumentCorpusIndex',
        label: 'All document corpora',
        edgeLabel: 'contains corpus',
      },
      collapsedObjectTypes: ['Document', 'DocumentSection', 'ExtractedEntity'],
      collapsedLinkTypes: ['ContainsDocument', 'MentionsEntity'],
      aggregates: [
        {
          slug: 'Document',
          label: 'Documents',
          linkSlug: 'ContainsDocument',
          edgeLabel: 'contains documents',
          countObjectTypes: ['Document'],
        },
        {
          slug: 'ExtractedEntity',
          label: 'Extracted entities',
          linkSlug: 'MentionsEntity',
          edgeLabel: 'mentions entities',
          countObjectTypes: ['ExtractedEntity'],
          countLinkTypes: ['MentionsEntity'],
        },
      ],
    },
  },

  objectTypes: [],
  linkTypes: [],
  actionTypes: [],
  identityRules: [],
  bindings: [],
})

Règles d'identité — déterministes + probabilistes

Les règles d'identité indiquent à la plateforme comment fusionner les instances. Le chemin déterministe est obligatoire ; le chemin probabiliste est optionnel pour les sources plus floues.

defineIdentityRule({
  objectTypeSlug: 'Account',
  deterministicKeys: ['external_id', 'name + jurisdiction'],
  probabilistic: {
    blockingKeys: ['name'],
    signals: [
      { name: 'name_levenshtein', weight: 0.6, on: 'name' },
      { name: 'jurisdiction_match', weight: 0.4, on: 'jurisdiction' },
    ],
    thresholds: { auto: 0.85, review: 0.70 },
  },
})

Types d'actions — mutations côté serveur contrôlées par la façade

Les types d'actions sont le seul chemin d'écriture typé. Chaque action s'exécute de la même façon :

  1. Validation Zod de l'entrée contre inputZod.
  2. Le service propriétaire de l'action résout la cible réelle et consomme une décision unique de la façade d'autorisation. OPA peut évaluer l'exigence Rego comme adaptateur privé ; le code de l'action ne l'appelle pas directement.
  3. Le descripteur d'effet secondaire décide de ce qui s'exécute — édition directe d'objet/lien, dispatch d'outbox, ou webhook.
  4. Une preuve d'autorisation durable est écrite avec la provenance des adaptateurs applicables, notamment bundleHash et policy_execution_grant.grantId lorsqu'ils sont présents.
sideEffectDescriptor.kindCe qui se passe
object_editUpsert dans le store transactionnel du type d'objet
link_editUpsert dans le store d'arêtes du type de lien
outboxMise en file d'attente d'un événement d'outbox transactionnel
webhookDéclenchement d'un webhook configuré (URL dans config)

Maturité

Choisissez-en une — affichée comme un badge dans le workbench :

  • verified — prêt pour la production, possédé et maintenu
  • preview — fonctionnellement complet, peut évoluer
  • experimental — accès anticipé, s'attendre à des changements cassants

Validation

Le pack est validé contre OntologyManifestSchema (Zod) au moment du build et à nouveau côté serveur au moment du téléversement. Échecs courants :

ErreurCause
id must be lowercase kebab-caseL'id du Pack doit correspondre à /^[a-z][a-z0-9-]*$/
Slug must start with a letter, alnum + _ -Le slug d'objet/lien/action/propriété a échoué le regex
Must be a valid semver stringversion n'est pas semver
linkType source/target slug not foundsourceTypes / targetTypes référencent un slug de type d'objet inconnu
expected array to have exactly 1 itemChaque extrémité concrète sourceTypes / targetTypes doit contenir exactement un slug de type d'objet ; utilisez "*" uniquement pour un lien de compatibilité générique

Empaquetés via le pipeline de bundle d'intégration (aujourd'hui)

Les packs d'ontologie sont distribués dans un bundle d'intégration via definePackOntology — la couche de registre dispatche les packs de la même façon qu'elle dispatche les vendeurs. Un CLI autonome scrydon-ontologies est prévu ; en attendant, suivez le flux de build Intégrations avec un export definePackOntology(...) de niveau supérieur.

import { definePackOntology } from '@scrydon/sdk-authoring/integrations/define'
import myCrmOntology from './crm.ontology'

export default definePackOntology({
  id: 'acme-crm',
  version: '1.0.0',
  displayName: 'ACME CRM',
  description: 'Salesforce-style CRM ontology',
  maturity: 'preview',
  author: { name: 'Acme Inc.' },
  objectTypes: myCrmOntology.objectTypes,
  linkTypes: myCrmOntology.linkTypes,
  actionTypes: myCrmOntology.actionTypes,
  bindings: myCrmOntology.bindings,
  identityRules: myCrmOntology.identityRules,
})

Où aller ensuite

Sur cette page

Sur cette page