chatgpt.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. # -*- coding:utf-8 -*-
  2. if __name__ == '__main__':
  3. import os
  4. os.chdir("..")
  5. import requests
  6. import random
  7. import json
  8. import time
  9. from tools.loglog import logger, simple_logger, log_err_e
  10. from tools.new_mysql import MySQLUploader
  11. from typing import Optional, Dict, Any, Union
  12. import httpx
  13. import asyncio
  14. m = MySQLUploader()
  15. def get_openai_model(model_text: str):
  16. """模糊获得模型名"""
  17. if "3.5" in model_text or "3.5-turbo" in model_text or "3.5turbo" in model_text:
  18. model = "gpt-3.5-turbo"
  19. elif "4o" in model_text or "gpt4o" in model_text:
  20. model = "gpt-4o"
  21. elif "4turbo" in model_text or "4-turbo" in model_text:
  22. model = "gpt-4-turbo"
  23. else:
  24. model = "gpt-4o"
  25. return model
  26. def insert_ip_token(ip, demo_name, gpt_content, prompt_tokens, completion_tokens, total_tokens):
  27. sql = "insert into consumer_token (ip,demo_name,gpt_content,prompt_tokens,completion_tokens,total_tokens) values (%s,%s,%s,%s,%s,%s)"
  28. m.execute_(sql, (ip, demo_name, str(gpt_content), prompt_tokens, completion_tokens, total_tokens))
  29. def get_answer_from_gpt(question, real_ip="localhost", demo_name="无", model="gpt-4o", max_tokens=3500, temperature: float = 0,
  30. json_resp: Union[Dict[Any, Any], bool] = False, n=1, check_fucn=None, sys_prompt=None):
  31. model = get_openai_model(model)
  32. d2 = {"model": model, "messages": [], "max_tokens": max_tokens, "temperature": temperature, 'n': n}
  33. if sys_prompt:
  34. d2['messages'].append({"role": "system", "content": sys_prompt})
  35. d2['messages'].append({"role": "user", "content": question})
  36. if json_resp is True:
  37. d2["response_format"] = {"type": "json_object"}
  38. elif json_resp is False:
  39. pass
  40. else:
  41. d2["response_format"] = json_resp
  42. for num_count in range(3):
  43. try:
  44. response = requests.post(f'http://170.106.108.95/v1/chat/completions', json=d2)
  45. r_json = response.json()
  46. if r2 := r_json.get("choices", None):
  47. if n > 1:
  48. gpt_res = []
  49. for i in r2:
  50. gpt_res.append(i["message"]["content"])
  51. else:
  52. gpt_res = r2[0]["message"]["content"]
  53. gpt_content = str(gpt_res)
  54. prompt_tokens = r_json["usage"]["prompt_tokens"]
  55. completion_tokens = r_json["usage"]["completion_tokens"]
  56. total_tokens = r_json["usage"]["total_tokens"]
  57. insert_ip_token(real_ip, demo_name, gpt_content, prompt_tokens, completion_tokens, total_tokens)
  58. simple_logger.info(f"问题日志:\n{question}\n回答日志:\n{gpt_res}")
  59. if not check_fucn:
  60. return gpt_res
  61. check_result = check_fucn(str(gpt_res))
  62. if check_result:
  63. return gpt_res
  64. else:
  65. raise Exception(f"第{num_count + 1}次共3次,GPT的校验没有通过,校验函数:{check_fucn.__name__}")
  66. elif r_json.get("message") == "IP address blocked":
  67. print("IP address blocked")
  68. raise Exception("IP address blocked")
  69. else:
  70. print(f"小错误:{question[:10]}")
  71. logger.error(response.text)
  72. except Exception as e:
  73. logger.info(f"小报错忽略{e}")
  74. time.sleep(10)
  75. logger.critical("get_answer_from_gpt 严重错误,3次后都失败了")
  76. def get_article_gpt_pydantic(question, real_ip="localhost", demo_name="无", model="gpt-4.1", max_tokens=3500, temperature: float = 0, n=1,
  77. check_fucn=None, sys_prompt=None, task_id=0, exercise_id=0):
  78. """
  79. 异步获取文章
  80. :param question: 问题
  81. :param real_ip: 真实IP
  82. :param demo_name: 项目名称
  83. :param model: 模型名称
  84. :param max_tokens: 最大token数
  85. :param temperature: 温度
  86. :param n: 生成数量
  87. :param check_fucn: 校验函数
  88. :param sys_prompt: 系统提示
  89. :param task_id: 任务id
  90. :param exercise_id: 学案id
  91. :return: 文章内容
  92. """
  93. d2 = {"model": model, "messages": [], "max_tokens": max_tokens, "temperature": temperature, "n": n,
  94. "response_format": {"type": "json_schema", 'json_schema': {'name': 'Article', 'schema': {'$defs': {'Candidate': {
  95. 'properties': {'label': {'description': 'ABCD序号的一种', 'title': '序号', 'type': 'string'},
  96. 'text': {'description': '英文,ABCD选项的文本', 'title': '选项文本', 'type': 'string'},
  97. 'isRight': {'description': '1是正确,0是错误', 'title': '是否是正确答案', 'type': 'integer'}},
  98. 'required': ['label', 'text', 'isRight'], 'title': 'Candidate', 'type': 'object'}, 'DifficultSentence': {
  99. 'properties': {'english': {'description': '文章中的一句难句', 'title': '英语难句', 'type': 'string'},
  100. 'chinese': {'description': '对英语难句的翻译', 'title': '中文难句', 'type': 'string'}},
  101. 'required': ['english', 'chinese'], 'title': 'DifficultSentence', 'type': 'object'}, 'Question': {
  102. 'properties': {'trunk': {'description': '用英语给出的选择题题目', 'title': '选择题题目', 'type': 'string'},
  103. 'analysis': {'description': '中文,选择题答案的分析思路', 'title': '选择题分析', 'type': 'string'},
  104. 'candidates': {'items': {'$ref': '#/$defs/Candidate'}, 'title': '选项对象', 'type': 'array'}},
  105. 'required': ['trunk', 'analysis', 'candidates'], 'title': 'Question', 'type': 'object'}}, 'properties': {
  106. 'difficultSentences': {'description': '挑选一句难句对象', 'items': {'$ref': '#/$defs/DifficultSentence'}, 'title': '难句对象',
  107. 'type': 'array'},
  108. 'usedMeanIds': {'items': {'type': 'integer'}, 'title': '用到的词义id', 'type': 'array'},
  109. 'questions': {'description': '针对英语文章的选择题', 'items': {'$ref': '#/$defs/Question'}, 'title': '问题对象',
  110. 'type': 'array'}, 'englishArticle': {'description': '', 'title': '英语文章', 'type': 'string'},
  111. 'chineseArticle': {'description': '', 'title': '中文翻译', 'type': 'string'}}, 'required': ['difficultSentences',
  112. 'usedMeanIds', 'questions',
  113. 'englishArticle',
  114. 'chineseArticle'],
  115. 'title': 'Article', 'type': 'object'}}}
  116. }
  117. if sys_prompt:
  118. d2['messages'].append({"role": "system", "content": sys_prompt})
  119. d2['messages'].append({"role": "user", "content": question})
  120. for num_count in range(3):
  121. try:
  122. response = requests.post('http://170.106.108.95/v1/chat/completions', json=d2)
  123. r_json = response.json()
  124. simple_logger.info(f"问题日志task_id:{task_id},exercise_id:{exercise_id}:\n{question}\n回答日志:\n{r_json}")
  125. return r_json
  126. #
  127. #
  128. except httpx.HTTPError as e:
  129. logger.error(f"HTTP请求错误: {str(e)}")
  130. if num_count < 2:
  131. time.sleep(10)
  132. else:
  133. raise
  134. except Exception as e:
  135. log_err_e(e, "其他错误")
  136. if num_count < 2:
  137. time.sleep(10)
  138. else:
  139. raise
  140. logger.critical("get_article_gpt_pydantic 严重错误,3次后都失败了")
  141. raise Exception("获取文章失败,已达到最大重试次数")
  142. def parse_gpt_phon_to_tuplelist(text: str) -> list:
  143. """解析gpt返回的音标数据"""
  144. result = []
  145. if not text:
  146. return []
  147. for i in text.split("\n"):
  148. ii = i.split("***")
  149. if len(ii) >= 3:
  150. result.append((ii[0].strip(), ii[1].strip(), ii[2].strip()))
  151. return result
  152. if __name__ == '__main__':
  153. def get_article():
  154. """可以在这里测试提示词的好坏"""
  155. sys_prompt = "你是一个专业的英语老师,擅长根据用户提供的词汇生成对应的英语文章和中文翻译和4个配套选择题。注意:生成的文章用到提供的单词,其词义一定要是提供的中文词义,不要使用同个单词,与提供词义不同的单词。例如:单词might的词义可能有[1.可能 2.强大力量]两种词义,不要混用同个单词的词义,一定要按照提供词义来使用。"
  156. q = """下面我会为你提供一组数据,[单词组](里面包含词义id,英语单词,中文词义),请根据这些单词的中文词义,生成一篇带中文翻译的考场英语文章,英语文章和中文翻译要有[标题]。注意这个单词有多个词义时,生成的英语文章一定要用提供的中文词义。并挑选一句复杂的句子和其中文翻译,放入difficultSentences。英语文章,放入"englishArticle"中。中文翻译,放入"chineseArticle"中。最终文中使用到的单词id放入"usedMeanIds"中。4个选择题,放入questions字段。questions结构下有4个选择题对象,其中trunk是[英语]问题文本,analysis是[中文]的问题分析,candidates是4个ABCD选项,内部有label是指选项序号A B C D ,text是[英语]选项文本,isRight是否正确答案1是正确0是错误。
  157. 要求:
  158. 1.必须用提供的这个词义的单词,其他单词使用最简单最容易没有难度的单词。文章整体非常简洁,通俗易懂,适合初学者,刚入门,单词全是最常见的,语句通顺即可。选择题难度尽可能简单,参考中国小学生水平
  159. 2.优先保证文章语句通顺,意思不要太生硬。不要为了使用特定的单词,造成文章语义前后不搭,允许不使用个别词义。
  160. 3.文章中使用提供单词,一定要和提供单词的中文词义匹配,尤其是一词多义时,务必使用提供单词的词义。必须要用提供单词的词义。如果用到的词义与提供单词词义不一致,请不要使用这个单词。
  161. 4.生成的文章要求70词左右,可以用\\n\\n字符分段,一般1-2个段落左右。第一段是文章标题。
  162. 5.允许不使用[单词组]的个别单词,优先保证文章整体意思通顺连贯和故事完整。
  163. 6.注意回复字段的中英文,englishArticle是英文,chineseArticle是中文,其中trunk是英文,analysis是中文,text是英文。
  164. 提供[单词组]:[260 important 重要的]; [287 off 离开, 从…下去]; [74 get 变得]; [251 change 零钱]; [219 put 放]; [212 feel 触, 摸]; [239 few 很少的, 几乎没有的]; [262 still 仍然]; [283 country 国家]; [270 part 部分, 部件];
  165. """
  166. gpt_resp = get_article_gpt_pydantic(question=q, temperature=1.2, sys_prompt=sys_prompt, model="gpt-4.1", task_id=888,
  167. exercise_id=999, n=4)
  168. return_json = {"articles": []}
  169. for choice in gpt_resp["choices"]:
  170. single_article_dict = json.loads(choice["message"]["content"])
  171. return_json["articles"].append(single_article_dict)
  172. return return_json