Agent问答

Agent项目

目的

我们要实现Agent的问答,回答至少包含一下几种情感。

  1. 找到所有药监局备案信息里标注视黄醇为主要备案成分的面霜产品(主要成分定义:含量>0.1%)
  2. 筛选出官宣营销文案中讲到“全天可用或早晚可用”的所有产品
  3. 按照消费者受欢迎程度排名(声量热度)
  4. 按照电商销量排名
  5. 根据消费者对这些面霜产品的相关社媒讨论,总结出消费者提及声量最高的使用场景
  6. 同样找到消费者提及量最高的功效点并排序

设计知识图谱结构

节点,关系,属性

节点类型和关系,以及属性类型, 产品和成分都有声量,即提及的次数

  1. 产品(Product)

    • 属性:产品名称(name)、声量热度(volume_heat)、销量(sales_volume)
  2. 成分(Ingredient)

    • 属性:成分名称(name)、声量热度(volume_heat)
  3. 使用场景(UsageScenario)

    • 属性:使用场景描述(name)
  4. 功效点(Efficacy)

    • 属性:功效点描述(name)
  5. 使用体验(Experience)

    ​ 属性:功效点描述(name)

  6. 营销文案(MarketingCopy)

    • 属性:营销文案内容(name)
  7. 社交媒体讨论(SocialMediaDiscussion)

    • 属性:社交媒体讨论内容(name)

关系类型:

  1. 含有(CONTAINS):产品含有成分
    • 属性:含量(concentration)
  2. 适用于(APPLIES_TO):产品适用于特定使用场景
  3. 讨论于(DISCUSSED_IN):产品、成分、功效、场景的数据来源的帖子
  4. 具有功效(HAS_EFFECT):产品具有特定功效点
  5. 具有体验(HAS_EXPERIENCE):产品具有何种体验

数据处理步骤

  • 读取数据库中的评论,我们把ugc作为社媒讨论,kol作为营销文案
  • 调用LLM进行预测
  • 创建CONSTRAINT, 例如CREATE CONSTRAINT ON (c:Product) ASSERT c.name IS UNIQUE
  • 插入数据库

为简化起见,针对UGC和KOL我们使用同样的抽取模型,Prompt:

你是一个信息抽取模型,抽取美妆产品的相关信息。根据下面评论,抽取产品名称,包含成分词,产品功效词,使用场景词,使用体验,输出格式如下:

产品名称: 只写1个产品就行
包含成分: 针对上面产品的成分词,可以有多个,用逗号隔开,如果不存在写无
产品功效: 针对上面产品的功效词,和成分的要求一样,可以有多个,用逗号隔开,如果不存在写无
使用场景: 和成分的要求一样
体验: 和成分的要求一样

评论是: {question}

开始输出:

处理完成的数据格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[
{
"node_type": "Product",
"properties": {"name": "产品名称", "volume_heat": 0, "sales_volume": 0},
"relations": [
{
"relation_type": "CONTAINS",
"from_node_type": "Product",
"from_node_properties": {"name": "产品名称"},
"to_node_type": "Ingredient",
"to_node_properties": {"name": "成分名称"},
"properties": {"concentration": 0.0}
},
{
"relation_type": "APPLIES_TO",
"from_node_type": "Product",
"from_node_properties": {"name": "产品名称"},
"to_node_type": "UsageScenario",
"to_node_properties": {"description": "使用场景描述"},
"properties": {}
},
// 其他关系...
]
},
// 其他节点...
]

