knowledge_robot.py 15 KB

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