📖 项目概述
知识图谱问答(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):
"""回答问题"""
entities = self._recognize_entities(question)
intent, template, match = self._match_intent(question)
if not intent or not entities:
return {"question": question, "answer": "抱歉,我无法理解这个问题。", "confidence": 0.0}
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}")
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}
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
- Text2SPARQL:用LLM将自然语言直接翻译为SPARQL查询
- Retrieval-Augmented KGQA:先从KG检索子图,再用LLM生成答案
- 多轮对话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工程师
❓ 意图识别
🔍 实体链接
📊 查询构造
💬 答案生成