# 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: PREFIX hcc: PREFIX crm: PREFIX schema: PREFIX skos: PREFIX org: PREFIX foaf: PREFIX dcterms: PREFIX xsd: PREFIX wd: PREFIX geo: # 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: 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: 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!