report_generation.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875
  1. # -- encoding: utf-8 --
  2. from openai import OpenAI
  3. import base64
  4. import httpx
  5. import json
  6. from xlsx_functions_impl import execute_function, save_workbook
  7. from docx_function_impl import save_document, execute_docx_function
  8. import sys
  9. import os
  10. import io
  11. import docx
  12. import openpyxl
  13. # 只在程序开始时设置一次编码
  14. if hasattr(sys.stdout, 'buffer'):
  15. sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
  16. if hasattr(sys.stderr, 'buffer'):
  17. sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
  18. # ---- 本地函数定义 ----
  19. def encode_image(image_path):
  20. with open(image_path, "rb") as image_file:
  21. return base64.b64encode(image_file.read()).decode("utf-8")
  22. def read_test_result(file_path):
  23. with open(file_path, "r", encoding="utf-8") as file:
  24. return file.read()
  25. def read_function_json(file_path):
  26. with open(file_path, "r", encoding="utf-8") as file:
  27. return json.load(file)
  28. def generate_report():
  29. client = OpenAI(
  30. api_key="sk-BlGqfNYqP1JwAz7TpYJkPpYxCylb8KUb0R6eHndFQeT3BlbkFJSOr91dOESOeYGKXGif2OJyUE-de7EdOvEx9uwLZ84A",
  31. http_client=httpx.Client(
  32. proxy="socks5://socksproxy:8uhb9ijn@34.143.134.123:1080"
  33. )
  34. )
  35. test_result = read_test_result("solid.md")
  36. image_path = "report_table_sample.png"
  37. image_base64 = encode_image(image_path)
  38. functions = read_function_json("docx_functions.json")
  39. userPrompt = f"""我需要将一份检测结果生成检测报告,以docx的形式输出,目标报告的表格如提供的图片所示,
  40. 检测数据如下:
  41. {test_result}
  42. 请根据检测数据,生成一份检测报告,并按照图片所示的格式输出。
  43. 你可以操作python-docx生产docx文件,你可以进行多轮操作实现你的目标,如执行完操作后还有后续操作,请回复:'尚未完成',如执行完成,请回复'已完成'。
  44. """
  45. messages = [
  46. {"role": "user", "content": [
  47. {"type": "text", "text": userPrompt},
  48. {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{image_base64}"}}
  49. ]}
  50. ]
  51. response = client.chat.completions.create(
  52. model="gpt-4.1",
  53. messages=messages,
  54. functions=functions,
  55. function_call="auto"
  56. )
  57. message = response.choices[0].message
  58. print(message)
  59. messages.append({
  60. "role": "assistant",
  61. "content": message.content,
  62. "function_call": message.function_call
  63. })
  64. while True:
  65. if message.content and "已完成" in message.content:
  66. break
  67. if message.function_call:
  68. name = message.function_call.name
  69. args = json.loads(message.function_call.arguments)
  70. result = execute_function(name, **args)
  71. messages.append({
  72. "role": "function",
  73. "name": name,
  74. "content": result
  75. })
  76. response = client.chat.completions.create(
  77. model="gpt-4.1",
  78. messages=messages,
  79. functions=functions,
  80. function_call="auto"
  81. )
  82. message = response.choices[0].message
  83. print(message)
  84. messages.append({
  85. "role": "assistant",
  86. "content": message.content,
  87. "function_call": message.function_call
  88. })
  89. else:
  90. break
  91. # 保存docx文件
  92. docx_path = "target_report.docx"
  93. save_workbook(docx_path)
  94. print(f"报告已保存到 {docx_path}")
  95. def print_progress(message):
  96. """打印进度信息并立即刷新输出缓冲区,确保 UTF-8 编码"""
  97. if isinstance(message, dict) or isinstance(message, list):
  98. # 如果是字典或列表,转换为格式化的 JSON 字符串
  99. import json
  100. message = json.dumps(message, ensure_ascii=False, indent=2)
  101. # 确保消息是字符串
  102. if not isinstance(message, str):
  103. message = str(message)
  104. # 输出消息并刷新缓冲区
  105. print(message, flush=True)
  106. def read_confirmation_docx(file_path):
  107. docx_path = os.path.join(os.path.dirname(__file__), file_path)
  108. doc = docx.Document(docx_path)
  109. content = ""
  110. for paragraph in doc.paragraphs:
  111. content += paragraph.text
  112. for table in doc.tables:
  113. for row in table.rows:
  114. for cell in row.cells:
  115. content += cell.text
  116. content += " | "
  117. content += "\n"
  118. return content
  119. def read_test_xlsx(file_path):
  120. workbook = openpyxl.load_workbook(file_path, data_only=True)
  121. sheets = workbook.sheetnames
  122. content = []
  123. for sheet in sheets:
  124. sheet = workbook[sheet]
  125. sheet_content = f"sheet: {sheet}\n"
  126. for row in sheet.iter_rows(values_only=True):
  127. row_content = ""
  128. # 读取cell的值而不是公式
  129. for cell in row:
  130. row_content += str(cell) + " | "
  131. # 判断row_content是否值都为None
  132. if all(cell is None for cell in row):
  133. continue
  134. else:
  135. sheet_content += row_content + "\n"
  136. content.append(sheet_content)
  137. return content
  138. def extract_test_result(content):
  139. client = OpenAI(
  140. # api_key="sk-BlGqfNYqP1JwAz7TpYJkPpYxCylb8KUb0R6eHndFQeT3BlbkFJSOr91dOESOeYGKXGif2OJyUE-de7EdOvEx9uwLZ84A",
  141. # api_key="AIzaSyBL7YV2mfYjlM97pkn3_lKdlvniXnWkcno",
  142. # base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
  143. # http_client=httpx.Client(
  144. # proxy="socks5://socksproxy:8uhb9ijn@34.143.134.123:1080"
  145. # )
  146. api_key="sk-90fa152386b849e29737c6c2812de7d3",
  147. base_url="https://api.deepseek.com"
  148. )
  149. base_prompt = f"""
  150. 你是一名环境检测领域专家,你需要从环境检测化验单中读取检测结果。
  151. 你所需要读取的内容是环境检测化验单内容,里面可能包含污水、废气、噪声、土壤、固废等各种检测项目,涉及到各类检测方法,如化学滴定法、色谱法等等。
  152. 检测报告单已经读取为文本,包含了表单名称以及表单内容。
  153. 文本内容如下:
  154. {content}
  155. 文本行与行之间以换行符分隔,单元格与单元格之间以|分隔。
  156. 你需要先理解表单格式,再进行有效信息提取。
  157. 对于污水检测,通常会对一个采样点进行多次采样,然后对每一份样品进行检测。对于检测结果,你需要重点关注以下几个字段(字段名不一定完全一致,你需要有自己的判断):
  158. 1. 测点编号
  159. 2. 样品编号
  160. 3. 样品浓度
  161. 4. 样品浓度平均值 (平均值或最大值大多数情况下都可以从表单中找到,不需要自己计算,如果表单中没有平均值,则取最大值)
  162. 5. 采样点标干流量(标干流量只有固定排放源废气才会有,对于无组织废气则不存在标干流量)
  163. 这些字段所对应的值需要体现在检测结果报告中。
  164. """
  165. prompt = base_prompt + """
  166. 以JSON格式返回数据,返回格式如下:
  167. {
  168. "sheet_name": "sheet_name",
  169. "test_item": "检测项目(检测项目有时候并不在表单内容模块而在表单标题里,需要你自己判断)",
  170. "sampling_date": "采样日期",
  171. "sample_type": "样品类型",
  172. "test_method": "检测方法",
  173. "test_threshold": "检出限",
  174. "test_instrument": "检测仪器",
  175. "test_result": [
  176. {
  177. "sampling_point_code": "采样点编号",
  178. "test_detail": [
  179. {
  180. "sample_code": "样品编号",
  181. "test_item": "检测项目",
  182. "test_value": "检测值",
  183. "test_unit": "检测单位"
  184. }
  185. ], //for test_item in test_items
  186. "avg_or_max_value": "检出平均值或最大值",
  187. "standard_dry_flow": "标干流量",
  188. "flow_unit": "标干流量单位",
  189. "emission_rate": "排放速率",
  190. "emission_unit": "速率单位"
  191. }
  192. ] //for sampling_point_code in sampling_point_codes
  193. }
  194. 生成json时请确保检测单中所有数据都写到json中,不要有任何遗漏,因为这是一份检测报告,数据完整性非常重要。不要因为返回的json长度过长而进行省略。
  195. """
  196. messages = [
  197. {"role": "user", "content": prompt}
  198. ]
  199. response = client.chat.completions.create(
  200. model="deepseek-chat",
  201. messages=messages,
  202. )
  203. print(response.choices[0].message.content)
  204. # 将json格式化
  205. try:
  206. json_content = json.loads(response.choices[0].message.content)
  207. return json_content
  208. except:
  209. start_index = response.choices[0].message.content.find("{")
  210. end_index = response.choices[0].message.content.rfind("}") + 1
  211. json_content = json.loads(response.choices[0].message.content[start_index:end_index])
  212. return json_content
  213. def extract_sampling_point_info(content):
  214. client = OpenAI(
  215. # api_key="sk-BlGqfNYqP1JwAz7TpYJkPpYxCylb8KUb0R6eHndFQeT3BlbkFJSOr91dOESOeYGKXGif2OJyUE-de7EdOvEx9uwLZ84A",
  216. # # api_key="AIzaSyBL7YV2mfYjlM97pkn3_lKdlvniXnWkcno",
  217. # # base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
  218. # http_client=httpx.Client(
  219. # proxy="socks5://socksproxy:8uhb9ijn@34.143.134.123:1080"
  220. # )
  221. api_key="sk-90fa152386b849e29737c6c2812de7d3",
  222. base_url="https://api.deepseek.com"
  223. )
  224. base_prompt = f"""
  225. 你是一名环境检测领域专家,现在我有一份环境检测现场采样确认单,你需要从这份确认单中提取采样日期、采样点编号、采样点名称、检测项目信息。
  226. 确认单内容如下:
  227. {content}
  228. 确认单已经读取为文本,表格单元格以|分隔,表格行与行以换行符分隔。
  229. 合并单元格,如A1与A2合并后,那么内容里A1与A2的值是一样的,当你发现几个单元格值都是一样的时候,代表这几个单元格是合并的。
  230. 你需要先理解表单格式,然后再提取相应数据。
  231. 以JSON格式返回数据,返回格式如下:
  232. """
  233. prompt = base_prompt + """
  234. {
  235. "sampling_date": "采样日期",
  236. "sampling_info": [
  237. {
  238. "sampling_point_code": "采样点编号",
  239. "sampling_point": "采样点名称",
  240. "test_item": "检测项目信息"
  241. } //for sampling_point_code in sampling_point_codes
  242. ]
  243. }
  244. """
  245. messages = [
  246. {"role": "user", "content": prompt}
  247. ]
  248. response = client.chat.completions.create(
  249. model="gpt-4.1",
  250. messages=messages,
  251. )
  252. try:
  253. json_content = json.loads(response.choices[0].message.content)
  254. return json_content
  255. except:
  256. start_index = response.choices[0].message.content.find("{")
  257. end_index = response.choices[0].message.content.rfind("}") + 1
  258. json_content = json.loads(response.choices[0].message.content[start_index:end_index])
  259. return json_content
  260. def extract_sampling_scheme(content):
  261. client = OpenAI(
  262. # api_key="sk-BlGqfNYqP1JwAz7TpYJkPpYxCylb8KUb0R6eHndFQeT3BlbkFJSOr91dOESOeYGKXGif2OJyUE-de7EdOvEx9uwLZ84A",
  263. # # api_key="AIzaSyBL7YV2mfYjlM97pkn3_lKdlvniXnWkcno",
  264. # # base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
  265. # http_client=httpx.Client(
  266. # proxy="socks5://socksproxy:8uhb9ijn@34.143.134.123:1080"
  267. # )
  268. api_key="sk-90fa152386b849e29737c6c2812de7d3",
  269. base_url="https://api.deepseek.com"
  270. )
  271. base_prompt = f"""
  272. 你是一名环境检测领域专家,现在我有一份环境检测现场采样方案,你需要从这份方案中提取监测类别、采样点编号、监测点位、监测频次、监测项目信息。
  273. 方案内容如下:
  274. {content}
  275. 方案已经读取为文本,表格单元格以|分隔,表格行与行以换行符分隔。
  276. 合并单元格,如A1与A2合并后,那么内容里A1与A2的值是一样的,当你发现几个单元格值都是一样的时候,代表这几个单元格是合并的。
  277. 你需要先理解表单格式,然后再提取相应数据。
  278. 以JSON格式返回数据,返回格式如下:
  279. """
  280. prompt = base_prompt + """
  281. [
  282. {
  283. "monitoring_category": "监测类别",
  284. "sampling_info": [
  285. {
  286. "sampling_point_code": "采样点编号",
  287. "sampling_point": "监测点位",
  288. "sampling_frequency": "监测频次",
  289. "test_item": "检测项目信息"
  290. }
  291. ] //for sampling_point_code in sampling_point_codes
  292. }
  293. ]
  294. """
  295. messages = [
  296. {"role": "user", "content": prompt}
  297. ]
  298. response = client.chat.completions.create(
  299. model="gpt-4.1",
  300. messages=messages,
  301. )
  302. try:
  303. json_content = json.loads(response.choices[0].message.content)
  304. return json_content
  305. except:
  306. start_index = response.choices[0].message.content.find("{")
  307. end_index = response.choices[0].message.content.rfind("}") + 1
  308. json_content = json.loads(response.choices[0].message.content[start_index:end_index])
  309. return json_content
  310. def llm_operating_docx(prompt):
  311. client = OpenAI(
  312. api_key="sk-BlGqfNYqP1JwAz7TpYJkPpYxCylb8KUb0R6eHndFQeT3BlbkFJSOr91dOESOeYGKXGif2OJyUE-de7EdOvEx9uwLZ84A",
  313. # api_key="AIzaSyBL7YV2mfYjlM97pkn3_lKdlvniXnWkcno",
  314. # base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
  315. http_client=httpx.Client(
  316. proxy="socks5://socksproxy:8uhb9ijn@34.143.134.123:1080"
  317. )
  318. )
  319. # image_path = os.path.join(os.path.dirname(__file__), "table_template.png")
  320. # image_url = encode_image(image_path)
  321. docx_functions = read_function_json(os.path.join(os.path.dirname(__file__), "docx_functions.json"))
  322. image_path = os.path.join(os.path.dirname(__file__), "table_template.png")
  323. waste_water_image_url = encode_image(image_path)
  324. gas_image_path = os.path.join(os.path.dirname(__file__), "gas.png")
  325. gas_image_url = encode_image(gas_image_path)
  326. gas_free_image_path = os.path.join(os.path.dirname(__file__), "gas_free.png")
  327. gas_free_image_url = encode_image(gas_free_image_path)
  328. noise_image_path = os.path.join(os.path.dirname(__file__), "noise.png")
  329. noise_image_url = encode_image(noise_image_path)
  330. messages = [
  331. {"role": "user", "content": prompt}
  332. ]
  333. response = client.chat.completions.create(
  334. model="gpt-4.1",
  335. messages=messages,
  336. functions=docx_functions,
  337. function_call="auto"
  338. )
  339. message = response.choices[0].message
  340. print(message)
  341. messages.append({
  342. "role": "assistant",
  343. "content": message.content,
  344. "function_call": message.function_call
  345. })
  346. while True:
  347. if message.content and "已完成" in message.content:
  348. print(message.content)
  349. break
  350. if message.function_call:
  351. name = message.function_call.name
  352. args = json.loads(message.function_call.arguments)
  353. result = execute_docx_function(name, **args)
  354. print(result)
  355. messages.append({
  356. "role": "function",
  357. "name": name,
  358. "content": json.dumps(result)
  359. })
  360. response = client.chat.completions.create(
  361. model="gpt-4.1-mini",
  362. messages=messages,
  363. functions=docx_functions,
  364. function_call="auto"
  365. )
  366. message = response.choices[0].message
  367. print(message)
  368. messages.append({
  369. "role": "assistant",
  370. "content": message.content,
  371. "function_call": message.function_call
  372. })
  373. else:
  374. messages.append({
  375. "role": "user",
  376. "content": "你回复了尚未完成,但并没有返回function call,是遇到什么问题了吗?如果需要继续执行,请继续回复:尚未完成"
  377. })
  378. response = client.chat.completions.create(
  379. model="gpt-4.1",
  380. messages=messages,
  381. functions=docx_functions,
  382. function_call="auto"
  383. )
  384. message = response.choices[0].message
  385. messages.append({
  386. "role": "assistant",
  387. "content": message.content,
  388. "function_call": message.function_call
  389. })
  390. return "ok"
  391. def llm_operating_xlsx(prompt):
  392. client = OpenAI(
  393. api_key="sk-BlGqfNYqP1JwAz7TpYJkPpYxCylb8KUb0R6eHndFQeT3BlbkFJSOr91dOESOeYGKXGif2OJyUE-de7EdOvEx9uwLZ84A",
  394. # api_key="AIzaSyBL7YV2mfYjlM97pkn3_lKdlvniXnWkcno",
  395. # base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
  396. http_client=httpx.Client(
  397. proxy="socks5://socksproxy:8uhb9ijn@34.143.134.123:1080"
  398. )
  399. )
  400. image_path = os.path.join(os.path.dirname(__file__), "tpl.png")
  401. image_url = encode_image(image_path)
  402. xlsx_functions = read_function_json(os.path.join(os.path.dirname(__file__), "xlsx_functions.json"))
  403. messages = [
  404. {"role": "user", "content": [
  405. {"type": "text", "text": prompt},
  406. {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{image_url}"}},
  407. ]}
  408. ]
  409. response = client.chat.completions.create(
  410. model="gpt-4.1",
  411. messages=messages,
  412. functions=xlsx_functions,
  413. function_call="auto"
  414. )
  415. message = response.choices[0].message
  416. print(message.content)
  417. messages.append({
  418. "role": "assistant",
  419. "content": message.content,
  420. "function_call": message.function_call
  421. })
  422. while True:
  423. if message.content and "已完成" in message.content:
  424. print(message.content)
  425. messages.append({
  426. "role": "assistant",
  427. "content": message.content,
  428. })
  429. break
  430. if message.function_call:
  431. name = message.function_call.name
  432. args = json.loads(message.function_call.arguments)
  433. result = execute_function(name, **args)
  434. print(result)
  435. messages.append({
  436. "role": "function",
  437. "name": name,
  438. "content": json.dumps(result)
  439. })
  440. response = client.chat.completions.create(
  441. model="gpt-4.1",
  442. messages=messages,
  443. functions=xlsx_functions,
  444. function_call="auto"
  445. )
  446. message = response.choices[0].message
  447. print(message.content)
  448. messages.append({
  449. "role": "assistant",
  450. "content": message.content,
  451. "function_call": message.function_call
  452. })
  453. else:
  454. messages.append({
  455. "role": "user",
  456. "content": "你回复了尚未完成,但并没有返回function call,是遇到什么问题了吗?如果需要继续执行,请继续回复:尚未完成"
  457. })
  458. response = client.chat.completions.create(
  459. model="gpt-4.1",
  460. messages=messages,
  461. functions=xlsx_functions,
  462. function_call="auto"
  463. )
  464. message = response.choices[0].message
  465. messages.append({
  466. "role": "assistant",
  467. "content": message.content,
  468. "function_call": message.function_call
  469. })
  470. return messages
  471. def let_llm_read_excel(file_path, output_path, file_type):
  472. client = OpenAI(
  473. api_key="sk-BlGqfNYqP1JwAz7TpYJkPpYxCylb8KUb0R6eHndFQeT3BlbkFJSOr91dOESOeYGKXGif2OJyUE-de7EdOvEx9uwLZ84A",
  474. # api_key="AIzaSyBL7YV2mfYjlM97pkn3_lKdlvniXnWkcno",
  475. # base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
  476. http_client=httpx.Client(
  477. proxy="socks5://socksproxy:8uhb9ijn@34.143.134.123:1080"
  478. )
  479. )
  480. # deepseek api
  481. # client = OpenAI(
  482. # api_key="sk-90fa152386b849e29737c6c2812de7d3",
  483. # base_url="https://api.deepseek.com"
  484. # )
  485. # json路径替换为全局路径
  486. functions = read_function_json(os.path.join(os.path.dirname(__file__), "xlsx_functions.json"))
  487. # Break down the nested f-string into separate parts
  488. base_prompt = f"""
  489. 你是一名环境检测领域专家,你可以读取excel的内容以及相关格式从而对excel内容有全面的理解。
  490. 你所需要读取的excel内容是环境检测化验单内容,里面可能包含污水、废气、噪声、土壤、固废等各种检测项目,涉及到各类检测方法,如化学滴定法、色谱法等等。
  491. 现在有一些检测化验单需要你读取并理解其中内容,你可以操作openpyxl读取excel内容。
  492. 对于污水检测,通常会对一个采样点进行多次采样,然后对每一份样品进行检测。对于检测结果,你需要重点关注以下几个字段:
  493. 1. 测点编号
  494. 2. 样品编号
  495. 3. 样品浓度
  496. 4. 样品浓度平均值
  497. 5. 采样点标干流量(标干流量只有固定排放源废气才会有,对于无组织废气则不存在标干流量)
  498. 这些字段所对应的值需要体现在检测结果报告中。
  499. 需要读取并理解的excel文件路径是:{file_path}。
  500. 读取excel时保证把sheet内有数据区域全部读取到,不要有遗漏的数据区域。
  501. 读取时先调用load_workbook方法加载工作表再执行后续操作。
  502. 如果excel存在多个sheet,全部读取。
  503. 如果你觉得你已经理解了文件内容,请回复:'已完成'。如果你觉得还需要更多信息,请回复:'尚未完成'。
  504. """
  505. excel_data = []
  506. messages = [
  507. {"role": "user", "content": base_prompt}
  508. ]
  509. print_progress("开始读取excel内容...")
  510. response = client.chat.completions.create(
  511. model="gpt-4.1",
  512. messages=messages,
  513. functions=functions,
  514. function_call="auto"
  515. )
  516. message = response.choices[0].message
  517. print_progress(message)
  518. messages.append({
  519. "role": "assistant",
  520. "content": message.content,
  521. "function_call": message.function_call
  522. })
  523. while True:
  524. if message.content and "已完成" in message.content:
  525. print_progress("读取完成")
  526. print_progress(message.content)
  527. prompt = """你已完成读取,请将excel内容转化为JSON格式返回,JSON格式如下:
  528. [
  529. {
  530. "sheet_name": "sheet_name",
  531. "sheet_data": [
  532. {
  533. "test_item": "检测项目",
  534. "sampling_date": "采样日期",
  535. "sample_type": "样品类型",
  536. "sampling_point_code": "采样点编号",
  537. "sampling_point": "采样点",
  538. "avg_value": "检出平均值",
  539. "test_result": [
  540. {
  541. "sample_code": "样品编号",
  542. "test_value": "样品检出值",
  543. "test_unit": "测试单位",
  544. "standart_dry_flow": "标干流量",
  545. "flow_unit": "标干流量单位",
  546. "emission_rate": "排放速率",
  547. "emission_unit": "速率单位"
  548. }
  549. ] //for result in sampling point
  550. } //for sheet_name in sheet_names
  551. ]
  552. }
  553. ]
  554. 字段如果在excel内没有读到,可以为空。
  555. 生成json时请确保检测单中所有数据都写到json中,不要有任何遗漏,因为这是一份检测报告,数据完整性非常重要。不要因为返回的json长度过长而进行省略。
  556. """
  557. messages.append({
  558. "role": "user",
  559. "content": prompt
  560. })
  561. response = client.chat.completions.create(
  562. model="gpt-4.1",
  563. messages=messages,
  564. functions=functions,
  565. function_call="auto"
  566. )
  567. message = response.choices[0].message
  568. try:
  569. json_result = json.loads(message.content)
  570. print_progress(json_result)
  571. with open(os.path.join(os.path.dirname(__file__), "data2.json"), "w", encoding="utf-8") as f:
  572. json.dump(json_result, f, ensure_ascii=False)
  573. except Exception as e:
  574. # 手动解析json
  575. start_index = message.content.find("[")
  576. end_index = message.content.rfind("]") + 1
  577. json_result = json.loads(message.content[start_index:end_index])
  578. print_progress(json_result)
  579. with open(os.path.join(os.path.dirname(__file__), "data2.json"), "w", encoding="utf-8") as f:
  580. json.dump(json_result, f, ensure_ascii=False)
  581. excel_data = message.content
  582. messages.append({
  583. "role": "assistant",
  584. "content": message.content,
  585. })
  586. break
  587. if message.function_call:
  588. name = message.function_call.name
  589. args = json.loads(message.function_call.arguments)
  590. result = execute_function(name, **args)
  591. print_progress(result)
  592. messages.append({
  593. "role": "function",
  594. "name": name,
  595. "content": json.dumps(result)
  596. })
  597. response = client.chat.completions.create(
  598. model="gpt-4.1",
  599. messages=messages,
  600. functions=functions,
  601. function_call="auto"
  602. )
  603. message = response.choices[0].message
  604. print_progress(message)
  605. messages.append({
  606. "role": "assistant",
  607. "content": message.content,
  608. "function_call": message.function_call
  609. })
  610. else:
  611. break
  612. print_progress("读取完成")
  613. # print_progress(excel_data)
  614. # prompt = f"""
  615. # 你已经完整读取了excel内容,请根据你的理解对这份检测单做一份完整总结
  616. # """
  617. # messages.append({
  618. # "role": "user",
  619. # "content": prompt
  620. # })
  621. # response = client.chat.completions.create(
  622. # model="gpt-4.1",
  623. # messages=messages,
  624. # functions=functions,
  625. # function_call="auto"
  626. # )
  627. # message = response.choices[0].message.content
  628. # print(message)
  629. image_path = os.path.join(os.path.dirname(__file__), "table_template.png")
  630. image_base64 = encode_image(image_path)
  631. tpl_path = os.path.join(os.path.dirname(__file__), "report_tpl.xlsx")
  632. # output_path = "report_222.xlsx"
  633. messages.clear()
  634. # Break down the second nested f-string
  635. # report_prompt = f"""你已经完整读取了excel内容,请根据检测单内容JSON,填写excel最终检测报告单,
  636. # 检测报告单模板路径:{tpl_path}
  637. # 报告模板中包含多个sheet,每个sheet对应不同的检测项目,请根据读取的内容选择合适的报告模板sheet并填写内容。
  638. # 在填写检测报告时,请先读取excel的表头,根据表头名称从上一环节读取的检测单内容中选择正确的检测项目,然后填写检测报告。
  639. # 检测报告单模板的excel表格行数或列数可能不够,你需要根据检测单内容自主决定是否需要添加行列。
  640. # 你可以操作openpyxl操作excel文件,
  641. # 你可以进行多轮操作实现你的目标,
  642. # 最终内容填写完成后,将excel文件另存为:{output_path}
  643. # 如执行完操作后还有后续操作,请回复:'尚未完成',如执行完成,请回复'已完成'。
  644. # """
  645. report_prompt = f"""
  646. 你是一名环境检测报告生成专家,熟悉环境检测报告的生成流程及报告典型要素。我现在有一份json格式的环境检测单数据,需要你根据这份数据生成一份检测报告,
  647. 数据如下:
  648. {excel_data}
  649. 对于检测结果,你需要重点关注以下几个字段:
  650. 1. 测点编号 json中的sheet_data.sampling_point_code
  651. 2. 样品编号 json中的sheet_data.test_result.sample_code
  652. 3. 样品浓度 json中的sheet_data.test_result.test_value
  653. 4. 样品浓度平均值 json中的sheet_data.avg_value
  654. 检测报告是以表格作为检测结果的展示方式,你可以参考图片所示的检测模板。
  655. 通常对于废水检测报告,检测报告单重点字段如下:
  656. 1.测点编号
  657. 2.测点名称
  658. 3.检测项目名称
  659. 4.检测项目单位
  660. 5.多轮检测值(如:第一次、第二次、第三次)
  661. 6.检测平均值或最大值
  662. 7.参考限值
  663. 对于废气检测报告,存在两种形式的废气:
  664. 1. 固定排放源废气
  665. 2. 无组织废气
  666. 对于固定排放源废气,检测报告单重点字段如下:
  667. 1.测点编号
  668. 2.测点名称
  669. 3.检测项目名称(如果是固定源废气,检测项目内需要包含一项:标杆流量)
  670. 4.检测项目单位
  671. 5.多轮检测值(如:第一次、第二次、第三次)
  672. 6.检测平均值
  673. 7.参考限值
  674. 对于无组织废气,检测报告单重点字段如下:
  675. 1.测点编号
  676. 2.测点名称
  677. 3.检测项目名称
  678. 4.检测项目单位
  679. 5.多轮检测值(如:第一次、第二次、第三次)
  680. 6.检测最大值
  681. 7.参考限值
  682. 请根据检测单内容JSON,基于报告模板xlsx文件填写最终检测报告单,
  683. 检测报告单模板路径:{tpl_path}
  684. 报告模板中包含多个sheet,每个sheet对应不同的检测项目,请根据读取的内容选择合适的报告模板sheet并填写内容。
  685. 在填写检测报告时,请先读取xlsx模板的整体结构,从而在填写时可以把内容填写到正确位置。
  686. 检测报告单模板的excel表格行数或列数可能不够,你需要根据检测单内容自主决定是否需要添加行列。
  687. 检测报告单模板里可能包含合并单元格,请根据合并单元格的样式来填写内容。
  688. 你可以操作openpyxl操作xlsx文件,
  689. 图片是一份报告单表格的截图,你可以作为样式参考来生成你的报告表格。
  690. 最终内容填写完成后,将xlsx文件另存为:{output_path}
  691. 如执行完操作后还有后续操作,请回复:'尚未完成',如执行完成,请回复'已完成'。
  692. """
  693. messages.append(
  694. {"role": "user", "content": [
  695. {"type": "text", "text": report_prompt},
  696. {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{image_base64}"}}
  697. ]}
  698. )
  699. docx_functions = read_function_json(os.path.join(os.path.dirname(__file__), "docx_functions.json"))
  700. print_progress("开始生成检测报告...")
  701. response = client.chat.completions.create(
  702. model="gpt-4.1",
  703. messages=messages,
  704. functions=functions,
  705. function_call="auto"
  706. )
  707. message = response.choices[0].message
  708. print_progress(message)
  709. messages.append({
  710. "role": "assistant",
  711. "content": message.content,
  712. "function_call": message.function_call
  713. })
  714. while True:
  715. if message.content and "已完成" in message.content:
  716. print_progress("检测报告生成完成")
  717. break
  718. if message.function_call:
  719. name = message.function_call.name
  720. args = json.loads(message.function_call.arguments)
  721. result = execute_function(name, **args)
  722. print_progress(result)
  723. messages.append({
  724. "role": "function",
  725. "name": name,
  726. "content": json.dumps(result)
  727. })
  728. response = client.chat.completions.create(
  729. model="gpt-4.1",
  730. messages=messages,
  731. functions=functions,
  732. function_call="auto"
  733. )
  734. message = response.choices[0].message
  735. print_progress(message)
  736. messages.append({
  737. "role": "assistant",
  738. "content": message.content,
  739. "function_call": message.function_call
  740. })
  741. else:
  742. messages.append({
  743. "role": "user",
  744. "content": "你回复了尚未完成,但并没有返回function call,是遇到什么问题了吗?如果需要继续执行,请继续回复:尚未完成"
  745. })
  746. response = client.chat.completions.create(
  747. model="gpt-4.1",
  748. messages=messages,
  749. functions=functions,
  750. function_call="auto"
  751. )
  752. message = response.choices[0].message
  753. print(message)
  754. messages.append({
  755. "role": "assistant",
  756. "content": message.content,
  757. "function_call": message.function_call
  758. })
  759. # 保存docx文件
  760. # docx_path = "report_ds.docx"
  761. # save_document(docx_path)
  762. # print(f"报告已保存到 {docx_path}")
  763. if __name__ == "__main__":
  764. # args = sys.argv
  765. # let_llm_read_excel(args[1], args[2], args[3])
  766. # content = read_confirmation_docx("file/scheme.docx")
  767. # confirmation_content_raw = read_confirmation_docx("file/confirm.docx")
  768. # print('采样确认单读取完成')
  769. # confirmation_content = extract_sampling_point_info(confirmation_content_raw)
  770. # print('采样确认单提取完成')
  771. # scheme_content_raw = read_confirmation_docx("file/scheme.docx")
  772. # print('采样方案读取完成')
  773. # scheme_content = extract_sampling_scheme(scheme_content_raw)
  774. # print('采样方案提取完成')
  775. # extract_sampling_scheme(content)
  776. # test_content = []
  777. # content = read_test_xlsx("file/cod.xlsx")
  778. # print('检测结果读取完成')
  779. # for sheet in content:
  780. # test_content.append(extract_test_result(sheet))
  781. # print('检测结果提取完成')
  782. # print(test_content)
  783. # output_path = "report_zzz.xlsx"
  784. # xlsx_tpl_path = os.path.join(os.path.dirname(__file__), "report_tpl.xlsx")
  785. # prompt = f"""
  786. # 你是一名环境检测报告生成专家,熟悉环境检测报告的生成流程及报告典型要素,具有环境检测行业深厚的背景知识。
  787. # 我现在有一套环保检测数据,需要你根据这几份数据生成一份检测报告,
  788. # 数据如下:
  789. # 采样确认单:{confirmation_content}
  790. # 采样方案:{scheme_content}
  791. # 检测结果:{test_content}
  792. # 数据里的字段含义如下:
  793. # 1.sampling_point_code:测点编号
  794. # 2.sampling_point:测点名称
  795. # 3.test_item:检测项目名称
  796. # 4.test_value:检测结果
  797. # 5.test_unit:检测单位
  798. # 6.sampling_date:采样日期
  799. # 7.sample_type:样品类型
  800. # 8.test_detail:检测结果
  801. # 9.emission_unit:速率单位
  802. # 10.standart_dry_flow:标干流量
  803. # 11.flow_unit:标干流量单位
  804. # 12.emission_rate:排放速率
  805. # 13.emission_unit:速率单位
  806. # 14.avg_value:检测平均值
  807. # 15.test_method:检测方法
  808. # 16.test_threshold:检出限
  809. # 17.test_instrument:检测仪器
  810. # 请根据检测结果数据填写最终报告表单,以下字段为填写时重点内容,不可遗漏:
  811. # 1、测点编号
  812. # 2、测点名称
  813. # 3、项目名称
  814. # 4、单位
  815. # 5、检测结果(检测结果可能包含多轮检测值,如:第一次、第二次、第三次、均值或最大值,需要根据检测结果的实际情况来填写)
  816. # 6、参考限值
  817. # 你需要把检测结果的数据完整填写到报告表单里。
  818. # 图片是一张污水检测结果报告单的截图,你可以参考图片的表格样式,但注意最终报告数据需要根据实际检测结果来填写,不要把参考图片里的数据填写到报告表格里。
  819. # 你可以使用openpyxl操作xlsx文件,现有一份报告单模板xlsx文件,路径为:{xlsx_tpl_path},文件里已经设置好表格样式,只需要把相应的数据填到正确的位置即可。
  820. # 模板xlsx里包含多个sheet,每个sheet对应不通的检测类型,如污水、噪音、废气等,你需要根据检测结果选择合适的sheet填写内容,
  821. # 选择sheet后,先完整读取模板结构,然后再根据模板结构填写数据内容,请注意表格中可能会有合并单元格,遇到合并单元格格式时请正确理解合并单元格含义再进行数据填写。
  822. # 如果模板的行列不够填写数据,请自行添加行列,但添加行列时注意保持表格的样式,
  823. # 内容填写完成后,请将xlsx文件另存为:{output_path}。
  824. # 你可以进行多轮操作来进行报告表单填写,如果还有后续操作,请回复:'尚未完成',如果已经填写完成,请回复:'已完成'。
  825. # """
  826. # llm_operating_xlsx(prompt)
  827. print('heelo')