evaluate_reply_agent.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. import json
  2. import datetime
  3. import random
  4. import traceback
  5. import concurrent.futures
  6. from threading import Lock
  7. from tqdm import tqdm
  8. from openai import OpenAI
  9. from pymysql.cursors import DictCursor
  10. from pqai_agent.database import MySQLManager
  11. evaluation_metrics_dict = {
  12. "1.2": "是否识别关键信息",
  13. "1.3": "是否能够理解歧义词/模糊词",
  14. "1.4": "是否能理解表情包,图片消息",
  15. "1.5": "是否能理解语音/方言",
  16. "2.1": "回复是否与用户意图相关",
  17. "2.2": "回复是否清晰简洁",
  18. "2.3": "回复是否流畅",
  19. "2.4": "回复语法是否规范",
  20. "3.1": "是否能理解代词(他,她, 她, 这个那个)",
  21. "3.2": "是否能延续上文话题",
  22. "3.3": "是否记住上文的基础信息",
  23. "3.4": "是否及时结束聊天",
  24. "4.1": "是否讨论超出角色认知范围的信息",
  25. "4.2": "是否讨论了不符合当前时代背景的语言、物品、事件、概念",
  26. "4.3": "是否表现出与agent 人设相符的专业知识、生活经验或者常识",
  27. "5.1": "agent 的言行是否反映其预设的核心性格",
  28. "5.2": "agent 的价值观和道德观是否符合其预设标准",
  29. "6.1": "agent 使用的词汇、句式、语法复杂度、行话/俚语是否符合其身份、教育背景和时代?",
  30. "6.2": "agent 语气、语调(恭敬、傲慢、亲切、疏离、热情、冷淡)是否稳定?",
  31. "6.3": "agent 表达习惯、口头禅是否符合角色预设特点",
  32. "7.1": "agent 在对话中表现出的目标、关注重心是否与其设定的核心动机一致?",
  33. "8.1": "agent 是否按照预设的互动模式与用户沟通",
  34. "8.2": "agent 是否对自身角色有正确理解",
  35. "8.3": "agent 是否回复超越用户认知的信息"
  36. }
  37. def fetch_deepseek_completion(prompt, output_type='text'):
  38. """
  39. deep_seek方法
  40. """
  41. client = OpenAI(
  42. api_key='sk-cfd2df92c8864ab999d66a615ee812c5',
  43. base_url="https://api.deepseek.com"
  44. )
  45. # get response format
  46. if output_type == "json":
  47. response_format = {"type": "json_object"}
  48. else:
  49. response_format = {"type": "text"}
  50. chat_completion = client.chat.completions.create(
  51. messages=[
  52. {
  53. "role": "user",
  54. "content": prompt,
  55. }
  56. ],
  57. model="deepseek-reasoner",
  58. response_format=response_format,
  59. )
  60. response = chat_completion.choices[0].message.content
  61. if output_type == "json":
  62. response_json = json.loads(response)
  63. return response_json
  64. return response
  65. def get_profile_info(user_id_, user_type):
  66. match user_type:
  67. case "user":
  68. sql = f"""
  69. select iconurl as 'avatar', profile_data_v1 as 'profile'
  70. from third_party_user where third_party_user_id = %s;
  71. """
  72. case "staff":
  73. sql = f"""
  74. select agent_profile as 'profile'
  75. from qywx_employee where third_party_user_id = %s;
  76. """
  77. case _:
  78. raise ValueError("user_type must be 'user' or 'staff'")
  79. return mysql_client.select(sql, cursor_type=DictCursor, args=(user_id_,))
  80. def evaluate_reply_agent_prompt(dialogue_history, message, user_profile_, agent_profile, push_time):
  81. """
  82. :param dialogue_history:
  83. :param message:
  84. :param user_profile_:
  85. :param agent_profile:
  86. :return:
  87. """
  88. output_format = {
  89. "1.1": {
  90. "score": 1,
  91. "reason": "理由"
  92. },
  93. "1.2": {
  94. "score": 0,
  95. "reason": "理由"
  96. }
  97. }
  98. prompt_ = f"""
  99. **评估任务:** 基于给定的对话历史和 Agent 预设信息和用户的预设信息,评估 Agent 在对话中的表现。
  100. 使用以下维度和指标进行评分。
  101. **评估指标:**
  102. 1. 理解能力
  103. 1.1 是否识别用户核心意图
  104. 1.2 是否识别关键信息
  105. 1.3 是否能够理解歧义词/模糊词
  106. 1.4 是否能理解表情包,图片消息
  107. 1.5 是否能理解语音/方言
  108. 2. 回复能力
  109. 2.1 回复是否与用户意图相关
  110. 2.2 回复是否清晰简洁
  111. 2.3 回复是否流畅
  112. 2.4 回复语法是否规范
  113. 3. 上下文管理能力
  114. 3.1 是否能理解代词(他,她, 她, 这个那个)
  115. 3.2 是否能延续上文话题
  116. 3.4 是否及时结束聊天
  117. 4. 背景知识一致性
  118. 4.1 是否讨论超出角色认知范围的信息
  119. 4.2 是否讨论了不符合当前时代背景的语言、物品、事件、概念
  120. 4.3 是否表现出与agent 人设相符的专业知识、生活经验或者常识
  121. 5. 性格行为一致性
  122. 5.1 agent 的言行是否反映其预设的核心性格
  123. 5.2 agent 的价值观和道德观是否符合其预设标准
  124. 6. 语言风格一致性
  125. 6.1 agent 使用的词汇、句式、语法复杂度、行话/俚语是否符合其身份、教育背景和时代?
  126. 6.2 agent 语气、语调(恭敬、傲慢、亲切、疏离、热情、冷淡)是否稳定?
  127. 6.3 agent 表达习惯、口头禅是否符合角色预设特点
  128. 7. 目标动机一致性
  129. 7.1 agent 在对话中表现出的目标、关注重心是否与其设定的核心动机一致?
  130. 8. 关系认知一致性
  131. 8.1 agent 是否按照预设的互动模式与用户沟通
  132. 8.2 agent 是否对自身角色有正确理解
  133. 8.3 agent 是否回复超越用户认知的信息
  134. **评估规则:**
  135. - 对于每个指标:
  136. - 如果符合要求,得 1 分。
  137. - 如果不符合要求,得 0 分。
  138. - 如果指标不适用(如对话未涉及相关场景),得 1 分(无需评估。
  139. - 理由必须基于对话内容,简短且客观,理由需要是中文, 如果是无需评估,则理由写无需评估
  140. **输入:**
  141. - **对话历史**: {dialogue_history}
  142. - **Agent 预设信息**: {agent_profile}
  143. - **用户预设信息**: {user_profile_}
  144. - **Agent 消息**: {message}
  145. - **Agent 发送消息时间**:{push_time}
  146. **输出格式要求:JSON 格式**
  147. 输出格式参考:{output_format}
  148. """
  149. return prompt_
  150. config = {
  151. 'host': 'rm-bp13g3ra2f59q49xs.mysql.rds.aliyuncs.com',
  152. 'port': 3306,
  153. 'user': 'wqsd',
  154. 'password': 'wqsd@2025',
  155. 'database': 'ai_agent',
  156. 'charset': 'utf8mb4'
  157. }
  158. mysql_client = MySQLManager(config)
  159. if __name__ == '__main__':
  160. import pqai_agent.logging_service
  161. pqai_agent.logging_service.setup_root_logger()
  162. with open("reply_data_set_filter.json", "r", encoding="utf-8") as f:
  163. data = json.load(f)
  164. # 随机选择100个对话
  165. dialogues = random.sample(data, 1)
  166. dialogue_with_profile = []
  167. for dialogue in dialogues:
  168. agent_profile = get_profile_info(dialogue['staff_id'], 'staff')
  169. user_profile = get_profile_info(dialogue['user_id'], 'user')
  170. dialogue['agent_profile'] = json.loads(agent_profile[0]['profile'])
  171. dialogue['user_profile'] = json.loads(user_profile[0]['profile'])
  172. dialogue_with_profile.append(dialogue)
  173. F = []
  174. errors = []
  175. from threading import Lock
  176. import concurrent.futures
  177. write_lock = Lock()
  178. def process_sample(sub_dialogues):
  179. try:
  180. message = sub_dialogues["conversation"]
  181. agent_message = sub_dialogues["reply_msg"]
  182. push_time = sub_dialogues["reply_time"]
  183. user_profile = sub_dialogues["user_profile"]
  184. staff_profile = sub_dialogues["agent_profile"]
  185. if not agent_message:
  186. return None
  187. prompt = evaluate_reply_agent_prompt(
  188. message, agent_message, user_profile, staff_profile, push_time
  189. )
  190. response = fetch_deepseek_completion(prompt, output_type='json')
  191. return {
  192. "user_profile": user_profile,
  193. "agent_profile": staff_profile,
  194. "dialogue_history": message,
  195. "push_message": agent_message,
  196. "push_time": push_time,
  197. "evaluation_result": response
  198. }
  199. except Exception as e:
  200. # 捕获异常并存储
  201. error_msg = f"Error processing sample: {e}\n{traceback.format_exc()}"
  202. with write_lock:
  203. errors.append(error_msg)
  204. return None
  205. # 使用线程池处理
  206. with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:
  207. # 提交所有任务
  208. futures = {executor.submit(process_sample, sample): sample for sample in dialogues}
  209. # 使用tqdm创建进度条
  210. for future in tqdm(concurrent.futures.as_completed(futures), total=len(dialogues), desc="Evaluating"):
  211. result = future.result()
  212. if result:
  213. with write_lock:
  214. F.append(result)
  215. # 打印处理过程中遇到的错误
  216. if errors:
  217. print(f"\nEncountered {len(errors)} errors during processing:")
  218. for error in errors[:5]: # 最多打印前5个错误
  219. print(error)
  220. # 保存结果
  221. with open("push_message_evaluation_result_4.json", "w", encoding="utf-8") as f:
  222. json.dump(F, f, ensure_ascii=False, indent=4)