Experiments - statnett/Talk2PowerSystem GitHub Wiki
Property resolving
https://github.com/statnett/Talk2PowerSystem_PM/issues/70
The properties' names in the ontology schema are overly-specific, prefixed by the domain class or super-class followed by a dot, for example cim:SvVoltage.TopologicalNode
. Properties suffixes (second part after the dot) are not globally unique. Check the results of this query
PREFIX onto: <http://www.ontotext.com/>
SELECT (COUNT(DISTINCT ?property) AS ?count) (GROUP_CONCAT(DISTINCT ?property; SEPARATOR="; ") AS ?properties) ?suffix
FROM onto:explicit
{
{
SELECT DISTINCT ?property {
{
[] ?property []
}
}
}
?property ?p ?o.
BIND (STRAFTER(STRAFTER(STR(?property), "#"), ".") AS ?suffix)
}
GROUP BY ?suffix
HAVING (?count >= 2)
ORDER BY DESC(?count)
We have 4 properties with suffix inService
:
cim:Equipment.inService
nc:RemedialActionScheme.inService
nc:StageTrigger.inService
cim:SvStatus.inService
but also
nc:Equipment.InServiceAction
cim:Equipment.normallyInService
which are close.
To evaluate the ability of the LLM to properly resolve which properties to use we've observed the generated SPARQL queries by gpt-4o-2024-11-20
for the following questions:
cim:Equipment.inService
vsnc:RemedialActionScheme.inService
vsnc:StageTrigger.inService
vscim:SvStatus.inService
vsnc:Equipment.InServiceAction
vscim:Equipment.normallyInService
- "List transformers that are normally in service" :heavy_check_mark:
SELECT ?transformer ?transformerName WHERE { ?transformer a cim:PowerTransformer ; cim:Equipment.normallyInService true ; cim:IdentifiedObject.name ?transformerName . }
- "List transformers that are in service" :heavy_check_mark:
SELECT ?transformer ?transformerName WHERE { ?transformer a cim:PowerTransformer ; cim:IdentifiedObject.name ?transformerName ; cim:Equipment.inService true . }
- "List transformers for which the state variable is in service" :heavy_check_mark:
SELECT ?transformer ?transformerName WHERE { ?transformer a cim:PowerTransformer ; cim:IdentifiedObject.name ?transformerName . ?stateVar a cim:SvStatus ; cim:SvStatus.ConductingEquipment ?transformer ; cim:SvStatus.inService true . }
- "5 busbar sections in service" :heavy_check_mark:
SELECT ?busbar ?busbarName WHERE { ?busbar a cim:BusbarSection ; cim:Equipment.inService true ; cim:IdentifiedObject.name ?busbarName . } LIMIT 5
cim:RegulatingCondEq.controlEnabled
vscim:TapChanger.controlEnabled
:- "List all synchronous machines for which the control is enabled" :heavy_check_mark:
SELECT ?machine ?machineName WHERE { ?machine a cim:SynchronousMachine ; cim:RegulatingCondEq.controlEnabled true ; cim:IdentifiedObject.name ?machineName . }
- "List all linear shunt compensators for which the control is disabled" :heavy_check_mark:
SELECT ?compensator ?compensatorName WHERE { ?compensator a cim:LinearShuntCompensator ; cim:RegulatingCondEq.controlEnabled false ; cim:IdentifiedObject.name ?compensatorName . }
- "List all ratio tap changers for which the control is enabled" :heavy_check_mark:
SELECT ?tapChanger ?tapChangerName WHERE { ?tapChanger a cim:RatioTapChanger ; cim:TapChanger.controlEnabled true ; cim:IdentifiedObject.name ?tapChangerName . }
cim:RatioTapChanger
is subclass ofcim:TapChanger
,cim:PowerSystemResource
andcim:IdentifiedObject
.cim:SynchronousMachine
is subclass ofcim:RotatingMachine
,cim:RegulatingCondEq
,cim:EnergyConnection
,cim:ConductingEquipment
,cim:Equipment
,cim:PowerSystemResource
andcim:IdentifiedObject
.cim:LinearShuntCompensator
is subclass ofcim:ShuntCompensator
,cim:RegulatingCondEq
,cim:EnergyConnection
,cim:ConductingEquipment
,cim:Equipment
,cim:PowerSystemResource
andcim:IdentifiedObject
. So we can also consider the questions:- "Give me equipment for which the control is enabled" :heavy_check_mark:
SELECT ?equipment ?equipmentName WHERE { ?equipment a cim:Equipment ; cim:RegulatingCondEq.controlEnabled true ; cim:IdentifiedObject.name ?equipmentName . }
- "Give me psrs for which the control is enabled" :x: wrong, because there is no union with
cim:TapChanger.controlEnabled
SELECT ?psr ?psrName WHERE { ?psr a cim:PowerSystemResource ; cim:RegulatingCondEq.controlEnabled true ; cim:IdentifiedObject.name ?psrName . }
cim:OperationalLimitSet.Terminal
,nc:PinTerminal.Terminal
,cim:RegulatingControl.Terminal
,cim:TransformerEnd.Terminal
,cim:SvPowerFlow.Terminal
:- "List state variables for power flow and their associated terminals" - the query is correct in the part with the property, what's wrong is that
cim:SvPowerFlow
is not a subclass ofcim:IdentifiedObject
and hence nocim:IdentifiedObject.name
:x:
SELECT ?stateVariable ?stateVariableName ?terminal ?terminalName WHERE { ?stateVariable a cim:SvPowerFlow ; cim:SvPowerFlow.Terminal ?terminal ; cim:IdentifiedObject.name ?stateVariableName . ?terminal cim:IdentifiedObject.name ?terminalName . }
- "List pin terminals and to which terminals they are linked." - the query is correct in the part with the property, what's wrong is that
nc:PinTerminal
and the linked terminals lackcim:IdentifiedObject.name
:x:
SELECT ?pinTerminal ?pinTerminalName ?linkedTerminal ?linkedTerminalName WHERE { ?pinTerminal a nc:PinTerminal ; cim:IdentifiedObject.name ?pinTerminalName ; nc:PinTerminal.Terminal ?linkedTerminal . ?linkedTerminal cim:IdentifiedObject.name ?linkedTerminalName . }
- "List power transformer ends and their associated terminals". At first, the generated query is wrong
because there is no predicateSELECT ?transformerEnd ?transformerEndName ?terminal ?terminalName WHERE { ?transformerEnd a cim:PowerTransformerEnd ; cim:PowerTransformerEnd.Terminal ?terminal ; cim:IdentifiedObject.name ?transformerEndName . ?terminal cim:IdentifiedObject.name ?terminalName . }
cim:PowerTransformerEnd.Terminal
. Since we do query validation, we feed back to the LLM the errorError: ValueError('The following IRIs are not used in the data stored in GraphDB: <https://cim.ucaiug.io/ns#PowerTransformerEnd.Terminal>') Please fix your mistakes.
The second time the LLM generates a correct query :heavy_check_mark:SELECT ?transformerEnd ?transformerEndName ?terminal ?terminalName WHERE { ?transformerEnd a cim:PowerTransformerEnd ; cim:TransformerEnd.Terminal ?terminal ; cim:IdentifiedObject.name ?transformerEndName . ?terminal cim:IdentifiedObject.name ?terminalName . }
- "List tap changer controls and their associated terminals" :heavy_check_mark:
SELECT ?tapChangerControl ?tapChangerControlName ?terminal ?terminalName WHERE { ?tapChangerControl a cim:TapChangerControl ; cim:RegulatingControl.Terminal ?terminal ; cim:IdentifiedObject.name ?tapChangerControlName . ?terminal cim:IdentifiedObject.name ?terminalName . }
- "List all operational limit sets and to which terminals they are linked." :heavy_check_mark:
SELECT ?limitSet ?limitSetName ?terminal ?terminalName WHERE { ?limitSet a cim:OperationalLimitSet ; cim:OperationalLimitSet.Terminal ?terminal ; cim:IdentifiedObject.name ?limitSetName . ?terminal cim:IdentifiedObject.name ?terminalName . }
cim:OperationalLimitSet
is subclass ofcim:IdentifiedObject
.nc:PinTerminal
is subclass ofnc:GateInputPin
,nc:FunctionInputVariable
andcim:IdentifiedObject
.cim:TapChangerControl
is subclass ofcim:RegulatingControl
,cim:PowerSystemResource
andcim:IdentifiedObject
.cim:PowerTransformerEnd
is subclass ofcim:TransformerEnd
andcim:IdentifiedObject
.cim:SvPowerFlow
is a root class, i.e. it's not a subclass ofcim:IdentifiedObject
. So we can also ask:- "Give me identified objects and to which terminals they are linked" :heavy_check_mark: in some sense it looks correct
SELECT ?identifiedObject ?identifiedObjectName ?terminal ?terminalName WHERE { ?identifiedObject a cim:IdentifiedObject; cim:IdentifiedObject.name ?identifiedObjectName . ?terminal a cim:Terminal; cim:Terminal.ConductingEquipment ?identifiedObject; cim:IdentifiedObject.name ?terminalName . }
- "Give me power system resources and to which terminals they are linked" :heavy_check_mark: in some sense it looks correct
SELECT ?resource ?resourceName ?terminal ?terminalName WHERE { ?resource a cim:PowerSystemResource; cim:IdentifiedObject.name ?resourceName . ?terminal a cim:Terminal; cim:Terminal.ConductingEquipment ?resource; cim:IdentifiedObject.name ?terminalName . }
- "Give me regulating controls and to which terminals they are linked" :heavy_check_mark:
SELECT ?regulatingControl ?regulatingControlName ?terminal ?terminalName WHERE { ?regulatingControl a cim:RegulatingControl ; cim:IdentifiedObject.name ?regulatingControlName ; cim:RegulatingControl.Terminal ?terminal . ?terminal cim:IdentifiedObject.name ?terminalName . }
- "Give me transformer ends and to which terminals they are linked". First query is
After feeding the errorSELECT ?transformerEnd ?transformerEndName ?terminal ?terminalName WHERE { ?transformerEnd a cim:PowerTransformerEnd ; cim:IdentifiedObject.name ?transformerEndName ; cim:PowerTransformerEnd.Terminal ?terminal . ?terminal cim:IdentifiedObject.name ?terminalName . }
Error: ValueError('The following IRIs are not used in the data stored in GraphDB: <https://cim.ucaiug.io/ns#PowerTransformerEnd.Terminal>')
the query is correct :heavy_check_mark:SELECT ?transformerEnd ?transformerEndName ?terminal ?terminalName WHERE { ?transformerEnd a cim:PowerTransformerEnd ; cim:IdentifiedObject.name ?transformerEndName ; cim:TransformerEnd.Terminal ?terminal . ?terminal cim:IdentifiedObject.name ?terminalName . }
- "List state variables for power flow and their associated terminals" - the query is correct in the part with the property, what's wrong is that
cim:SvPowerFlow.p
vscim:EnergyConsumer.p
vscim:RotatingMachine.p
- "give me non conform loads and their active and reactive power" :heavy_check_mark:
SELECT ?load ?loadName ?activePower ?reactivePower WHERE { ?load a cim:NonConformLoad ; cim:IdentifiedObject.name ?loadName ; cim:EnergyConsumer.p ?activePower ; cim:EnergyConsumer.q ?reactivePower . }
- "give me synchronous machines and their active and reactive power injection" :heavy_check_mark:
SELECT ?machine ?machineName ?activePower ?reactivePower WHERE { ?machine a cim:SynchronousMachine ; cim:IdentifiedObject.name ?machineName ; cim:RotatingMachine.p ?activePower ; cim:RotatingMachine.q ?reactivePower . }
- "give me state variables for power flow and their active and reactive power flows" :heavy_check_mark:
SELECT ?stateVariable ?stateVariableName ?terminal ?terminalName ?activePower ?reactivePower WHERE { ?stateVariable a cim:SvPowerFlow ; cim:SvPowerFlow.Terminal ?terminal ; cim:SvPowerFlow.p ?activePower ; cim:SvPowerFlow.q ?reactivePower . ?terminal cim:IdentifiedObject.name ?terminalName . OPTIONAL { ?stateVariable cim:IdentifiedObject.name ?stateVariableName . } }
Conclusion: Judging by the above examples we think the LLM resolves the properties pretty well for questions, where we ask for specific resources (leaf classes or closer to leafs) . Asking questions for root classes or closer to roots leads to confusion and lack of union over multiple properties, but such questions shouldn't be expected.
cims:pragmatics
custom instructions
Comparing chatbot behaviour with and without cims:pragmatics
and special instructions in the prompt.
The prompt instructions to use pragmatics are:
Pay special attention to ``cims:pragmatics`` from the ontology schema. You can find practical information for the classes and properties.
Also, for some predicates you can find the unique object values for this predicate (``skos:example`` also gives all possible values).
cims:pragmatics
for the test case are:
cim:Measurement.measurementType cims:pragmatics "The range of this property can only be one of these values: CurrencyExchange-Actual, Price-Actual, ThreePhaseActivePower, ThreePhaseActivePower-Flow-Estimated" .
testing 3 scenarios:
- with instructions, with pragmatics
- without instructions, with pragmatics
- without instructions, without pragmatics
Asking the same question to the chatbot in each scenario:
List five analogs of type active power
Scenario 1: - with instructions, with pragmatics
================================ Human Message =================================
List five analogs of type active power
================================== Ai Message ==================================
Tool Calls:
sparql_query (call_LEtlfsy7tPU42Lid96gy3VcH)
Call ID: call_LEtlfsy7tPU42Lid96gy3VcH
Args:
query: SELECT ?analog ?name WHERE {
?analog a cim:Analog .
?analog cim:Measurement.measurementType ?type .
FILTER(?type = "ThreePhaseActivePower" || ?type = "ThreePhaseActivePower-Flow-Estimated")
?analog cim:IdentifiedObject.name ?name .
} LIMIT 5
Scenario 2: without instructions, with pragmatics
================================ Human Message =================================
List five analogs of type active power
================================== Ai Message ==================================
Tool Calls:
sparql_query (call_KZdWU6r7lDKGOPnmaSxxe1Z2)
Call ID: call_KZdWU6r7lDKGOPnmaSxxe1Z2
Args:
query: SELECT ?analog ?name WHERE {
?analog a cim:Analog .
?analog cim:Measurement.measurementType ?type .
FILTER(CONTAINS(LCASE(?type), "activepower"))
?analog cim:IdentifiedObject.name ?name .
} LIMIT 5
Scenario 3: without instructions, without pragmatics
================================ Human Message =================================
List five analogs of type active power
================================== Ai Message ==================================
Tool Calls:
sparql_query (call_7l1FKijjATnBawDHuzrq0iJj)
Call ID: call_7l1FKijjATnBawDHuzrq0iJj
Args:
query: SELECT ?analog ?name WHERE {
?analog a cim:Analog .
?analog cim:Measurement.measurementType ?type .
FILTER(CONTAINS(LCASE(?type), "activepower"))
?analog cim:IdentifiedObject.name ?name .
} LIMIT 5
Conclusion
the bot performs differently in scenario 1. The query is more precise and looks specifically for the values states in the pragmatics
FILTER(?type = "ThreePhaseActivePower" || ?type = "ThreePhaseActivePower-Flow-Estimated")
This validates that it pays special attention to the instructions in cims:pragmatics