naas-abi 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- naas_abi/__init__.py +35 -0
- naas_abi/agents/AbiAgent.py +442 -0
- naas_abi/agents/AbiAgent_test.py +157 -0
- naas_abi/agents/EntitytoSPARQLAgent.py +952 -0
- naas_abi/agents/EntitytoSPARQLAgent_test.py +66 -0
- naas_abi/agents/KnowledgeGraphBuilderAgent.py +321 -0
- naas_abi/agents/KnowledgeGraphBuilderAgent_test.py +86 -0
- naas_abi/agents/OntologyEngineerAgent.py +115 -0
- naas_abi/agents/OntologyEngineerAgent_test.py +42 -0
- naas_abi/apps/oxigraph_admin/main.py +392 -0
- naas_abi/apps/oxigraph_admin/terminal_style.py +151 -0
- naas_abi/apps/sparql_terminal/main.py +68 -0
- naas_abi/apps/sparql_terminal/terminal_style.py +236 -0
- naas_abi/apps/terminal_agent/main.py +553 -0
- naas_abi/apps/terminal_agent/terminal_style.py +175 -0
- naas_abi/cli.py +714 -0
- naas_abi/mappings.py +83 -0
- naas_abi/models/airgap_gemma.py +220 -0
- naas_abi/models/airgap_qwen.py +24 -0
- naas_abi/models/default.py +23 -0
- naas_abi/models/gpt_4_1.py +25 -0
- naas_abi/pipelines/AIAgentOntologyGenerationPipeline.py +635 -0
- naas_abi/pipelines/AIAgentOntologyGenerationPipeline_test.py +133 -0
- naas_abi/pipelines/AddIndividualPipeline.py +215 -0
- naas_abi/pipelines/AddIndividualPipeline_test.py +66 -0
- naas_abi/pipelines/InsertDataSPARQLPipeline.py +197 -0
- naas_abi/pipelines/InsertDataSPARQLPipeline_test.py +96 -0
- naas_abi/pipelines/MergeIndividualsPipeline.py +245 -0
- naas_abi/pipelines/MergeIndividualsPipeline_test.py +98 -0
- naas_abi/pipelines/RemoveIndividualPipeline.py +166 -0
- naas_abi/pipelines/RemoveIndividualPipeline_test.py +58 -0
- naas_abi/pipelines/UpdateCommercialOrganizationPipeline.py +198 -0
- naas_abi/pipelines/UpdateDataPropertyPipeline.py +175 -0
- naas_abi/pipelines/UpdateLegalNamePipeline.py +107 -0
- naas_abi/pipelines/UpdateLinkedInPagePipeline.py +179 -0
- naas_abi/pipelines/UpdatePersonPipeline.py +184 -0
- naas_abi/pipelines/UpdateSkillPipeline.py +118 -0
- naas_abi/pipelines/UpdateTickerPipeline.py +104 -0
- naas_abi/pipelines/UpdateWebsitePipeline.py +106 -0
- naas_abi/triggers.py +131 -0
- naas_abi/workflows/AgentRecommendationWorkflow.py +321 -0
- naas_abi/workflows/AgentRecommendationWorkflow_test.py +160 -0
- naas_abi/workflows/ArtificialAnalysisWorkflow.py +337 -0
- naas_abi/workflows/ArtificialAnalysisWorkflow_test.py +57 -0
- naas_abi/workflows/ConvertOntologyGraphToYamlWorkflow.py +210 -0
- naas_abi/workflows/ConvertOntologyGraphToYamlWorkflow_test.py +78 -0
- naas_abi/workflows/CreateClassOntologyYamlWorkflow.py +208 -0
- naas_abi/workflows/CreateClassOntologyYamlWorkflow_test.py +65 -0
- naas_abi/workflows/CreateIndividualOntologyYamlWorkflow.py +183 -0
- naas_abi/workflows/CreateIndividualOntologyYamlWorkflow_test.py +86 -0
- naas_abi/workflows/ExportGraphInstancesToExcelWorkflow.py +450 -0
- naas_abi/workflows/ExportGraphInstancesToExcelWorkflow_test.py +33 -0
- naas_abi/workflows/GetObjectPropertiesFromClassWorkflow.py +385 -0
- naas_abi/workflows/GetObjectPropertiesFromClassWorkflow_test.py +57 -0
- naas_abi/workflows/GetSubjectGraphWorkflow.py +84 -0
- naas_abi/workflows/GetSubjectGraphWorkflow_test.py +71 -0
- naas_abi/workflows/SearchIndividualWorkflow.py +190 -0
- naas_abi/workflows/SearchIndividualWorkflow_test.py +98 -0
- naas_abi-1.0.0.dist-info/METADATA +9 -0
- naas_abi-1.0.0.dist-info/RECORD +62 -0
- naas_abi-1.0.0.dist-info/WHEEL +5 -0
- naas_abi-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from naas_abi.agents.EntitytoSPARQLAgent import create_agent
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@pytest.fixture
|
|
6
|
+
def agent():
|
|
7
|
+
return create_agent()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_basic_functionality_and_output_structure(agent):
|
|
11
|
+
"""
|
|
12
|
+
Test 1: Basic Functionality and Output Structure
|
|
13
|
+
|
|
14
|
+
Verifies that the agent returns a response containing:
|
|
15
|
+
1. Original text acknowledgment
|
|
16
|
+
2. Entity extraction explanation with BFO reasoning
|
|
17
|
+
3. Relationship analysis and justification
|
|
18
|
+
4. Complete SPARQL INSERT DATA statement
|
|
19
|
+
"""
|
|
20
|
+
from datetime import datetime
|
|
21
|
+
|
|
22
|
+
statement = """latest news on France today:
|
|
23
|
+
Major Wildfire in Southern France (Aude Region)
|
|
24
|
+
A devastating wildfire—France's largest in decades—has been brought under control after sweeping through over 16,000 hectares (160 km²), an area larger than Paris
|
|
25
|
+
Reuters
|
|
26
|
+
France 24
|
|
27
|
+
AP News
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
result = agent.invoke(statement)
|
|
31
|
+
result_str = str(result)
|
|
32
|
+
|
|
33
|
+
# Test that result is not empty
|
|
34
|
+
assert result_str is not None and result_str.strip() != "", (
|
|
35
|
+
"Agent should return non-empty result"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# Test for original text acknowledgment
|
|
39
|
+
assert "Entity Extraction Analysis" in result_str, (
|
|
40
|
+
"Should contain Entity Extraction Analysis section"
|
|
41
|
+
)
|
|
42
|
+
assert "original text" in result_str.lower(), "Should acknowledge original text"
|
|
43
|
+
|
|
44
|
+
# Test for entity extraction explanation
|
|
45
|
+
assert "Extracted Entities" in result_str, (
|
|
46
|
+
"Should contain Extracted Entities section"
|
|
47
|
+
)
|
|
48
|
+
assert "BFO" in result_str, "Should reference BFO ontology framework"
|
|
49
|
+
assert "Continuants" in result_str or "Occurrents" in result_str, (
|
|
50
|
+
"Should categorize entities as Continuants or Occurrents"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Test for relationship analysis
|
|
54
|
+
assert "Relationships" in result_str, "Should contain relationship analysis"
|
|
55
|
+
assert "Justification" in result_str or "Reasoning" in result_str, (
|
|
56
|
+
"Should provide justification for relationships"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Test for SPARQL statement
|
|
60
|
+
assert "```sparql" in result_str, "Should contain SPARQL statement"
|
|
61
|
+
assert "INSERT DATA" in result_str, "Should contain INSERT DATA clause"
|
|
62
|
+
|
|
63
|
+
# Test date
|
|
64
|
+
assert datetime.now().strftime("%Y-%m-%d") in result_str, (
|
|
65
|
+
"Should contain today's date"
|
|
66
|
+
)
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
# from naas_abi import secret
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from langchain_openai import ChatOpenAI # noqa: F401
|
|
5
|
+
from naas_abi import ABIModule
|
|
6
|
+
from naas_abi_core.services.agent.Agent import (
|
|
7
|
+
Agent,
|
|
8
|
+
AgentConfiguration,
|
|
9
|
+
AgentSharedState,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
MODULE: ABIModule = ABIModule.get_instance()
|
|
13
|
+
|
|
14
|
+
NAME: str = "Knowledge_Graph_Builder"
|
|
15
|
+
AVATAR_URL: str = "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f3/Rdf_logo.svg/1200px-Rdf_logo.svg.png"
|
|
16
|
+
DESCRIPTION: str = (
|
|
17
|
+
"A Knowledge Graph Builder Agent that helps users to build Knowledge Graphs."
|
|
18
|
+
)
|
|
19
|
+
SYSTEM_PROMPT: str = """
|
|
20
|
+
# ROLE:
|
|
21
|
+
You are a friendly and helpful Knowledge Graph Builder Agent and your role is to help users interact with instances within a Knowledge Graph by getting, adding, updating, merging and removing them.
|
|
22
|
+
|
|
23
|
+
# OBJECTIVE:
|
|
24
|
+
- Execute the tasks provided by the user.
|
|
25
|
+
- Do not damage the triplestore by removing or merging individuals that are not related to the prompt.
|
|
26
|
+
- Do not add individuals that are not related to the prompt.
|
|
27
|
+
- Do not update data properties that are not related to the prompt.
|
|
28
|
+
- Do not merge individuals that are not related to the prompt.
|
|
29
|
+
- Do not remove individuals that are not related to the prompt.
|
|
30
|
+
|
|
31
|
+
# CONTEXT:
|
|
32
|
+
You will receive prompts from users or other agents.
|
|
33
|
+
|
|
34
|
+
# TOOLS:
|
|
35
|
+
- search_class: Search for ontology classes based on their labels, definitions, examples, and comments.
|
|
36
|
+
- get_individuals_from_class: Get all individuals that are instances of a specific class.
|
|
37
|
+
- search_individuals_from_class: Search for individuals that are instances of a specific class starting with a search string.
|
|
38
|
+
- get_subject_graph: Retrieve detailed information about an entity.
|
|
39
|
+
- add_individual: Add an individual from label and class URI to the triplestore.
|
|
40
|
+
- insert_data_sparql: Insert individual data into the triplestore using SPARQL statement INSERT DATA.
|
|
41
|
+
- update_data_property: Update a data property of an entity.
|
|
42
|
+
- merge_individuals: Merge two individuals in the triplestore by transferring all triples from one to another.
|
|
43
|
+
- remove_individuals: Remove individuals from the triplestore by deleting all their associated triples.
|
|
44
|
+
|
|
45
|
+
# TASKS:
|
|
46
|
+
1. Search for ontology classes based on their labels, definitions, examples, and comments
|
|
47
|
+
2. Get all individuals that are instances of a specific class
|
|
48
|
+
3. Add new individuals to the triplestore
|
|
49
|
+
4. Update data properties for a given individual
|
|
50
|
+
5. Merge two individuals in the triplestore by transferring all triples from one to another
|
|
51
|
+
6. Remove individuals from the triplestore by deleting all their associated triples
|
|
52
|
+
|
|
53
|
+
# OPERATING GUIDELINES:
|
|
54
|
+
1. Search instances in triplestore using appropriate search tool to find the information you need
|
|
55
|
+
|
|
56
|
+
2.1 Add Individual by using add_individual tool.
|
|
57
|
+
Before using the tool, use get_subject_graph tool to get the individual and validate the individual to add is related to the prompt and provide the correct URI and label like:
|
|
58
|
+
"We are going to add the following individual to the triplestore:
|
|
59
|
+
- Individual label (rdfs:label): Naas.ai
|
|
60
|
+
- Class URI: https://www.commoncoreontologies.org/ont00000443
|
|
61
|
+
Are you sure you want to add this individual? (y/n)"
|
|
62
|
+
Finish by using the tool get_subject_graph of the uri to add to check if the individual is added.
|
|
63
|
+
|
|
64
|
+
2.2 Insert data into the triplestore using SPARQL statement INSERT DATA.
|
|
65
|
+
If you receive a sparql statement starting with ```sparql `` and with "INSERT DATA" in it, use the insert_data_sparql tool to insert the data into the triplestore.
|
|
66
|
+
Before using the tool, use extract_sparql_from_text tool to get the sparql statement and validate it with the user:
|
|
67
|
+
"We are going to insert data from the following sparql statement into the triplestore:
|
|
68
|
+
```sparql
|
|
69
|
+
INSERT DATA {
|
|
70
|
+
<http://ontology.naas.ai/abi/69a231b9-e87a-4503-8f80-a530ed8eaa4b> <http://www.w3.org/2000/01/rdf-schema#label> "Naas.ai" .
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
Are you sure you want to insert this data? (y/n)"
|
|
74
|
+
If the user confirms, use the insert_data_sparql tool to insert the data into the triplestore.
|
|
75
|
+
|
|
76
|
+
3. Update Data Property of an Entity by getting all its triples first using get_subject_graph tool and then use update_data_property tool to update the data property:
|
|
77
|
+
"We are going to update the following data property of the following individual:
|
|
78
|
+
- Individual URI: http://ontology.naas.ai/abi/69a231b9-e87a-4503-8f80-a530ed8eaa4b
|
|
79
|
+
- Data property to update: "http://www.w3.org/2000/01/rdf-schema#label"
|
|
80
|
+
- Old value: Naas
|
|
81
|
+
- New value: Naas.ai
|
|
82
|
+
Are you sure you want to update this data property? (y/n)"
|
|
83
|
+
Finish by using the tool get_subject_graph of the uri to update to check if the data property is updated.
|
|
84
|
+
|
|
85
|
+
4. Merge Individuals by using merge_individuals tool.
|
|
86
|
+
Before using the tool, use get_subject_graph tool to get the individuals and validate the individuals to merge are related to the prompt and provide the correct URIs and labels like:
|
|
87
|
+
"We are going to merge the following individuals:
|
|
88
|
+
- Instance to keep:
|
|
89
|
+
- Label: Naas.ai
|
|
90
|
+
- URI: http://ontology.naas.ai/abi/69a231b9-e87a-4503-8f80-a530ed8eaa4b
|
|
91
|
+
- Class URI: https://www.commoncoreontologies.org/ont00000443
|
|
92
|
+
- Instance to merge:
|
|
93
|
+
- Label: Naas.ai
|
|
94
|
+
- URI: http://ontology.naas.ai/abi/4f92bbdd-e710-4e43-9480-9b6cd6d9af80
|
|
95
|
+
- Class URI: https://www.commoncoreontologies.org/ont00000443
|
|
96
|
+
Are you sure you want to merge these individuals? (y/n)"
|
|
97
|
+
If class URI are not the same, you can NOT merge them.
|
|
98
|
+
If class URI are the same, finish by using the tool get_subject_graph of the uri to keep.
|
|
99
|
+
|
|
100
|
+
5. Remove Individual by using remove_individuals tool.
|
|
101
|
+
Before using the tool, use get_subject_graph tool to get the individual and validate the individual to remove is related to the prompt and provide the correct URI and label like:
|
|
102
|
+
"We are going to remove the following individuals from the triplestore:
|
|
103
|
+
- URI: http://ontology.naas.ai/abi/69a231b9-e87a-4503-8f80-a530ed8eaa4b
|
|
104
|
+
- Label: Naas.ai
|
|
105
|
+
- Class URI: https://www.commoncoreontologies.org/ont00000443
|
|
106
|
+
Are you sure you want to remove this individual? (y/n)"
|
|
107
|
+
Finish by using the tool get_subject_graph of the uri to remove to check if the individual is removed.
|
|
108
|
+
|
|
109
|
+
# CONSTRAINTS:
|
|
110
|
+
- Never use internal knowledge for answers
|
|
111
|
+
- Always ask for confirmation before performing UPDATE, MERGE, ADD or REMOVE operations.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
SUGGESTIONS: list = [
|
|
115
|
+
{
|
|
116
|
+
"label": "Search classes",
|
|
117
|
+
"value": "Search classes representing a ...",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"label": "Search individuals",
|
|
121
|
+
"value": "Search for individuals from a class ...",
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"label": "Add individual",
|
|
125
|
+
"value": "Add an individual ...",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"label": "Update data property",
|
|
129
|
+
"value": "Update a data property of an individual ...",
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"label": "Merge individuals",
|
|
133
|
+
"value": "Merge two individuals ...",
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"label": "Remove individuals",
|
|
137
|
+
"value": "Remove individual from triplestore ...",
|
|
138
|
+
},
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def create_agent(
|
|
143
|
+
agent_shared_state: Optional[AgentSharedState] = None,
|
|
144
|
+
agent_configuration: Optional[AgentConfiguration] = None,
|
|
145
|
+
) -> Optional[Agent]:
|
|
146
|
+
# Define model
|
|
147
|
+
from naas_abi.models.default import get_model
|
|
148
|
+
|
|
149
|
+
model = get_model()
|
|
150
|
+
|
|
151
|
+
# Use provided configuration or create default one
|
|
152
|
+
if agent_configuration is None:
|
|
153
|
+
agent_configuration = AgentConfiguration(system_prompt=SYSTEM_PROMPT)
|
|
154
|
+
|
|
155
|
+
# Use provided shared state or create new one
|
|
156
|
+
if agent_shared_state is None:
|
|
157
|
+
agent_shared_state = AgentSharedState()
|
|
158
|
+
|
|
159
|
+
# Init tools
|
|
160
|
+
tools: list = []
|
|
161
|
+
from naas_abi.pipelines.AddIndividualPipeline import (
|
|
162
|
+
AddIndividualPipeline,
|
|
163
|
+
AddIndividualPipelineConfiguration,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# Add Foundational Tools
|
|
167
|
+
from naas_abi.workflows.SearchIndividualWorkflow import (
|
|
168
|
+
SearchIndividualWorkflow,
|
|
169
|
+
SearchIndividualWorkflowConfiguration,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
## Initialize search workflow first since add pipeline depends on it
|
|
173
|
+
search_config = SearchIndividualWorkflowConfiguration(
|
|
174
|
+
MODULE.engine.services.triple_store
|
|
175
|
+
)
|
|
176
|
+
search_workflow = SearchIndividualWorkflow(search_config)
|
|
177
|
+
tools += search_workflow.as_tools()
|
|
178
|
+
|
|
179
|
+
## Initialize add pipeline with search workflow config
|
|
180
|
+
add_config = AddIndividualPipelineConfiguration(
|
|
181
|
+
MODULE.engine.services.triple_store, search_config
|
|
182
|
+
)
|
|
183
|
+
add_pipeline = AddIndividualPipeline(add_config)
|
|
184
|
+
tools += add_pipeline.as_tools()
|
|
185
|
+
|
|
186
|
+
from naas_abi.pipelines.InsertDataSPARQLPipeline import (
|
|
187
|
+
InsertDataSPARQLPipeline,
|
|
188
|
+
InsertDataSPARQLPipelineConfiguration,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
insert_data_spql_pipeline = InsertDataSPARQLPipeline(
|
|
192
|
+
InsertDataSPARQLPipelineConfiguration(
|
|
193
|
+
triple_store=MODULE.engine.services.triple_store
|
|
194
|
+
)
|
|
195
|
+
)
|
|
196
|
+
tools += insert_data_spql_pipeline.as_tools()
|
|
197
|
+
|
|
198
|
+
# Add GetSubjectGraphWorkflow
|
|
199
|
+
from naas_abi.workflows.GetSubjectGraphWorkflow import (
|
|
200
|
+
GetSubjectGraphWorkflow,
|
|
201
|
+
GetSubjectGraphWorkflowConfiguration,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
get_subject_graph_config = GetSubjectGraphWorkflowConfiguration()
|
|
205
|
+
get_subject_graph_workflow = GetSubjectGraphWorkflow(get_subject_graph_config)
|
|
206
|
+
tools += get_subject_graph_workflow.as_tools()
|
|
207
|
+
|
|
208
|
+
# Add UpdateDataPropertyPipeline
|
|
209
|
+
from naas_abi.pipelines.UpdateDataPropertyPipeline import (
|
|
210
|
+
UpdateDataPropertyPipeline,
|
|
211
|
+
UpdateDataPropertyPipelineConfiguration,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
update_data_property_pipeline = UpdateDataPropertyPipeline(
|
|
215
|
+
UpdateDataPropertyPipelineConfiguration(
|
|
216
|
+
triple_store=MODULE.engine.services.triple_store
|
|
217
|
+
)
|
|
218
|
+
)
|
|
219
|
+
tools += update_data_property_pipeline.as_tools()
|
|
220
|
+
|
|
221
|
+
# Add MergeIndividualsPipeline
|
|
222
|
+
from naas_abi.pipelines.MergeIndividualsPipeline import (
|
|
223
|
+
MergeIndividualsPipeline,
|
|
224
|
+
MergeIndividualsPipelineConfiguration,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
merge_individuals_pipeline = MergeIndividualsPipeline(
|
|
228
|
+
MergeIndividualsPipelineConfiguration(
|
|
229
|
+
triple_store=MODULE.engine.services.triple_store
|
|
230
|
+
)
|
|
231
|
+
)
|
|
232
|
+
tools += merge_individuals_pipeline.as_tools()
|
|
233
|
+
|
|
234
|
+
# Add RemoveIndividualPipeline
|
|
235
|
+
from naas_abi.pipelines.RemoveIndividualPipeline import (
|
|
236
|
+
RemoveIndividualPipeline,
|
|
237
|
+
RemoveIndividualPipelineConfiguration,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
remove_individuals_pipeline = RemoveIndividualPipeline(
|
|
241
|
+
RemoveIndividualPipelineConfiguration(
|
|
242
|
+
triple_store=MODULE.engine.services.triple_store
|
|
243
|
+
)
|
|
244
|
+
)
|
|
245
|
+
tools += remove_individuals_pipeline.as_tools()
|
|
246
|
+
|
|
247
|
+
# Add specialized pipelines
|
|
248
|
+
from naas_abi.pipelines.UpdateCommercialOrganizationPipeline import (
|
|
249
|
+
UpdateCommercialOrganizationPipeline,
|
|
250
|
+
UpdateCommercialOrganizationPipelineConfiguration,
|
|
251
|
+
)
|
|
252
|
+
from naas_abi.pipelines.UpdateLegalNamePipeline import (
|
|
253
|
+
UpdateLegalNamePipeline,
|
|
254
|
+
UpdateLegalNamePipelineConfiguration,
|
|
255
|
+
)
|
|
256
|
+
from naas_abi.pipelines.UpdateLinkedInPagePipeline import (
|
|
257
|
+
UpdateLinkedInPagePipeline,
|
|
258
|
+
UpdateLinkedInPagePipelineConfiguration,
|
|
259
|
+
)
|
|
260
|
+
from naas_abi.pipelines.UpdatePersonPipeline import (
|
|
261
|
+
UpdatePersonPipeline,
|
|
262
|
+
UpdatePersonPipelineConfiguration,
|
|
263
|
+
)
|
|
264
|
+
from naas_abi.pipelines.UpdateSkillPipeline import (
|
|
265
|
+
UpdateSkillPipeline,
|
|
266
|
+
UpdateSkillPipelineConfiguration,
|
|
267
|
+
)
|
|
268
|
+
from naas_abi.pipelines.UpdateTickerPipeline import (
|
|
269
|
+
UpdateTickerPipeline,
|
|
270
|
+
UpdateTickerPipelineConfiguration,
|
|
271
|
+
)
|
|
272
|
+
from naas_abi.pipelines.UpdateWebsitePipeline import (
|
|
273
|
+
UpdateWebsitePipeline,
|
|
274
|
+
UpdateWebsitePipelineConfiguration,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
specialized_pipelines = [
|
|
278
|
+
(UpdatePersonPipeline, UpdatePersonPipelineConfiguration),
|
|
279
|
+
(UpdateSkillPipeline, UpdateSkillPipelineConfiguration),
|
|
280
|
+
(
|
|
281
|
+
UpdateCommercialOrganizationPipeline,
|
|
282
|
+
UpdateCommercialOrganizationPipelineConfiguration,
|
|
283
|
+
),
|
|
284
|
+
(UpdateLinkedInPagePipeline, UpdateLinkedInPagePipelineConfiguration),
|
|
285
|
+
(UpdateWebsitePipeline, UpdateWebsitePipelineConfiguration),
|
|
286
|
+
(UpdateLegalNamePipeline, UpdateLegalNamePipelineConfiguration),
|
|
287
|
+
(UpdateTickerPipeline, UpdateTickerPipelineConfiguration),
|
|
288
|
+
]
|
|
289
|
+
for Pipeline, Configuration in specialized_pipelines:
|
|
290
|
+
tools += Pipeline(Configuration(MODULE.engine.services.triple_store)).as_tools()
|
|
291
|
+
|
|
292
|
+
# Add search organizations tools
|
|
293
|
+
templatable_sparql_query_module = ABIModule.get_instance().engine.modules[
|
|
294
|
+
"naas_abi_core.modules.templatablesparqlquery"
|
|
295
|
+
]
|
|
296
|
+
|
|
297
|
+
ontology_tools: list = [
|
|
298
|
+
"search_class",
|
|
299
|
+
"count_instances_by_class",
|
|
300
|
+
"get_individuals_from_class",
|
|
301
|
+
"search_individuals_from_class",
|
|
302
|
+
"add_individual",
|
|
303
|
+
"update_data_property",
|
|
304
|
+
"merge_individuals",
|
|
305
|
+
"remove_individuals",
|
|
306
|
+
]
|
|
307
|
+
tools.extend(templatable_sparql_query_module.get_tools(ontology_tools))
|
|
308
|
+
|
|
309
|
+
return KnowledgeGraphBuilderAgent(
|
|
310
|
+
name=NAME,
|
|
311
|
+
description=DESCRIPTION,
|
|
312
|
+
chat_model=model,
|
|
313
|
+
tools=tools,
|
|
314
|
+
agents=[],
|
|
315
|
+
state=agent_shared_state,
|
|
316
|
+
configuration=agent_configuration,
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
class KnowledgeGraphBuilderAgent(Agent):
|
|
321
|
+
pass
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from naas_abi.agents.KnowledgeGraphBuilderAgent import create_agent
|
|
5
|
+
from naas_abi_core.services.agent.Agent import Agent
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.fixture
|
|
9
|
+
def agent() -> Optional[Agent]:
|
|
10
|
+
return create_agent()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_add_individual(agent: Agent):
|
|
14
|
+
e = agent.invoke("Add individual Naas.ai as Organization")
|
|
15
|
+
assert "We are going to add the following individual" in e, e
|
|
16
|
+
assert "Individual label" in e, e
|
|
17
|
+
assert "Class URI" in e, e
|
|
18
|
+
assert "Are you sure you want to add this individual?" in e, e
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_insert_data_sparql(agent: Agent):
|
|
22
|
+
e = agent.invoke("""
|
|
23
|
+
```sparql
|
|
24
|
+
INSERT DATA {
|
|
25
|
+
<http://ontology.naas.ai/abi/test> <http://www.w3.org/2000/01/rdf-schema#label> "Test" .
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
""")
|
|
29
|
+
assert (
|
|
30
|
+
"we are going to insert data from the following sparql statement" in e.lower()
|
|
31
|
+
or "multiple individuals" in e.lower()
|
|
32
|
+
or "multiple instances" in e.lower()
|
|
33
|
+
), e
|
|
34
|
+
if (
|
|
35
|
+
"multiple individuals" not in e.lower()
|
|
36
|
+
and "multiple instances" not in e.lower()
|
|
37
|
+
):
|
|
38
|
+
assert (
|
|
39
|
+
"Are you sure you want to insert this data?" in e
|
|
40
|
+
or "please confirm" in e.lower()
|
|
41
|
+
), e
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_update_data_property(agent: Agent):
|
|
45
|
+
e = agent.invoke("Update label of Naas.ai to Naas")
|
|
46
|
+
assert (
|
|
47
|
+
"We are going to update the following data property" in e
|
|
48
|
+
or "multiple individuals" in e.lower()
|
|
49
|
+
), e
|
|
50
|
+
if "multiple individuals" not in e.lower():
|
|
51
|
+
assert "Individual URI" in e, e
|
|
52
|
+
assert "Data property to update" in e, e
|
|
53
|
+
assert "Old value" in e, e
|
|
54
|
+
assert "New value" in e, e
|
|
55
|
+
assert (
|
|
56
|
+
"Are you sure you want to update this data property?" in e
|
|
57
|
+
or "please confirm" in e.lower()
|
|
58
|
+
), e
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_merge_individuals(agent: Agent):
|
|
62
|
+
e = agent.invoke("Merge duplicate Naas.ai instances")
|
|
63
|
+
assert (
|
|
64
|
+
"We are going to merge the following individuals" in e
|
|
65
|
+
or "multiple individuals" in e.lower()
|
|
66
|
+
or "only one" in e.lower()
|
|
67
|
+
), e
|
|
68
|
+
if "multiple individuals" not in e.lower() and "only one" not in e.lower():
|
|
69
|
+
assert "Instance to keep" in e, e
|
|
70
|
+
assert "Instance to merge" in e, e
|
|
71
|
+
assert (
|
|
72
|
+
"Are you sure you want to merge these individuals?" in e
|
|
73
|
+
or "please confirm" in e.lower()
|
|
74
|
+
), e
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_remove_individual(agent: Agent):
|
|
78
|
+
e = agent.invoke("Remove individual Naas.ai")
|
|
79
|
+
assert (
|
|
80
|
+
"We are going to remove the following individual" in e
|
|
81
|
+
or "multiple individuals" in e
|
|
82
|
+
), e
|
|
83
|
+
assert (
|
|
84
|
+
"Are you sure you want to remove this individual?" in e
|
|
85
|
+
or "please confirm" in e.lower()
|
|
86
|
+
), e
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from langchain_openai import ChatOpenAI # noqa: F401
|
|
4
|
+
from naas_abi import ABIModule
|
|
5
|
+
|
|
6
|
+
# from naas_abi import secret
|
|
7
|
+
from naas_abi_core.services.agent.Agent import (
|
|
8
|
+
Agent,
|
|
9
|
+
AgentConfiguration,
|
|
10
|
+
AgentSharedState,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
MODULE: ABIModule = ABIModule.get_instance()
|
|
14
|
+
|
|
15
|
+
NAME = "Ontology_Engineer_Agent"
|
|
16
|
+
DESCRIPTION = "A agent that helps users understand BFO Ontology and transform text into ontologies."
|
|
17
|
+
SYSTEM_PROMPT = """
|
|
18
|
+
# ROLE:
|
|
19
|
+
You are a BFO (Basic Formal Ontology) Expert and Ontology Engineering Specialist.
|
|
20
|
+
Your role involves both educational guidance and practical implementation.
|
|
21
|
+
|
|
22
|
+
# OBJECTIVE:
|
|
23
|
+
Your primary objective is to help users understand BFO Ontology and transform natural language text into structured, semantically accurate ontological representations.
|
|
24
|
+
|
|
25
|
+
# CONTEXT:
|
|
26
|
+
You will receive messages from users or the supervisor agent ABI.
|
|
27
|
+
|
|
28
|
+
# TOOLS/AGENTS:
|
|
29
|
+
- Entity_to_SPARQL: Extracts entities from text and generates SPARQL INSERT DATA statements with proper BFO mappings
|
|
30
|
+
- Knowledge_Graph_Builder: Manages triplestore operations including data insertion, querying, updating, and validation
|
|
31
|
+
|
|
32
|
+
# OPERATING GUIDELINES:
|
|
33
|
+
|
|
34
|
+
1. EDUCATIONAL QUERIES ABOUT ONTOLOGY
|
|
35
|
+
When users ask about ontology engineering concepts, or theoretical questions, use your comprehensive internal knowledge of BFO 2.0.
|
|
36
|
+
Provide an answer first with the BFO classes with its URI representing the answer of the user's question and then an clear and concise explanation of the answer.
|
|
37
|
+
Answer expected for question 'What is a Person in BFO Ontology?' is:
|
|
38
|
+
"
|
|
39
|
+
The BFO class representing a Person is a material entity (bfo:BFO_0000040).
|
|
40
|
+
|
|
41
|
+
A Person in BFO is modeled as a material entity - a physical object made of matter that occupies space and has mass.
|
|
42
|
+
This classification reflects that humans are physical, material beings composed of cells, tissues and organs that form an integrated whole.
|
|
43
|
+
As material entities, persons can bear physical qualities, participate in processes, and maintain their material nature while undergoing changes over time.
|
|
44
|
+
"
|
|
45
|
+
|
|
46
|
+
2. TEXT-TO-ONTOLOGY TRANSFORMATION WORKFLOW
|
|
47
|
+
If a user wants to transform text into ontological representation, use Entity_to_SPARQL agent.
|
|
48
|
+
Before delegating to agent, try to resolve ambiguities about:
|
|
49
|
+
- pronouns ("I", "you", "they") => must be a named entity. Example: "I" => "Florent Ravenel"
|
|
50
|
+
- dates ("today", "yesterday", "tomorrow") => must be a named entity. Example: "today" => "2025-08-12"
|
|
51
|
+
If there is no disambiguation, use the Entity_to_SPARQL agent to map the text to ontology.
|
|
52
|
+
|
|
53
|
+
3. SPARQL INSERT DATA TO TRIPESTORE
|
|
54
|
+
If the user wants to insert data into the triplestore, use the Knowledge_Graph_Builder agent to insert the data into the triplestore.
|
|
55
|
+
Before delegating to agent, validate the SPARQL statement that will be added to the triplestore like:
|
|
56
|
+
"I am going to add the following SPARQL statement to the triplestore:
|
|
57
|
+
```sparql
|
|
58
|
+
{SPARQL_STATEMENT}
|
|
59
|
+
```
|
|
60
|
+
Are you sure you want to add this SPARQL statement to the triplestore?"
|
|
61
|
+
If the user confirms, delegate to Knowledge_Graph_Builder agent.
|
|
62
|
+
|
|
63
|
+
# CONSTRAINTS:
|
|
64
|
+
- Delegate all mapping to Entity_to_SPARQL agent, do not try to do it yourself.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def create_agent(
|
|
69
|
+
agent_shared_state: Optional[AgentSharedState] = None,
|
|
70
|
+
agent_configuration: Optional[AgentConfiguration] = None,
|
|
71
|
+
) -> Optional[Agent]:
|
|
72
|
+
# Set model based on AI_MODE
|
|
73
|
+
ai_mode = MODULE.configuration.global_config.ai_mode
|
|
74
|
+
|
|
75
|
+
if ai_mode == "airgap":
|
|
76
|
+
from naas_abi.models.default import get_model
|
|
77
|
+
|
|
78
|
+
model = get_model()
|
|
79
|
+
else:
|
|
80
|
+
from naas_abi_marketplace.ai.chatgpt.models.o3_mini import model
|
|
81
|
+
|
|
82
|
+
# Use provided configuration or create default one
|
|
83
|
+
if agent_configuration is None:
|
|
84
|
+
agent_configuration = AgentConfiguration(system_prompt=SYSTEM_PROMPT)
|
|
85
|
+
|
|
86
|
+
# Use provided shared state or create new one
|
|
87
|
+
if agent_shared_state is None:
|
|
88
|
+
agent_shared_state = AgentSharedState()
|
|
89
|
+
|
|
90
|
+
tools: list = []
|
|
91
|
+
|
|
92
|
+
agents: list = []
|
|
93
|
+
from naas_abi.agents.EntitytoSPARQLAgent import (
|
|
94
|
+
create_agent as entity_to_sparql_agent,
|
|
95
|
+
)
|
|
96
|
+
from naas_abi.agents.KnowledgeGraphBuilderAgent import (
|
|
97
|
+
create_agent as knowledge_graph_builder_agent,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
agents += [entity_to_sparql_agent(), knowledge_graph_builder_agent()]
|
|
101
|
+
|
|
102
|
+
return OntologyEngineerAgent(
|
|
103
|
+
name=NAME,
|
|
104
|
+
description=DESCRIPTION,
|
|
105
|
+
chat_model=model,
|
|
106
|
+
tools=tools,
|
|
107
|
+
agents=agents,
|
|
108
|
+
memory=None,
|
|
109
|
+
state=agent_shared_state,
|
|
110
|
+
configuration=agent_configuration,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class OntologyEngineerAgent(Agent):
|
|
115
|
+
pass
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from naas_abi.agents.OntologyEngineerAgent import create_agent
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@pytest.fixture
|
|
6
|
+
def agent():
|
|
7
|
+
return create_agent()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_question_about_bfo_ontology(agent):
|
|
11
|
+
intent = "What's an Organization?"
|
|
12
|
+
result = agent.invoke(intent)
|
|
13
|
+
assert result is not None, result
|
|
14
|
+
assert "BFO_0000040" or "material entity" in result, result
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_text_to_ontology_transformation(agent):
|
|
18
|
+
intent = "Map to ontology: 'Florent Ravenel is working for Naas.ai'"
|
|
19
|
+
result = agent.invoke(intent)
|
|
20
|
+
assert result is not None, result
|
|
21
|
+
assert "Florent Ravenel is working for Naas.ai" in result, result
|
|
22
|
+
assert "```sparql" in result, result
|
|
23
|
+
assert "INSERT DATA" in result, result
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_add_to_triplestore_confirmation(agent):
|
|
27
|
+
intent = """Add to triplestore the following SPARQL statement:
|
|
28
|
+
```sparql
|
|
29
|
+
INSERT DATA {
|
|
30
|
+
<http://ontology.naas.ai/abi/69a231b9-e87a-4503-8f80-a530ed8eaa4b> <http://www.w3.org/2000/01/rdf-schema#label> "Naas.ai" .
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
"""
|
|
34
|
+
result = agent.invoke(intent)
|
|
35
|
+
assert result is not None, result
|
|
36
|
+
assert (
|
|
37
|
+
"I am going to add the following SPARQL statement to the triplestore" in result
|
|
38
|
+
), result
|
|
39
|
+
assert (
|
|
40
|
+
"Are you sure you want to add this SPARQL statement to the triplestore?"
|
|
41
|
+
in result
|
|
42
|
+
), result
|