LLMAgent.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. # -*- coding: utf-8 -*-
  2. # @Author: privacy
  3. # @Date: 2024-06-11 13:43:14
  4. # @Last Modified by: privacy
  5. # @Last Modified time: 2024-12-23 18:17:53
  6. import re
  7. import json
  8. from enum import Enum
  9. from abc import ABC, abstractmethod
  10. from typing import Dict, List, Optional, Union
  11. import instructor
  12. from openai import OpenAI
  13. from pydantic import BaseModel
  14. class BaseLlmConfig(ABC):
  15. def __init__(
  16. self,
  17. model: Optional[str] = None,
  18. base_url: Optional[str] = None,
  19. temperature: float = 0.0,
  20. max_tokens: int = 3000,
  21. top_p: float = 1.0
  22. ):
  23. self.model = model
  24. self.base_url = base_url
  25. self.temperature = temperature
  26. self.max_tokens = max_tokens
  27. self.top_p = top_p
  28. class LLMBase(ABC):
  29. def __init__(self, config: Optional[BaseLlmConfig] = None):
  30. """Initialize a base LLM class
  31. :param config: LLM configuration option class, defaults to None
  32. :type config: Optional[BaseLlmConfig], optional
  33. """
  34. if config is None:
  35. self.config = BaseLlmConfig()
  36. else:
  37. self.config = config
  38. @abstractmethod
  39. def generate_response(self, messages: List[dict]):
  40. """
  41. Generate a response based on the given messages.
  42. Args:
  43. messages (list): List of message dicts containing 'role' and 'content'.
  44. Returns:
  45. str: The generated response.
  46. """
  47. pass
  48. class LLMAgent(LLMBase):
  49. def __init__(self, config: Optional[BaseLlmConfig] = None):
  50. super().__init__(config)
  51. if not self.config.model:
  52. self.config.model = "gpt-4o"
  53. self.client = OpenAI(
  54. base_url=self.config.base_url,
  55. api_key='ollama'
  56. )
  57. def _parse_response(self, response, tools):
  58. """
  59. Process the response based on whether tools are used or not.
  60. Args:
  61. response: The raw response from API.
  62. tools: The list of tools provided in the request.
  63. Returns:
  64. str or dict: The processed response.
  65. """
  66. if tools:
  67. processed_response = {
  68. "content": response.choices[0].message.content,
  69. "tool_calls": []
  70. }
  71. if response.choices[0].message.tool_calls:
  72. for tool_call in response.choices[0].message.tool_calls:
  73. processed_response["tool_calls"].append({
  74. "name": tool_call.function.name,
  75. "arguments": json.loads(tool_call.function.arguments)
  76. })
  77. return processed_response
  78. else:
  79. return response.choices[0].message.content
  80. def generate_response(
  81. self,
  82. messages: List[Dict[str, str]],
  83. response_format=None,
  84. tools: Optional[List[Dict]] = None,
  85. tool_choice: str = "auto",
  86. ):
  87. """
  88. Generate a response based on the given messages using OpenAI.
  89. Args:
  90. messages (list): List of message dicts containing 'role' and 'content'.
  91. response_format (str or object, optional): Format of the response. Defaults to "text".
  92. tools (list, optional): List of tools that the model can call. Defaults to None.
  93. tool_choice (str, optional): Tool choice method. Defaults to "auto".
  94. Returns:
  95. str: The generated response.
  96. """
  97. params = {
  98. "model": self.config.model,
  99. "messages": messages,
  100. "temperature": self.config.temperature,
  101. "max_tokens": self.config.max_tokens,
  102. "top_p": self.config.top_p
  103. }
  104. if response_format:
  105. params["response_format"] = response_format
  106. if tools:
  107. params["tools"] = tools
  108. params["tool_choice"] = tool_choice
  109. response = self.client.chat.completions.create(**params)
  110. return self._parse_response(response, tools)
  111. def get_proj(input_json: dict, standard: str):
  112. # client = LLMAgent(
  113. # config=BaseLlmConfig(
  114. # base_url='http://180.76.147.97:11434/v1',
  115. # # model='qwen2:7b',
  116. # model='qwen2.5:7b',
  117. # # model='sam4096/qwen2tools:latest',
  118. # # model='wangshenzhi/llama3.1_8b_chinese_chat:latest',
  119. # temperature=1.0,
  120. # max_tokens=32 * 1024
  121. # )
  122. # )
  123. class LevelEnum(str, Enum):
  124. A = 'A'
  125. B = 'B'
  126. C = 'C'
  127. D = 'D'
  128. class ResInfo(BaseModel):
  129. 最终得分: int
  130. 最终等级: LevelEnum
  131. 评价原因: str
  132. client = instructor.from_openai(
  133. OpenAI(
  134. # base_url='http://180.76.147.97:11434/v1',
  135. base_url='https://dashscope.aliyuncs.com/compatible-mode/v1',
  136. # api_key='ollama'
  137. api_key='sk-45971a6af6d94ccd89321f8f6d370b38'
  138. ),
  139. mode=instructor.Mode.JSON,
  140. )
  141. messages = [
  142. {"role": "system", "content": "你是一位优秀的数据分析师"},
  143. {"role": "user", "content": "Q: 现在有这样一个数据 input_json: %s 数据集以JSON形式呈现,在数据集 input_json 上分析结果,%s,请一步步进行推理并得出结论。如果无法得出结论,则返回一个中等的评价。" % (input_json, standard)}
  144. ]
  145. response = client.chat.completions.create(
  146. # model='qwen2.5:7b',
  147. model='qwen-turbo',
  148. # model='wangshenzhi/llama3.1_8b_chinese_chat:latest',
  149. response_model=ResInfo,
  150. messages=messages,
  151. max_retries=3
  152. )
  153. return response
  154. if __name__ == '__main__':
  155. input_json = """
  156. [{'page_numbers': [351],
  157. 'table': [['项目名称', '七一七所实验楼操作间改造工程项目施工合同'],
  158. ['项目所在地', '武汉市江夏区阳光大道717号'],
  159. ['发包人名称', '中国船舶重工集团公司第七一七研究所'],
  160. ['发包人地址', '中国船舶重工集团公司第七一七研究所'],
  161. ['发包人联系人及电话', '刘伟13521523315'],
  162. ['合同价格', '315.1995万'],
  163. ['开工日期', '2019.06.10'],
  164. ['竣工日期', '2019.06.30'],
  165. ['承担的工作', '在现有值班工作区修建噪音隔离工作间,主体框架结构,天花板采用加厚保温隔热隔音防火板,室内安装空调柜机1台。'],
  166. ['工程质量', '合格'],
  167. ['项目经理', '游洋'],
  168. ['技术负责人', '魏娟'],
  169. ['总监理工程师及电话', '蔡工13521523215'],
  170. ['项目描述', '在现有值班工作区修建噪音隔离工作间,主体框架结构,天花板采用加厚保温隔热隔音防火板,室内安装空调柜机1台。'],
  171. ['备注', '']]},
  172. {'page_numbers': [370],
  173. 'table': [['项目名称', '南京医药湖北工业园(续建)项目高位立体库地坪施工'],
  174. ['项目所在地', '武汉江夏区藏龙岛科技园凤凰大道18号'],
  175. ['发包人名称', '南京医药湖北有限公司'],
  176. ['发包人地址', '南京医药园湖北分部'],
  177. ['发包人联系人及电话', '张工18911231255'],
  178. ['合同价格', '538万'],
  179. ['开工日期', '2020.12.18'],
  180. ['竣工日期', '2021.04.18'],
  181. ['承担的工作', '详见工程量清单'],
  182. ['工程质量', '合格'],
  183. ['项目经理', '魏娟'],
  184. ['技术负责人', '张宏兵'],
  185. ['总监理工程师及电话', '秦工13521525155'],
  186. ['项目描述', '详见工程量清单'],
  187. ['备注', '']]},
  188. {'page_numbers': [384],
  189. 'table': [['项目名称', '武汉国家生物产业创新基地D1-3a国家人类遗传资源样本库无机魔石地坪施工项目'],
  190. ['项目所在地', '光谷三路和高新大道交汇处。武汉国家生物产业创新基地园区内'],
  191. ['发包人名称', '山河建设集团有限公司'],
  192. ['发包人地址', '武汉市光谷三路和高新大道'],
  193. ['发包人联系人及电话', '张华15527812012'],
  194. ['合同价格', '347.003万元'],
  195. ['开工日期', '2019.05.01'],
  196. ['竣工日期', '2019.07.31'],
  197. ['承担的工作',
  198. '本次工程建设内容主要为武汉国家生物产业创新基地D1-3a国家人类遗传资源样本库,建筑面积13182.28平方米,其中无机磨石地坪施工面积为10547.36平方米。'],
  199. ['工程质量', '合格'],
  200. ['项目经理', '游洋'],
  201. ['技术负责人', '秦晓罡'],
  202. ['总监理工程师及电话', '张工18125152152'],
  203. ['项目描述',
  204. '本次工程建设内容主要为武汉国家生物产业创新基地D1-3a国家人类遗传资源样本库,建筑面积13182.28平方米,其中无机磨石地坪施工面积为10547.36平方米。'],
  205. ['备注', '']]},
  206. {'page_numbers': [395],
  207. 'table': [['项目名称', '汉江新集水电站地下室地坪整治'],
  208. ['项目所在地', '湖北襄阳太平店镇新集水电站'],
  209. ['发包人名称', '中国水利水电第四工程局有限公司'],
  210. ['发包人地址', '中国水利水电第四工程局有限公司武汉分部'],
  211. ['发包人联系人及电话', '吴华镇15231522525'],
  212. ['合同价格', '598.4116万元'],
  213. ['开工日期', '2021.12.31'],
  214. ['竣工日期', '2022.04.02'],
  215. ['承担的工作', '新集水电站地下室原有地坪为地砖,空鼓破损现象严重,线凿除原地面,改为无机磨石地坪。'],
  216. ['工程质量', '合格'],
  217. ['项目经理', '董娜'],
  218. ['技术负责人', '秦晓罡'],
  219. ['总监理工程师及电话', '袁工13213251225'],
  220. ['项目描述', '新集水电站地下室原有地坪为地砖,空鼓破损现象严重,线凿除原地面,改为无机磨石地坪。'],
  221. ['备注', '']]},
  222. {'page_numbers': [424],
  223. 'table': [['项目名称', '武汉金光电子器械有限公司车间(二期)工程'],
  224. ['项目所在地', '武汉市江夏区大桥新区民营工业园'],
  225. ['发包人名称', '武汉金光电子器械有限公司'],
  226. ['发包人地址', '武汉市江夏区经济开发区'],
  227. ['发包人联系人及电话', '刘江15231522525'],
  228. ['合同价格', '2550万元'],
  229. ['开工日期', '2019.06.26'],
  230. ['竣工日期', '2020.03.20'],
  231. ['承担的工作', '详见工程量清单。'],
  232. ['工程质量', '合格'],
  233. ['项目经理', '辛正军'],
  234. ['技术负责人', '张宏兵'],
  235. ['总监理工程师及电话', '魏工18911251325'],
  236. ['项目描述', '详见工程量清单。'],
  237. ['备注', '']]},
  238. {'page_numbers': [455],
  239. 'table': [['项目名称', '宇通客车VMI仓储中心扩建项目总承包工程'],
  240. ['项目所在地', '郑州市郑州经济技术开发区兰心西路以北、龙兴南街以东'],
  241. ['发包人名称', '郑州宇通客车股份有限公司'],
  242. ['发包人地址', '郑州市郑州市郑州经济技术开发区'],
  243. ['发包人联系人及电话', '王江伟15528012500'],
  244. ['合同价格', '4428万元'],
  245. ['开工日期', '2020.12.25'],
  246. ['竣工日期', '2021.06.20'],
  247. ['承担的工作', '详见工程量清单。'],
  248. ['工程质量', '合格'],
  249. ['项目经理', '秦亚飞'],
  250. ['技术负责人', '张宏兵'],
  251. ['总监理工程师及电话', '李鹏18124215525'],
  252. ['项目描述', '该工程施工图纸范围内的土建、钢结构、公用安装等全部工程,总建筑面积28652.02平方米。'],
  253. ['备注', '']]},
  254. {'page_numbers': [677],
  255. 'table': [['项目名称', '/'],
  256. ['项目所在地', '/'],
  257. ['发包人名称', '/'],
  258. ['发包人地址', '/'],
  259. ['发包人电话', '/'],
  260. ['签约合同价', '/'],
  261. ['开工日期', '/'],
  262. ['计划竣工日期', '/'],
  263. ['承担的工作', '/'],
  264. ['工程质量', '/'],
  265. ['项目经理', '/'],
  266. ['技术负责人', '/'],
  267. ['总监理工程师及电话', '/'],
  268. ['项目描述', '/'],
  269. ['备注', '/']]}]
  270. """
  271. standard = """满足招标文件投标人资格要求的业绩得 50 分,每增加 1 项单项合同面积在 8000 平方及以上无机磨石地坪工程的施工业绩加 10 分,加至 100 分为止。"""
  272. print(get_proj(input_json=input_json, standard=standard))