❓ 第24课:问答系统

实战项目4——基于知识图谱的智能问答

📖 项目概述

知识图谱问答(KBQA)是知识图谱最直接的应用——用户用自然语言提问,系统从知识图谱中找到答案。本课实现一个完整的KBQA系统。

🎯 KBQA的核心流程

💻 Python实现:KBQA系统

import re from collections import defaultdict class KBQASystem: """基于知识图谱的问答系统""" def __init__(self, kg): self.kg = kg # 知识图谱实例 self.intent_patterns = [] self.entity_dict = defaultdict(list) def add_entity(self, name, entity_id): self.entity_dict[name].append(entity_id) def add_intent_pattern(self, pattern, intent, template): """ pattern: 正则模式 intent: 意图类型 template: 回答模板 {entity} {relation} {answer} """ self.intent_patterns.append((re.compile(pattern), intent, template)) def _recognize_entities(self, question): """识别问题中的实体""" entities = [] for name, ids in sorted(self.entity_dict.items(), key=lambda x: -len(x[0])): if name in question: entities.append((name, ids)) return entities def _match_intent(self, question): """匹配问题意图""" for pattern, intent, template in self.intent_patterns: match = pattern.search(question) if match: return intent, template, match return None, None, None def answer(self, question): """回答问题""" # 1. 实体识别 entities = self._recognize_entities(question) # 2. 意图匹配 intent, template, match = self._match_intent(question) if not intent or not entities: return {"question": question, "answer": "抱歉,我无法理解这个问题。", "confidence": 0.0} # 3. 查询知识图谱 entity_name = entities[0][0] results = [] if intent == "query_relation": # 查询某实体的某关系 relation_keywords = { "创作": "创作", "写了": "创作", "作品": "创作", "出生": "出生地", ">>哪里人": ">>出生地", ">>籍贯": ">>出生地", ">>原名": ">>原名", ">>本名": ">>原名", } for kw, rel in relation_keywords.items(): if kw in question: for h, r, t in self.kg.query_by_entity(entity_name): if r == rel and h == entity_name: results.append(t) break elif intent == ">>query_all": for h, r, t in self.kg.query_by_entity(entity_name): results.append(f"{r}: {t}") # 4. 生成回答 if results: answer_text = template.format(entity=entity_name, answer=", ".join(str(r) for r in results)) confidence = 0.9 else: answer_text = f"我没有找到关于{entity_name}的相关信息。" confidence = 0.3 return {">>question": question, ">>intent": intent, ">>entity": entity_name, ">>answer": answer_text, ">>confidence": confidence} # ========== 使用第1课的KnowledgeGraph ========== class SimpleKG: def __init__(self): self.triples = [] self.entities = set() def add_triple(self, h, r, t): self.triples.append((h, r, t)) self.entities.add(h) self.entities.add(t) def query_by_entity(self, entity): return [(h, r, t) for h, r, t in self.triples if h == entity or t == entity] kg = SimpleKG() kg.add_triple("鲁迅", ">>创作", ">>呐喊") kg.add_triple(">>鲁迅", ">>创作", ">>彷徨") kg.add_triple(">>鲁迅", ">>出生地", ">>绍兴") kg.add_triple(">>鲁迅", ">>原名", ">>周树人") kg.add_triple(">>老舍", ">>创作", ">>骆驼祥子") kg.add_triple(">>老舍", ">>出生地", ">>北京") # 构建问答系统 qa = KBQASystem(kg) for e in kg.entities: qa.add_entity(e, e) qa.add_intent_pattern(r'.*(创作|写了|作品).*, ">>query_relation", "{entity}创作了: {answer}") qa.add_intent_pattern(r'.*(出生|哪里人|籍贯).*, ">>query_relation", "{entity}的出生地是: {answer}") qa.add_intent_pattern(r'.*(原名|本名).*, ">>query_relation", "{entity}的原名是: {answer}") qa.add_intent_pattern(r'.*(介绍|信息|是谁).*, ">>query_all", "{entity}的信息: {answer}") # 测试 questions = [ ">>鲁迅创作了什么?", ">>鲁迅是哪里人?", ">>鲁迅的原名是什么?", ">>老舍写了什么?", ">>介绍一下鲁迅", ] print(">>=== KBQA测试 ===") for q in questions: result = qa.answer(q) print(f" Q: {q}") print(f" A: {result['answer']} (conf={result['confidence']:.1f})") print()
=== KBQA测试 === Q: 鲁迅创作了什么? A: 鲁迅创作了: 呐喊, 彷徨 (conf=0.9) Q: 鲁迅是哪里人? A: 鲁迅的出生地是: 绍兴 (conf=0.9) Q: 鲁迅的原名是什么? A: 鲁迅的原名是: 周树人 (conf=0.9) Q: 老舍写了什么? A: 老舍创作了: 骆驼祥子 (conf=0.9) Q: 介绍一下鲁迅 A: 鲁迅的信息: 创作: 呐喊, 创作: 彷徨, 出生地: 绍兴, 原名: 周树人 (conf=0.9)

📝 实战练习

练习1:多实体问题

支持"鲁迅和老舍都创作了什么?"这样的多实体问题。

练习2:多跳推理

支持"绍兴属于哪个省?"这类需要多跳推理的问题。

练习3:模糊匹配

实现实体名称的模糊匹配,当用户输入"迅"时也能匹配到"鲁迅"。

🌐 KBQA的前沿方向

大模型时代的KBQA

💡 选型建议:简单场景(<100种问题模板)→ 规则模板方法;中等复杂度 → 语义解析方法;最灵活 → LLM+KG检索。三种方法可以组合使用,形成互补的混合系统。
# 多轮对话管理器 class DialogManager: """多轮对话管理""" def __init__(self, qa_system): self.qa = qa_system self.context = {} # 对话上下文 self.history = [] def chat(self, user_input): """处理用户输入""" # 如果是追问(包含代词),从上下文补全 pronouns = ["他", "她", "它", "这个", "那个"] resolved_input = user_input for p in pronouns: if p in user_input and "last_entity" in self.context: resolved_input = user_input.replace(p, self.context["last_entity"]) result = self.qa.answer(resolved_input) self.history.append((user_input, result["answer"])) if "entity" in result: self.context["last_entity"] = result["entity"] return result print("✅ 多轮对话管理器实现完成")

🏆 第24课成就解锁

KBQA工程师

❓ 意图识别
🔍 实体链接
📊 查询构造
💬 答案生成