一条完整的处理过的数据示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
00 = {dict: 3} {'node_type': 'SocialMediaDiscussion', 'properties': {'name': '省钱攻略!控油去屑洗发水选对不选贵!:洗发水这块我有发言权!用过的少说也有几十款了!!今天总结了几款热门的控油去屑洗发水平价的分享给大家不知道怎么选洗发水的姐妹可以码住抄作业!-欧舒丹洗发水无硅油天然植物成分的洗发水,里面有五重草本精油,可以清洁头皮油脂,还能修护受损发质,洗完头很蓬松还有股淡淡的草药清香~-可可缇亚洗发水控油去屑二合一的洗发水,油屑姐妹真的要试试这个,里面有二硫化硒
01 = {dict: 3} {'node_type': 'Ingredient', 'properties': {'name': '五重草本精油', 'volume_heat': 728}, 'relations': [{'relation_type': 'CONTAINS', 'from_node_type': 'Product', 'from_node_properties': {'name': ' 欧舒丹洗发水'}, 'to_node_type': 'Ingredient', 'to_node_properties': {'na
02 = {dict: 3} {'node_type': 'Efficacy', 'properties': {'name': '清洁头皮油脂, 修护发质'}, 'relations': []}
03 = {dict: 3} {'node_type': 'Experience', 'properties': {'name': '洗完头很蓬松, 有股淡淡的草药清香'}, 'relations': [{'relation_type': 'DISCUSSED_IN', 'from_node_type': 'Experience', 'from_node_properties': {'name': '洗完头很蓬松, 有股淡淡的草药清香'}, 'to_node_type': 'SocialMediaDiscussion', 'to_nod
04 = {dict: 3} {'node_type': 'UsageScenario', 'properties': {'name': '控油去屑'}, 'relations': [{'relation_type': 'DISCUSSED_IN', 'from_node_type': 'UsageScenario', 'from_node_properties': {'name': '控油去屑'}, 'to_node_type': 'SocialMediaDiscussion', 'to_node_properties': {'nam
05 = {dict: 3} {'node_type': 'Product', 'properties': {'name': ' 欧舒丹洗发水', 'volume_heat': 927, 'sales_volume': 347}, 'relations': [{'relation_type': 'DISCUSSED_IN', 'from_node_type': 'Product', 'from_node_properties': {'name': ' 欧舒丹洗发水'}, 'to_node_type': 'SocialMediaDiscu
06 = {dict: 3} {'node_type': 'Ingredient', 'properties': {'name': '二硫化硒, 水杨酸', 'volume_heat': 45}, 'relations': [{'relation_type': 'CONTAINS', 'from_node_type': 'Product', 'from_node_properties': {'name': ' 可可缇亚洗发水'}, 'to_node_type': 'Ingredient', 'to_node_properties': {
07 = {dict: 3} {'node_type': 'Efficacy', 'properties': {'name': '控油去屑'}, 'relations': []}
08 = {dict: 3} {'node_type': 'Experience', 'properties': {'name': '头皮干净, 洗一次会少一次出油量'}, 'relations': [{'relation_type': 'DISCUSSED_IN', 'from_node_type': 'Experience', 'from_node_properties': {'name': '头皮干净, 洗一次会少一次出油量'}, 'to_node_type': 'SocialMediaDiscussion', 'to_node_
09 = {dict: 3} {'node_type': 'UsageScenario', 'properties': {'name': '控油去屑'}, 'relations': [{'relation_type': 'DISCUSSED_IN', 'from_node_type': 'UsageScenario', 'from_node_properties': {'name': '控油去屑'}, 'to_node_type': 'SocialMediaDiscussion', 'to_node_properties': {'nam
10 = {dict: 3} {'node_type': 'Product', 'properties': {'name': ' 可可缇亚洗发水', 'volume_heat': 417, 'sales_volume': 360}, 'relations': [{'relation_type': 'DISCUSSED_IN', 'from_node_type': 'Product', 'from_node_properties': {'name': ' 可可缇亚洗发水'}, 'to_node_type': 'SocialMediaDis
11 = {dict: 3} {'node_type': 'Ingredient', 'properties': {'name': '生姜精华', 'volume_heat': 769}, 'relations': [{'relation_type': 'CONTAINS', 'from_node_type': 'Product', 'from_node_properties': {'name': ' 施华蔻洗发水'}, 'to_node_type': 'Ingredient', 'to_node_properties': {'name
12 = {dict: 3} {'node_type': 'Efficacy', 'properties': {'name': '养护头皮, 调节水油平衡'}, 'relations': []}
13 = {dict: 3} {'node_type': 'Experience', 'properties': {'name': '洗完发根直接支棱起来'}, 'relations': [{'relation_type': 'DISCUSSED_IN', 'from_node_type': 'Experience', 'from_node_properties': {'name': '洗完发根直接支棱起来'}, 'to_node_type': 'SocialMediaDiscussion', 'to_node_properties':
14 = {dict: 3} {'node_type': 'UsageScenario', 'properties': {'name': '控油去屑'}, 'relations': [{'relation_type': 'DISCUSSED_IN', 'from_node_type': 'UsageScenario', 'from_node_properties': {'name': '控油去屑'}, 'to_node_type': 'SocialMediaDiscussion', 'to_node_properties': {'nam
15 = {dict: 3} {'node_type': 'Product', 'properties': {'name': ' 施华蔻洗发水', 'volume_heat': 153, 'sales_volume': 238}, 'relations': [{'relation_type': 'DISCUSSED_IN', 'from_node_type': 'Product', 'from_node_properties': {'name': ' 施华蔻洗发水'}, 'to_node_type': 'SocialMediaDiscu
16 = {dict: 3} {'node_type': 'Ingredient', 'properties': {'name': '无花果, 奇异果的提取物', 'volume_heat': 696}, 'relations': [{'relation_type': 'CONTAINS', 'from_node_type': 'Product', 'from_node_properties': {'name': ' 发之食谱洗发水'}, 'to_node_type': 'Ingredient', 'to_node_properties
17 = {dict: 3} {'node_type': 'Efficacy', 'properties': {'name': '无'}, 'relations': []}
18 = {dict: 3} {'node_type': 'Experience', 'properties': {'name': '洗完头发清爽, 蓬松'}, 'relations': [{'relation_type': 'DISCUSSED_IN', 'from_node_type': 'Experience', 'from_node_properties': {'name': '洗完头发清爽, 蓬松'}, 'to_node_type': 'SocialMediaDiscussion', 'to_node_properties':
19 = {dict: 3} {'node_type': 'UsageScenario', 'properties': {'name': '防止油头贴头皮'}, 'relations': [{'relation_type': 'DISCUSSED_IN', 'from_node_type': 'UsageScenario', 'from_node_properties': {'name': '防止油头贴头皮'}, 'to_node_type': 'SocialMediaDiscussion', 'to_node_properties':
20 = {dict: 3} {'node_type': 'Product', 'properties': {'name': ' 发之食谱洗发水', 'volume_heat': 392, 'sales_volume': 20}, 'relations': [{'relation_type': 'DISCUSSED_IN', 'from_node_type': 'Product', 'from_node_properties': {'name': ' 发之食谱洗发水'}, 'to_node_type': 'SocialMediaDisc

插入数据库的示例代码

// 创建或合并产品节点
MERGE (p:Product {name: “产品名称”, volume_heat: 0, sales_volume: 0})

// 创建或合并成分节点
MERGE (i:Ingredient {name: “成分名称”, volume_heat: 0})

// 创建或合并营销文案节点
MERGE (m:MarketingCopy {content: “营销文案内容”})

// 创建或合并社交媒体讨论节点
MERGE (s:SocialMediaDiscussion {content: “社交媒体讨论内容”})

// 创建或合并使用场景节点
MERGE (u:UsageScenario {description: “使用场景描述”})

// 创建或合并功效点节点
MERGE (e:Efficacy {description: “功效点描述”})

// 创建产品与成分之间的关系, 属性是含量
MATCH (p:Product {name: “产品名称”})
MATCH (i:Ingredient {name: “成分名称”})
MERGE (p)-[:CONTAINS {concentration: 0.0}]->(i)

// 创建产品与使用场景之间的关系
MATCH (p:Product {name: “产品名称”})
MATCH (u:UsageScenario {description: “使用场景描述”})
MERGE (p)-[:APPLIES_TO]->(u)

// 创建营销文案与产品之间的关系
MATCH (p:Product {name: “产品名称”})
MATCH (m:MarketingCopy {content: “营销文案内容”})
MERGE (m)-[:DISCUSSED_IN]->(p)

// 创建社交媒体讨论与产品之间的关系
MATCH (p:Product {name: “产品名称”})
MATCH (s:SocialMediaDiscussion {content: “社交媒体讨论内容”})
MERGE (s)-[:DISCUSSED_IN]->(p)

// 创建产品与功效点之间的关系
MATCH (p:Product {name: “产品名称”})
MATCH (e:Efficacy {description: “功效点描述”})
MERGE (p)-[:HAS_EFFECT]->(e)

构建的知识图谱介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def generate_knowledge_graph_introduction():
# 构建知识图谱的介绍
content = "这是1个美妆知识图谱涵,主要包括产品、成分、使用场景、功效点、使用体验、营销文案和社交媒体讨论。"
content += "\n\n节点类型及其属性如下:\n"
# 添加节点类型及其属性
node_properties = {
"Product": ["产品名称(name)", "声量热度(volume_heat)", "销量(sales_volume)"],
"Ingredient": ["成分名称(name)", "声量热度(volume_heat)"],
"UsageScenario": ["使用场景描述(name)"],
"Efficacy": ["功效点描述(name)"],
"Experience": ["功效点描述(name)"],
"MarketingCopy": ["营销文案内容(name)"],
"SocialMediaDiscussion": ["社交媒体讨论内容(name)"]
}
for node_type, properties in node_properties.items():
content += f"- {node_type}:\n"
for prop in properties:
content += f" - {prop}\n"

content += "\n关系类型如下:\n"
# 添加关系类型
relationship_types = {
"CONTAINS": "产品含有成分",
"APPLIES_TO": "产品适用于特定使用场景",
"DISCUSSED_IN": "产品、成分、功效、场景的数据来源的帖子",
"HAS_EFFECT": "产品具有特定功效点",
"HAS_EXPERIENCE": "产品具有何种体验"
}
for rel_type, description in relationship_types.items():
content += f"- {rel_type}: {description}\n"
print(content)
return content

美妆知识图谱: 主要包括产品、成分、使用场景、功效点、使用体验、营销文案和社交媒体讨论。

节点类型及其属性如下:

  • Product:
    • 产品名称(name)
    • 声量热度(volume_heat)
    • 销量(sales_volume)
  • Ingredient:
    • 成分名称(name)
    • 声量热度(volume_heat)
  • UsageScenario:
    • 使用场景描述(name)
  • Efficacy:
    • 功效点描述(name)
  • Experience:
    • 功效点描述(name)
  • MarketingCopy:
    • 营销文案内容(name)
  • SocialMediaDiscussion:
    • 社交媒体讨论内容(name)

关系类型如下:

  • CONTAINS: 产品含有成分
  • APPLIES_TO: 产品适用于特定使用场景
  • DISCUSSED_IN: 产品、成分、功效、场景的数据来源的帖子
  • HAS_EFFECT: 产品具有特定功效点
  • HAS_EXPERIENCE: 产品具有何种体验

知识图谱的插入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
from py2neo import Graph,ClientError
class NEO4JExample():
def __init__(self, host='l0', user='neo4j', password='welcome123', database='neo4j', verbose=False, dryrun=False, merge_insert=True):
"""
:param uri: neo4j的数据库
:type uri:
:param user: 用户名
:type user:
:param password: 密码
:type password:
:param mini_data: 是否适用迷你数据
:type mini_data:
:param verbose: 是否打印日志
:type verbose:
:param dryrun: 模拟运行,不允许sql提交
:param skip_imported: 跳过已经导入过的labels和relations, 获取neo4j存在的labels和relations,然后对比配置文件,如果已经导入过,那么跳过导入
"""
self.graph = Graph(host=host, user=user, password=password, name=database)
# True表示通过merge的方式插入,否则是先match,如果存在,那么就不插入了
self.merge_insert = merge_insert
self.verbose = verbose
self.dryrun = dryrun
if dryrun:
print(f"注意这是dryrun状态,没有真正的运行")
self.existed_labels = [] #neo4j中已经存在的实体label
self.existed_relations = [] #neo4j中已经存在的关系名称
self.constriant_labels = self.list_constraints()
self.existed_labels = self.list_labels()
self.existed_relations = self.list_relations()

def _format_properties(self, properties):
"""格式化neo4j 的properties,针对数字和字符串进行特殊处理"""
formatted_properties = []
for k, v in properties.items():
if isinstance(v, str):
formatted_properties.append(f"{k}: '{v}'")
else:
formatted_properties.append(f"{k}: {v}")
return "{" + ", ".join(formatted_properties) + "}"

def merge_node(self, node_type, properties):
"""
插入数据
Args:
node_type (): 'SocialMediaDiscussion'
properties (): {'name': '#谷雨氨基酸慕斯洗面奶:由于刷头坏了,于是换一个新瓶子,不小心爬到了地上,拖地就留下了这个印子,就是说,这东西往脸上涂,,,,,,,,'}
Returns:
"""
query = f"MERGE (n:{node_type} {self._format_properties(properties)})"
if self.dryrun:
print(query)
else:
print(query)
try:
# Your code to run the MERGE statement
self.graph.run(query)
except ClientError as e:
if "Schema.ConstraintValidationFailed" in str(e):
# Handle the constraint validation error, or simply skip it
print("Node already exists, Skipping...")
else:
raise Exception(f"发生了其它的,错误,请检查:",e)

def create_relation(self, relation_type, from_node_type, from_node_properties, to_node_type, to_node_properties, properties):
query = (
f"MATCH (a:{from_node_type} {self._format_properties(from_node_properties)}) "
f"MATCH (b:{to_node_type} {self._format_properties(to_node_properties)}) "
f"MERGE (a)-[r:{relation_type} {self._format_properties(properties)}]->(b)"
)
if self.dryrun:
print(query)
else:
print(query)
self.graph.run(query)

def check_create_constrains(self,data):
"""
检查neo4j是否已经存在约束,如果不存在,则创建约束
Returns:
CREATE CONSTRAINT ON (p:Product) ASSERT p.name IS UNIQUE;
"""
has_constrains = self.list_constraints()
nodes_types = self.get_all_node_types(data)
for node_type in nodes_types:
if node_type not in has_constrains:
sql = f"CREATE CONSTRAINT FOR (p:{node_type}) REQUIRE p.name IS UNIQUE;"
if self.dryrun:
print(sql)
else:
self.graph.run(sql)

def replace_special_characters(self,data):
"""
替换特殊字符
Returns:
"""
def do_replace(value):
value = value.replace("'", "’") # 替换单引号
value = value.replace('"', '“') # 替换双引号
value = value.replace('\\', '') # 替换反斜杠
value = value.replace(':', ':') # 替换冒号
value = value.replace('{', '{') # 替换左大括号
value = value.replace('}', '}') # 替换右大括号
value = value.replace('[', '【') # 替换左方括号
value = value.replace(']', '】') # 替换右方括号
value = value.replace('.', '。') # 替换句号
value = value.replace(',', ',') # 替换逗号
value = value.replace('=', '=') # 替换等号
value = value.strip()
return value
for one_idx, one_data in enumerate(data):
for item_idx, item in enumerate(one_data):
# 遍历节点属性
if "properties" in item:
properties = item["properties"]
for key, value in properties.items():
if isinstance(value, str):
# 替换特殊字符
value = do_replace(value)
properties[key] = value
# 遍历关系
if "relations" in item:
relations = item["relations"]
for relation in relations:
if "from_node_properties" in relation:
from_node_properties = relation["from_node_properties"]
for key, value in from_node_properties.items():
if isinstance(value, str):
# 替换特殊字符
value = do_replace(value)
from_node_properties[key] = value
if "to_node_properties" in relation:
to_node_properties = relation["to_node_properties"]
for key, value in to_node_properties.items():
if isinstance(value, str):
# 替换特殊字符
value = do_replace(value)
to_node_properties[key] = value
return data
def get_all_node_types(self, data):
"""
获取所有的节点类型
Returns:
"""
node_types = []
for one_idx, one_data in enumerate(data):
for item_idx, item in enumerate(one_data):
node_type = item["node_type"]
properties = item["properties"]
assert "name" in properties, f"如果要限制节点,这个{item_idx} 缺少name属性"
if node_type not in node_types:
node_types.append(node_type)
print("所有节点类型", node_types)
return node_types
def check_duplicates(self, data, remove_duplicate=True):
"""
检查data中的数据和neo4j中的数据是否重复, 代码有问题
如果重复选中是否则删除重复的数据。
Args:
data ():
remove_duplicate (): bool ,是否删除重复数据
Returns:
new data
"""
# Neo4j 查询语句模板
node_exists_query = "MATCH (n:{node_type}) WHERE n.name = $name RETURN COUNT(n) AS count"
relation_exists_query = """
MATCH (from:{from_node_type} {{ name: $from_name }})
MATCH (to:{to_node_type} {{ name: $to_name }})
MATCH (from)-[r:{relation_type}]->(to)
RETURN COUNT(r) AS count
"""
new_data = []
repeat_node = 0
repeat_relation = 0
for one_idx, one_data in enumerate(data):
for node_idx, node_data in one_data:
node_type = node_data['node_type']
node_properties = node_data['properties']
# Check if node already exists in Neo4j
result = self.graph.run(node_exists_query.format(node_type=node_type), name=node_properties['name'])
count = result.single()['count']
if count > 0:
print(f"Duplicate node found at index {one_idx + 1}: {node_type} - {node_properties}")
repeat_node += 1
if remove_duplicate:
continue
new_node_data = {'node_type': node_type, 'properties': node_properties, 'relations': []}
for relation_data in node_data['relations']:
relation_type = relation_data['relation_type']
from_node_type = relation_data['from_node_type']
from_node_properties = relation_data['from_node_properties']
to_node_type = relation_data['to_node_type']
to_node_properties = relation_data['to_node_properties']
# Check if relation already exists in Neo4j
result = self.graph.run(
relation_exists_query.format(from_node_type=from_node_type, to_node_type=to_node_type,
relation_type=relation_type),
from_name=from_node_properties['name'], to_name=to_node_properties['name'])
count = result.single()['count']
if count > 0:
print(
f"Duplicate relation found at index {one_idx + 1}: {from_node_type} - {to_node_type} - {relation_type}")
repeat_relation += 1
if remove_duplicate:
continue
new_node_data['relations'].append(relation_data)
new_data.append(new_node_data)
print(f"已过滤重复节点{repeat_node}条,重复关系{repeat_relation}条")
return new_data
def check_data_valid(self,data):
"""
检查数据是否合法,即是否是合适的数据
Args:
[
{
"node_type": "Product",
"properties": {"name": "产品名称", "volume_heat": 0, "sales_volume": 0},
"relations": [
{
"relation_type": "CONTAINS",
"from_node_type": "Product",
"from_node_properties": {"name": "产品名称"},
"to_node_type": "Ingredient",
"to_node_properties": {"name": "成分名称"},
"properties": {"concentration": 0.0}
},
{
"relation_type": "APPLIES_TO",
"from_node_type": "Product",
"from_node_properties": {"name": "产品名称"},
"to_node_type": "UsageScenario",
"to_node_properties": {"description": "使用场景描述"},
"properties": {}
},
// 其他关系...
]
},
// 其他节点...
]
Returns:
如果数据合法,返回True,否则返回False
"""
if not isinstance(data, list):
print("数据格式不正确:应该是一个包含字典的列表的列表")
return False

for one_idx, one_data in enumerate(data):
for item_idx, item in enumerate(one_data):
if not isinstance(item, dict):
print(f"第{one_idx}条数据的第{item_idx}个节点数据格式不正确:应该是一个字典")
return False

required_keys = ["node_type", "properties", "relations"]
for key in required_keys:
if key not in item:
print(f"第{one_idx}条数据的第{item_idx}个节点缺少必要的键: {key}")
return False

if not isinstance(item["node_type"], str):
print("第{one_idx}条数据的第{item_idx}个节点类型应该是字符串")
return False

if not isinstance(item["properties"], dict):
print("第{one_idx}条数据的第{item_idx}个节点属性应该是一个字典")
return False

if not isinstance(item["relations"], list):
print("第{one_idx}条数据的第{item_idx}个节点关系应该是一个包含字典的列表")
return False

for relation in item["relations"]:
if not isinstance(relation, dict):
print("第{one_idx}条数据的第{item_idx}个关系数据格式不正确:应该是一个字典")
return False

required_relation_keys = ["relation_type", "from_node_type", "from_node_properties",
"to_node_type", "to_node_properties", "properties"]
for relation_key in required_relation_keys:
if relation_key not in relation:
print(f"第{one_idx}条数据的第{item_idx}个关系缺少必要的键: {relation_key}")
return False

if not isinstance(relation["relation_type"], str):
print("第{one_idx}条数据的第{item_idx}个关系类型应该是字符串")
return False

if not isinstance(relation["from_node_type"], str):
print("第{one_idx}条数据的第{item_idx}个关系起始节点类型应该是字符串")
return False

if not isinstance(relation["from_node_properties"], dict):
print("第{one_idx}条数据的第{item_idx}个关系起始节点属性应该是一个字典")
return False

if not isinstance(relation["to_node_type"], str):
print("第{one_idx}条数据的第{item_idx}个关系结束节点类型应该是字符串")
return False

if not isinstance(relation["to_node_properties"], dict):
print("第{one_idx}条数据的第{item_idx}个关系结束节点属性应该是一个字典")
return False

if not isinstance(relation["properties"], dict):
print("第{one_idx}条数据的第{item_idx}个关系属性应该是一个字典")
return False
# 检查propertities
propert_required_keys = ["name"]
for key in propert_required_keys:
if key not in item["properties"].keys():
print(f"第{one_idx}条数据的第{item_idx}个属性中缺少{key}字段")
return False
if item["properties"]["name"] == "无":
print(f"第{one_idx}条数据的第{item_idx}个属性中name字段不能为无")
return False
return True
def import_data(self, data):
"""
根据自定义的config配置,和excel,导入数据
:return:
:rtype:
"""
data = self.replace_special_characters(data)
valid = self.check_data_valid(data)
if not valid:
print("数据格式不正确,请修正数据格式后重试")
return
# 检查约束,如果不存在,那么就创建
self.check_create_constrains(data)
print(f"数据校验通过,开始插入数据到neo4j")
for one_idx, one_data in enumerate(data):
for item_idx, item in enumerate(one_data):
self.merge_node(item["node_type"], item["properties"])
for relation in item["relations"]:
self.create_relation(relation["relation_type"], relation["from_node_type"], relation["from_node_properties"],
relation["to_node_type"], relation["to_node_properties"], relation["properties"])
print(f"共导入{len(data)}条数据")
if self.dryrun:
print(f"dryrun状态运行完成,模拟检查完毕")
def dell_all_contstrant(self):
con_sql = """CALL db.constraints"""
all_constraints = self.graph.run(con_sql)
sqls = []
for record in all_constraints:
print(f"Constraint名字是{record['name']}")
constraint_name = record['name']
sql = """DROP CONSTRAINT %s""" % constraint_name
sqls.append(sql)
print(f"共要执行删除限制{len(sqls)}条")
if not sqls:
# 如果要删除0条,那么直接返回
return
sql_str = '\n'.join(sqls)
result = self.graph.run(sql_str)
print(result)
def list_relations(self):
"""
列出数据库中存在的关系
:return:
:rtype:
"""
res = self.graph.run("CALL db.relationshipTypes()")
res_list = list(res)
relations = []
for rl in res_list:
value = rl.values()
relations.extend(value)
return relations
def list_labels(self):
"""
列出数据库中存在的实体的label
:return:
:rtype:
"""
res = self.graph.run("CALL db.labels()")
res_list = list(res)
labels = []
for rl in res_list:
value = rl.values()
labels.extend(value)
return labels
def list_constraints(self):
"""
列出所有contraints的label的名字
:return:
:rtype:
"""
con_sql = """SHOW CONSTRAINTS"""
all_constraints = self.graph.run(con_sql)
constraint_labels = []
for record in all_constraints:
constraint_labelsOrTypes = record['labelsOrTypes']
constraint_labels.extend(constraint_labelsOrTypes)
return constraint_labels

根据问题设定CQL查询和工具

包含玻色因的化妆品有哪些?

1
2
3
MATCH (product:Product)-[:CONTAINS]->(component:Ingredient)
WHERE component.name =~ '.*玻色因.*'
RETURN product

Agent问答
https://johnson7788.github.io/2024/05/07/Agent%E9%97%AE%E7%AD%94/
作者
Johnson
发布于
2024年5月7日
许可协议