update_llm_intent.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import json
  2. from app.admin.schema.org import CurrentIntentOrgIns
  3. from app.call_center.crud.crud_intent_records import intent_records_dao
  4. from app.call_center.schema.intent_records import GetIntentRecordsDetails
  5. from common.log import log
  6. from common.oai import generate_json, send_request_with_retry
  7. from core.conf import settings
  8. from database.db_mysql import async_db_session
  9. from database.db_redis import redis_client
  10. from utils.serializers import select_as_dict
  11. async def update_llm_intent():
  12. async with async_db_session.begin() as db:
  13. record = await intent_records_dao.get_earliest_record(db)
  14. if not record:
  15. return None
  16. record_data = GetIntentRecordsDetails(**select_as_dict(record))
  17. log.info(f"[oai] record failed: {record}")
  18. # 机构 id
  19. org_id = record_data.org_id
  20. # 从缓存中获取机构信息
  21. key = f'{settings.TOKEN_CALL_REDIS_PREFIX}:{org_id}'
  22. org_json = await redis_client.get(key)
  23. if not org_json:
  24. # 缓存中没有,从数据库中获取
  25. from app.admin.crud.crud_org import org_dao
  26. org = await org_dao.get(db, org_id)
  27. if not org and org.status is not 1:
  28. log.error(f"意向评级时,机构不存在 org_id: {org_id}")
  29. return None
  30. org_data = CurrentIntentOrgIns(**select_as_dict(org))
  31. # 将数据放进缓存
  32. await redis_client.setex(
  33. key,
  34. settings.JWT_USER_REDIS_EXPIRE_SECONDS,
  35. org_data.model_dump_json(),
  36. )
  37. else:
  38. org_data = CurrentIntentOrgIns(**json.loads(org_json))
  39. # 开始评级
  40. intent_schema = {
  41. "name": "intent_schema",
  42. "schema": { # 添加 schema 字段
  43. "type": "object",
  44. "description": "为通话记录进行评级",
  45. "properties": {
  46. "score": {"type": "string", "description": "评分结果:有意向、待进一步分析、暂时无法沟通、其他"},
  47. "confidence_score": {"type": "number", "description": "置信度分数,范围从0.0到1.0"},
  48. "scoring_criteria": {"type": "string", "description": "请逐步介绍为何评为这个结果"}
  49. },
  50. "required": ["score", "confidence_score", "scoring_criteria"]
  51. }
  52. }
  53. rules = [
  54. {
  55. 'rule': "用户表现出对产品有积极回应,询问有关产品的问题,或愿意进行后续操作,如确认微信号、询问发货地址或表达接受产品(例:'行'、'好'、'可以'、'是的')。",
  56. 'score': '有意向'},
  57. {
  58. 'rule': "用户虽然未积极回应或表示不需要,但却有相关需求、或着符合某项购买条件、又或着询问了机器人问题,即便后期疲软或持否定态度,可移交销售顾问进行进一步的需求分析,以便改善产品或定制符合客户要求的产品",
  59. 'score': '待进一步分析'},
  60. {
  61. 'rule': "用户表示自己在忙、在开会等,现在不太方便沟通。又或者对电话的真实性存疑,怀疑是诈骗电话,可后续让专业客服继续跟进",
  62. 'score': '暂时无法沟通'},
  63. {
  64. 'rule': "除上述规则外的其他用户,请注意客户资源珍贵,请尽量归类为'有意向'或'待进一步分析',后续再由销售专家定性。",
  65. 'score': '其他'}
  66. ]
  67. score_map = {
  68. '有意向': 1,
  69. '待进一步分析': 2,
  70. '暂时无法沟通': 3,
  71. '其他': 4
  72. }
  73. intent_map = {
  74. '有意向': "有意向",
  75. '待进一步分析': "无法判定",
  76. '暂时无法沟通': "在忙",
  77. '其他': "无意向"
  78. }
  79. messages = [
  80. {"role": "system", "content": f"""# 任务
  81. 1. 首先,判断用户的第一句话是否说了:“你好,(任意内容)通话”,如果说了,则不用理会评级规则,直接强制分配为"语音助手"
  82. 2. 如果不属于“语音助手”,请根据评级规则,对聊天记录给出评级、置信度、评分依据(逐项分析不要遗漏)
  83. # 细节说明
  84. 置信度从0到1,0为置信度最低,1为置信度最高。"""
  85. },
  86. {
  87. "role": "user",
  88. "content": f"""# 评级规则:
  89. {rules}
  90. # 聊天记录
  91. {record_data.chat_history}
  92. """
  93. }
  94. ]
  95. response_data = generate_json(org_data.openai_key, org_data.openai_base, org_data.model, messages, intent_schema)
  96. if response_data and isinstance(response_data.choices, list) and len(response_data.choices) > 0:
  97. first_choice = response_data.choices[0]
  98. if first_choice and first_choice.message:
  99. response_json = first_choice.message.content
  100. if response_json:
  101. intent = json.loads(response_json)
  102. score = intent.get('score', "未知")
  103. llm_intent = score_map.get(score, 0)
  104. if llm_intent == 0:
  105. properties = intent.get('properties', {})
  106. score = properties.get('score', "未知")
  107. llm_intent = score_map.get(score, 0)
  108. if llm_intent != 0:
  109. # confidence_score = intent.get('confidence_score', 0)
  110. # scoring_criteria = intent.get('scoring_criteria', "未知")
  111. log.info(f"response_data.to_dict(): {response_data.to_dict()}")
  112. status = 1
  113. # 推送
  114. url = org_data.intent_callback
  115. log.error(f"url: {url}")
  116. if url:
  117. headers = {
  118. "Content-Type": "application/json"
  119. }
  120. data = {
  121. "internal_id": record_data.id,
  122. "external_id": record_data.external_id,
  123. "score": llm_intent,
  124. "intent": intent_map.get(score, "无法判定")
  125. }
  126. is_success = await send_request_with_retry(url, data, headers, max_retries=3, delay_between_retries=2)
  127. if is_success:
  128. status = 2
  129. async with async_db_session.begin() as db:
  130. try:
  131. await intent_records_dao.update_llm_intent(db, record_data.id, llm_intent,
  132. {"messages": messages},
  133. response_data.to_dict(),
  134. status)
  135. except Exception as e:
  136. log.error(f"更新意图记录时发生异常:{e}")