• Platform
  • Language
  • Docs
  • Blog
  • Get Started
  • Teaching ConceptEngine to Infer

    By Eric Weber

    March 16, 2022

    In our previous post, we introduced ConceptEngine and ran through a toy example of a talent marketplace. We capped off the post by mentioning a feature: the ability to react to patterns of data by adding and/or removing concepts, as well as the ability to notify external webhooks of pattern matches. This is all accomplished via triggers.

    What are the implications? With triggers, we can...

    1. Automatically infer concepts.
    2. Integrate with external tools.
    3. Create helpers that take simple inputs and map them into a complex set of concepts.
    4. Migrate from one way of representing information to another.

    First, I’ll introduce the syntax, then run through an example of inference. If you want to follow along, create a test knowledge space, and use the Compose tab to try out the examples.


    Here’s an example that uses all of the features:

    triggerTaskDone [
      @matches [
        $task [Task, status done-status]
        $person [Person, assigned-to $task]
      @adds [
        $person [completed $task]
      @removes [
        $task [status {unstarted-status, started-status}]
      @notifies [

    As you can see, there are five components of a trigger:

    1. Trigger key - Each trigger has a unique identifier. In the example, it’s triggerTaskDone.
    2. @matches - The rules that must be fulfilled for the trigger to fire.
    3. @adds - Templates for concepts that will be added in response to a trigger.
    4. @removes - Templates for concepts to remove.
    5. @notifies - URLs to notify when triggered.

    At minimum, you must specify at least one @matches pattern and one @adds, @removes, or @notifies directive.


    Imagine you have a knowledge space that stores your social network, and you’re adding concepts that represent your family relationships.

    Often times a family relationship can be described at different levels of abstraction. For example, a brother is a sibling, a mother is a parent, and any of those are called relatives.

    Why is it important to capture all of these relationships? For one, it allows you to perform fuzzier queries. Let’s say you want to find all sibling relationships amongst your relatives. Well, then you can write a query like this:

    eric-weber [relative-of $relative]
    $relative [sibling-of $relativeSibling]

    However, this would mean that for each family relationship, you’ll have to write a concept that accounts for each way of describing it. It may be cumbersome, and perhaps error-prone to write these concepts out every time you add a new family member (if you’re writing them manually vs. an app).

    This is where inference comes in.

    Here are some triggers that would infer relationships between siblings:

    inferReverseSibling [
      @matches [$person1 [sibling-of $person2]]
      @adds [$person2 [sibling-of $person1]]
    inferSiblingFromSister [
      @matches [$person1 [sister-of $person2]]
      @adds [$person1 [sibling-of $person2]]
    inferSiblingFromBrother [
      @matches [$person1 [brother-of $person2]]
      @adds [$person1 [sibling-of $person2]]
    inferSisterFromGender [
      @matches [$person1 [Female, sibling-of $person2]]
      @adds [$person1 [sister-of $person2]]
    inferBrotherFromGender [
      @matches [$person1 [Male, sibling-of $person2]]
      @adds [$person1 [brother-of $person2]]

    Advanced: Smarter Triggers with Meta-Concepts

    Is there any way to cut down on the repetition of these triggers? They’re all very similar, and we’d have to repeat them for other kinds of family relationships.

    Well, we can leverage meta-concepts (concepts about concepts) to make our @matches patterns more robust:

    {Male, Female} Gender
    {brother-of, sister-of} [extends sibling-of]
    sibling-of [implies-reverse sibling-of]
    brother-of [gendered Male]
    sister-of [gendered Female]
    inferExtendedRel [
      @matches [
        $person1 [$specificRel $person2]
        $specificRel [extends $generalRel]
      @adds [$person1 [$generalRel $person2]]
    inferReverseRel [
      @matches [
        $person1 [$rel $person2]
        $rel [implies-reverse $reverseRel]
      @adds [$person2 [$reverseRel $person1]]
    inferGenderedRel [
      @matches [
        $person1 [$gender, $generalRel $person2]
        $gender Gender
        $specificRel [gendered $gender, extends $generalRel]
      @adds [$person1 [$specificRel $person2]]

    These are a bit more complicated than the previous triggers we wrote out. However, they can be reused for many other kinds of concepts. In fact, inferExtendedRel and inferReverseRel are pretty generic. Just add the appropriate extends, implies-reverse meta-concepts.

    In the current example of family relationships, we can now easily account for parent-child relationships:

    {mother-of, father-of} [extends parent-of]
    parent-of [implies-reverse child-of]
    mother-of [gendered Female]
    father-of [gendered Male]
    {daughter-of, son-of} [extends child-of]
    child-of [implies-reverse parent-of]
    daughter-of [gendered Female]
    son-of [gendered Male]

    Use all of these triggers in your own knowledge graph if you’d like, and don’t be afraid to change around the grammar to your liking!

    Invoking our Triggers

    Here's the payoff. If we were to, for example, add this concept:

    marie [Female, child-of john]

    ConceptEngine should automatically infer:

    marie [daughter-of john]
    john [father-of marie]
    john [parent-of marie]


    Triggers give you the ability to enforce pattern-based logic on your data, which saves you work and enforces consistency in your data.

    We also alluded to the ability to integrate with external tools using triggers and webhooks. In an upcoming post, we’ll demonstrate how awesome this can be for automation workflows.