knowledge_base.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 双知识库智能问答系统
  5. - 知识库A:存储介绍性语句
  6. - 知识库B:存储问答对数据
  7. """
  8. import time
  9. from logger import logger
  10. class KnowledgeBase:
  11. """双知识库管理类"""
  12. def __init__(self):
  13. """初始化双知识库系统"""
  14. # 知识库A:介绍性语句
  15. self.knowledge_a = {
  16. "full": [
  17. "欢迎所有新",
  18. "进直播间的朋友!",
  19. "今天给大家带来",
  20. "咱们本地超好用的生活服务平台,",
  21. "吃喝玩乐全都有!",
  22. "家人们晚上好!",
  23. "不管你是想点外卖、找团购、还是找生活服务,",
  24. "今天一个 App 全部搞定!",
  25. "新进来的宝宝停留一下!今天不带货,",
  26. "只给大家送福利,",
  27. "教你怎么用一个 App 省心省钱!",
  28. "欢迎大家!咱们直播间不讲虚的,",
  29. "全是你家门口的实惠、方便、好用的生活服务!",
  30. "哈喽各位!",
  31. "今天专门给同城的朋友准备的福利场,",
  32. "本地生活一键搞定,千万别走开!",
  33. "欢迎使用本地生活App!",
  34. "我们为您提供美食外卖、休闲娱乐、",
  35. "生活服务和附近团购,随时为您推荐最合适的本地服务!",
  36. "本地生活App超实用!",
  37. "外卖半小时极速达、休闲娱乐折扣多,",
  38. "还有上门家政、生鲜配送等一站式生活服务!",
  39. "选本地生活App准没错!覆盖全城的团购优惠、",
  40. "24小时外卖服务,让您的居家生活更便捷省心!",
  41. "本地生活App上新啦!新人注册立减15元,",
  42. "美食外卖、生鲜果蔬、鲜花绿植都能一键下单!",
  43. "本地生活App,您的专属生活管家!家政保洁、",
  44. "家电维修、跑腿代办,各类生活服务一键预约!",
  45. "不用再对比啦!本地生活App团购低至3折,",
  46. "火锅、烤肉、奶茶全覆盖,每周还有专属特价!",
  47. "本地生活App夏季特惠!冰镇饮品、生鲜水果外卖满减,在家就能享受清凉一夏!",
  48. "上班族福利!本地生活App工作餐专区,营养搭配、准时送达,解决你的午餐烦恼!",
  49. "咱们这个本地生活 App,外卖、美食、休闲娱乐、家政服务,打开就能用,不用到处找!",
  50. "想吃火锅、烤肉、奶茶?打开 App,附近所有优惠一目了然,比你自己找便宜太多!",
  51. "不想做饭?外卖半小时就能到,早餐、午餐、晚餐、下午茶、夜宵,24 小时都能点!",
  52. "家里需要保洁、维修、跑腿?不用到处打电话,App 一键预约,上门服务超省心!",
  53. "生鲜果蔬、日用品、鲜花蛋糕,足不出户,给你送到家,新鲜又快捷!",
  54. "周末想放松,看电影、唱歌、足疗,所有门店优惠全都在这一个 App 里!",
  55. "附近的团购天天有,低至三折,没有任何套路,到店直接用!",
  56. "不管你在哪个片区,周边的优质商家全覆盖,真正的本地生活小管家!",
  57. "操作特别简单,老人小孩都会用,点一点,服务直接上门!",
  58. "一个 App 顶十个平台,外卖、团购、家政、生鲜、跑腿,全部搞定!"
  59. ],
  60. "short": [
  61. "本地生活App,便民服务一键搞定!",
  62. "本地生活App,外卖团购超划算!",
  63. "本地生活App,您的专属生活助手!",
  64. "本地生活App,新人立减15元!"
  65. ]
  66. }
  67. # 知识库B:问答对数据
  68. self.knowledge_b = {
  69. "外卖服务": {
  70. "如何点外卖": "打开本地生活App,点击外卖入口,选择您喜欢的餐厅和菜品,确认地址和支付方式后下单即可。",
  71. "外卖配送时间多长": "一般情况下,外卖配送时间为30-45分钟,具体时间取决于餐厅出餐速度和距离远近。",
  72. "外卖可以退款吗": "可以,在订单状态为待接单或待取餐时,您可以在订单详情页面申请退款。",
  73. "如何查看外卖配送进度": "在订单详情页面,您可以实时查看外卖配送进度和骑手位置。"
  74. },
  75. "团购服务": {
  76. "如何使用团购券": "在本地生活App上购买团购券后,到店消费时出示核销码即可使用。",
  77. "团购券可以退款吗": "未使用的团购券在有效期内可以申请退款,具体退款规则请查看商品详情。",
  78. "如何查找附近的团购": "在本地生活App首页点击团购入口,系统会自动推荐附近的团购优惠。",
  79. "团购有哪些优惠": "本地生活App每日更新团购优惠,包括美食、休闲娱乐、生活服务等多个品类,低至3折起。"
  80. },
  81. "生活服务": {
  82. "如何预约家政服务": "打开本地生活App,点击生活服务入口,选择家政服务类型,预约时间和服务人员即可。",
  83. "家政服务有哪些类型": "本地生活App提供保洁、做饭、带娃、家电维修等多种家政服务。",
  84. "如何预约上门维修": "在生活服务入口选择维修服务,填写维修内容和预约时间,师傅会上门服务。",
  85. "生活服务可以开发票吗": "可以,在服务完成后,您可以在订单详情页面申请开具发票。"
  86. },
  87. "会员福利": {
  88. "如何成为会员": "打开本地生活App,点击我的页面,选择会员中心,按照提示开通会员即可。",
  89. "会员有哪些权益": "会员可以享受免费配送券、团购折上折、专属特价等多项权益。",
  90. "会员积分如何使用": "会员积分可以在积分商城兑换商品或抵扣订单金额。",
  91. "会员日有什么活动": "每周五为会员日,全场团购5折起,外卖满减叠加,省钱超划算!"
  92. },
  93. "新人福利": {
  94. "新人注册有什么优惠": "本地生活App新人注册立减15元,美食外卖、生鲜果蔬、鲜花绿植都能一键下单!",
  95. "新人注册立减多少元": "新人注册立减15元,可以在美食外卖、生鲜果蔬、鲜花绿植等品类使用。",
  96. "新人福利有哪些": "新人注册可享受立减15元优惠,还有新人专享特价商品和免费配送券等多重福利。",
  97. "如何领取新人优惠": "下载本地生活App并注册账号,系统会自动发放15元新人优惠券到您的账户。"
  98. },
  99. "常见问题": {
  100. "如何修改收货地址": "在我的页面点击地址管理,添加或修改收货地址即可。",
  101. "如何联系客服": "在我的页面点击客服中心,可以通过在线客服或电话联系客服。",
  102. "如何查看历史订单": "在我的页面点击订单管理,可以查看所有历史订单。",
  103. "如何使用优惠券": "在结算页面,选择可用的优惠券即可享受折扣。"
  104. }
  105. }
  106. logger.info("双知识库系统初始化完成")
  107. def query_knowledge_a(self, play_type: str = "full") -> list:
  108. """
  109. 获取知识库A的介绍性内容
  110. :param play_type: 播放类型,"full"(完整版)或"short"(简短版)
  111. :return: 介绍性内容列表
  112. """
  113. if play_type not in self.knowledge_a:
  114. logger.error(f"无效的播放类型:{play_type},默认使用完整版")
  115. play_type = "full"
  116. return self.knowledge_a[play_type]
  117. def query_knowledge_b(self, question: str) -> tuple:
  118. """
  119. 查询知识库B获取答案
  120. :param question: 用户问题
  121. :return: (answer, match_score) 答案和匹配度分数
  122. """
  123. start_time = time.time()
  124. logger.info(f"收到知识库B查询请求: {question}")
  125. best_match = None
  126. best_score = 0
  127. best_answer = None
  128. # 遍历知识库B中的所有类别
  129. for category, qna_pairs in self.knowledge_b.items():
  130. # 遍历每个问答对
  131. for q, a in qna_pairs.items():
  132. # 计算匹配度
  133. score = self._calculate_match_score(question, q)
  134. if score > best_score:
  135. best_score = score
  136. best_match = q
  137. best_answer = a
  138. # 记录响应时间
  139. response_time = (time.time() - start_time) * 1000
  140. logger.info(f"知识库B查询响应时间: {response_time:.2f}ms")
  141. if best_score >= 0.85:
  142. logger.info(f"找到匹配度为 {best_score:.2f} 的答案: {best_answer}")
  143. return best_answer, best_score
  144. else:
  145. logger.info(f"未找到匹配度≥85%的答案,最高匹配度为 {best_score:.2f}")
  146. return None, best_score
  147. def _calculate_match_score(self, question: str, keyword: str) -> float:
  148. """
  149. 计算问题与关键词的匹配度
  150. :param question: 用户问题
  151. :param keyword: 关键词
  152. :return: 匹配度分数(0-1)
  153. """
  154. # 转换为小写进行比较
  155. question = question.lower()
  156. keyword = keyword.lower()
  157. # 精确匹配
  158. if keyword in question:
  159. return 1.0
  160. # 计算字符级匹配
  161. question_chars = set(question)
  162. keyword_chars = set(keyword)
  163. common_chars = question_chars & keyword_chars
  164. # 计算字符匹配率
  165. if len(keyword_chars) == 0:
  166. return 0
  167. char_match_rate = len(common_chars) / len(keyword_chars)
  168. # 计算连续字符匹配
  169. max_consecutive_match = 0
  170. current_consecutive = 0
  171. for char in question:
  172. if char in keyword_chars:
  173. current_consecutive += 1
  174. max_consecutive_match = max(max_consecutive_match, current_consecutive)
  175. else:
  176. current_consecutive = 0
  177. # 连续匹配加分
  178. consecutive_bonus = min(max_consecutive_match / len(keyword), 0.3)
  179. # 计算最终匹配度
  180. match_score = char_match_rate * 0.7 + consecutive_bonus
  181. return min(match_score, 1.0)
  182. # 创建全局知识库实例
  183. knowledge_base = KnowledgeBase()
  184. # 便捷函数
  185. def query_knowledge_a(play_type: str = "full") -> list:
  186. """便捷查询知识库A函数"""
  187. return knowledge_base.query_knowledge_a(play_type)
  188. def query_knowledge_b(question: str) -> tuple:
  189. """便捷查询知识库B函数"""
  190. return knowledge_base.query_knowledge_b(question)