-
Notifications
You must be signed in to change notification settings - Fork 0
/
demo.py
1612 lines (1289 loc) · 71.7 KB
/
demo.py
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
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# coding:utf-8
import datetime
import re
import yaml
import threading
import sys
import time
import openai #需要安装库pip install openai
from openai import OpenAI
import json
import os
import tiktoken #需要安装库pip install tiktoken
import chromadb #需要安装库pip install chromadb
from chromadb.utils import embedding_functions
import http.client
from flask import Flask, request, jsonify #需要安装库pip install flask
#导入其他脚本
from toolkits import tool_library
from calendario import calendar_table #库名冲突了,就随便用一个了
from vits import TTS_vits,ATM_vits
# 获取当前工作目录,之前项目使用os.path.dirname(os.path.abspath(__file__))来获取路径,但打包后路径不正确
script_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
print("[INFO] 当前工作目录是:",script_dir,'\n')
#————————————————————————————————————————日程表执行器————————————————————————————————————————
class Calendar_executor:
def __init__(self):
pass
#循环检查日程表,并自动执行日程表中的任务
def run (self):
while 1 :
#print("[DEBUG] 日程表执行器正在检查今日的定时任务~",'\n')
#获取当前时间
now_time = datetime.datetime.now()
#输入当天的年月日,获取日程表当天的全部事件
schedule_list = my_calendar.query_scheduled_task_by_date(now_time)
#如果日程表是列表类型,说明当天有事件
if type(schedule_list) == list:
#遍历日程表当天的全部事件,如果事件的执行时间小于当前时间和状态是未完成,就执行事件
#print("[DEBUG] 已查询到当天的事件内容,正在检查是否需要执行",'\n')
for task in schedule_list:
#如果事件的执行时间小于当前时间和状态是未完成,就执行任务
if datetime.datetime.strptime(task['task_datetime'], "%Y-%m-%d %H:%M:%S")< now_time and task['task_status'] == '未完成':
print("[DEBUG] 已发现需要执行的日程安排,准备开始执行任务",'\n')
#获取事件状态
task_status = task['task_status']
while task_status == "未完成" :
print("[DEBUG] 日程表执行器正在执行日程表任务:",task['task_datetime'],'\n')
#执行任务,并添加执行错误重试次数限制
max_retries = 5
retry_count = 0
while 1:
try:
self.execute_unit_task_AI_agent(task['task_datetime'])
break
except Exception as e:
print("[DEBUG] 日程表执行器执行单元任务出现错误,正在重试, 错误信息:",e,'\n')
retry_count += 1 # 错误计次
if retry_count > max_retries:
break
#检查是否无法正常执行任务
if retry_count > max_retries:
# 更改任务状态,并退出该任务
my_calendar.update_task_status(task['task_datetime'],"无法正常执行任务")
break
#审查任务
#while 1:
# try:
# self.review_unit_task_AI_agent(task['task_datetime'])
# break
# except Exception as e:
# print("[DEBUG] 日程表执行器审查单元任务出现错误,正在重试, 错误信息:",e,'\n')
#重新获取事件状态
task_status = my_calendar.get_task_status(task['task_datetime'])
#如果事件的执行时间小于当前时间和状态是已完成,且未通知过,就执行通知事件
if datetime.datetime.strptime(task['task_datetime'], "%Y-%m-%d %H:%M:%S")< now_time and task['task_status'] == '已完成'and task['notification_status'] == "未通知":
#改变通知状态为已通知
my_calendar.update_task_notification_status(task['task_datetime'],"已通知")
#使用语音通知
self.send_daily_task_notification(task)
#如果日程表不是列表类型,说明当天没有安排事件
else :
#print("[DEBUG] 日程表执行器没有发现需要执行的日程安排",'\n')
pass
#每隔10s检查一次日程表
#print("[DEBUG] 日程表执行器正在休息中,等待下次检查",'\n')
time.sleep(15)
#执行单元任务的AI代理
def execute_unit_task_AI_agent(self,task_datetime):
print("[DEBUG]开始执行单元任务!!!!!!!!!!!!!!!!!!")
#无工具调用任务目标示例
task_goal_example1 = '''我现在有70块,想买5个苹果,想知道买完后还剩多少钱'''
#无工具调用任务列表示例
task_list_example1 = '''
[
{
"task_id": 1,
"task_description": "获取苹果的单价",
"function_used": True,
"function_name": "get_the_price_of_the_item",
"function_parameters": {"item": "苹果",
"unit": "人民币"},
"function_response": "五块人民币",
"task_result": "一个苹果的价格是五块人民币"
},
{
"task_id": 2,
"task_description": "计算五个苹果的总价",
"function_used": False
}
]
'''
#无工具调用执行任务结果示例
task_result_example1 = '''
根据任务列表,任务id为2的任务描述是"计算五个苹果的总价"。已知一个苹果的价格是五块人民币(来自任务id为1的执行结果),那么我们可以计算五个苹果的总价。
五个苹果的总价为:5(苹果单价)* 5(苹果数量)= 25。
任务结果输出为json格式:
```json
{
"task_id": 2,
"task_result": "五个苹果的总价是25块人民币"
}
```
'''
#任务目标示例2
task_goal_example2 = '''我现在有70块,想买5个苹果,想知道买完后还剩多少钱'''
#任务列表示例2
task_list_example2 = '''
[
{
"task_id": 1,
"task_description": "获取苹果的单价",
"function_used": True,
"function_name": "get_the_price_of_the_item",
"function_parameters": {"item": "苹果",
"unit": "人民币"}
},
{
"task_id": 2,
"task_description": "计算五个苹果的总价",
"function_used": False
}
]
'''
#工具调用回应结果2
tool_return2 = ''' {"item": "苹果",
"unit": "人民币"
"price": "5" }'''
#执行任务结果示例2
task_result_example2 = '''
根据任务列表,任务id为1的任务描述是"获取苹果的单价"。已知经过工具调用,返回的结果是{"item": "苹果","unit": "人民币","price": "5" },那么可以知道一个苹果的价格是五块人民币。
任务结果输出为json格式:
```json
{
"task_id": 1,
"task_result": "一个苹果的价格是五块人民币"
}
```
'''
#根据日期时间,获取具体任务,需要重新获取任务,因为任务进度已经更新
task = my_calendar.query_scheduled_task_by_datetime(task_datetime)
#获取任务目标
task_objectives = task["task_objectives"]
#获取任务列表
task_list = task["task_list"]
#获取任务进度
task_progress = task["task_progress"]
#获取任务执行id
task_id = task_progress + 1
#获取相应的工具调用规范
tools_list = []
for unit_task in task_list:
#如果任务id与当前任务执行id相同
if unit_task["task_id"] == task_id:
#如果任务需要使用工具
if unit_task["function_used"] == True:
#获取工具名字
tool_name = unit_task["function_name"]
#获取工具调用说明
tool_specifications = extended_tools_library.get_tool_by_name(tool_name)
#将工具调用说明添加到工具列表中
tools_list.append(tool_specifications)
else :
#如果任务不需要使用工具,那么工具列表为空
tools_list = "none functions"
#如果不需要工具调用
if tools_list == "none functions":
#构建系统提示语句
prompt = (
f"你是一个专业的任务执行AI,请根据任务目标、 分步任务列表与所指定的任务id,完成对应的该步任务。"
f"分步任务列表是按时间顺序排列,请根据前面任务执行结果,工具调用结果,进行推理说明,输出该步任务的结果。"
f"任务目标示例###{task_goal_example1}###"
f"分步任务列表示例###{task_list_example1}###"
f"任务结果必须以json格式输出,并以json代码块标记,执行任务结果示例###{task_result_example1}###"
f"当前任务目标是:###{task_objectives}###,当前分步任务列表是:###{task_list}###。"
)
#构建当前任务目标
ai_task = f"你当前需要执行的任务id是{task_id}。"
#构建对话
messages = [{"role": "system", "content": prompt },
{"role": "user", "content": ai_task}]
print("[DEBUG] 次级执行AI请求发送内容为:",messages,'\n')
response = openaiclient.chat.completions.create(
model="gpt-3.5-turbo-1106",
messages=messages ,
temperature=0
)
#构建空的工具调用结果
function_response = None
#获取AI全部回复内容
task_result = response.choices[0].message.content
print("[DEBUG] 次级执行AI全部回复内容为:",task_result,'\n')
#如果需要工具调用
else :
#构建系统提示语句
prompt = (
f"你是一个专业的任务执行AI,请根据任务目标、 分步任务列表与所指定的任务id,完成对应的该步任务。"
f"分步任务列表是按时间顺序排列,请根据前面任务执行结果,工具调用结果,进行推理说明,输出该步任务的结果。"
f"任务目标示例###{task_goal_example2}###"
f"分步任务列表示例###{task_list_example2}###"
f"工具调用结果示例###{tool_return2}###"
f"任务结果以json格式输出,并以json代码块标记执行,任务结果示例###{task_result_example2}###"
f"当前任务目标是:###{task_objectives}###,当前分步任务列表是:###{task_list}###,如果需要使用到工具,仅使用为您提供的工具###{tools_list}###。"
)
#构建当前任务目标
ai_task = f"你当前需要执行的任务id是{task_id}。"
#构建对话
messages = [{"role": "system", "content": prompt },
{"role": "user", "content": ai_task}]
print("[DEBUG] 次级执行AI请求发送内容为:",messages,'\n')
#向模型发送用户查询以及它可以访问的函数
response = openaiclient.chat.completions.create(
model="gpt-3.5-turbo-1106",
messages=messages ,
temperature=0,
tools=tools_list, #关于调用函数说明内容可以放在这里,也可以放在上面的content中,AI都会识别并使用
tool_choice="auto",
)
#构建空的工具调用结果
function_response = None
#获取AI全部回复内容
task_result = response.choices[0].message.content
print("[DEBUG] 次级执行AI全部回复内容为:",task_result,'\n')
#解析回复,如果AI进行工具调用将自动执行相关工具
function_response,task_result = self.parse_response(response,messages,tools_list)
try:
# 从任务结果示例中提取JSON部分
json_str = re.search(r'```json(.*?)```', task_result, re.S).group(1)
# 将JSON字符串转换为字典变量
task_result_dict = json.loads(json_str)
# 从字典变量中提取任务执行结果
task_result_new = task_result_dict["task_result"]
except:
print("[DEBUG] 无法正常提取任务结果,将直接采用回复内容",'\n')
task_result_new = task_result
#更新任务进度
my_calendar.update_task_progress(task_datetime = task['task_datetime'],
task_id = task_id,
function_response = function_response,
task_result = task_result_new
)
print("[DEBUG] 次级AI单元任务执行结果为:",task_result,'\n')
print("[DEBUG] 该单元任务执行结束!!!!!!!!!!!!!!!!!!",'\n')
#如果任务进度等于任务分步列表长度,说明全部任务已经完成
if task_id == task["task_distribution"]:
print("[DEBUG] 该任务列表已经全部完成~",'\n')
my_calendar.update_task_status(task['task_datetime'],"已完成")
#审查单元任务的AI代理(审查单元任务的输入输出是否正确,正确就录入任务数据库,错误就返回信息到任务数据库)
def review_unit_task_AI_agent(self,task_datetime):
print("[DEBUG]开始审查单元任务!!!!!!!!!!!!!!!!!!")
#任务目标示例
task_goal_example = '''我现在有100块,想买一个苹果,一个香蕉,一个橘子,想知道买完后还剩多少钱'''
#任务列表示例
task_list_example = '''
[
{
"task_id": 1,
"task_description": "获取苹果的单价",
"function_used": True,
"function_name": "get_the_price_of_the_item",
"function_parameters": {
"item": "苹果",
"unit": "人民币"
},
"function_response": "5块人民币",
"task_result": "一个苹果的价格是五块人民币"
},
{
"task_id": 2,
"task_description": "获取香蕉的单价",
"function_used": True,
"function_name": "get_the_price_of_the_item",
"function_parameters": {
"item": "香蕉",
"unit": "人民币"
},
"function_response": "10块人民币",
"task_result": "一个香蕉的价格是五块人民币"
}
]
'''
#审查任务结果示例
task_result_example = '''
根据您提供的任务列表,我将审查任务ID为2的任务。任务目标是计算买一个苹果、一个香蕉、一个橘子后剩余的钱数。
任务2的描述是 "获取香蕉的单价",工具已被正确使用(“get_the_price_of_the_item”函数),参数也正确(item为"香蕉",unit为"人民币")。工具的响应是"10块人民币"。
但是,在当前任务结果中,任务2的结果出现错误。任务结果为:“一个香蕉的价格是五块人民币”,实际的任务结果应该是:“一个香蕉的价格是十块人民币”。
审查结果如下:
```json
{
"task_id": 2,
"review_result": "incorrect",
"correct_task_result": "一个香蕉的价格是十块人民币"
}
```
任务2的结果不正确,请更正后继续。
'''
#根据日期时间,获取具体任务,需要重新获取任务,因为任务进度已经更新
task = my_calendar.query_scheduled_task_by_datetime(task_datetime)
#获取任务目标
task_objectives = task["task_objectives"]
#获取任务列表
task_list = task["task_list"]
#获取任务进度
task_progress = task["task_progress"]
#获取任务审查id
task_id = task_progress
#获取工具调用规范
tools_list = []
#根据任务审查id与任务列表,获取当前任务需要到的工具id
for unit_task in task_list:
#如果任务id与当前任务审查id相同
if unit_task["task_id"] == task_id:
#如果任务需要使用工具
if unit_task["function_used"] == True:
#获取工具名字
function_name = unit_task["function_name"]
#获取工具调用说明
tool_specifications = extended_tools_library.get_tool_by_name(function_name)
#将工具调用说明添加到工具列表中
tools_list.append(tool_specifications)
else :
#如果任务不需要使用工具,那么工具列表为空
tools_list = "none functions"
#构建系统提示语句
prompt = (
f"你是一个专业的任务执行结果审查AI,请根据任务目标、分步任务列表与所指定的任务id,审查该步任务是否被正确执行。"
f"分步任务列表是按时间顺序排列,请根据前面任务执行结果,进行推理说明,审查该步任务的执行结果,将审查结果以json格式输出,并以json代码块标记。”"
f"任务目标示例:###{task_goal_example}###"
f"分步任务列表示例:###{task_list_example}###"
f"审查结果示例:###{task_result_example}###"
f"当前任务目标是:###{task_objectives}###,当前分步任务列表是:###{task_list}###,如果需要使用到工具,仅使用为您提供的工具:###{tools_list}###。"
)
#指定审查任务id
task_review = f"你当前需要审查的任务id是{task_progress }。"
messages = [{"role": "system", "content": prompt },
{"role": "user", "content": task_review}]
print("[DEBUG] 次级审查AI请求发送内容为:",messages,'\n')
# 如果没有工具调用
if tools_list == "none functions":
response = openaiclient.chat.completions.create(
model="gpt-3.5-turbo-1106",
messages=messages ,
temperature=0
)
#获取AI全部回复内容
review_result = response.choices[0].message.content
print("[DEBUG] 次级审查AI内容为:",review_result,'\n')
# 如果有工具调用
else :
#向模型发送用户查询以及它可以访问的函数
response = openaiclient.chat.completions.create(
model="gpt-3.5-turbo-1106",
messages=messages ,
temperature=0,
tools=tools_list, #关于调用函数说明内容可以放在这里,也可以放在上面的content中,AI都会识别并使用
tool_choice="auto",
)
#获取AI全部回复内容
review_result = response.choices[0].message.content
print("[DEBUG] 次级审查AI全部回复内容为:",review_result,'\n')
#解析回复,如果AI进行工具调用将自动执行相关工具
function_response,review_result = self.parse_response(response,messages,tools_list)
# 从任务结果示例中提取JSON部分
json_str = re.search(r'```json(.*?)```', review_result, re.S).group(1)
# 将JSON字符串转换为字典变量
review_result_dict = json.loads(json_str)
# 从字典变量中提取任务审查结果
review_result_new = review_result_dict["review_result"]
# 解析审查结果
if review_result_new == "incorrect":
#提取正确结果
#correct_task_result = review_result_dict["correct_task_result"]
#直接回退进度
my_calendar.delete_task_progress(task['task_datetime'],task_progress)
print("[DEBUG] 任务执行结果错误~需要回退任务进度,并重新执行",'\n')
else:
print("[DEBUG] 任务执行结果正确~",'\n')
#如果任务进度等于任务分步列表长度,说明任务已经完成
if task_progress == task["task_distribution"]:
print("[DEBUG] 该任务列表已经全部完成~",'\n')
my_calendar.update_task_status(task['task_datetime'],"已完成")
print("[DEBUG] 任务执行结果审查结束~",'\n')
#执行与审查AI的解析回复函数
def parse_response(self,response,conversation_history,functions_list):
#提取AI回复内容中message部分
message = response.choices[0].message
#提取ai调用的所有工具内容
tool_calls = message.tool_calls
#存储调用工具里的函数回复
tool_return = None #以免没有工具调用时,无法返回工具调用结果
# 如果AI进行工具调用
if tool_calls:
try:
print("[DEBUG] 次级AI正在申请调用工具~",'\n')
# 只调用一个工具
tool_call = tool_calls[0]
#获取工具调用名称
tool_name = tool_call.function.name
#获取工具调用id
tool_id = tool_call.id
#获取工具调用参数
tool_arguments = tool_call.function.arguments
#获取工具调用附加回复
tool_content = message.content
#将函数输入参数转换为字典格式
print("[DEBUG] ",'传入参数为:',tool_arguments,'\n')
tool_arguments = json.loads(tool_arguments)
#调用函数,获得工具调用结果
tool_return = extended_tools_library.call_tool(tool_name,tool_arguments)
except Exception as e:
print("[ERROR] 主AI调用工具时出错,","错误信息为:",e,'\n')
tool_return = "[ERROR] 调用工具时出错,无法成功提取调用工具"
#print("[DEBUG] 工具调用附加说明:",tool_content)
print("[DEBUG] AI调用工具名字为:",tool_name,'传入参数为:',tool_arguments,'\n','工具调用结果为:',tool_return,'\n')
#构建AI的工具调用历史
tools=[{'id': tool_id,
'type': 'function',
'function': {'name': tool_name,
'arguments': str(tool_arguments)
}
}]
#把ai申请调用函数动作与工具调用结果拼接到对话历史中
conversation_history.append({"role": "assistant",
"tool_calls": tools,
})
conversation_history.append({"role": "tool",
"name": tool_name ,
"tool_call_id": tool_id,
"content": str(tool_return)})
print("[DEBUG] 正在将工具调用结果发送给次级AI:",conversation_history,'\n')
#向模型发送用户查询以及它可以访问的函数
Ai_response = openaiclient.chat.completions.create(
model="gpt-3.5-turbo-1106",
messages=conversation_history ,
temperature=0,
tools=functions_list, #关于调用函数说明内容可以放在这里,也可以放在上面的content中,AI都会识别并使用
tool_choice="auto",
)
content = Ai_response.choices[0].message.content
print("[DEBUG] 次级AI获知工具调用结果后的回复内容为:",content,'\n')
return tool_return,content
# 如果AI不进行工具调用
content = message.content
return tool_return,content
#发送日常任务执行结果通知的函数
def send_daily_task_notification(self,task):
#获取任务目标
task_objectives = task["task_objectives"]
#获取任务执行结果
task_list = task["task_list"]
#获取任务列表最后一个任务的执行结果
task_result = task_list[-1]["task_result"]
#构建任务结果通知语句
content = f"主人,您的代办任务:{task_objectives},任务已经完成,任务执行结果是:{task_result}。"
# 生成 AI 回复的语音
audio_path = TTS_vits.voice_bert_vits2(text=content)
# 生成语音的口型数据文件
mouth_data_path = ATM_vits.convertAudioToMouthData(audio_path)
data = {
"assistant_audio_path": audio_path,
"assistant_mouth_data_path": mouth_data_path,
"assistant_response_text": content
}
# 将数据转换为 JSON 字符串
json_data = json.dumps(data)
# 设置请求头
headers = {
'Content-Type': 'application/json',
'Content-Length': len(json_data)
}
# 发送 POST 请求
conn = http.client.HTTPConnection('localhost', 4000)
conn.request('POST', '/notification', json_data, headers)
# 获取响应
response = conn.getresponse()
# 打印响应结果
#print('Response Status:', response.status)
#print('Response Body:', response.read().decode())
# 关闭连接
conn.close()
#————————————————————————————————————————主AI默认工具库————————————————————————————————————————
class Main_AI_tool_library:
#初始化工具库
def __init__(self):
#工具库
self.tools_library = {}
#工具库的结构为字典结构
#key为整数变量与工具id相同,但为数字id: 0
#value为字典结构,存储工具的信息:{
#工具id "tool_id":str,
#工具名字 "tool_name":str,
#工具描述 "tool_description":str,
#调用规范 "tool_specifications":{},
#工具权限 "tool_permission":str",
# }
#添加工具
self.add_tools("1", self.function_search_related_tools,"0")
self.add_tools("2", self.function_get_schedule_tool_call_specification, "0")
#self.add_tools("3", self.function_create_a_task_list, "0")
#搜索拓展工具库中的工具函数------------------------------------------------
def search_related_tools(self, tool_description):
#查询向量库,获取相似度最高前N个文本描述
results = collection.query(
query_texts=tool_description,
n_results=3,
include=[ "documents", "distances"])
print("[DEBUG] 语义搜索到的工具:",results,'\n')
#提取关联的函数id,注意返回的是字典结构,但key对应的value是嵌套列表结构,比如ids键对应的值为[['0','1','2']]
tools_id_list = results['ids'][0] # 获取ids键的值
print("[DEBUG] 提取到的工具id:" ,tools_id_list,'\n')
#根据函数id,获取相应的工具调用规范
tools_specifications_list = extended_tools_library.get_tool_by_id_list(tools_id_list)
print("[DEBUG] 所有工具相应的调用规范:" ,tools_specifications_list,'\n')
results_return = "搜索到的相关工具为:::" + str(tools_specifications_list)
return results_return
#搜索拓展工具库中的工具调用规范
function_search_related_tools = {
"type": "function",
"function": {
"name": "search_related_tools",
"description": "根据用户需要到的工具的功能描述,在工具库中搜索,将相关功能的工具返回",
"parameters": {
"type": "object",
"properties": {
"tool_description": {
"type": "string",
"description": "关于工具功能的详细描述",
}
},
"required": ["tool_description"]
},
}
}
#查询日程表的工具函数------------------------------------------------
def get_schedule_tool_call_specification(self, tool_class):
#创建存储函数说明的列表
tool_specifications_list = []
if tool_class == "创建定时任务的分步式任务列表":
#将添加类的工具说明添加到函数列表中
tool_specifications_list.append(self.function_create_a_task_list)
if tool_class == "添加定时任务":
#将添加类的工具说明添加到函数列表中
tool_specifications_list.append(my_calendar.function_add_scheduled_task)
elif tool_class == "删除定时任务":
#将删除类的工具说明添加到函数列表中
tool_specifications_list.append(my_calendar.function_delete_scheduled_task)
elif tool_class == "查询定时任务":
#将查询类的工具说明添加到函数列表中
tool_specifications_list.append(my_calendar.function_query_scheduled_task_by_datetime)
tool_specifications_list.append(my_calendar.function_query_scheduled_task_by_date)
return tool_specifications_list
#查询日程表的工具调用规范
function_get_schedule_tool_call_specification = {
"type": "function",
"function": {
"name": "get_schedule_tool_call_specification",
"description": "获取“创建定时任务的分步式任务列表”,“添加新的定时任务”,“删除定时任务”和“查询定时任务”的工具调用规范",
"parameters": {
"type": "object",
"properties": {
"tool_class": {
"type": "string",
"description": "需要获取的日程表工具调用规范",
"enum": ["创建定时任务的分步式任务列表","添加定时任务","删除定时任务","查询定时任务"],
}
},
"required": ["tool_class"]
},
}
}
#根据任务目标创建分步式任务的AI代理------------------------------------------------
def create_a_task_list(self,task_objectives):
#任务目标示例
task_objectives_example = '''我现在有100块,请帮我计算一下五个苹果的价格,买完五个苹果后,我还剩多少钱?'''
#任务列表格式示例
task_list_example = ''' 据用户的任务目标,我们需要创建一个包含以下步骤的任务列表:
1. 理解任务目标:
- 用户在当前有100块钱。
- 用户想知道五个苹果的价格。
- 用户想知道购买五个苹果后剩余多少钱。
2. 创建任务列表:
1. 使用函数`get_the_price_of_the_item`,输入物品名称为"苹果",金额单位为"人民币",获取苹果的单价。
2. 将获取到的苹果单价乘以5,得到五个苹果的总价。
3. 将用户当前的钱数减去五个苹果的总价,得到购买五个苹果后剩余的钱数。
以下是符合要求的任务列表JSON数组:
```json
[
{
"task_id": 1,
"task_description": "获取苹果的单价",
"function_used": True,
"function_name": "get_the_price_of_the_item",
"function_parameters": {
"item": "苹果",
"unit": "人民币"
}
},
{
"task_id": 2,
"task_description": "计算五个苹果的总价",
"function_used": False
},
{
"task_id": 3,
"task_description": "计算购买五个苹果后剩余的钱数",
"function_used": False
}
}
]
```
'''
#次级AI挂载的函数功能列表
tools_list = []
#从数据库获取权限为1的函数功能列表,根据id,再从ai函数数据库读取数据,作为次级ai挂载函数功能列表
tools_list = extended_tools_library.get_tool_by_permission("1")
#构建prompt
prompt = (
f"你是一个专业的任务创建AI,请根据用户提出的任务目标,创建一个实现该目标的JSON数组的任务列表。"
f"根据最终目标创建每一步任务,每步任务应该详细说明,每步任务应该尽量使用库中的工具完成,当前工具库为###{tools_list}###。"
f"确保所有任务ID按时间顺序排列。第一步始终是关于任务目标的理解,以及拆分任务的推理说明,尽可能详细。"
f"任务目标示例:###{task_objectives_example}###"
f"任务列表示例:###{task_list_example}###"
)
# 获取当前日期和时间
current_datetime = datetime.datetime.now()
# 格式化输出日期和时间
formatted_datetime = current_datetime.strftime("%Y-%m-%d %H:%M:%S")
# 获取星期几
weekday = current_datetime.strftime("%A")
#添加到系统提示语句前面
new_prompt = "【现实当前时间】:"+ formatted_datetime +' '+ weekday +'\n'+prompt
#构建messages
messages = [{"role": "system", "content": new_prompt },
{"role": "user", "content": task_objectives}]
#向模型发送用户查询以及它可以访问的函数
response = openaiclient.chat.completions.create(
model="gpt-3.5-turbo-16k",
messages=messages ,
)
#返回任务列表
task_list = response.choices[0].message.content
return task_list
#配套的供AI申请调用的函数说明
function_create_a_task_list = {
"type": "function",
"function": {
"name": "create_a_task_list",
"description": "输入定时任务的目标,代理AI会创建如何执行定时任务的分步式任务列表,并返回该列表",
"parameters": {
"type": "object",
"properties": {
"task_objectives": {
"type": "string",
"description": "关于定时任务目标的详细描述",
}
},
"required": ["task_objectives"]
},
}
}
#添加工具
def add_tools(self, tool_id,tool_specifications, tool_permission):
tool_name = tool_specifications["function"]["name"]
tool_description = tool_specifications["function"]["description"]
#构建工具结构
tools = {
"tool_id": tool_id,
"tool_name": tool_name,
"tool_description": tool_description,
"tool_specifications": tool_specifications,
"tool_permission": tool_permission
}
#写入数据库
self.tools_library[int(tool_id)] = tools
#根据函数权限,获取相应的全部工具说明
def get_tools_by_permission(self, permission):
tools_list = []
#遍历工具库,注意是字典结构,所以需要用到items()方法
for tool_id, tool in self.tools_library.items():
if tool["tool_permission"] == permission:
#只获取工具说明的内容
tool_call = tool["tool_specifications"].copy()
tools_list.append(tool_call)
return tools_list
#————————————————————————————————————————主AI对话记忆库————————————————————————————————————————
class Ai_memory:
#初始化,创建实例时,输入历史文件路径
def __init__(self,file_path):
#文件路径
self.file_path = file_path
#存储可发送的对话历史的列表
self.conversation_history = []
#存储完整的对话历史的列表
self.conversation_history_all = []
#在文件夹下寻找conversation_history.json文件,如果存在则读取json文件(增加错误跳过语句)
try:
if os.path.exists(os.path.join(self.file_path, "conversation_history.json")):
with open(os.path.join(self.file_path, "conversation_history.json"), "r", encoding="utf-8") as f:
data = json.load(f)
# 检查一下记忆数据是否合规有效
if self.check_conversation_history(data) == 'Correct':
self.conversation_history = data
print("[DEBUG] 已成功读取对话历史文件!!!!!!!!!!!!!)",'\n')
else:
print("[DEBUG] 历史文件内容格式存在错误!!!!!!!!!!!!!)",'\n')
except:
print("[DEBUG] 读取最近对话历史文件失败!!!!!!!!!!!!!)",'\n')
#在文件夹下寻找conversation_history_all.json文件,如果存在则读取json文件(增加错误跳过语句)
try:
if os.path.exists(os.path.join(self.file_path, "conversation_history_all.json")):
with open(os.path.join(self.file_path, "conversation_history_all.json"), "r", encoding="utf-8") as f:
data = json.load(f)
# 检查一下记忆数据是否合规有效
if self.check_conversation_history(data) == 'Correct':
self.conversation_history_all = data
print("[DEBUG] 已成功读取完整对话历史文件!!!!!!!!!!!!!)",'\n')
else:
print("[DEBUG] 完整历史文件内容格式存在错误!!!!!!!!!!!!!)",'\n')
except:
print("[DEBUG] 读取完整对话历史文件失败!!!!!!!!!!!!!)",'\n')
#对话历史文件检查函数
def check_conversation_history(self,data):
assistant_tool_calls = []
tool_tool_call_ids = []
# 处理 assistant 角色的数据,获取工具调用id
for item in data:
if item.get("role") == "assistant" and "tool_calls" in item:
tool_calls = item["tool_calls"]
for tool_call in tool_calls:
if "id" in tool_call:
assistant_tool_calls.append(tool_call["id"])
# 处理 tool 角色的数据,获取工具调用id
for item in data:
if item.get("role") == "tool" and "tool_call_id" in item:
tool_tool_call_ids.append(item["tool_call_id"])
# 检查两个id列表是否相同
if set(assistant_tool_calls) == set(tool_tool_call_ids):
return("Correct")
else:
return("Error")
#获取最近的对话历史
def read_log(self):
return self.conversation_history
#记录对话历史
def log_message(self,role,content=None,tool_calls=None,tool_result=None):
#用户内容格式参考
self.user_content_structure = {"role": "user",
"content": "What's the weather like in Boston?"}
#AI回复格式参考
self.AI_content_structure = {"role": "assistant",
"content": "The current weather in Boston is sunny and windy with a temperature of 72 degrees Fahrenheit."}
#AI调用工具格式参考
self.function_call_structure = {"role": "assistant",
"content": None ,
'tool_calls': [{'id': 'call_o7uyztQLeVIoRdjcDkDJY3ni',
'type': 'function',
'function': {'name': 'get_current_weather','arguments': '{\n "location": "Tokyo",\n "format": "celsius"\n}'}
},
{'id': 'call_o7uyztQLeVIoRdjcDkDJYxxx',
'type': 'function',
'function': {'name': 'get_current_weather','arguments': '{\n "location": "beijing",\n "format": "celsius"\n}'}
},
]
}
#工具调用结果格式参考
self.function_return_result ={"role": "tool",
"tool_call_id": 'call_o7uyztQLeVIoRdjcDkDJY3ni',
"name": 'get_current_weather',
"content": "15",
}
#如果是用户输入消息
if role == "user":
The_message = {"role": "user", "content": content}
#如果是AI回复
elif role == "assistant":
#如果是通常回复
if tool_calls is None:
The_message = {"role": "assistant", "content": content}
#如果是工具调用
else:
tools = []
for tool_call in tool_calls:
tool={'id': tool_call.id,
'type': 'function',
'function': {'name': tool_call.function.name,
'arguments': tool_call.function.arguments
}
}
tools.append(tool)
The_message = {"role": "assistant",
"content": content ,
"tool_calls": tools,
}
#如果是工具调用结果
elif role == "tool":
#如果content是列表,表明需要记录的是搜索工具的结果
if isinstance(tool_result["content"],list):
#使用新列表变量,来保存,避免原列表被修改
content_copy = []
#将content_copy转化成字符串变量,避免请求时格式错误
content_copy = json.dumps(tool_result["content"])
#如果是字典,表明需要记录的是工具的结果
elif isinstance(tool_result["content"],dict):
#使用新字典变量,来保存,避免原字典被修改