Intelligence Catalog
Central, version-controlled repository of Insight templates — JSON files that pair a Cypher query over the knowledge graph with a rendering template for the resulting Insight node.
Source: intelligence-catalog
Overview
Unlike the other backend components, this isn't a Python library — it's a data catalog: plain JSON template files, loaded from disk at runtime. It's consumed in two steps:
- Loading —
dem.intelligence.loader.load_templates_from_disk(backend/dem/src/dem/intelligence/loader.py) walksinsight-templates/**/*.json, parses each file into anInsightTemplateModel(backend/dem/src/dem/intelligence/template_model.py), and validates it:scopemust be one ofSession,Agent,MAS,SemanticGroup, and every$variableplaceholder used innameTemplate/descriptionTemplate/labels/priority/targetNodeIdmust also appear in thekgQuery'sRETURNclause. Invalid templates are logged and skipped, not fatal. - Generation — the intelligence-worker's
IntelligenceWrapper.generate_insights(backend/workers/intelligence-worker/src/intelligence_worker/wrapper/intelligence_wrapper.py) runs each template'skgQueryagainst the knowledge graph; for every result row it substitutes the row's variables into the templates to build anInsightnode (deduplicated by a hash of template id + target node + rendered name) and persists the batch to the KG.
Repository structure
intelligence-catalog/
├── insight-templates/
│ ├── group/ # SemanticGroup-scoped templates (3 today)
│ └── session/ # Session-scoped templates (6 today)
├── recommendations/ # empty (.gitkeep) — reserved for future remediation content
└── root-causes/ # empty (.gitkeep) — reserved for future root-cause patterns
recommendations/ and root-causes/ are placeholders for now; nothing reads from them yet.
Template format
Each insight-templates/**/*.json file is one InsightTemplateModel:
| Field | Required | Description |
|---|---|---|
nameTemplate |
yes | Insight title, with $variable placeholders |
descriptionTemplate |
yes | Insight body, with $variable placeholders |
kgQuery |
yes | Cypher query; either a plain string or (as used by every template today) a JSON array of lines, joined with \n by the loader |
labels |
no | List of tag strings, may contain placeholders |
scope |
no | One of Session, Agent, MAS, SemanticGroup |
priority |
no | e.g. "P1"–"P3", or itself a $variable resolved from the query |
targetNodeId |
no | KG node id the generated Insight attaches to, usually a $variable |
Real example (insight-templates/group/execution-graph-inconsistency-group.json):
{
"nameTemplate": "Unreliable execution patterns in group \"$groupName\"",
"descriptionTemplate": "Traces about \"$groupName\" have unreliable execution patterns (consistency=$meanValue, below the threshold $threshold).",
"kgQuery": [
"MATCH (sg:SemanticGroup)-[:hasConsistencyReport]->(cr:ConsistencyReport)",
"WHERE cr.dataType = 'graph'",
"WITH sg, cr, 0.70 AS threshold, toFloat(cr.mean) AS consistencyValue",
"WHERE consistencyValue < threshold",
"RETURN sg.id AS groupId, sg.groupName AS groupName,",
" round(consistencyValue, 4) AS meanValue, round(threshold, 2) AS threshold"
],
"labels": ["Reliability"],
"scope": "SemanticGroup",
"priority": "P2",
"targetNodeId": "$groupId"
}
Session-scoped templates follow the same shape, reading from Metric, AnomalyReport, and ConsistencyReport nodes attached to a Session/SemanticGroup (see insight-templates/session/*.json).
Adding a new template
- Add a kebab-case JSON file under
insight-templates/group/orinsight-templates/session/. - Make sure every
RETURN-clause alias needed by the templates/labels/priority/targetNodeId is present — the loader rejects templates where a$variableisn't returned by the query. - No build step is required: the catalog is read from disk at worker startup (
catalog_rootpassed toIntelligenceWrapper), so a new template only needs redeploying/restarting the intelligence-worker.
See also analysis for the ConsistencyReport/AnomalyReport nodes these templates query against.