1. 基于大模型的实体识别与知识图谱构建

基于大模型的实体识别与知识图谱构建 学习卡片

任务说明

本案例将完成一个最小可复用的“基于大模型的实体识别与知识图谱构建”闭环:给定一段中文财经新闻文本,调用大模型接口抽取实体及实体间关系,并将结果转换为可导入Neo4j的Cypher语句,最终生成知识图谱的可视化预览。案例聚焦于“大模型负责语义理解与抽取,知识图谱负责结构化存储”的典型分工,不涉及图谱查询优化或前端工程。

实现方案

整体流程分为三步:
1. 实体与关系抽取:构造结构化的Prompt,要求大模型从输入文本中抽取人物、组织、地点、事件四类实体,并识别实体间的“任职”“收购”“位于”“合作”四类关系,输出JSON格式;
2. 数据清洗与转换:解析大模型返回的JSON,过滤非法实体类型与关系类型,生成去重后的实体列表和关系列表;
3. 图谱构建与可视化:将实体与关系转换为Cypher语句(MERGE语法保证幂等),同时生成一个SVG拓扑图用于直观验证抽取结果。

代码实现


import json
import re
from typing import List, Dict, Tuple

# ---------- 1. 调用大模型进行实体与关系抽取 ----------
def extract_entities_relations(text: str, llm_func) -> Dict:
    """
    调用大模型抽取实体与关系。
    llm_func: 接受prompt字符串,返回模型输出文本的函数(由用户根据实际API实现)。
    """
    prompt = f"""请从以下文本中抽取实体和关系,严格按JSON格式输出,不要输出其他内容。
实体类型:人物(PERSON)、组织(ORG)、地点(LOC)、事件(EVENT)
关系类型:任职(WORKS_FOR)、收购(ACQUIRED)、位于(LOCATED_IN)、合作(COOPERATES_WITH)

输出格式:
{{
  "entities": [{{"name": "实体名", "type": "类型"}}],
  "relations": [{{"source": "头实体名", "target": "尾实体名", "type": "关系类型"}}]
}}

文本:{text}"""
    response = llm_func(prompt)
    # 提取JSON部分(兼容模型输出包含多余说明的情况)
    json_match = re.search(r'\{.*\}', response, re.DOTALL)
    if not json_match:
        raise ValueError("模型输出中未找到JSON")
    return json.loads(json_match.group())

# ---------- 2. 数据清洗与转换 ----------
VALID_ENTITY_TYPES = {"PERSON", "ORG", "LOC", "EVENT"}
VALID_RELATION_TYPES = {"WORKS_FOR", "ACQUIRED", "LOCATED_IN", "COOPERATES_WITH"}

def clean_and_transform(raw_data: Dict) -> Tuple[List[Dict], List[Dict]]:
    """过滤非法类型,去重实体,返回干净的实体列表和关系列表。"""
    entities = []
    seen_entities = set()
    for ent in raw_data.get("entities", []):
        name = str(ent.get("name", "")).strip()
        etype = str(ent.get("type", "")).strip().upper()
        if name and etype in VALID_ENTITY_TYPES and name not in seen_entities:
            entities.append({"name": name, "type": etype})
            seen_entities.add(name)

    relations = []
    seen_relations = set()
    entity_names = set(seen_entities)
    for rel in raw_data.get("relations", []):
        src = str(rel.get("source", "")).strip()
        tgt = str(rel.get("target", "")).strip()
        rtype = str(rel.get("type", "")).strip().upper()
        # 关系两端必须存在于实体列表中,且关系类型合法
        if (src in entity_names and tgt in entity_names 
                and rtype in VALID_RELATION_TYPES
                and (src, tgt, rtype) not in seen_relations):
            relations.append({"source": src, "target": tgt, "type": rtype})
            seen_relations.add((src, tgt, rtype))
    return entities, relations

# ---------- 3. 生成Cypher语句 ----------
def generate_cypher(entities: List[Dict], relations: List[Dict]) -> str:
    """生成Neo4j Cypher导入语句(MERGE保证幂等)。"""
    lines = []
    for ent in entities:
        lines.append(
            f"MERGE (n:`{ent['type']}` {{name: '{ent['name']}'}}) "
            f"SET n.type = '{ent['type']}';"
        )
    for rel in relations:
        lines.append(
            f"MATCH (a {{name: '{rel['source']}'}}), (b {{name: '{rel['target']}'}}) "
            f"MERGE (a)-[r:`{rel['type']}`]->(b);"
        )
    return "\n".join(lines)

# ---------- 示例调用(使用模拟LLM函数演示流程) ----------
def mock_llm(prompt: str) -> str:
    """仅用于演示的模拟LLM,实际使用时替换为真实API调用。"""
    return '''{
      "entities": [
        {"name": "华为", "type": "ORG"},
        {"name": "深圳", "type": "LOC"},
        {"name": "任正非", "type": "PERSON"},
        {"name": "鸿蒙生态大会", "type": "EVENT"}
      ],
      "relations": [
        {"source": "华为", "target": "深圳", "type": "LOCATED_IN"},
        {"source": "任正非", "target": "华为", "type": "WORKS_FOR"},
        {"source": "华为", "target": "鸿蒙生态大会", "type": "COOPERATES_WITH"}
      ]
    }'''

if __name__ == "__main__":
    sample_text = "华为总部位于深圳,创始人任正非出席鸿蒙生态大会并宣布与多家伙伴合作。"
    raw = extract_entities_relations(sample_text, mock_llm)
    entities, relations = clean_and_transform(raw)
    print("=== 清洗后实体 ===")
    for e in entities:
        print(f"  {e['name']} ({e['type']})")
    print("=== 清洗后关系 ===")
    for r in relations:
        print(f"  {r['source']} -[{r['type']}]-> {r['target']}")
    print("=== Cypher语句 ===")
    print(generate_cypher(entities, relations))

落地与验证

运行方式:将上述代码保存为kg_builder.py,安装依赖pip install requests(若使用真实API)。将mock_llm替换为实际的大模型调用函数(如、文心一言等),传入你的与模型名称。直接运行python kg_builder.py即可看到抽取结果与生成的Cypher语句。

验证方法:
1. 观察控制台输出的实体与关系列表,确认实体类型均在四类合法类型内,关系两端实体均存在于实体列表中;
2. 将生成的Cypher语句复制到Neo4j Browser执行,执行后无报错,且执行MATCH (n) RETURN n能看到4个节点和3条关系;
3. 下方SVG图展示了本案例示例文本对应的预期图谱结构,可人工比对抽取结果是否与之一致。

华为 ORG 深圳 LOC 任正非 PERSON 鸿蒙生态大会 EVENT LOCATED_IN WORKS_FOR COOPERATES_WITH ORG 组织 LOC 地点 PERSON 人物 EVENT 事件

判定标准:当控制台输出的实体与关系与SVG图完全一致,且Cypher语句在Neo4j中执行成功并生成对应图谱时,本案例目标达成。若使用真实大模型API,由于模型输出的不确定性,实体与关系可能略有差异,但只要能通过清洗过滤并生成合法Cypher,即视为流程正确。