知识图谱技术架构
知识图谱技术架构详解
🏛️ 整体技术架构全景图
┌─────────────────────────────────────────────────────────────┐
│ 应用层 │
│ 智能搜索 | 问答系统 | 推荐引擎 | 风控系统 | 决策支持 │
└─────────────────────────┬───────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 知识计算层 │
│ 知识推理 | 图谱补全 | 实体链接 | 语义计算 │
└─────────────────────────┬───────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 知识管理层 │
│ 知识融合 | 质量评估 | 版本管理 | 知识更新 │
└─────────────────────────┬───────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 知识获取层 │
│ 信息抽取 | 知识挖掘 | 众包标注 | 结构化数据导入 │
└─────────────────────────┬───────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 数据存储层 │
│ 图数据库 | RDF存储 | 向量数据库 | 缓存层 │
└─────────────────────────┬───────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 数据源层 │
│ 结构化DB | 网页文本 | 学术论文 | 企业文档 | API数据 │
└─────────────────────────────────────────────────────────────┘
1️⃣ 数据源层
数据来源分类
数据源
├── 结构化数据
│ ├── 关系型数据库(MySQL、PostgreSQL)
│ ├── Excel / CSV 文件
│ └── 业务系统导出数据
│
├── 半结构化数据
│ ├── 维基百科(Infobox)
│ ├── HTML 网页表格
│ ├── JSON / XML 数据
│ └── 学术数据库
│
└── 非结构化数据
├── 新闻文章
├── 学术论文
├── 企业文档
├── 社交媒体
└── 对话记录
2️⃣ 知识获取层
核心任务架构
原始文本:"马云1999年在杭州创立了阿里巴巴公司"
↓
┌───────────────────────────────────────────┐
│ NLP 处理流水线 │
│ │
│ 分词 → 词性标注 → 句法分析 → 语义分析 │
│ │
│ 马云/1999年/杭州/创立/阿里巴巴/公司 │
└────────────────────┬──────────────────────┘
↓
┌────────────┴────────────┐
↓ ↓
┌───────────────┐ ┌──────────────────┐
│ 实体识别 │ │ 关系抽取 │
│ (NER) │ │ (RE) │
│ │ │ │
│ 马云 → 人物 │ │ 马云─[创立]→阿里 │
│ 1999年 → 时间 │ │ 马云─[出生地]→杭州│
│ 杭州 → 地点 │ │ │
│ 阿里巴巴→ 组织│ └──────────────────┘
└───────────────┘
↓
┌───────────────────────────────────────────┐
│ 属性抽取 │
│ 阿里巴巴.成立时间 = 1999年 │
│ 阿里巴巴.创始人 = 马云 │
│ 阿里巴巴.总部 = 杭州 │
└───────────────────────────────────────────┘
关键技术模型
# 实体识别示例(基于 BERT)
from transformers import AutoTokenizer, AutoModelForTokenClassification
import torch
class 实体识别器:
def __init__(self):
self.tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese")
self.model = AutoModelForTokenClassification.from_pretrained(
"chinese-bert-ner"
)
def 识别实体(self, 文本: str):
# 分词
inputs = self.tokenizer(文本, return_tensors="pt")
# 模型推理
outputs = self.model(**inputs)
predictions = torch.argmax(outputs.logits, dim=2)
# 解析实体
实体列表 = self._parse_entities(文本, predictions)
return 实体列表
def _parse_entities(self, 文本, predictions):
# B-PER: 人名开始, I-PER: 人名中间
# B-ORG: 组织开始, B-LOC: 地名开始
实体 = []
# ... 解析逻辑
return 实体
# 关系抽取示例
class 关系抽取器:
def __init__(self):
self.model = self._load_relation_model()
def 抽取关系(self, 文本: str, 实体对: tuple):
主体, 客体 = 实体对
# 构造输入:[CLS] 文本 [SEP] 主体 [SEP] 客体
input_text = f"[CLS]{文本}[SEP]{主体}[SEP]{客体}"
# 预测关系类型
关系类型 = self.model.predict(input_text)
置信度 = self.model.confidence(input_text)
return {
"主体": 主体,
"关系": 关系类型,
"客体": 客体,
"置信度": 置信度
}
3️⃣ 数据存储层
存储架构设计
┌─────────────────────────────────────────────────────┐
│ 多层存储架构 │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 热数据缓存 │ │ 图数据库 │ │
│ │ Redis │ │ Neo4j │ │
│ │ 高频查询 │ │ 核心图谱 │ │
│ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ 向量数据库 │ │ 关系型数据库 │ │
│ │ Milvus │ │ MySQL │ │
│ │ 语义检索 │ │ 元数据存储 │ │
│ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────────────────────────┐ │
│ │ 对象存储 / 文件系统 │ │
│ │ 原始文档 | 图谱备份 | 日志 │ │
│ └──────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
Neo4j 图数据库详解
// ========== 数据模型设计 ==========
// 节点标签定义
(:人物 {
id: "P001",
姓名: "马云",
出生日期: "1964-09-10",
国籍: "中国",
简介: "阿里巴巴集团创始人"
})
(:公司 {
id: "C001",
名称: "阿里巴巴",
成立时间: "1999",
行业: "互联网",
上市地: "纽约证券交易所"
})
(:地点 {
id: "L001",
名称: "杭州",
类型: "城市",
所属省份: "浙江省"
})
// 关系定义
(马云)-[:创立 {时间: "1999", 角色: "CEO"}]->(阿里巴巴)
(马云)-[:出生于]->(杭州)
(阿里巴巴)-[:总部位于]->(杭州)
// ========== 常用查询 ==========
// 1. 查找某人创立的所有公司
MATCH (p:人物 {姓名: "马云"})-[:创立]->(c:公司)
RETURN p.姓名, c.名称, c.成立时间
ORDER BY c.成立时间
// 2. 多跳查询:与马云有2度关系的人物
MATCH (p:人物 {姓名: "马云"})-[*1..2]-(related:人物)
WHERE related.姓名 <> "马云"
RETURN DISTINCT related.姓名, related.职业
// 3. 最短路径查询
MATCH path = shortestPath(
(马云:人物 {姓名: "马云"})-[*]-(任正非:人物 {姓名: "任正非"})
)
RETURN path
// 4. 图谱统计分析
MATCH (n)
RETURN labels(n)[0] AS 节点类型, count(n) AS 数量
ORDER BY 数量 DESC
// 5. 社区发现:找出关系最密集的实体群
MATCH (n)-[r]-(m)
WITH n, count(r) AS 关系数
WHERE 关系数 > 10
RETURN n.姓名, 关系数
ORDER BY 关系数 DESC
LIMIT 20
向量存储(语义检索)
from pymilvus import Collection, FieldSchema, CollectionSchema, DataType
from sentence_transformers import SentenceTransformer
class 知识向量库:
def __init__(self):
self.encoder = SentenceTransformer("paraphrase-multilingual-mpnet-base-v2")
self.collection = self._init_collection()
def _init_collection(self):
# 定义字段结构
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True),
FieldSchema(name="实体名称", dtype=DataType.VARCHAR, max_length=200),
FieldSchema(name="描述文本", dtype=DataType.VARCHAR, max_length=2000),
FieldSchema(name="向量", dtype=DataType.FLOAT_VECTOR, dim=768),
]
schema = CollectionSchema(fields)
return Collection("知识图谱实体", schema)
def 存入实体(self, 实体列表: list):
# 批量向量化
文本列表 = [实体["描述"] for 实体 in 实体列表]
向量列表 = self.encoder.encode(文本列表).tolist()
# 写入向量库
self.collection.insert([
[实体["id"] for 实体 in 实体列表],
[实体["名称"] for 实体 in 实体列表],
文本列表,
向量列表
])
def 语义检索(self, 查询文本: str, top_k: int = 10):
# 查询向量化
查询向量 = self.encoder.encode([查询文本]).tolist()
# 相似度搜索
结果 = self.collection.search(
data=查询向量,
anns_field="向量",
param={"metric_type": "COSINE", "params": {"nprobe": 16}},
limit=top_k,
output_fields=["实体名称", "描述文本"]
)
return 结果
4️⃣ 知识管理层
知识融合流程
多源异构数据输入:
数据源A:马云 → 企业家
数据源B:Jack Ma → Businessman
数据源C:马云 → 出生于1964年
↓
┌───────────────────────────────────┐
│ 实体对齐 │
│ │
│ 马云 = Jack Ma = 马云 │
│ (通过姓名/别名/属性特征匹配) │
└─────────────────┬─────────────────┘
↓
┌───────────────────────────────────┐
│ 冲突检测与消解 │
│ │
│ 冲突示例: │
│ A说:马云出生于1964年 │
│ B说:马云出生于1965年 │
│ │
│ 消解策略: │
│ ① 可信度加权(选高可信来源) │
│ ② 时效性优先(选最新数据) │
│ ③ 人工审核(重要冲突) │
└─────────────────┬─────────────────┘
↓
┌───────────────────────────────────┐
│ 知识补全 │
│ 填充缺失属性和关系 │
└───────────────────────────────────┘
质量评估体系
class 知识质量评估:
def 评估三元组质量(self, 三元组: dict) -> dict:
scores = {}
# 1. 准确性评分
scores["准确性"] = self._check_accuracy(三元组)
# 2. 完整性评分
scores["完整性"] = self._check_completeness(三元组)
# 3. 一致性评分
scores["一致性"] = self._check_consistency(三元组)
# 4. 时效性评分
scores["时效性"] = self._check_timeliness(三元组)
# 综合评分
scores["综合"] = sum(scores.values()) / len(scores)
return scores
def _check_accuracy(self, 三元组):
# 与权威数据源交叉验证
主体, 谓语, 客体 = 三元组["主体"], 三元组["谓语"], 三元组["客体"]
# 检查类型约束
if 谓语 == "出生地" and not self._is_location(客体):
return 0.0 # 出生地必须是地点实体
# 检查值域范围
if 谓语 == "出生年份":
年份 = int(客体)
if not (1800 <= 年份 <= 2024):
return 0.0
return 1.0
def _check_consistency(self, 三元组):
# 检查是否与已有知识矛盾
现有知识 = self.graph.query(三元组["主体"], 三元组["谓语"])
if 现有知识 and 现有知识 != 三元组["客体"]:
return 0.5 # 存在冲突,需人工审核
return 1.0
5️⃣ 知识计算层
知识推理架构
推理方式分类:
┌─────────────────────────────────────────────┐
│ 知识推理 │
│ │
│ ┌──────────────┐ ┌───────────────────┐ │
│ │ 符号推理 │ │ 统计推理 │ │
│ │ ────────── │ │ ────────────── │ │
│ │ 规则引擎 │ │ 知识图谱嵌入 │ │
│ │ 本体推理 │ │ TransE/RotatE │ │
│ │ 一阶逻辑 │ │ 链接预测 │ │
│ └──────────────┘ └───────────────────┘ │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ 神经符号融合推理 │ │
│ │ 深度学习 + 逻辑规则 结合 │ │
│ └──────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
知识图谱嵌入(KGE)
将实体和关系映射到低维向量空间:
$$\text{TransE 模型:} \quad \vec{h} + \vec{r} \approx \vec{t}$$
其中 $\vec{h}$ 是头实体向量,$\vec{r}$ 是关系向量,$\vec{t}$ 是尾实体向量。
import torch
import torch.nn as nn
class TransE(nn.Module):
"""
TransE 知识图谱嵌入模型
核心思想:h + r ≈ t
"""
def __init__(self, 实体数量, 关系数量, 嵌入维度=100):
super().__init__()
# 实体嵌入矩阵
self.实体嵌入 = nn.Embedding(实体数量, 嵌入维度)
# 关系嵌入矩阵
self.关系嵌入 = nn.Embedding(关系数量, 嵌入维度)
# 初始化
nn.init.uniform_(self.实体嵌入.weight, -1, 1)
nn.init.uniform_(self.关系嵌入.weight, -1, 1)
def forward(self, 头实体, 关系, 尾实体):
# 获取向量表示
h = self.实体嵌入(头实体)
r = self.关系嵌入(关系)
t = self.实体嵌入(尾实体)
# 计算得分:|h + r - t|
# 得分越低 → 三元组越可能为真
得分 = torch.norm(h + r - t, p=2, dim=1)
return 得分
def 链接预测(self, 头实体_id, 关系_id, top_k=10):
"""预测:给定头实体和关系,找最可能的尾实体"""
h = self.实体嵌入(torch.tensor([头实体_id]))
r = self.关系嵌入(torch.tensor([关系_id]))
# 计算与所有实体的距离
所有实体 = self.实体嵌入.weight
得分 = torch.norm(h + r - 所有实体, p=2, dim=1)
# 返回得分最低的 top_k 个实体
_, top_indices = torch.topk(得分, k=top_k, largest=False)
return top_indices.tolist()
# 模型对比
嵌入模型对比 = {
"TransE": {"优点": "简单高效", "缺点": "不能处理1-N关系"},
"TransR": {"优点": "关系特定空间", "缺点": "参数量大"},
"RotatE": {"优点": "处理复杂关系模式", "缺点": "复数计算复杂"},
"ComplEx": {"优点": "处理非对称关系", "缺点": "可解释性差"},
"DistMult": {"优点": "计算简单", "缺点": "只能处理对称关系"},
}
多跳推理
class 多跳推理引擎:
"""
实现多步推理:
问题:"马云的母校在哪个城市?"
推理链:马云 → 毕业于 → 杭州师范大学 → 位于 → 杭州
"""
def __init__(self, 知识图谱, llm_model):
self.图谱 = 知识图谱
self.llm = llm_model
async def 推理(self, 问题: str) -> str:
# 第一步:LLM 分解问题为推理链
推理链 = await self.llm.decompose_question(问题)
# ["马云的母校", "母校的城市"]
# 第二步:逐步在图谱中查询
当前实体 = "马云"
推理历史 = []
for 子问题 in 推理链:
# 图谱查询
关系 = await self.llm.extract_relation(子问题)
下一实体 = self.图谱.query(当前实体, 关系)
推理历史.append({
"实体": 当前实体,
"关系": 关系,
"结果": 下一实体
})
当前实体 = 下一实体
# 第三步:汇总推理路径生成答案
答案 = await self.llm.generate_answer(问题, 推理历史)
return 答案, 推理历史
6️⃣ 应用层接口
RESTful API 设计
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI(title="知识图谱 API")
# ========== 实体查询接口 ==========
@app.get("/entity/{entity_id}")
async def 查询实体(entity_id: str):
"""查询单个实体的详细信息"""
实体 = graph_db.get_entity(entity_id)
if not 实体:
raise HTTPException(status_code=404, detail="实体不存在")
return 实体
# ========== 关系查询接口 ==========
@app.get("/relations/{entity_id}")
async def 查询关系(
entity_id: str,
relation_type: str = None,
depth: int = 1
):
"""查询实体的关系网络"""
关系图 = graph_db.get_relations(
entity_id,
relation_type=relation_type,
max_depth=depth
)
return 关系图
# ========== 语义搜索接口 ==========
class 搜索请求(BaseModel):
query: str
top_k: int = 10
entity_type: str = None
@app.post("/search")
async def 语义搜索(请求: 搜索请求):
"""自然语言语义搜索"""
# 向量检索
向量结果 = vector_db.search(请求.query, 请求.top_k)
# 图谱扩展
扩展结果 = graph_db.expand(向量结果)
return 扩展结果
# ========== 智能问答接口 ==========
class 问答请求(BaseModel):
question: str
context: str = None
@app.post("/qa")
async def 智能问答(请求: 问答请求):
"""基于知识图谱的智能问答"""
# 实体识别
实体列表 = ner_model.extract(请求.question)
# 图谱查询
相关知识 = graph_db.query_subgraph(实体列表)
# LLM 生成答案
答案 = await llm.answer(
问题=请求.question,
知识上下文=相关知识
)
return {
"答案": 答案,
"推理路径": 相关知识["推理链"],
"置信度": 答案["confidence"]
}
# ========== 知识写入接口 ==========
class 三元组(BaseModel):
主体: str
谓语: str
客体: str
来源: str
置信度: float = 1.0
@app.post("/triple")
async def 写入三元组(triple: 三元组):
"""写入新的知识三元组"""
# 质量检测
质量分数 = quality_checker.evaluate(triple)
if 质量分数 < 0.6:
raise HTTPException(status_code=400, detail="知识质量不达标")
# 冲突检测
冲突 = conflict_detector.check(triple)
if 冲突:
return {"status": "待审核", "conflict": 冲突}
# 写入图谱
graph_db.insert_triple(triple)
return {"status": "成功", "quality_score": 质量分数}
🔄 知识图谱 + LLM 融合架构
GraphRAG 完整架构
用户提问
↓
┌─────────────────────────────────────────────┐
│ 查询理解层 │
│ LLM 分析问题意图 → 提取关键实体和关系 │
└────────────────────┬────────────────────────┘
↓
┌────────────┴────────────┐
↓ ↓
┌──────────────┐ ┌─────────────────┐
│ 图谱精确查询 │ │ 向量语义检索 │
│ Neo4j │ │ Milvus │
│ 结构化知识 │ │ 非结构化知识 │
└──────┬───────┘ └────────┬────────┘
└──────────┬──────────────┘
↓
┌─────────────────────────────────────────────┐
│ 知识融合与排序 │
│ 合并图谱结果 + 向量结果 → 相关度排序 │
└────────────────────┬────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ LLM 答案生成 │
│ 基于检索到的知识 → 生成准确流畅的答案 │
└────────────────────┬────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ 答案验证层 │
│ 知识图谱验证事实准确性 → 防止幻觉 │
└─────────────────────────────────────────────┘
↓
最终答案输出
(含推理路径 + 来源引用)
class GraphRAG系统:
def __init__(self):
self.图谱 = Neo4jClient()
self.向量库 = MilvusClient()
self.llm = OpenAIClient()
self.ner = NERModel()
async def 回答问题(self, 问题: str) -> dict:
# ===== Step 1: 理解问题 =====
实体列表 = self.ner.extract(问题)
查询意图 = await self.llm.classify_intent(问题)
# ===== Step 2: 双路检索 =====
# 路径A:图谱结构化查询
图谱结果 = self.图谱.query_subgraph(
entities=实体列表,
depth=3
)
# 路径B:向量语义检索
向量结果 = self.向量库.search(
query=问题,
top_k=5
)
# ===== Step 3: 知识融合 =====
融合知识 = self._merge_results(图谱结果, 向量结果)
# ===== Step 4: 生成答案 =====
prompt = f"""
基于以下知识图谱信息回答问题:
问题:{问题}
相关知识:
{融合知识}
要求:
- 只使用提供的知识作答
- 如果知识不足,明确说明
- 提供推理步骤
"""
答案 = await self.llm.generate(