glam/data/sparql_templates.yaml
kempersc 2c2a312e0a feat(rag): add database routing to 8 more factual query templates
Add databases: ["oxigraph"] to skip vector search for deterministic queries:
- count_institutions_by_type_location (count)
- count_institutions_by_type (aggregation)
- find_institutions_by_founding_date (temporal)
- find_custodians_by_budget_threshold (financial)
- compare_locations (comparative)
- find_by_founding (temporal)
- events_in_period (temporal events)
- institutions_by_founding_decade (temporal aggregation)

Total templates with oxigraph-only routing: 12
2026-01-09 12:33:41 +01:00

1909 lines
71 KiB
YAML

# SPARQL Query Templates for Heritage Custodian Knowledge Graph
#
# This file defines parameterized SPARQL templates that replace LLM-generated queries
# with deterministic, validated templates. Based on docs/plan/prompt-query_template_mapping/
#
# CRITICAL: Processing Pipeline Order
# ===================================
# 1. User question → ConversationContextResolver (DSPy) → RESOLVED question
# 2. RESOLVED question → FykeFilter (DSPy) → relevant/irrelevant
# ⚠️ FYKE MUST OPERATE ON RESOLVED QUESTION, NOT RAW INPUT!
# "En in Enschede?" resolved to "Welke archieven zijn er in Enschede?" is clearly relevant
# 3. If relevant: resolved question → TemplateClassifier → template_id
# 4. template_id + resolved question → SlotExtractor → slot values
# 5. template + slot values → TemplateInstantiator (Jinja2) → SPARQL query
#
# Slot Value Sources:
# - data/validation/sparql_validation_rules.json (institution_type_mappings, subregion_mappings, etc.)
# - backend/rag/ontology_mapping.py (fuzzy matching, multilingual synonyms)
_metadata:
version: "1.1.0"
created: "2025-01-06"
updated: "2025-01-08"
schema_source: "schemas/20251121/linkml/"
validation_rules: "data/validation/sparql_validation_rules.json"
# Response Mode Configuration
# ==========================
# Each template declares how its results should be rendered via `response_modes` (list).
# Multiple modes can be combined for flexible rendering:
#
# Available modes:
# - "table": Render SPARQL results as a table
# - "count": Display count/aggregate value
# - "prose": Generate LLM natural language explanation
# - "chart": Render as visualization (bar, pie, timeline, etc.)
# - "map": Render geographic results on a map
#
# Examples:
# response_modes: ["table"] # Table only, skip LLM (fast path)
# response_modes: ["count"] # Count only, skip LLM (fast path)
# response_modes: ["prose"] # LLM prose only
# response_modes: ["prose", "table"] # LLM prose + table
# response_modes: ["prose", "chart"] # LLM prose + visualization
# response_modes: ["table", "map"] # Table + map view
#
# Fast path rule: If "prose" is NOT in response_modes, LLM generation is skipped.
#
# Templates can also define `ui_template` for localized answer formatting:
# ui_template:
# nl: "Gevonden: {{ count }} {{ institution_type_nl }} in {{ city }}."
# en: "Found: {{ count }} {{ institution_type_en }} in {{ city }}."
#
# Database Routing Configuration
# ==============================
# Each template can specify which databases to query via `databases` (list).
# This allows skipping unnecessary data sources for faster, deterministic results.
#
# Available databases:
# - "oxigraph": SPARQL Knowledge Graph (deterministic, has coordinates)
# - "qdrant": Vector search (probabilistic, semantic similarity)
#
# Examples:
# databases: ["oxigraph", "qdrant"] # Default: query both (backward compatible)
# databases: ["oxigraph"] # SPARQL only - skip vector search
# databases: ["qdrant"] # Vector only - for exploration queries
#
# Use ["oxigraph"] for:
# - Geographic/map queries (Oxigraph has lat/lon coordinates)
# - Factual queries with known schema (counts, lists by type)
# - Deterministic results without vector noise
#
# Use ["qdrant"] for:
# - Semantic exploration ("similar institutions to X")
# - Free-text search without structured slots
# Standard SPARQL prefixes used in all templates
_prefixes: |
PREFIX hc: <https://nde.nl/ontology/hc/>
PREFIX hcc: <https://nde.nl/ontology/hc/class/>
PREFIX crm: <http://www.cidoc-crm.org/cidoc-crm/>
PREFIX schema: <http://schema.org/>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
PREFIX org: <http://www.w3.org/ns/org#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX dcterms: <http://purl.org/dc/terms/>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX wd: <http://www.wikidata.org/entity/>
PREFIX geo: <http://www.w3.org/2003/01/geo/wgs84_pos#>
# Slot type definitions with validation sources
# Per Rule 41: Types classes are the single source of truth for slot values
_slot_types:
institution_type:
description: "Single-letter custodian type code from GLAMORCUBESFIXPHDNT taxonomy"
# Schema-driven: values derived from CustodianType subclasses (Rule 41)
schema_source: "schemas/20251121/linkml/modules/classes/CustodianType.yaml"
source: "sparql_validation_rules.json#institution_type_mappings"
valid_values: ["G", "L", "A", "M", "O", "R", "C", "U", "B", "E", "S", "F", "I", "X", "P", "H", "D", "N", "T"]
# Labels resolved at RUNTIME from CustodianType subclass definitions
# Each subclass has type_label with multilingual labels (skos:prefLabel)
label_sources:
- "schemas/20251121/linkml/modules/classes/CustodianType.yaml" # type_label slot
- "schemas/20251121/linkml/modules/enums/InstitutionTypeCodeEnum.yaml" # descriptions
synonyms:
# G - Gallery (GalleryType)
galerie: "G"
galerij: "G"
galerijen: "G"
gallery: "G"
galleries: "G"
kunstgalerie: "G"
art_gallery: "G"
# L - Library (LibraryType)
bibliotheek: "L"
bibliotheken: "L"
library: "L"
libraries: "L"
bibliothek: "L" # German
bib: "L"
openbare_bibliotheek: "L"
public_library: "L"
university_library: "L"
# A - Archive (ArchiveOrganizationType)
archief: "A"
archieven: "A"
archive: "A"
archives: "A"
archiv: "A" # German
stadsarchief: "A"
rijksarchief: "A"
gemeentearchief: "A"
nationaal_archief: "A"
national_archive: "A"
state_archive: "A"
regional_archive: "A"
# M - Museum (MuseumType)
museum: "M"
musea: "M"
museums: "M"
musée: "M"
museo: "M"
kunstmuseum: "M"
art_museum: "M"
history_museum: "M"
science_museum: "M"
natuurmuseum: "M"
# O - Official Institution (OfficialInstitutionType)
overheidsinstelling: "O"
overheidsinstellingen: "O"
official_institution: "O"
government_agency: "O"
heritage_agency: "O"
rijksdienst: "O"
provinciale_dienst: "O"
# R - Research Center (ResearchOrganizationType)
onderzoekscentrum: "R"
onderzoekscentra: "R"
research_center: "R"
research_institute: "R"
kenniscentrum: "R"
documentatiecentrum: "R"
# C - Corporation (CommercialOrganizationType)
bedrijfsarchief: "C"
corporate_archive: "C"
company_archive: "C"
bedrijfscollectie: "C"
corporate_collection: "C"
# U - Unknown (UnspecifiedType)
onbekend: "U"
unknown: "U"
# B - Botanical/Zoo (BioCustodianType)
dierentuin: "B"
zoo: "B"
botanische_tuin: "B"
botanical_garden: "B"
aquarium: "B"
hortus: "B"
arboretum: "B"
# E - Education Provider (EducationProviderType)
universiteit: "E"
university: "E"
hogeschool: "E"
school: "E"
onderwijsinstelling: "E"
education_provider: "E"
# S - Society (HeritageSocietyType)
heemkundige_kring: "S"
historische_vereniging: "S"
historical_society: "S"
heritage_society: "S"
oudheidkundige_kring: "S"
verzamelaarvereniging: "S"
# F - Feature (FeatureCustodianType)
monument: "F"
feature: "F"
landmark: "F"
standbeeld: "F"
statue: "F"
memorial: "F"
# I - Intangible Heritage (IntangibleHeritageGroupType)
immaterieel_erfgoed: "I"
intangible_heritage: "I"
folklore: "I"
traditie: "I"
tradition: "I"
oral_history: "I"
# X - Mixed
gemengd: "X"
mixed: "X"
gecombineerd: "X"
combined: "X"
# P - Personal Collection (PersonalCollectionType)
prive_verzameling: "P"
personal_collection: "P"
private_collection: "P"
particuliere_collectie: "P"
# H - Holy Sites (HolySacredSiteType)
kerk: "H"
church: "H"
moskee: "H"
mosque: "H"
synagoge: "H"
synagogue: "H"
tempel: "H"
temple: "H"
klooster: "H"
monastery: "H"
kathedraal: "H"
cathedral: "H"
abdij: "H"
abbey: "H"
# D - Digital Platform (DigitalPlatformType)
digitaal_platform: "D"
digital_platform: "D"
digitale_bibliotheek: "D"
digital_library: "D"
online_archief: "D"
online_archive: "D"
# N - NGO (NonProfitType)
erfgoedorganisatie: "N"
heritage_ngo: "N"
ngo: "N"
stichting: "N"
foundation: "N"
# T - Taste/Smell Heritage (TasteScentHeritageType)
culinair_erfgoed: "T"
culinary_heritage: "T"
taste_heritage: "T"
gastronomisch_erfgoed: "T"
parfumerie: "T"
distilleerderij: "T"
brouwerij: "T"
subregion:
description: "ISO 3166-2 subdivision code (NL-NH, DE-BY, etc.)"
# Schema-driven: geographic hierarchy from GeoNames + ISO 3166-2 (Rule 41)
# Labels resolved at RUNTIME from these sources - NOT hardcoded
schema_source: "schemas/20251121/linkml/modules/classes/Subregion.yaml"
label_sources:
# Priority order for label resolution at runtime
- "data/reference/iso_3166_2_{country}.json" # e.g., iso_3166_2_nl.json
- "data/reference/geonames.db" # GeoNames admin1 table
- "data/reference/admin1CodesASCII.txt" # GeoNames fallback
source: "sparql_validation_rules.json#subregion_mappings"
# NOTE: Labels are NOT hardcoded here - they are loaded dynamically
# from label_sources at runtime by the SlotExtractor
synonyms:
# Netherlands
noord-holland: "NL-NH"
noord_holland: "NL-NH"
noordholland: "NL-NH"
amsterdam_province: "NL-NH"
zuid-holland: "NL-ZH"
zuid_holland: "NL-ZH"
zuidholland: "NL-ZH"
rotterdam_province: "NL-ZH"
den_haag_province: "NL-ZH"
the_hague_province: "NL-ZH"
noord-brabant: "NL-NB"
brabant: "NL-NB"
eindhoven_province: "NL-NB"
gelderland: "NL-GE"
arnhem_province: "NL-GE"
nijmegen_province: "NL-GE"
utrecht_province: "NL-UT"
overijssel: "NL-OV"
zwolle_province: "NL-OV"
enschede_province: "NL-OV"
limburg_nl: "NL-LI"
maastricht_province: "NL-LI"
friesland: "NL-FR"
frisia: "NL-FR"
leeuwarden_province: "NL-FR"
groningen_province: "NL-GR"
drenthe: "NL-DR"
assen_province: "NL-DR"
flevoland: "NL-FL"
almere_province: "NL-FL"
lelystad_province: "NL-FL"
zeeland: "NL-ZE"
middelburg_province: "NL-ZE"
# Belgium
vlaanderen: "BE-VLG"
flanders: "BE-VLG"
antwerpen_province: "BE-VLG"
gent_province: "BE-VLG"
brugge_province: "BE-VLG"
wallonie: "BE-WAL"
wallonia: "BE-WAL"
brussel: "BE-BRU"
brussels: "BE-BRU"
bruxelles: "BE-BRU"
# Germany
bayern: "DE-BY"
bavaria: "DE-BY"
muenchen_province: "DE-BY"
munich_province: "DE-BY"
berlin_state: "DE-BE"
baden_wuerttemberg: "DE-BW"
stuttgart_province: "DE-BW"
nordrhein_westfalen: "DE-NW"
north_rhine_westphalia: "DE-NW"
koeln_province: "DE-NW"
cologne_province: "DE-NW"
duesseldorf_province: "DE-NW"
sachsen: "DE-SN"
saxony: "DE-SN"
dresden_province: "DE-SN"
hessen: "DE-HE"
hesse: "DE-HE"
frankfurt_province: "DE-HE"
country:
description: "ISO 3166-1 alpha-2 country code"
source: "sparql_validation_rules.json#country_mappings"
format: "iso_3166_1_alpha2"
synonyms:
nederland: "NL"
netherlands: "NL"
holland: "NL"
nl: "NL"
belgie: "BE"
belgium: "BE"
be: "BE"
duitsland: "DE"
germany: "DE"
de_country: "DE"
frankrijk: "FR"
france: "FR"
fr: "FR"
verenigd_koninkrijk: "GB"
united_kingdom: "GB"
uk: "GB"
gb: "GB"
engeland: "GB"
england: "GB"
verenigde_staten: "US"
united_states: "US"
usa: "US"
us: "US"
japan: "JP"
jp: "JP"
tsjechie: "CZ"
czech_republic: "CZ"
czechia: "CZ"
cz: "CZ"
oostenrijk: "AT"
austria: "AT"
at: "AT"
zwitserland: "CH"
switzerland: "CH"
ch: "CH"
city:
description: "City/locality name (string literal)"
source: "fuzzy_match"
institution_name:
description: "Institution name for lookup (string literal)"
source: "fuzzy_match"
limit:
description: "Result limit (integer)"
default: 10
max: 100
budget_category:
description: "Budget or expense category for financial queries"
source: "ontology"
valid_values: ["innovation", "digitization", "preservation", "personnel", "acquisition", "operating", "capital", "external_funding", "internal_funding", "endowment_draw"]
synonyms:
# Dutch - Innovation
innovatie: "innovation"
innovaties: "innovation"
vernieuwing: "innovation"
digital_transformatie: "innovation"
digitale_transformatie: "innovation"
r_d: "innovation"
onderzoek_ontwikkeling: "innovation"
# English - Innovation
innovations: "innovation"
r_and_d: "innovation"
research_development: "innovation"
digital_transformation: "innovation"
technology: "innovation"
tech: "innovation"
# German - Innovation
innovationen: "innovation"
erneuerung: "innovation"
# Dutch - Digitization
digitalisering: "digitization"
digitaliseringsbudget: "digitization"
digitale_collectie: "digitization"
# English - Digitization
digitisation: "digitization" # UK spelling
digital: "digitization"
scanning: "digitization"
# German - Digitization
digitalisierung: "digitization"
# Dutch - Preservation
conservering: "preservation"
restauratie: "preservation"
behoud: "preservation"
onderhoud: "preservation"
# English - Preservation
conservation: "preservation"
restoration: "preservation"
maintenance: "preservation"
# German - Preservation
konservierung: "preservation"
restaurierung: "preservation"
# Dutch - Personnel
personeel: "personnel"
personele_kosten: "personnel"
salarissen: "personnel"
medewerkers: "personnel"
fte: "personnel"
# English - Personnel
staff: "personnel"
salaries: "personnel"
employees: "personnel"
hr: "personnel"
human_resources: "personnel"
# German - Personnel
personal: "personnel"
personalkosten: "personnel"
gehälter: "personnel"
# Dutch - Acquisition
aanwinsten: "acquisition"
aankopen: "acquisition"
collectie_aankopen: "acquisition"
verwervingen: "acquisition"
# English - Acquisition
acquisitions: "acquisition"
purchases: "acquisition"
collection_development: "acquisition"
# German - Acquisition
erwerbungen: "acquisition"
ankäufe: "acquisition"
# Dutch - Operating
operationeel: "operating"
exploitatie: "operating"
bedrijfskosten: "operating"
# English - Operating
operations: "operating"
operational: "operating"
running_costs: "operating"
# German - Operating
betriebskosten: "operating"
betrieb: "operating"
# Dutch - Capital
kapitaal: "capital"
investeringen: "capital"
bouw: "capital"
verbouwing: "capital"
# English - Capital
capex: "capital"
investments: "capital"
construction: "capital"
building: "capital"
# German - Capital
kapital: "capital"
investitionen: "capital"
# =============================================================================
# TEMPLATE DEFINITIONS
# =============================================================================
templates:
# ---------------------------------------------------------------------------
# Template 1: List institutions by type and location (city)
# ---------------------------------------------------------------------------
list_institutions_by_type_city:
id: "list_institutions_by_type_city"
description: "List heritage institutions of a specific type in a city"
intent: ["geographic", "exploration"]
response_modes: ["table", "map"]
databases: ["oxigraph"] # Geographic query - Oxigraph has lat/lon coordinates
ui_template:
nl: "Gevonden: {{ result_count }} {{ institution_type_nl }} in {{ city }}."
en: "Found: {{ result_count }} {{ institution_type_en }} in {{ city }}."
question_patterns:
# Dutch - formal
- "Welke {institution_type_nl} zijn er in {city}?"
- "Welke {institution_type_nl} heeft {city}?"
- "Wat zijn de {institution_type_nl} in {city}?"
- "Geef me de {institution_type_nl} in {city}"
- "Toon {institution_type_nl} in {city}"
- "{institution_type_nl} in {city}"
# Dutch - conversational
- "Wat voor {institution_type_nl} hebben ze in {city}?"
- "Wat voor {institution_type_nl} zijn er in {city}?"
- "Wat voor {institution_type_nl} heeft {city}?"
- "Geef een overzicht van {institution_type_nl} in {city}"
- "Geef een overzicht van de {institution_type_nl} in {city}"
- "Geef een overzicht van alle {institution_type_nl} in {city}"
- "Ik zoek {institution_type_nl} in {city}"
- "Zijn er {institution_type_nl} in {city}?"
- "Heeft {city} {institution_type_nl}?"
- "Ken je {institution_type_nl} in {city}?"
- "Welke {institution_type_nl} kan ik vinden in {city}?"
- "Noem {institution_type_nl} in {city}"
- "Lijst van {institution_type_nl} in {city}"
# English
- "What {institution_type_en} are in {city}?"
- "Which {institution_type_en} are there in {city}?"
- "List {institution_type_en} in {city}"
- "Show me {institution_type_en} in {city}"
- "{institution_type_en} in {city}"
- "What kind of {institution_type_en} are in {city}?"
- "Give me an overview of {institution_type_en} in {city}"
- "I'm looking for {institution_type_en} in {city}"
- "Are there {institution_type_en} in {city}?"
# German
- "Welche {institution_type_de} gibt es in {city}?"
- "Welche {institution_type_de} hat {city}?"
- "Was für {institution_type_de} gibt es in {city}?"
- "Gib mir eine Übersicht der {institution_type_de} in {city}"
slots:
institution_type:
type: institution_type
required: true
examples: ["musea", "archieven", "bibliotheken", "museums", "archives"]
city:
type: city
required: true
examples: ["Amsterdam", "Den Haag", "Rotterdam", "Utrecht"]
sparql_template: |
{{ prefixes }}
SELECT DISTINCT ?institution ?name ?website ?lat ?lon WHERE {
?institution a hcc:Custodian ;
hc:institutionType "{{ institution_type }}" ;
hc:settlementName "{{ city }}" ;
schema:name ?name .
OPTIONAL { ?institution foaf:homepage ?website }
OPTIONAL {
?institution schema:location ?loc .
?loc geo:lat ?lat ;
geo:long ?lon .
}
}
ORDER BY ?name
{% if limit %}LIMIT {{ limit }}{% endif %}
examples:
- question: "Welke musea zijn er in Amsterdam?"
slots: {institution_type: "M", city: "Amsterdam"}
- question: "What archives are in The Hague?"
slots: {institution_type: "A", city: "Den Haag"}
# ---------------------------------------------------------------------------
# Template 2: List institutions by type and province/region
# ---------------------------------------------------------------------------
list_institutions_by_type_region:
id: "list_institutions_by_type_region"
description: "List heritage institutions of a specific type in a province/region"
intent: ["geographic", "exploration"]
response_modes: ["table", "map"]
databases: ["oxigraph"] # Geographic query - Oxigraph has lat/lon coordinates
ui_template:
nl: "Gevonden: {{ result_count }} {{ institution_type_nl }} in {{ region }}."
en: "Found: {{ result_count }} {{ institution_type_en }} in {{ region }}."
question_patterns:
# Dutch - formal
- "Welke {institution_type_nl} zijn er in {region}?"
- "Hoeveel {institution_type_nl} heeft {region}?"
- "{institution_type_nl} in {region}"
- "Alle {institution_type_nl} in de provincie {region}"
# Dutch - conversational
- "Wat voor {institution_type_nl} hebben ze in {region}?"
- "Wat voor {institution_type_nl} zijn er in {region}?"
- "Geef een overzicht van {institution_type_nl} in {region}"
- "Geef een overzicht van de {institution_type_nl} in {region}"
- "Geef een overzicht van alle {institution_type_nl} in {region}"
- "Ik zoek {institution_type_nl} in {region}"
- "Zijn er {institution_type_nl} in {region}?"
- "Ken je {institution_type_nl} in {region}?"
- "Welke {institution_type_nl} kan ik vinden in {region}?"
- "Noem {institution_type_nl} in {region}"
- "Lijst van {institution_type_nl} in {region}"
- "{institution_type_nl} in de provincie {region}"
# English
- "What {institution_type_en} are in {region}?"
- "Which {institution_type_en} are there in {region}?"
- "{institution_type_en} in {region}"
- "Give me an overview of {institution_type_en} in {region}"
- "I'm looking for {institution_type_en} in {region}"
slots:
institution_type:
type: institution_type
required: true
region:
type: subregion
required: true
examples: ["Noord-Holland", "Gelderland", "Limburg", "Bavaria", "Flanders"]
sparql_template: |
{{ prefixes }}
SELECT DISTINCT ?institution ?name ?city ?lat ?lon WHERE {
?institution a hcc:Custodian ;
hc:institutionType "{{ institution_type }}" ;
hc:subregionCode "{{ region }}" ;
schema:name ?name .
OPTIONAL { ?institution hc:settlementName ?city }
OPTIONAL {
?institution schema:location ?loc .
?loc geo:lat ?lat ;
geo:long ?lon .
}
}
ORDER BY ?name
{% if limit %}LIMIT {{ limit }}{% endif %}
# ---------------------------------------------------------------------------
# Template 3: List institutions by type and country
# ---------------------------------------------------------------------------
list_institutions_by_type_country:
id: "list_institutions_by_type_country"
description: "List heritage institutions of a specific type in a country"
intent: ["geographic", "exploration"]
response_modes: ["table", "map"]
databases: ["oxigraph"] # Geographic query - Oxigraph has lat/lon coordinates
ui_template:
nl: "Gevonden: {{ result_count }} {{ institution_type_nl }} in {{ country }}."
en: "Found: {{ result_count }} {{ institution_type_en }} in {{ country }}."
question_patterns:
# Dutch
- "Welke {institution_type_nl} zijn er in {country}?"
- "Alle {institution_type_nl} in {country}"
- "{institution_type_nl} in {country}"
- "Geef alle {institution_type_nl} in {country}"
- "Toon {institution_type_nl} in {country}"
# English
- "What {institution_type_en} are in {country}?"
- "List all {institution_type_en} in {country}"
- "List {institution_type_en} in {country}"
- "Show {institution_type_en} in {country}"
- "Show all {institution_type_en} in {country}"
- "{institution_type_en} in {country}"
slots:
institution_type:
type: institution_type
required: true
country:
type: country
required: true
examples: ["Nederland", "Belgium", "Germany", "France"]
sparql_template: |
{{ prefixes }}
SELECT DISTINCT ?institution ?name ?city ?lat ?lon WHERE {
?institution a hcc:Custodian ;
hc:institutionType "{{ institution_type }}" ;
hc:countryCode "{{ country }}" ;
schema:name ?name .
OPTIONAL { ?institution hc:settlementName ?city }
OPTIONAL {
?institution schema:location ?loc .
?loc geo:lat ?lat ;
geo:long ?lon .
}
}
ORDER BY ?name
{% if limit %}LIMIT {{ limit }}{% endif %}
# ---------------------------------------------------------------------------
# Template 4: Count institutions by type and location
# ---------------------------------------------------------------------------
count_institutions_by_type_location:
id: "count_institutions_by_type_location"
description: "Count heritage institutions of a specific type in a location"
intent: ["statistical"]
response_modes: ["count"]
databases: ["oxigraph"] # Count query - Oxigraph only, skip vector search
ui_template:
nl: "Er zijn {{ count }} {{ institution_type_nl }} in {{ location }}."
en: "There are {{ count }} {{ institution_type_en }} in {{ location }}."
question_patterns:
# Dutch - formal
- "Hoeveel {institution_type_nl} zijn er in {location}?"
- "Hoeveel {institution_type_nl} heeft {location}?"
- "Aantal {institution_type_nl} in {location}"
- "Tel de {institution_type_nl} in {location}"
# Dutch - conversational
- "Hoeveel {institution_type_nl} telt {location}?"
- "Wat is het aantal {institution_type_nl} in {location}?"
- "Hoeveel {institution_type_nl} kan ik vinden in {location}?"
- "Kun je tellen hoeveel {institution_type_nl} er in {location} zijn?"
- "Hoeveel {institution_type_nl} zitten er in {location}?"
# English
- "How many {institution_type_en} are in {location}?"
- "How many {institution_type_en} does {location} have?"
- "Count of {institution_type_en} in {location}"
- "Number of {institution_type_en} in {location}"
- "What's the number of {institution_type_en} in {location}?"
- "Can you count {institution_type_en} in {location}?"
# German
- "Wie viele {institution_type_de} gibt es in {location}?"
- "Wie viele {institution_type_de} hat {location}?"
- "Anzahl der {institution_type_de} in {location}"
slots:
institution_type:
type: institution_type
required: true
location:
type: city
required: true
fallback_types: [subregion, country]
sparql_template: |
{{ prefixes }}
SELECT (COUNT(DISTINCT ?institution) AS ?count) WHERE {
?institution a hcc:Custodian ;
hc:institutionType "{{ institution_type }}" ;
hc:settlementName "{{ location }}" .
}
sparql_template_region: |
{{ prefixes }}
SELECT (COUNT(DISTINCT ?institution) AS ?count) WHERE {
?institution a hcc:Custodian ;
hc:institutionType "{{ institution_type }}" ;
hc:subregionCode "{{ location }}" .
}
sparql_template_country: |
{{ prefixes }}
SELECT (COUNT(DISTINCT ?institution) AS ?count) WHERE {
?institution a hcc:Custodian ;
hc:institutionType "{{ institution_type }}" ;
hc:countryCode "{{ location }}" .
}
# ---------------------------------------------------------------------------
# Template 5: Count all institutions by type (distribution)
# ---------------------------------------------------------------------------
count_institutions_by_type:
id: "count_institutions_by_type"
description: "Count institutions grouped by type"
intent: ["statistical"]
response_modes: ["count", "chart"]
databases: ["oxigraph"] # Aggregation query - Oxigraph only, skip vector search
ui_template:
nl: "Verdeling van instellingen per type:"
en: "Distribution of institutions by type:"
question_patterns:
# Dutch
- "Hoeveel instellingen per type?"
- "Verdeling van instellingen per type"
- "Hoeveel musea, archieven en bibliotheken zijn er?"
- "Statistieken per instellingstype"
# English
- "How many institutions per type?"
- "Distribution of institutions by type"
- "Statistics by institution type"
- "How many museums, archives and libraries are there?"
slots: {}
sparql_template: |
{{ prefixes }}
SELECT ?type (COUNT(DISTINCT ?institution) AS ?count) WHERE {
?institution a hcc:Custodian ;
hc:institutionType ?type .
}
GROUP BY ?type
ORDER BY DESC(?count)
# ---------------------------------------------------------------------------
# Template 6: Find institution by name
# ---------------------------------------------------------------------------
find_institution_by_name:
id: "find_institution_by_name"
description: "Find a specific institution by name"
intent: ["entity_lookup"]
response_modes: ["prose", "table"]
question_patterns:
# Dutch - formal
- "Waar is {institution_name}?"
- "Informatie over {institution_name}"
- "Gegevens van {institution_name}"
- "Wat is {institution_name}?"
- "Zoek {institution_name}"
# Dutch - conversational
- "Geef informatie over {institution_name}"
- "Geef me informatie over {institution_name}"
- "Vertel me over {institution_name}"
- "Wat weet je over {institution_name}?"
- "Ken je {institution_name}?"
- "Wat kun je vertellen over {institution_name}?"
- "Ik zoek informatie over {institution_name}"
- "Ik wil meer weten over {institution_name}"
- "Details over {institution_name}"
- "Geef details over {institution_name}"
# English
- "Where is {institution_name}?"
- "Information about {institution_name}"
- "What is {institution_name}?"
- "Find {institution_name}"
- "Tell me about {institution_name}"
- "Give me information about {institution_name}"
- "What do you know about {institution_name}?"
- "I'm looking for information about {institution_name}"
# German
- "Wo ist {institution_name}?"
- "Informationen über {institution_name}"
- "Was ist {institution_name}?"
- "Erzähl mir über {institution_name}"
slots:
institution_name:
type: institution_name
required: true
examples: ["Rijksmuseum", "Nationaal Archief", "Koninklijke Bibliotheek"]
sparql_template: |
{{ prefixes }}
SELECT ?institution ?name ?type ?city ?country ?website ?description WHERE {
?institution a hcc:Custodian ;
schema:name ?name .
FILTER(CONTAINS(LCASE(STR(?name)), LCASE("{{ institution_name }}")))
OPTIONAL { ?institution hc:institutionType ?type }
OPTIONAL { ?institution hc:settlementName ?city }
OPTIONAL { ?institution schema:addressCountry ?country }
OPTIONAL { ?institution foaf:homepage ?website }
OPTIONAL { ?institution schema:description ?description }
}
LIMIT 10
# ---------------------------------------------------------------------------
# Template 7: List institutions in city (all types)
# ---------------------------------------------------------------------------
list_all_institutions_in_city:
id: "list_all_institutions_in_city"
description: "List all heritage institutions in a city"
intent: ["geographic", "exploration"]
response_modes: ["table", "map"]
databases: ["oxigraph"] # Geographic query - Oxigraph has lat/lon coordinates
ui_template:
nl: "Gevonden: {{ result_count }} erfgoedinstellingen in {{ city }}."
en: "Found: {{ result_count }} heritage institutions in {{ city }}."
question_patterns:
# Dutch
- "Welke erfgoedinstellingen zijn er in {city}?"
- "Alle instellingen in {city}"
- "Erfgoed in {city}"
- "Wat is er te zien in {city}?"
- "Culturele instellingen in {city}"
# English
- "What heritage institutions are in {city}?"
- "All institutions in {city}"
- "Heritage in {city}"
- "What is there to see in {city}?"
- "Cultural institutions in {city}"
slots:
city:
type: city
required: true
sparql_template: |
{{ prefixes }}
SELECT ?institution ?name ?type ?website ?lat ?lon WHERE {
?institution a hcc:Custodian ;
hc:settlementName "{{ city }}" ;
schema:name ?name .
OPTIONAL { ?institution hc:institutionType ?type }
OPTIONAL { ?institution foaf:homepage ?website }
OPTIONAL {
?institution schema:location ?loc .
?loc geo:lat ?lat ;
geo:long ?lon .
}
}
ORDER BY ?type ?name
{% if limit %}LIMIT {{ limit }}{% endif %}
# ---------------------------------------------------------------------------
# Template 8: Find oldest/youngest institutions
# ---------------------------------------------------------------------------
find_institutions_by_founding_date:
id: "find_institutions_by_founding_date"
description: "Find oldest or most recently founded institutions"
intent: ["temporal", "exploration"]
response_modes: ["table"]
databases: ["oxigraph"] # Temporal query - Oxigraph only, skip vector search
ui_template:
nl: "Gevonden: {{ result_count }} instellingen gesorteerd op oprichtingsdatum."
en: "Found: {{ result_count }} institutions sorted by founding date."
question_patterns:
# Dutch
- "Wat zijn de oudste {institution_type_nl}?"
- "Wat is het oudste {institution_type_nl}?"
- "Oudste {institution_type_nl} in {location}"
- "Wanneer is {institution_name} opgericht?"
- "Wat zijn de nieuwste {institution_type_nl}?"
- "Recent opgerichte {institution_type_nl}"
# English
- "What are the oldest {institution_type_en}?"
- "What is the oldest {institution_type_en}?"
- "Oldest {institution_type_en} in {location}"
- "When was {institution_name} founded?"
- "What are the newest {institution_type_en}?"
- "Recently founded {institution_type_en}"
slots:
institution_type:
type: institution_type
required: false
location:
type: city
required: false
fallback_types: [subregion, country]
order:
type: string
default: "ASC"
valid_values: ["ASC", "DESC"]
sparql_template: |
{{ prefixes }}
SELECT ?institution ?name ?founded ?city WHERE {
?institution a hcc:Custodian ;
schema:name ?name ;
schema:foundingDate ?founded .
{% if institution_type %}
?institution hc:institutionType "{{ institution_type }}" .
{% endif %}
{% if location %}
?institution hc:settlementName "{{ location }}" .
{% endif %}
OPTIONAL { ?institution hc:settlementName ?city }
}
ORDER BY {{ order }}(?founded)
LIMIT {{ limit | default(10) }}
sparql_template_region: |
{{ prefixes }}
SELECT ?institution ?name ?founded ?city WHERE {
?institution a hcc:Custodian ;
schema:name ?name ;
schema:foundingDate ?founded ;
hc:subregionCode "{{ location }}" .
{% if institution_type %}
?institution hc:institutionType "{{ institution_type }}" .
{% endif %}
OPTIONAL { ?institution hc:settlementName ?city }
}
ORDER BY {{ order }}(?founded)
LIMIT {{ limit | default(10) }}
sparql_template_country: |
{{ prefixes }}
SELECT ?institution ?name ?founded ?city WHERE {
?institution a hcc:Custodian ;
schema:name ?name ;
schema:foundingDate ?founded ;
hc:countryCode "{{ location }}" .
{% if institution_type %}
?institution hc:institutionType "{{ institution_type }}" .
{% endif %}
OPTIONAL { ?institution hc:settlementName ?city }
}
ORDER BY {{ order }}(?founded)
LIMIT {{ limit | default(10) }}
# ---------------------------------------------------------------------------
# Template 9: Find institutions with specific identifier (ISIL, etc.)
# ---------------------------------------------------------------------------
find_institution_by_identifier:
id: "find_institution_by_identifier"
description: "Find institution by ISIL, GHCID, or other identifier"
intent: ["entity_lookup"]
response_modes: ["prose", "table"]
question_patterns:
- "Welke instelling heeft ISIL {identifier}?"
- "Zoek ISIL {identifier}"
- "GHCID {identifier}"
- "Institution with ISIL {identifier}"
- "Find ISIL {identifier}"
slots:
identifier:
type: string
required: true
examples: ["NL-AmRMA", "NL-HaNA", "DE-1"]
identifier_type:
type: string
default: "isil"
valid_values: ["isil", "ghcid", "wikidata"]
sparql_template_isil: |
{{ prefixes }}
SELECT ?institution ?name ?city ?country ?website WHERE {
?institution a hcc:Custodian ;
hc:isil "{{ identifier }}" ;
schema:name ?name .
OPTIONAL { ?institution hc:settlementName ?city }
OPTIONAL { ?institution schema:addressCountry ?country }
OPTIONAL { ?institution foaf:homepage ?website }
}
sparql_template_ghcid: |
{{ prefixes }}
SELECT ?institution ?name ?city ?country ?website WHERE {
?institution a hcc:Custodian ;
hc:ghcid "{{ identifier }}" ;
schema:name ?name .
OPTIONAL { ?institution hc:settlementName ?city }
OPTIONAL { ?institution schema:addressCountry ?country }
OPTIONAL { ?institution foaf:homepage ?website }
}
# ---------------------------------------------------------------------------
# Template 10: Compare institutions in different locations
# ---------------------------------------------------------------------------
compare_locations:
id: "compare_locations"
description: "Compare number of institutions between locations"
intent: ["comparative", "statistical"]
response_modes: ["table", "chart"]
databases: ["oxigraph"] # Comparative query - Oxigraph only, skip vector search
ui_template:
nl: "Vergelijking van {{ location1 }} en {{ location2 }}:"
en: "Comparison of {{ location1 }} and {{ location2 }}:"
question_patterns:
# Dutch
- "Vergelijk {location1} en {location2}"
- "Hoeveel meer {institution_type_nl} heeft {location1} dan {location2}?"
- "Verschil tussen {location1} en {location2}"
- "{location1} versus {location2}"
# English
- "Compare {location1} and {location2}"
- "How many more {institution_type_en} does {location1} have than {location2}?"
- "Difference between {location1} and {location2}"
- "{location1} vs {location2}"
slots:
location1:
type: city
required: true
fallback_types: [subregion, country]
location2:
type: city
required: true
fallback_types: [subregion, country]
institution_type:
type: institution_type
required: false
sparql_template: |
{{ prefixes }}
SELECT ?location (COUNT(DISTINCT ?institution) AS ?count) WHERE {
VALUES ?location { "{{ location1 }}" "{{ location2 }}" }
?institution a hcc:Custodian ;
hc:settlementName ?location .
{% if institution_type %}
?institution hc:institutionType "{{ institution_type }}" .
{% endif %}
}
GROUP BY ?location
sparql_template_region: |
{{ prefixes }}
SELECT ?region (COUNT(DISTINCT ?institution) AS ?count) WHERE {
VALUES ?region { "{{ location1 }}" "{{ location2 }}" }
?institution a hcc:Custodian ;
hc:subregionCode ?region .
{% if institution_type %}
?institution hc:institutionType "{{ institution_type }}" .
{% endif %}
}
GROUP BY ?region
sparql_template_country: |
{{ prefixes }}
SELECT ?country (COUNT(DISTINCT ?institution) AS ?count) WHERE {
VALUES ?country { "{{ location1 }}" "{{ location2 }}" }
?institution a hcc:Custodian ;
hc:countryCode ?country .
{% if institution_type %}
?institution hc:institutionType "{{ institution_type }}" .
{% endif %}
}
GROUP BY ?country
# ---------------------------------------------------------------------------
# Template 11: Find custodians by budget threshold
# ---------------------------------------------------------------------------
find_custodians_by_budget_threshold:
id: "find_custodians_by_budget_threshold"
description: "Find custodians with budget/expense category above or below a threshold"
intent: ["financial", "exploration"]
response_modes: ["table"]
databases: ["oxigraph"] # Financial query - Oxigraph only, skip vector search
ui_template:
nl: "Gevonden: {{ result_count }} instellingen."
en: "Found: {{ result_count }} institutions."
question_patterns:
# Dutch - Budget (planned) - Standard patterns
- "Welke instellingen besteden meer dan {amount} aan {budget_category}?"
- "Welke instellingen geven meer dan {amount} uit aan {budget_category}?"
- "Welke instellingen hebben een {budget_category}budget van meer dan {amount}?"
- "Welke {institution_type_nl} besteden meer dan {amount} aan {budget_category}?"
- "Instellingen met {budget_category} boven {amount}"
- "Wie geeft meer dan {amount} uit aan {budget_category}?"
# Dutch - Conversational (NEW)
- "Ik zoek {institution_type_nl} met een hoog {budget_category}budget"
- "Ik zoek {institution_type_nl} met een {budget_category}budget boven {amount}"
- "Ik zoek instellingen met een hoog {budget_category}budget"
- "Geef mij een lijst van {institution_type_nl} met een {budget_category}budget boven {amount}"
- "Geef mij een lijst van {institution_type_nl} met een {budget_category}budget boven {amount} euro"
- "Ken je {institution_type_nl} met een hoog {budget_category}budget?"
- "Ken je instellingen die veel uitgeven aan {budget_category}?"
- "Waar vind ik {institution_type_nl} met een groot {budget_category}budget?"
- "{institution_type_nl} met hoge {budget_category}uitgaven"
- "{institution_type_nl} die veel investeren in {budget_category}"
# Dutch - Alternative phrasings (existence/list patterns)
- "Zijn er instellingen die meer dan {amount} uitgeven aan {budget_category}?"
- "Zijn er {institution_type_nl} die meer dan {amount} uitgeven aan {budget_category}?"
- "Zijn er {institution_type_nl} die meer uitgeven dan {amount} aan {budget_category}?"
- "Geef mij instellingen met meer dan {amount} aan {budget_category}"
- "Geef een lijst van instellingen met {budget_category} boven {amount}"
- "Toon instellingen die meer dan {amount} uitgeven aan {budget_category}"
- "Toon alle instellingen die meer dan {amount} uitgeven aan {budget_category}"
- "Welke organisaties besteden meer dan {amount} aan {budget_category}?"
- "Welke organisaties geven meer dan {amount} uit aan {budget_category}?"
# Dutch - With "euro" explicit
- "Welke instellingen geven meer dan {amount} euro uit aan {budget_category}?"
- "Welke instellingen besteden meer dan {amount} euro aan {budget_category}?"
- "Instellingen met een {budget_category}budget van meer dan {amount} euro"
# Dutch - Budget with year
- "Welke instellingen besteden meer dan {amount} aan {budget_category} in {year}?"
- "Hoeveel instellingen geven meer dan {amount} uit aan {budget_category} in {year}?"
# Dutch - Less than
- "Welke instellingen besteden minder dan {amount} aan {budget_category}?"
- "Instellingen met {budget_category} onder {amount}"
- "Zijn er instellingen die minder dan {amount} uitgeven aan {budget_category}?"
- "Toon instellingen die minder dan {amount} uitgeven aan {budget_category}"
# English - Budget (planned)
- "Which custodians spend more than {amount} on {budget_category}?"
- "Which institutions have a {budget_category} budget over {amount}?"
- "Which {institution_type_en} spend more than {amount} on {budget_category}?"
- "Institutions with {budget_category} above {amount}"
- "Who spends more than {amount} on {budget_category}?"
# English - Conversational (NEW)
- "I'm looking for {institution_type_en} with a high {budget_category} budget"
- "I'm looking for {institution_type_en} with a {budget_category} budget over {amount}"
- "I'm looking for institutions with a high {budget_category} budget"
- "Give me a list of {institution_type_en} with a {budget_category} budget over {amount}"
- "Do you know {institution_type_en} with a high {budget_category} budget?"
- "{institution_type_en} with high {budget_category} spending"
- "{institution_type_en} that invest heavily in {budget_category}"
# English - Alternative phrasings
- "Are there institutions that spend more than {amount} on {budget_category}?"
- "Are there {institution_type_en} that spend more than {amount} on {budget_category}?"
- "Show me institutions with {budget_category} over {amount}"
- "List institutions spending more than {amount} on {budget_category}"
- "Give me institutions with {budget_category} budget above {amount}"
- "Which organizations spend more than {amount} on {budget_category}?"
# English - Budget with year
- "Which custodians spend more than {amount} on {budget_category} in {year}?"
- "How many institutions spend more than {amount} on {budget_category} in {year}?"
# English - Less than
- "Which custodians spend less than {amount} on {budget_category}?"
- "Institutions with {budget_category} under {amount}"
- "Are there institutions that spend less than {amount} on {budget_category}?"
# German
- "Welche Institutionen geben mehr als {amount} für {budget_category} aus?"
- "Welche Institutionen haben ein {budget_category}budget über {amount}?"
- "Gibt es Institutionen die mehr als {amount} für {budget_category} ausgeben?"
- "Ich suche {institution_type_de} mit einem hohen {budget_category}budget"
slots:
budget_category:
type: budget_category
required: true
examples: ["innovation", "digitization", "preservation", "personnel", "acquisition"]
amount:
type: decimal
required: true
examples: ["5000", "10000", "50000", "100000"]
year:
type: integer
required: false
examples: ["2024", "2025"]
comparison:
type: string
default: ">"
valid_values: [">", "<", ">=", "<=", "="]
institution_type:
type: institution_type
required: false
source:
type: string
default: "budget"
valid_values: ["budget", "actuals"]
description: "Whether to query Budget (planned) or FinancialStatement (actuals)"
sparql_template: |
{{ prefixes }}
PREFIX frapo: <http://purl.org/cerif/frapo/>
SELECT DISTINCT ?institution ?name ?budget_amount ?fiscal_year WHERE {
?institution a hcc:Custodian ;
schema:name ?name .
?budget a hc:class/Budget ;
hc:refers_to_custodian ?institution ;
hc:{{ budget_category }}_budget ?budget_amount .
{% if year %}
?budget hc:fiscal_year_start ?fy_start .
FILTER(YEAR(?fy_start) = {{ year }})
{% endif %}
FILTER(?budget_amount {{ comparison | default(">") }} {{ amount }})
{% if institution_type %}
?institution hc:institutionType "{{ institution_type }}" .
{% endif %}
OPTIONAL {
?budget hc:fiscal_year_start ?fy_start .
BIND(YEAR(?fy_start) AS ?fiscal_year)
}
}
ORDER BY DESC(?budget_amount)
{% if limit %}LIMIT {{ limit }}{% endif %}
sparql_template_actuals: |
{{ prefixes }}
PREFIX frapo: <http://purl.org/cerif/frapo/>
SELECT DISTINCT ?institution ?name ?expense_amount ?reporting_year WHERE {
?institution a hcc:Custodian ;
schema:name ?name .
?statement a hc:class/FinancialStatement ;
hc:refers_to_custodian ?institution ;
hc:{{ budget_category }}_expenses ?expense_amount .
{% if year %}
?statement hc:reporting_period_start ?rp_start .
FILTER(YEAR(?rp_start) = {{ year }})
{% endif %}
FILTER(?expense_amount {{ comparison | default(">") }} {{ amount }})
{% if institution_type %}
?institution hc:institutionType "{{ institution_type }}" .
{% endif %}
OPTIONAL {
?statement hc:reporting_period_start ?rp_start .
BIND(YEAR(?rp_start) AS ?reporting_year)
}
}
ORDER BY DESC(?expense_amount)
{% if limit %}LIMIT {{ limit }}{% endif %}
examples:
- question: "Welke instellingen besteden meer dan 5000 euro aan innovatie in 2024?"
slots: {budget_category: "innovation", amount: 5000, year: 2024, comparison: ">"}
- question: "Which custodians spend more than 10000 on digitization?"
slots: {budget_category: "digitization", amount: 10000, comparison: ">"}
- question: "Institutions with preservation budget above 50000"
slots: {budget_category: "preservation", amount: 50000, comparison: ">"}
- question: "Which museums spend less than 1000 on innovation?"
slots: {budget_category: "innovation", amount: 1000, comparison: "<", institution_type: "M"}
# ---------------------------------------------------------------------------
# TEMPORAL QUERY TEMPLATES
# ---------------------------------------------------------------------------
# These templates handle time-based queries about heritage institutions:
# - Historical state at point in time
# - Institution timelines and history
# - Organizational change events (mergers, closures, foundings)
# - Finding oldest/newest institutions
#
# Reference: docs/plan/external_design_patterns/04_temporal_semantic_hypergraph.md
# Template: Point-in-time institution state
point_in_time_state:
id: "point_in_time_state"
description: "Get institution state at a specific point in time"
intent: ["temporal", "entity_lookup"]
response_modes: ["prose", "table"]
question_patterns:
# Dutch
- "Wat was de status van {institution_name} in {year}?"
- "Hoe zag {institution_name} eruit in {year}?"
- "Bestond {institution_name} al in {year}?"
- "Wie beheerde {institution_name} in {year}?"
- "{institution_name} in {year}"
# English
- "What was the status of {institution_name} in {year}?"
- "How was {institution_name} structured in {year}?"
- "Did {institution_name} exist in {year}?"
- "State of {institution_name} before {event}?"
- "{institution_name} in {year}"
slots:
institution_name:
type: string
required: false
description: "Institution name for lookup (alternative to ghcid)"
ghcid:
type: string
required: false
description: "Global Heritage Custodian Identifier"
query_date:
type: date
required: true
description: "Point in time to query (ISO format or year)"
sparql_template: |
{{ prefixes }}
SELECT ?ghcid ?name ?type ?city ?validFrom ?validTo WHERE {
?s a crm:E39_Actor ;
hc:ghcid ?ghcid ;
skos:prefLabel ?name ;
hc:institutionType ?type .
OPTIONAL { ?s hc:validFrom ?validFrom }
OPTIONAL { ?s schema:addressLocality ?city }
OPTIONAL { ?s hc:validTo ?validTo }
{% if ghcid %}
FILTER(?ghcid = "{{ ghcid }}")
{% elif institution_name %}
FILTER(CONTAINS(LCASE(?name), "{{ institution_name | lower }}"))
{% endif %}
# Temporal filter: valid at query_date
FILTER(!BOUND(?validFrom) || ?validFrom <= "{{ query_date }}"^^xsd:date)
FILTER(!BOUND(?validTo) || ?validTo > "{{ query_date }}"^^xsd:date)
}
ORDER BY ?validFrom
{% if limit %}LIMIT {{ limit }}{% else %}LIMIT 10{% endif %}
examples:
- question: "Wat was de status van Rijksmuseum in 1990?"
slots: {institution_name: "Rijksmuseum", query_date: "1990-01-01"}
- question: "How was Noord-Hollands Archief structured in 1995?"
slots: {institution_name: "Noord-Hollands Archief", query_date: "1995-01-01"}
# Template: Institution timeline/history
institution_timeline:
id: "institution_timeline"
description: "Get complete history and timeline of changes for an institution"
intent: ["temporal", "entity_lookup"]
response_modes: ["prose", "chart"]
question_patterns:
# Dutch
- "Geschiedenis van {institution_name}"
- "Wat is de geschiedenis van {institution_name}?"
- "Tijdlijn van {institution_name}"
- "Wat is er gebeurd met {institution_name}?"
- "Vertel me over de geschiedenis van {institution_name}"
- "Hoe is {institution_name} veranderd door de jaren?"
# English
- "History of {institution_name}"
- "Timeline of {institution_name}"
- "Timeline of changes for {institution_name}"
- "What happened to {institution_name}?"
- "Tell me about the history of {institution_name}"
- "How has {institution_name} changed over the years?"
slots:
institution_name:
type: string
required: false
ghcid:
type: string
required: false
sparql_template: |
{{ prefixes }}
SELECT ?ghcid ?name ?validFrom ?validTo ?changeType ?changeReason ?description WHERE {
?entry hc:ghcid ?ghcid ;
skos:prefLabel ?name .
OPTIONAL { ?entry hc:validFrom ?validFrom }
OPTIONAL { ?entry hc:validTo ?validTo }
OPTIONAL { ?entry hc:changeType ?changeType }
OPTIONAL { ?entry hc:changeReason ?changeReason }
OPTIONAL { ?entry schema:description ?description }
{% if ghcid %}
FILTER(?ghcid = "{{ ghcid }}")
{% elif institution_name %}
FILTER(CONTAINS(LCASE(?name), "{{ institution_name | lower }}"))
{% endif %}
}
ORDER BY ?validFrom
examples:
- question: "Geschiedenis van het Rijksmuseum"
slots: {institution_name: "Rijksmuseum"}
- question: "What happened to Noord-Hollands Archief?"
slots: {institution_name: "Noord-Hollands Archief"}
# Template: Organizational change events in time period
events_in_period:
id: "events_in_period"
description: "Find organizational change events in a time period"
intent: ["temporal", "statistical"]
response_modes: ["table", "chart"]
databases: ["oxigraph"] # Temporal event query - Oxigraph only, skip vector search
ui_template:
nl: "Gevonden: {{ result_count }} gebeurtenissen tussen {{ start_date }} en {{ end_date }}."
en: "Found: {{ result_count }} events between {{ start_date }} and {{ end_date }}."
question_patterns:
# Dutch
- "Welke fusies waren er tussen {start_year} en {end_year}?"
- "Welke {event_type_nl} waren er in {year}?"
- "Welke instellingen zijn gesloten in {year}?"
- "Welke archieven zijn gefuseerd na {year}?"
- "Nieuwe musea sinds {year}"
- "Sluitingen in {year}"
- "Fusies tussen {start_year} en {end_year}"
# English
- "Mergers between {start_year} and {end_year}"
- "What {event_type_en} happened in {year}?"
- "What institutions closed in {year}?"
- "Archives founded before {year}"
- "New museums since {year}"
- "Closures in {year}"
- "Mergers between {start_year} and {end_year}"
slots:
start_date:
type: date
required: true
description: "Start of time period (ISO format or year)"
end_date:
type: date
required: false
description: "End of time period (defaults to now)"
event_type:
type: string
required: false
valid_values: ["MERGER", "FOUNDING", "CLOSURE", "RELOCATION", "NAME_CHANGE", "SPLIT", "ACQUISITION"]
description: "Type of organizational change event"
institution_type:
type: institution_type
required: false
sparql_template: |
{{ prefixes }}
SELECT ?event ?eventType ?date ?actor1 ?actor1Name ?actor2 ?actor2Name ?description WHERE {
?event a hc:OrganizationalChangeEvent ;
hc:eventType ?eventType ;
hc:eventDate ?date .
OPTIONAL {
?event hc:affectedActor ?actor1 .
?actor1 skos:prefLabel ?actor1Name .
}
OPTIONAL {
?event hc:resultingActor ?actor2 .
?actor2 skos:prefLabel ?actor2Name .
}
OPTIONAL { ?event schema:description ?description }
FILTER(?date >= "{{ start_date }}"^^xsd:date)
{% if end_date %}
FILTER(?date <= "{{ end_date }}"^^xsd:date)
{% endif %}
{% if event_type %}
FILTER(?eventType = "{{ event_type }}")
{% endif %}
{% if institution_type %}
?actor1 hc:institutionType "{{ institution_type }}" .
{% endif %}
}
ORDER BY ?date
{% if limit %}LIMIT {{ limit }}{% else %}LIMIT 50{% endif %}
examples:
- question: "Welke fusies waren er tussen 2000 en 2010?"
slots: {start_date: "2000-01-01", end_date: "2010-12-31", event_type: "MERGER"}
- question: "What museums closed in 2020?"
slots: {start_date: "2020-01-01", end_date: "2020-12-31", event_type: "CLOSURE", institution_type: "M"}
- question: "Archives founded before 1900"
slots: {start_date: "1800-01-01", end_date: "1899-12-31", event_type: "FOUNDING", institution_type: "A"}
# Template: Find oldest/newest institutions
find_by_founding:
id: "find_by_founding"
description: "Find oldest or newest (most recently founded) institutions"
intent: ["temporal", "exploration"]
response_modes: ["table"]
databases: ["oxigraph"] # Temporal query - Oxigraph only, skip vector search
ui_template:
nl: "Gevonden: {{ result_count }} {{ institution_type_nl }} gesorteerd op oprichtingsdatum."
en: "Found: {{ result_count }} {{ institution_type_en }} sorted by founding date."
question_patterns:
# Dutch
- "Oudste {institution_type_nl} in {location}"
- "Oudste {institution_type_nl} van Nederland"
- "Nieuwste {institution_type_nl} in {location}"
- "Welk {institution_type_nl} is het oudste in {location}?"
- "Welk {institution_type_nl} is het nieuwste?"
- "Eerst opgerichte {institution_type_nl}"
- "Laatst opgerichte {institution_type_nl}"
- "{institution_type_nl} opgericht na {year}"
- "{institution_type_nl} opgericht voor {year}"
# English
- "Oldest {institution_type_en} in {location}"
- "Oldest {institution_type_en} in the Netherlands"
- "Newest {institution_type_en} opened after {year}"
- "Most recently founded {institution_type_en}"
- "Which {institution_type_en} is the oldest in {location}?"
- "First established {institution_type_en}"
- "{institution_type_en} founded after {year}"
- "{institution_type_en} founded before {year}"
slots:
institution_type:
type: institution_type
required: true
order:
type: string
required: false
valid_values: ["ASC", "DESC"]
default: "ASC"
description: "ASC for oldest first, DESC for newest first"
location:
type: city
required: false
description: "City or region to filter by"
country:
type: country
required: false
default: "NL"
founding_after:
type: date
required: false
founding_before:
type: date
required: false
sparql_template: |
{{ prefixes }}
SELECT ?institution ?name ?foundingDate ?city ?country WHERE {
?institution a hcc:Custodian ;
hc:institutionType "{{ institution_type }}" ;
schema:name ?name .
OPTIONAL { ?institution schema:foundingDate ?foundingDate }
OPTIONAL { ?institution hc:settlementName ?city }
OPTIONAL { ?institution hc:countryCode ?country }
# Must have founding date for ordering
FILTER(BOUND(?foundingDate))
{% if location %}
FILTER(CONTAINS(LCASE(?city), "{{ location | lower }}"))
{% endif %}
{% if country %}
FILTER(?country = "{{ country }}")
{% endif %}
{% if founding_after %}
FILTER(?foundingDate >= "{{ founding_after }}"^^xsd:date)
{% endif %}
{% if founding_before %}
FILTER(?foundingDate <= "{{ founding_before }}"^^xsd:date)
{% endif %}
}
ORDER BY {{ order | default('ASC') }}(?foundingDate)
{% if limit %}LIMIT {{ limit }}{% else %}LIMIT 10{% endif %}
examples:
- question: "Oudste musea in Amsterdam"
slots: {institution_type: "M", location: "Amsterdam", order: "ASC"}
- question: "Newest libraries in the Netherlands"
slots: {institution_type: "L", country: "NL", order: "DESC"}
- question: "Archives founded after 2000"
slots: {institution_type: "A", founding_after: "2000-01-01", order: "ASC"}
# Template: Institutions by founding decade
institutions_by_founding_decade:
id: "institutions_by_founding_decade"
description: "Count or list institutions by founding decade"
intent: ["temporal", "statistical"]
response_modes: ["table", "chart"]
databases: ["oxigraph"] # Temporal aggregation - Oxigraph only, skip vector search
ui_template:
nl: "Verdeling van {{ institution_type_nl }} per decennium:"
en: "Distribution of {{ institution_type_en }} by decade:"
question_patterns:
# Dutch
- "Hoeveel {institution_type_nl} zijn opgericht per decennium?"
- "{institution_type_nl} opgericht in de jaren {decade}"
- "Welke {institution_type_nl} zijn in de 19e eeuw opgericht?"
- "Verdeling van oprichtingsjaren voor {institution_type_nl}"
# English
- "How many {institution_type_en} were founded per decade?"
- "{institution_type_en} founded in the {decade}s"
- "Which {institution_type_en} were founded in the 19th century?"
- "Distribution of founding years for {institution_type_en}"
slots:
institution_type:
type: institution_type
required: true
decade:
type: integer
required: false
description: "Decade start year (e.g., 1990 for 1990s)"
century:
type: integer
required: false
description: "Century (e.g., 19 for 19th century)"
country:
type: country
required: false
sparql_template: |
{{ prefixes }}
SELECT ?decade (COUNT(?institution) AS ?count) WHERE {
?institution a hcc:Custodian ;
hc:institutionType "{{ institution_type }}" ;
schema:foundingDate ?foundingDate .
{% if country %}
?institution hc:countryCode "{{ country }}" .
{% endif %}
BIND(YEAR(?foundingDate) AS ?year)
BIND(FLOOR(?year / 10) * 10 AS ?decade)
{% if decade %}
FILTER(?decade = {{ decade }})
{% endif %}
{% if century %}
FILTER(?year >= {{ (century - 1) * 100 }} && ?year < {{ century * 100 }})
{% endif %}
}
GROUP BY ?decade
ORDER BY ?decade
# Alternative: list institutions in specific decade
sparql_template_list: |
{{ prefixes }}
SELECT ?institution ?name ?foundingDate WHERE {
?institution a hcc:Custodian ;
hc:institutionType "{{ institution_type }}" ;
schema:name ?name ;
schema:foundingDate ?foundingDate .
{% if country %}
?institution hc:countryCode "{{ country }}" .
{% endif %}
BIND(YEAR(?foundingDate) AS ?year)
{% if decade %}
FILTER(?year >= {{ decade }} && ?year < {{ decade + 10 }})
{% endif %}
{% if century %}
FILTER(?year >= {{ (century - 1) * 100 }} && ?year < {{ century * 100 }})
{% endif %}
}
ORDER BY ?foundingDate
{% if limit %}LIMIT {{ limit }}{% endif %}
examples:
- question: "Hoeveel musea zijn opgericht per decennium?"
slots: {institution_type: "M"}
- question: "Archives founded in the 1990s"
slots: {institution_type: "A", decade: 1990}
- question: "Libraries founded in the 19th century"
slots: {institution_type: "L", century: 19}
# =============================================================================
# FOLLOW-UP PATTERNS (Conversation Context Resolution)
# =============================================================================
# These patterns help ConversationContextResolver (DSPy) expand elliptical
# follow-up questions BEFORE the Fyke filter runs.
#
# CRITICAL: ConversationContextResolver runs FIRST, then Fyke operates on
# the RESOLVED question. This prevents false positives on short follow-ups.
#
# Example flow:
# Turn 1: "Welke archieven zijn er in Den Haag?" → lists archives
# Turn 2: "En in Enschede?" (raw input - would be caught by naive Fyke!)
# ↓ ConversationContextResolver
# "Welke archieven zijn er in Enschede?" (resolved - clearly relevant)
# ↓ FykeFilter
# PASS (relevant)
# ↓ TemplateClassifier
# list_institutions_by_type_city
follow_up_patterns:
location_swap:
description: "Same query type, different location"
patterns:
- "En in {new_location}?"
- "En {new_location}?"
- "What about {new_location}?"
- "And in {new_location}?"
- "Hoe zit het met {new_location}?"
- "In {new_location}?"
- "{new_location}?"
slot_inheritance:
- institution_type
resolution_strategy: "inherit_template_swap_location"
type_swap:
description: "Same location, different institution type"
patterns:
- "En de {new_type}?"
- "Hoe zit het met {new_type}?"
- "What about {new_type}?"
- "And {new_type}?"
- "En {new_type}?"
- "{new_type}?"
slot_inheritance:
- city
- region
- country
resolution_strategy: "inherit_location_swap_type"
count_from_list:
description: "Count after listing"
patterns:
- "Hoeveel zijn dat?"
- "How many is that?"
- "How many are there?"
- "Hoeveel?"
- "How many?"
- "Tel ze"
- "Count them"
transforms_to: "count_institutions_by_type_location"
slot_inheritance:
- institution_type
- city
- region
- country
resolution_strategy: "convert_list_to_count"
details_request:
description: "More details about specific result"
patterns:
- "Vertel me meer over {entity}"
- "Tell me more about {entity}"
- "Meer informatie over {entity}"
- "What about {entity}?"
- "More about {entity}"
- "Details over {entity}"
transforms_to: "find_institution_by_name"
resolution_strategy: "extract_entity_lookup"
ordinal_reference:
description: "Reference to result by position"
patterns:
- "De eerste"
- "De tweede"
- "De derde"
- "The first one"
- "The second one"
- "Number {n}"
- "Nummer {n}"
requires_previous_results: true
resolution_strategy: "resolve_ordinal_to_entity"
pronoun_reference:
description: "Reference using pronouns"
patterns:
- "Wat is hun website?"
- "What is their website?"
- "Waar zijn ze gevestigd?"
- "Where are they located?"
- "Wanneer zijn ze opgericht?"
- "When were they founded?"
requires_previous_results: true
resolution_strategy: "resolve_pronoun_to_entity"
# =============================================================================
# FYKE FILTER CONFIGURATION
# =============================================================================
# The Fyke filter catches irrelevant questions and returns a standard response.
#
# ⚠️ CRITICAL ORDERING:
# 1. ConversationContextResolver FIRST (expands follow-ups)
# 2. FykeFilter on RESOLVED question (not raw input!)
#
# This prevents false positives like:
# - "En in Enschede?" → resolved to "Welke archieven zijn er in Enschede?" → PASS
# - "Hoeveel?" → resolved to "Hoeveel archieven zijn er in Den Haag?" → PASS
fyke_filter:
# DSPy Signature for relevance classification
# Operates on RESOLVED question only!
dspy_signature:
inputs:
resolved_question: "The fully resolved question (after context resolution)"
conversation_summary: "Brief summary of conversation topic"
outputs:
is_relevant: "boolean - whether question is about heritage institutions"
confidence: "float 0-1 - confidence in classification"
reasoning: "Brief explanation of relevance decision"
# Hard-coded out-of-scope keywords (checked AFTER context resolution)
# These are terms that are NEVER relevant to heritage queries
out_of_scope_keywords:
- tandpasta
- toothpaste
- supermarkt
- supermarket
- restaurant
- hotel
- weer
- weather
- voetbal
- soccer
- football
- recept
- recipe
- vliegticket
- flight
- politiek
- politics
- bitcoin
- crypto
- dating
- tinder
# Categories that are out of scope
out_of_scope_categories:
- shopping
- travel_booking
- sports
- cooking
- entertainment
- personal_advice
- medical
- legal
- financial
# Keywords that indicate heritage relevance (boost confidence)
heritage_keywords:
- museum
- musea
- archief
- archieven
- bibliotheek
- bibliotheken
- galerie
- erfgoed
- heritage
- collectie
- collection
- tentoonstelling
- exhibition
- GLAM
- cultureel
- cultural
# Standard responses when question is out of scope
standard_response:
nl: |
Ik kan je helpen met vragen over erfgoedinstellingen zoals musea, archieven,
bibliotheken en galerijen in Nederland en daarbuiten. Stel gerust een vraag
over deze onderwerpen!
en: |
I can help you with questions about heritage institutions such as museums,
archives, libraries and galleries in the Netherlands and beyond. Feel free
to ask a question about these topics!
de: |
Ich kann Ihnen bei Fragen zu Kulturerbeinstitutionen wie Museen, Archiven,
Bibliotheken und Galerien in den Niederlanden und darüber hinaus helfen.
Stellen Sie gerne eine Frage zu diesen Themen!
fr: |
Je peux vous aider avec des questions sur les institutions patrimoniales
comme les musées, les archives, les bibliothèques et les galeries aux
Pays-Bas et au-delà. N'hésitez pas à poser une question sur ces sujets!