knowledge_robot.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. from openai import OpenAI
  2. from text_vector_database import knowledge_comp_rule_regulation_db, knowledge_comp_propaganda_db,knowledge_comp_water_treatment_db,knowledge_comp_project_plan_db,knowledge_comp_operation_report_db
  3. import json
  4. import time
  5. from plc_query_tool import plc_query_helper
  6. from web_tool import web_search_tool
  7. class KnowAgent:
  8. """致道Agent"""
  9. def __init__(self):
  10. # API
  11. self.client = OpenAI(
  12. api_key="sk-912ca44a0c04483f9de967de37e8d0ba",
  13. base_url="https://api.deepseek.com"
  14. )
  15. # 参数
  16. self.max_iterations = 8
  17. self.current_iteration = 0
  18. self.temperature=1.0
  19. # 工具定义
  20. self.tool_maps = {
  21. 'knowledge_comp_rule_regulation_db': knowledge_comp_rule_regulation_db.search,
  22. 'knowledge_comp_propaganda_db': knowledge_comp_propaganda_db.search,
  23. 'knowledge_comp_water_treatment_db': knowledge_comp_water_treatment_db.search,
  24. 'knowledge_comp_project_plan_db': knowledge_comp_project_plan_db.search,
  25. 'knowledge_comp_operation_report_db': knowledge_comp_operation_report_db.search,
  26. 'plc_query_helper':plc_query_helper,
  27. 'web_search_tool':web_search_tool.search
  28. }
  29. self.tool_names_map = {
  30. 'knowledge_comp_rule_regulation_db': '公司规章制度知识库',
  31. 'knowledge_comp_propaganda_db': '公司宣传资料产品介绍知识库',
  32. 'knowledge_comp_water_treatment_db': '污水处理工艺知识库',
  33. 'knowledge_comp_project_plan_db': '污水处理厂项目方案知识库',
  34. 'knowledge_comp_operation_report_db': '污水处理厂运行报告知识库',
  35. 'plc_query_helper':'PLC点位查询工具',
  36. 'web_search_tool': '联网搜索工具',
  37. ' ': '未知工具',
  38. }
  39. self.tools = [
  40. # 公司规章制度知识库
  41. {
  42. "type": "function",
  43. "function": {
  44. "name": "knowledge_comp_rule_regulation_db",
  45. "description": "这是一个关于公司规章制度的RAG知识库,存放着公司行政、人力、考勤、培训等内容,相当于一本规章制度的百科全书,当你需要查询公司规章制度时,请调用本工具。",
  46. "parameters": {
  47. "type": "object",
  48. "properties": {
  49. "query": {
  50. "type": "string",
  51. "description": "这个参数是需要查询的内容,请输入语义连贯的句子进行检索。",
  52. }
  53. },
  54. "required": ["query"]
  55. },
  56. }
  57. },
  58. # 公司宣传资料产品介绍知识库
  59. {
  60. "type": "function",
  61. "function": {
  62. "name": "knowledge_comp_propaganda_db",
  63. "description": "这是一个关于公司产品介绍和宣传资料的RAG知识库,存放着金科环境公司的简介、发展历程,水萝卜智能体产品和新水岛产品的介绍和宣传资料,也包括了金科环境公司的大事记和新闻报道等,当你需要查询公司的发展情况、水萝卜和新水岛产品相关资料时,请调用本工具。",
  64. "parameters": {
  65. "type": "object",
  66. "properties": {
  67. "query": {
  68. "type": "string",
  69. "description": "这个参数是需要查询的内容,请输入语义连贯的句子进行检索。",
  70. }
  71. },
  72. "required": ["query"]
  73. },
  74. }
  75. },
  76. # 污水处理工艺知识库
  77. {
  78. "type": "function",
  79. "function": {
  80. "name": "knowledge_comp_water_treatment_db",
  81. "description": "这是一个关于污水处理厂污水处理工艺的RAG知识库,它就像一个工艺专家的百科全书,存放着污水处理领域的物理、化学、生物等知识,水处理工艺知识以及行业标准规范等,当你有问题需要咨询水处理工艺专家时,请调用本工具。",
  82. "parameters": {
  83. "type": "object",
  84. "properties": {
  85. "query": {
  86. "type": "string",
  87. "description": "这个参数是需要查询的内容,请输入语义连贯的句子进行检索。",
  88. }
  89. },
  90. "required": ["query"]
  91. },
  92. }
  93. },
  94. # 污水处理项目方案
  95. {
  96. "type": "function",
  97. "function": {
  98. "name": "knowledge_comp_project_plan_db",
  99. "description": "这是一个关于污水处理厂竞标项目方案的RAG知识库,存放了原始的污水处理厂项目方案报告,这通常包括项目背景、水厂设计依据、产水能力、工艺和技术实施方案、运营成本等内容,当你需要浏览污水处理厂的投标方案,请调用本工具。",
  100. "parameters": {
  101. "type": "object",
  102. "properties": {
  103. "query": {
  104. "type": "string",
  105. "description": "这个参数是需要查询的内容,请输入语义连贯的句子进行检索。",
  106. }
  107. },
  108. "required": ["query"]
  109. },
  110. }
  111. },
  112. # 污水处理厂运行报告
  113. {
  114. "type": "function",
  115. "function": {
  116. "name": "knowledge_comp_operation_report_db",
  117. "description": "这是一个关于污水处理运行报告的RAG知识库,当你需要参考水厂运行报告的内容时,请调用本工具。",
  118. "parameters": {
  119. "type": "object",
  120. "properties": {
  121. "query": {
  122. "type": "string",
  123. "description": "这个参数是需要查询的内容,请输入语义连贯的句子进行检索。",
  124. }
  125. },
  126. "required": ["query"]
  127. },
  128. }
  129. },
  130. # 污水处理厂plc点位查询工具
  131. {
  132. "type": "function",
  133. "function": {
  134. "name": "plc_query_helper",
  135. "description": "这是一个污水处理厂PLC点位数据查询工具,当用户需要查询水厂的传感器的监测数据时,如超滤或反渗透过程中的电导、渗透率、流量、设备功率电耗、药耗等,可以尝试使用本工具。如果查询成功,工具会返回一些候选的点位数据,包括名称和数值。",
  136. "parameters": {
  137. "type": "object",
  138. "properties": {
  139. "query": {
  140. "type": "string",
  141. "description": "这个参数是需要查询的点位名称。如反渗透电导、超滤流量、总进水流量等",
  142. }
  143. },
  144. "required": ["query"]
  145. },
  146. }
  147. },
  148. # 联网搜索工具
  149. {
  150. "type": "function",
  151. "function": {
  152. "name": "web_search_tool",
  153. "description": "这是一个网络搜索引擎工具,如果知识库没有合适的参考内容,可以使用此工具联网搜索。",
  154. "parameters": {
  155. "type": "object",
  156. "properties": {
  157. "query": {
  158. "type": "string",
  159. "description": "请输入搜索关键词或问题",
  160. }
  161. },
  162. "required": ["query"]
  163. },
  164. }
  165. },
  166. ]
  167. # 系统提示词 - 指导AI如何智能使用工具
  168. self.system_prompt = f"""你是一个专业的水处理行业AI助手,请遵循以下规则回答问题:
  169. 1. **知识查询判断**:
  170. - 当需要查询污水处理厂PLC传感器数据时,请先使用【plc_query_helper】工具。(PLC点位查询工具)
  171. - 当用户询问公司规章制度、行政管理、人力资源等问题时,使用【knowledge_comp_rule_regulation_db】工具。(公司规章制度知识库)
  172. - 当用户询问公司介绍、产品信息(水萝卜智能体、新水岛)、发展历程时,使用【knowledge_comp_propaganda_db】工具。(公司宣传资料知识库)
  173. - 当用户询问污水处理工艺、技术标准、行业规范时,使用【knowledge_comp_water_treatment_db】工具。(污水处理工艺知识库)
  174. - 当用户询问项目方案、投标资料时,使用【knowledge_comp_project_plan_db】工具。(污水处理项目方案知识库)
  175. - 当用户询问运行报告、运营数据时,使用【knowledge_comp_operation_report_db】工具(污水处理厂运行报告知识库)
  176. 2. **智能工具调用**:
  177. - 只有当问题确实需要专业知识库支持时才调用工具
  178. - 对于简单的操作指令不需要调用任何工具,可以简单回复“好的”
  179. - 如果问题明显不需要专业知识,直接友好回答
  180. 3. **诚实性原则**:
  181. - 如果知识库中没有相关信息,如实告知用户"在现有知识库中未查阅到相关信息"
  182. - 不要编造不存在的信息
  183. 4. **回答要求**:
  184. - 专业问题:先给出结论,再分点解释
  185. - 操作问题:直接给出步骤说明
  186. - 知识库查询结果:注明信息来源
  187. - 当PLC查询工【plc_query_helper】返回很多候选数据时,请结合用户的需求挑选相关数据进行回复。
  188. - 如果需要分析一些深度污水处理领域问题,可以结合PLC查询工具来获取相关数据,然后可以查询相关知识库来检索知识,两者结合进行分析回答。
  189. 请根据问题内容智能判断是否需要调用工具,以及调用哪个工具最合适。"""
  190. # 存放历史对话,key为会话ID,value为会话内容,会话内容为列表
  191. self.history_chat = {
  192. '400': [{"role": "system", "content": self.system_prompt}]}
  193. def process_stream_response(self, stream_response, on_chunk=None):
  194. """
  195. 处理流式响应,逐块输出内容
  196. """
  197. full_content = ""
  198. for chunk in stream_response:
  199. if chunk.choices[0].delta.content is not None:
  200. content_chunk = chunk.choices[0].delta.content
  201. full_content += content_chunk
  202. # 如果有回调函数,调用它处理每个chunk
  203. if on_chunk and callable(on_chunk):
  204. on_chunk(content_chunk)
  205. else:
  206. # 默认输出方式:逐块打印
  207. print(content_chunk, end="", flush=True)
  208. return full_content
  209. def send_messages(self, messages, stream:bool=False):
  210. """
  211. 发送消息给模型
  212. """
  213. try:
  214. if not messages or messages[0]["role"] != "system":
  215. # 深拷贝消息列表避免修改原数据
  216. enhanced_messages = [{"role": "system", "content": self.system_prompt}]
  217. enhanced_messages.extend(messages)
  218. else:
  219. enhanced_messages = messages
  220. # 构建请求参数
  221. request_data = {
  222. 'model':"deepseek-chat",
  223. "messages": enhanced_messages,
  224. "stream": stream,
  225. "tools": self.tools,
  226. "temperature": self.temperature,
  227. }
  228. if stream:
  229. # 流式响应
  230. response = self.client.chat.completions.create(**request_data)
  231. return response # 返回流式响应对象
  232. else:
  233. # 非流式响应
  234. response = self.client.chat.completions.create(**request_data)
  235. return response.choices[0].message
  236. except Exception as e:
  237. print(f"Error: {e}")
  238. def function_call(self, function:str, arguments:str, project_id:int):
  239. if function not in self.tool_maps:
  240. return f"函数名称不在工具列表中,工具列表:{list(self.tool_maps.keys())}"
  241. if not arguments:
  242. return f"query参数不能为空"
  243. return str(self.tool_maps[function](arguments, project_id=project_id))
  244. def simulate_stream_response(self, content, delay=0.01):
  245. """
  246. 模拟流式响应输出
  247. :param content: 内容
  248. :param delay: 输出延迟
  249. """
  250. for char in content:
  251. print(char, end='', flush=True)
  252. time.sleep(delay)
  253. print() # 换行
  254. def simulate_stream_response_V2(self, content, base_delay=0.02):
  255. """
  256. 模拟更自然的流式响应输出
  257. :param content: 内容
  258. :param base_delay: 基础延迟
  259. """
  260. # 定义不同类型字符的延迟系数
  261. punctuation_delays = {
  262. '.': 0.3, # 句号 - 较长停顿
  263. '!': 0.25, # 感叹号 - 中等停顿
  264. '?': 0.25, # 问号 - 中等停顿
  265. ',': 0.15, # 逗号 - 短暂停顿
  266. ';': 0.15, # 中文分号
  267. ',': 0.15, # 中文逗号
  268. '。': 0.3, # 中文句号
  269. ';': 0.15, # 分号
  270. ':': 0.12, # 冒号
  271. '-': 0.08, # 破折号
  272. '"': 0.1, # 引号
  273. "'": 0.1, # 单引号
  274. '\n': 0.4, # 换行符
  275. '\t': 0.2, # 制表符
  276. }
  277. for i, char in enumerate(content):
  278. # 计算基础延迟
  279. delay = base_delay
  280. # 根据字符类型调整延迟
  281. if char in punctuation_delays:
  282. delay = punctuation_delays[char]
  283. elif char.isupper(): # 大写字母稍慢一点
  284. delay = base_delay * 1.2
  285. elif char.isdigit(): # 数字稍快一点
  286. delay = base_delay * 0.8
  287. elif char in 'abcdefghijklmnopqrstuvwxyz': # 小写字母正常速度
  288. delay = base_delay
  289. else: # 其他字符(如中文)略慢
  290. delay = base_delay * 1.1
  291. # 添加随机波动(±20%)
  292. import random
  293. delay *= random.uniform(0.8, 1.2)
  294. print(char, end='', flush=True)
  295. time.sleep(delay)
  296. print() # 换行
  297. def chat(self, user_input:str,project_id:int, stream:bool=True, conversion_id='400'):
  298. # 初始化对话历史
  299. print('='*50)
  300. print("开始对话...")
  301. print(f"User>\t {user_input}")
  302. # 检查历史会话
  303. if conversion_id in self.history_chat:
  304. history_messages = self.history_chat[conversion_id]
  305. history_messages.extend([{"role": "user", "content": user_input}])
  306. else:
  307. history_messages = [{"role": "user", "content": user_input}]
  308. self.history_chat[conversion_id] = history_messages
  309. tools_history = ['<think>']
  310. while self.current_iteration < self.max_iterations:
  311. water_robot_message = self.send_messages(history_messages, stream=False)
  312. # 检查是否由工具调用
  313. if hasattr(water_robot_message, 'tool_calls') and water_robot_message.tool_calls:
  314. # 添加助手消息到历史
  315. history_messages.append({
  316. "role": "assistant",
  317. "content": water_robot_message.content,
  318. "tool_calls": [
  319. {
  320. "id": tool_call.id,
  321. "type": tool_call.type,
  322. "function": {
  323. "name": tool_call.function.name,
  324. "arguments": tool_call.function.arguments
  325. }
  326. } for tool_call in water_robot_message.tool_calls
  327. ]
  328. })
  329. # 处理每个工具调用
  330. for i, tool_call in enumerate(water_robot_message.tool_calls):
  331. print(f"🔍 调用工具{i}:【{self.tool_names_map.get(tool_call.function.name, ' ')}】 ")
  332. tools_history.append(f"🔍 调用工具{i}:【{self.tool_names_map.get(tool_call.function.name, ' ')}】 ")
  333. arguments = json.loads(tool_call.function.arguments)
  334. print(f"📋 工具{i}参数:{arguments} ")
  335. tools_history.append(f"📋 工具{i}参数:{arguments} ")
  336. tool_result = know_agent.function_call(tool_call.function.name, arguments.get("query", " "), project_id=project_id)
  337. # 添加工具响应
  338. history_messages.append({
  339. "role": "tool",
  340. "tool_call_id": tool_call.id,
  341. "content": tool_result
  342. })
  343. self.current_iteration += 1
  344. else:
  345. # 没有工具调用,显示最终答案
  346. print(f"Model>")
  347. self.simulate_stream_response(water_robot_message.content)
  348. history_messages.append({
  349. "role": "assistant",
  350. "content": water_robot_message.content
  351. })
  352. break
  353. if self.current_iteration >= self.max_iterations: # 处理迭代次数超限的情况
  354. # 告诉模型迭代次数超限
  355. history_messages.append({
  356. "role": "tool",
  357. "tool_call_id": 0,
  358. "content": "当前工具调用次数达到上限,请根据现有结果进行分析回复。"
  359. })
  360. water_robot_message = self.send_messages(history_messages, stream=False)
  361. # 添加最后一次模型给出的结果
  362. history_messages.append({
  363. "role": "assistant",
  364. "content": water_robot_message.content
  365. })
  366. print("达到最大迭代次数,对话结束。")
  367. # 重置迭代计数器
  368. self.current_iteration = 0
  369. # 保存对话历史
  370. self.history_chat[conversion_id].extend(history_messages)
  371. tools_history.append('</think>')
  372. return ('\n'.join(tools_history) + history_messages[-1].get('content', 'null')) if history_messages[-1].get('role', ' ')=='assistant' else 'null'
  373. know_agent = KnowAgent()
  374. if __name__ == "__main__":
  375. while True:
  376. user_input = input("\n用户输入: ")
  377. if user_input.lower() == "exit":
  378. break
  379. res = know_agent.chat(user_input, 92)
  380. pass