$ cat node-template.py
Ontology Unified Retrieval
// Answer a question against the organization ontology using the unified retrieval flow: entity-name search + KB semantic search bridged to ontology entities, RRF-fused, profiled via Entity 360, then synthesized into an answer with the retrieved entities returned for provenance. Use for questions ABOUT organizational entities, relationships and themes (who/which/what-relates-to), not for content lookup inside a single document.
1import json2import sys345def main():6 print(json.dumps({7 "error": "Macro nodes are executed by the macro executor, not directly. "8 "Please configure the macro using the wizard.",9 }), file=sys.stderr)10 sys.exit(1)111213if __name__ == "__main__":14 main()15$ git log --oneline
1.4.0 — add an Ontology Aggregate node (1.0.0) feeding an EXACT corpus census into the synthesizer. E1 measured the macro answering 'which entity types are most frequent?' with 'Detainee and Ministry', read off the composition of its own 25-item relevance ranking; ground truth for that identity is Location 98 / Organization 38, with Detainee absent from the top 8. That is the worst failure shape available — not a refusal a reader can see, but a confident wrong number. The node is query-independent (no inputs wired), which also sidesteps the macro's strictly one-to-one input mapping. 1.3.3 — restore anchorFirst=true after the A/B. 1.3.2 was a TEMPORARY OFF build published only to measure the anchor's effect on the 8 non-lookup questions whose head it changes. Result: 3 pairs favour OFF, 2 favour ON, 3 tie — no systematic regression, and the differences sit within LLM synthesis noise (n=1 per arm). The deterministic gain stands: lookup precision@1 7/9 -> 8/9. 1.3.1 — merge node 1.4.1: anchorFirst now mirrors BOTH of topId's tiers. 1.3.0 anchored only on the lexical flag, which ontology-search computes as "every query token appears in the entity name" — no full-sentence question satisfies that, so the macro (which passes the raw question) produced zero lexical hits and the reorder was a measured no-op on all 9 E1 named-entity questions. Tier 2 (list-A rank 1) is the path that actually fires here. 1.3.0 — turn on Merge Ranked Lists 1.4.0's anchorFirst so lexical entity matches head the fused ranking. E1's precision pass measured plain RRF demoting list A's own rank-1 entity on 9/30 questions (RRF sums across lists, so a mediocre double-lister outscores an authoritative single-lister): 'Legge consolare' fell to fused #2 on Q1 while the atomic tools ranked it #1. merge-ranked-lists 1.3.0 already fixed this for topId only and left the ORDER alone; the synthesizer reads the ORDER, so it kept getting the demoted list. Expected: arm-2 precision@1 7/9 -> 8/9, matching the other two arms. 1.2.0 — add an Ontology Traverse node (1.1.0) on the top fused entity, walking EVERY relation (linkType empty) and feeding summaryText into the synthesizer. E1 measured the macro unable to answer relation questions ('chi ha firmato il DPR' -> 26s of 'non e possibile identificare' while the atomic tools returned Prodi in 240ms). The macro had no traversal at all; the node was previously uncomposable because linkType was mandatory and it emitted Json only.
1.3.3 — restore anchorFirst=true after the A/B. 1.3.2 was a TEMPORARY OFF build published only to measure the anchor's effect on the 8 non-lookup questions whose head it changes. Result: 3 pairs favour OFF, 2 favour ON, 3 tie — no systematic regression, and the differences sit within LLM synthesis noise (n=1 per arm). The deterministic gain stands: lookup precision@1 7/9 -> 8/9. 1.3.1 — merge node 1.4.1: anchorFirst now mirrors BOTH of topId's tiers. 1.3.0 anchored only on the lexical flag, which ontology-search computes as "every query token appears in the entity name" — no full-sentence question satisfies that, so the macro (which passes the raw question) produced zero lexical hits and the reorder was a measured no-op on all 9 E1 named-entity questions. Tier 2 (list-A rank 1) is the path that actually fires here. 1.3.0 — turn on Merge Ranked Lists 1.4.0's anchorFirst so lexical entity matches head the fused ranking. E1's precision pass measured plain RRF demoting list A's own rank-1 entity on 9/30 questions (RRF sums across lists, so a mediocre double-lister outscores an authoritative single-lister): 'Legge consolare' fell to fused #2 on Q1 while the atomic tools ranked it #1. merge-ranked-lists 1.3.0 already fixed this for topId only and left the ORDER alone; the synthesizer reads the ORDER, so it kept getting the demoted list. Expected: arm-2 precision@1 7/9 -> 8/9, matching the other two arms. 1.2.0 — add an Ontology Traverse node (1.1.0) on the top fused entity, walking EVERY relation (linkType empty) and feeding summaryText into the synthesizer. E1 measured the macro unable to answer relation questions ('chi ha firmato il DPR' -> 26s of 'non e possibile identificare' while the atomic tools returned Prodi in 240ms). The macro had no traversal at all; the node was previously uncomposable because linkType was mandatory and it emitted Json only.
1.3.2 — TEMPORARY A/B: anchorFirst OFF to measure the anchor's effect on non-lookup shapes.
1.3.1 — merge node 1.4.1: anchorFirst now mirrors BOTH of topId's tiers. 1.3.0 anchored only on the lexical flag, which ontology-search computes as "every query token appears in the entity name" — no full-sentence question satisfies that, so the macro (which passes the raw question) produced zero lexical hits and the reorder was a measured no-op on all 9 E1 named-entity questions. Tier 2 (list-A rank 1) is the path that actually fires here. 1.3.0 — turn on Merge Ranked Lists 1.4.0's anchorFirst so lexical entity matches head the fused ranking. E1's precision pass measured plain RRF demoting list A's own rank-1 entity on 9/30 questions (RRF sums across lists, so a mediocre double-lister outscores an authoritative single-lister): 'Legge consolare' fell to fused #2 on Q1 while the atomic tools ranked it #1. merge-ranked-lists 1.3.0 already fixed this for topId only and left the ORDER alone; the synthesizer reads the ORDER, so it kept getting the demoted list. Expected: arm-2 precision@1 7/9 -> 8/9, matching the other two arms. 1.2.0 — add an Ontology Traverse node (1.1.0) on the top fused entity, walking EVERY relation (linkType empty) and feeding summaryText into the synthesizer. E1 measured the macro unable to answer relation questions ('chi ha firmato il DPR' -> 26s of 'non e possibile identificare' while the atomic tools returned Prodi in 240ms). The macro had no traversal at all; the node was previously uncomposable because linkType was mandatory and it emitted Json only.
1.3.0 — turn on Merge Ranked Lists 1.4.0's anchorFirst so lexical entity matches head the fused ranking. E1's precision pass measured plain RRF demoting list A's own rank-1 entity on 9/30 questions (RRF sums across lists, so a mediocre double-lister outscores an authoritative single-lister): 'Legge consolare' fell to fused #2 on Q1 while the atomic tools ranked it #1. merge-ranked-lists 1.3.0 already fixed this for topId only and left the ORDER alone; the synthesizer reads the ORDER, so it kept getting the demoted list. Expected: arm-2 precision@1 7/9 -> 8/9, matching the other two arms. 1.2.0 — add an Ontology Traverse node (1.1.0) on the top fused entity, walking EVERY relation (linkType empty) and feeding summaryText into the synthesizer. E1 measured the macro unable to answer relation questions ('chi ha firmato il DPR' -> 26s of 'non e possibile identificare' while the atomic tools returned Prodi in 240ms). The macro had no traversal at all; the node was previously uncomposable because linkType was mandatory and it emitted Json only.
1.2.0 — add an Ontology Traverse node (1.1.0) on the top fused entity, walking EVERY relation (linkType empty) and feeding summaryText into the synthesizer. E1 measured the macro unable to answer relation questions ('chi ha firmato il DPR' -> 26s of 'non e possibile identificare' while the atomic tools returned Prodi in 240ms). The macro had no traversal at all; the node was previously uncomposable because linkType was mandatory and it emitted Json only.
1.0.1 — publish as an agent tool (isPublishedAsTool + toolDescription + toolExamples). 1.0.0 was invokable only via macroConfig on a canvas: macro_executor.execute_standalone fetches through fetch_invokable_macro, which verifies is_published_as_tool=true, so 1.0.0 could not be invoked standalone at all.