update_llm_intent.py 7.1 KB

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