llm.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. import time
  2. import os
  3. from basereal import BaseReal
  4. from logger import logger
  5. from knowledge_base import query_knowledge_b
  6. def llm_response(message, nerfreal:BaseReal, knowledge_base_type=None, during_intro=False):
  7. start = time.perf_counter()
  8. logger.info(f"收到用户问题: {message}")
  9. logger.info(f"知识库类型: {knowledge_base_type}")
  10. logger.info(f"是否在介绍过程中: {during_intro}")
  11. # 首先尝试从知识库B获取答案
  12. kb_response, match_score = query_knowledge_b(message)
  13. # 检查是否从知识库B获得了有效答案(匹配度≥85%)
  14. if kb_response and match_score >= 0.85:
  15. logger.info(f"从知识库B获取答案,匹配度: {match_score:.2f}")
  16. logger.info(f"答案: {kb_response}")
  17. # 根据是否在介绍过程中决定使用哪种方法
  18. if during_intro:
  19. # 在介绍过程中被打断,使用专门的处理方法
  20. logger.info("在介绍过程中,使用handle_interruption_during_intro方法")
  21. # 传递during_intro参数,以便TTS在回答完毕后自动恢复
  22. nerfreal.handle_interruption_during_intro(kb_response, {'knowledge_base': knowledge_base_type, 'mode': 'qa', 'during_intro': during_intro})
  23. else:
  24. # 不是在介绍过程中,使用常规方法
  25. logger.info("不在介绍过程中,使用put_user_question方法")
  26. nerfreal.put_user_question(kb_response) # 使用高优先级方法
  27. logger.info("知识库B答案已发送到播放队列")
  28. end = time.perf_counter()
  29. logger.info(f"问答处理总时间: {end-start:.2f}s")
  30. return
  31. else:
  32. logger.info(f"知识库B无匹配答案或匹配度不足,最高匹配度: {match_score:.2f},使用LLM处理")
  33. # 如果知识库B没有匹配答案,使用LLM
  34. api_key = os.getenv("DASHSCOPE_API_KEY")
  35. if not api_key:
  36. # 如果没有配置API密钥,给出友好提示
  37. no_api_response = "抱歉,我无法找到相关答案。请配置DASHSCOPE_API_KEY环境变量以启用智能问答功能,或者尝试其他相关问题。"
  38. logger.info(f"未配置API密钥,返回提示信息: {no_api_response}")
  39. # 使用put_user_question而不是flush_talk,以保持一致性
  40. if during_intro:
  41. nerfreal.handle_interruption_during_intro(no_api_response, {'knowledge_base': knowledge_base_type, 'mode': 'qa'})
  42. else:
  43. nerfreal.put_user_question(no_api_response)
  44. end = time.perf_counter()
  45. logger.info(f"问答处理总时间: {end-start:.2f}s")
  46. return
  47. from openai import OpenAI
  48. client = OpenAI(
  49. # 如果您没有配置环境变量,请在此处用您的API Key进行替换
  50. api_key=api_key,
  51. # 填写DashScope SDK的base_url
  52. base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
  53. )
  54. end = time.perf_counter()
  55. logger.info(f"llm Time init: {end-start}s")
  56. #构建系统提示词
  57. system_prompt = "You are a helpful assistant."
  58. if knowledge_base_type:
  59. if knowledge_base_type == "技术文档库":
  60. system_prompt = "您是技术专家助手,请基于技术文档库回答问题,提供准确的技术信息和解决方案。"
  61. elif knowledge_base_type == "生活百科库":
  62. system_prompt = "您是生活百科助手,请基于生活百科库回答问题,提供实用的生活建议和信息。"
  63. elif knowledge_base_type == "本地生活App":
  64. system_prompt = "您是本地生活App助手,请基于本地生活App知识库回答问题,提供关于美食外卖、休闲娱乐、生活服务、团购优惠等相关信息。"
  65. completion = client.chat.completions.create(
  66. model="qwen-plus",
  67. messages=[
  68. {'role': 'system', 'content': system_prompt},
  69. {'role': 'user', 'content': message}
  70. ],
  71. stream=True,
  72. # 通过以下设置,在流式输出的最后一行展示token使用信息
  73. stream_options={"include_usage": True}
  74. )
  75. result=""
  76. first = True
  77. for chunk in completion:
  78. if len(chunk.choices)>0:
  79. #print(chunk.choices[0].delta.content)
  80. if first:
  81. end = time.perf_counter()
  82. logger.info(f"llm Time to first chunk: {end-start}s")
  83. first = False
  84. msg = chunk.choices[0].delta.content
  85. if msg:
  86. result += msg
  87. end = time.perf_counter()
  88. logger.info(f"llm Time to last chunk: {end-start}s")
  89. logger.info(f"LLM完整回答: {result}")
  90. if during_intro:
  91. nerfreal.handle_interruption_during_intro(result, {'knowledge_base': knowledge_base_type, 'mode': 'qa'})
  92. else:
  93. nerfreal.put_user_question(result) # 使用高优先级方法
  94. logger.info("LLM答案已发送到播放队列")
  95. logger.info(f"问答处理总时间: {end-start:.2f}s")