配音

实体抽取与关系提取

课程简介

用 LLM Agent 自动抽取实体与关系,构建图谱骨架。

🎬 本课程视频:Agentic Knowledge Graph — 智能体驱动知识图谱构建


实体抽取与关系提取:Agent 驱动方案详解

一、超越传统 NER

传统命名实体识别(NER)系统基于序列标注模型,在标准数据集上表现良好,但在实际应用中存在明显局限:

  1. 领域迁移困难:在新闻语料上训练的 NER 模型迁移到医疗领域,准确率大幅下降
  2. 实体类型固定:只能识别预定义的几种实体类型(人名、地名、组织名)
  3. 难以处理嵌套实体:如「上海交通大学」中的「上海」
  4. 上下文利用有限:无法利用深层语义信息进行实体消歧

Agent 驱动的实体抽取通过 LLM 的语义理解和推理能力,在这些方面都实现了质的突破。

二、提示词设计

实体抽取 Agent 的核心是提示词设计。一个好的提示词应该包含以下要素:

2.1 角色定义

entity_extraction_prompt = '''
你是一个{领域}实体抽取专家。你的任务是从非结构化文本中精确抽取实体和关系。

角色要求:
- 对{领域}概念有深入理解
- 能够识别显式和隐式的实体提及
- 能够处理缩写、别名和共指关系
- 在不确定时标注置信度而非猜测

实体类型定义:
{entity_type_definitions}

关系类型定义:
{relation_type_definitions}

输出格式:
{output_schema}
'''

2.2 输出格式约束

使用 JSON Schema 确保输出可解析:

{
  "type": "object",
  "properties": {
    "entities": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "name": {"type": "string"},
          "type": {"type": "string", "enum": ["Company", "Person", "Product"]},
          "mentions": {"type": "array", "items": {"type": "string"}},
          "confidence": {"type": "number", "minimum": 0, "maximum": 1}
        },
        "required": ["name", "type"]
      }
    },
    "relations": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "subject": {"type": "string"},
          "relation": {"type": "string"},
          "object": {"type": "string"},
          "confidence": {"type": "number"}
        },
        "required": ["subject", "relation", "object"]
      }
    }
  }
}

2.3 处理规则

提示词中应该包含明确的处理规则:

处理规则:
1. 共指消解:如果「苹果公司」后文简称为「该公司」或「苹果」,归并为同一实体
2. 别名处理:「Apple Inc.」、「Apple」、「苹果公司」视为同一实体
3. 边界条件:
   - 如果对实体类型不确定,confidence 设为 < 0.7
   - 如果对实体边界不确定,标记为 low_confidence
   - 如果遇到超出定义类型但语义上应被抽取的实体,标记为 unknown_type
4. 需要请求人工确认的情况:
   - 实体类型完全无法确定
   - 两个候选实体高度相似但无法判断是否同一实体
   - 抽取结果中的关系与已知事实矛盾

三、多阶段抽取流程

一次性抽取所有实体和关系往往效果不佳。推荐多阶段流程:

3.1 第一阶段:粗粒度扫描

def stage1_coarse_extraction(text):
    '''第一阶段的粗粒度扫描'''
    prompt = f'''
    扫描以下文本,标记所有可能是实体的名词短语。
    不需要分类,只需要标记边界和可能的类型候选。

    文本:{text}

    输出格式:[{{"mention": "...", "span": [start, end], "type_candidates": ["..."]}}]
    '''
    candidates = llm.generate(prompt)
    return parse_candidates(candidates)

此阶段的目标是召回而非精确。宁可多召回非实体,也不要遗漏潜在实体。

3.2 第二阶段:细粒度分类

def stage2_fine_classification(candidates, context):
    '''第二阶段:对候选实体进行精确分类'''
    results = []

    for candidate in candidates:
        prompt = f'''
        在上下文中对候选实体进行精确分类。

        上下文:{context}
        候选实体:{candidate["mention"]}(位置:{candidate["span"]})

        候选类型:{self.entity_types}

        请判断该候选实体的类型。如果确信是实体,输出类型和置信度。
        如果判断不是实体,输出 null。
        '''
        result = llm.generate(prompt)
        if result["entity_type"]:
            results.append({
                "name": candidate["mention"],
                "type": result["entity_type"],
                "confidence": result["confidence"]
            })

    return results

3.3 第三阶段:关系抽取

def stage3_relation_extraction(entities, text):
    '''第三阶段:识别实体之间的关系'''
    # 构建实体上下文
    entity_context = "
".join([f"- {e['name']} ({e['type']})" for e in entities])

    prompt = f'''
    在以下文本中,分析已识别实体之间的关系。

    文本:{text}

    已识别实体:
    {entity_context}

    可能的关系类型:
    {self.relation_types}

    对每对可能有关系的实体,判断关系类型和方向。
    如果关系与文本时间信息相关,标注时间属性。
    '''
    relations = llm.generate(prompt)
    return parse_relations(relations)

3.4 多阶段的优势

多阶段相比一次性抽取的优势:
- 精度更高:每个阶段聚焦一个子任务,降低认知负载
- 错误隔离:前阶段的错误不会污染后阶段的判断
- 中间结果可审查:每个阶段的结果都可以由人工审核或自动化检查

四、冲突解决

多来源抽取必然产生冲突。Agent 驱动的冲突解决机制:

class ConflictResolver:
    '''基于 Agent 的冲突解决'''

    def resolve(self, conflicts, sources):
        '''解决实体抽取中的冲突'''
        resolved = []

        for conflict in conflicts:
            # 按置信度排序
            sorted_by_confidence = sorted(
                conflict.candidates,
                key=lambda x: x.confidence,
                reverse=True
            )

            # 最高的置信度明显高于其他 → 选择最高置信度
            if sorted_by_confidence[0].confidence - sorted_by_confidence[1].confidence > 0.3:
                resolved.append(sorted_by_confidence[0])

            # 置信度接近 → 让 Agent 根据证据推理
            elif sorted_by_confidence[0].confidence - sorted_by_confidence[1].confidence <= 0.3:
                resolution = self._agent_based_resolution(conflict)
                resolved.append(resolution)

            # 所有候选置信度都低 → 标记为需人工确认
            elif all(c.confidence < 0.6 for c in conflict.candidates):
                resolved.append({
                    "status": "needs_review",
                    "candidates": conflict.candidates,
                    "reason": "所有候选置信度均低于阈值"
                })

        return resolved

    def _agent_based_resolution(self, conflict):
        '''让 Agent 基于证据推理解决冲突'''
        evidence_prompt = f'''
        以下是对同一实体抽取结果的分歧:
        {conflict}

        请基于以下证据进行判断:
        1. 上下文证据:实体在文本中的使用上下文
        2. 一致性检查:与知识图谱已有事实的一致性
        3. 来源可靠性:不同数据源的可靠性评估

        请给出最终的实体类型和理由。
        '''
        resolution = llm.generate(evidence_prompt)
        return resolution

五、增量更新

知识图谱不是一次性构建完成的,而是持续更新的。Agent 驱动的增量更新策略:

class IncrementalUpdater:
    '''增量更新管理'''

    def process_new_document(self, document, existing_kg):
        # 1. 抽取新实体和关系
        new_entities, new_relations = self.extract(document)

        # 2. 与已有知识关联
        for new_entity in new_entities:
            # 检查是否已存在于图谱
            existing = existing_kg.find_similar(new_entity.name)

            if existing:
                # 关联到已有节点
                new_entity.link_to(existing)
            else:
                # 创建新节点
                new_node = existing_kg.create_node(new_entity)

                # 检查是否需要合并已有相似节点
                merge_candidates = existing_kg.find_merge_candidates(new_entity)
                if merge_candidates:
                    self._suggest_merge(new_entity, merge_candidates)

        # 3. 更新关系
        for relation in new_relations:
            # 优先关联到已有实体,而非创建新关系
            subject = existing_kg.find(relation.subject) or relation.subject
            obj = existing_kg.find(relation.object) or relation.object

            # 检查关系是否已存在
            if not existing_kg.has_relation(subject, relation.type, obj):
                existing_kg.create_relation(subject, relation.type, obj, relation.properties)

        return new_entities, new_relations

六、质量保证机制

def agent_quality_check(extracted_data, source_document):
    '''Agent 驱动的质量检查'''

    checks = [
        check_entity_completeness,     # 是否遗漏重要实体?
        check_relation_accuracy,       # 关系判断是否准确?
        check_consistency,             # 与已知事实是否一致?
        check_temporal_consistency,    # 时间信息是否一致?
        check_format_compliance        # 输出格式是否符合要求?
    ]

    issues = []
    for check in checks:
        result = check(extracted_data, source_document)
        issues.extend(result)

    return issues

七、总结

Agent 驱动的实体抽取和关系提取通过精心设计的提示词、多阶段流程、冲突解决机制和增量更新策略,实现了比传统 NER 系统更灵活、更精确的知识抽取能力。多阶段流程让每个 Agent 聚焦于单一子任务,冲突解决机制处理多来源不一致的情况,增量更新策略确保新知识与已有图谱的平滑集成。这些技术的综合应用,使得大规模、持续性的知识图谱构建成为可能。

五、实战:用 LLM Agent 进行实体抽取

5.1 单阶段抽取

最简单的方案是让 LLM 一次性完成实体识别和关系提取:

def single_pass_extraction(text):
    prompt = f"""
    从以下文本中抽取所有实体和关系:

    文本:{text}

    输出格式:
    实体列表:[{名称, 类型}, ...]
    关系列表:[{头实体, 关系, 尾实体}, ...]
    """
    return llm.generate(prompt)

5.2 多阶段抽取

更可靠的方式是分阶段进行:

def multi_stage_extraction(text):
    # 阶段1:实体识别
    entities = extract_entities(text)

    # 阶段2:实体链接(解决同义、歧义)
    linked = link_entities(entities, existing_kg)

    # 阶段3:关系提取
    relations = extract_relations(text, linked)

    # 阶段4:质量验证
    validated = validate_triples(linked, relations)

    return validated

六、实体链接(Entity Linking)

实体链接是决定图谱质量的关键步骤。它解决的主要问题:

  1. 同义合并:“Apple”、“Apple Inc.”、“苹果公司”指向同一实体
  2. 歧义消解:“苹果”是水果还是公司?根据上下文判断
  3. 别名管理:“埃隆·马斯克”、“Elon Musk”、“马斯克”

七、挑战与最佳实践

  1. 长文本处理:文档太长时需要分段处理
  2. 跨段落关联:同一实体的信息分散在多段中
  3. 隐含关系:有些关系不是显式表达的
  4. 领域术语:专业领域的实体识别需要领域知识

八、总结

实体抽取和关系提取是知识图谱构建的核心工序。

关键要点回顾:
- 两阶段流程:先实体识别,再关系提取
- 多阶段抽取方案比单阶段更可靠
- 实体链接解决同义合并和歧义消解
- 关系类型提示显着提升提取质量
- 质量验证是确保图谱准确性的关键

五、实战:用 LLM Agent 进行实体抽取

5.1 单阶段抽取

最简单的方案是让 LLM 一次性完成实体识别和关系提取:

def single_pass_extraction(text):
    prompt = f"""
    从以下文本中抽取所有实体和关系:

    文本:{text}

    输出格式:
    实体列表:[{名称, 类型}, ...]
    关系列表:[{头实体, 关系, 尾实体}, ...]
    """
    return llm.generate(prompt)

5.2 多阶段抽取

更可靠的方式是分阶段进行:

def multi_stage_extraction(text):
    # 阶段1:实体识别
    entities = extract_entities(text)

    # 阶段2:实体链接(解决同义、歧义)
    linked = link_entities(entities, existing_kg)

    # 阶段3:关系提取
    relations = extract_relations(text, linked)

    # 阶段4:质量验证
    validated = validate_triples(linked, relations)

    return validated

六、实体链接(Entity Linking)

实体链接是决定图谱质量的关键步骤。它解决的主要问题:

  1. 同义合并:“Apple”、“Apple Inc.”、“苹果公司”指向同一实体
  2. 歧义消解:“苹果”是水果还是公司?根据上下文判断
  3. 别名管理:“埃隆·马斯克”、“Elon Musk”、“马斯克”

七、挑战与最佳实践

  1. 长文本处理:文档太长时需要分段处理
  2. 跨段落关联:同一实体的信息分散在多段中
  3. 隐含关系:有些关系不是显式表达的
  4. 领域术语:专业领域的实体识别需要领域知识

八、总结

实体抽取和关系提取是知识图谱构建的核心工序。

关键要点回顾:
- 两阶段流程:先实体识别,再关系提取
- 多阶段抽取方案比单阶段更可靠
- 实体链接解决同义合并和歧义消解
- 关系类型提示显着提升提取质量
- 质量验证是确保图谱准确性的关键

五、实战:用 LLM Agent 进行实体抽取

5.1 单阶段抽取

最简单的方案是让 LLM 一次性完成实体识别和关系提取:

def single_pass_extraction(text):
    prompt = f"""
    从以下文本中抽取所有实体和关系:

    文本:{text}

    输出格式:
    实体列表:[{名称, 类型}, ...]
    关系列表:[{头实体, 关系, 尾实体}, ...]
    """
    return llm.generate(prompt)

5.2 多阶段抽取

更可靠的方式是分阶段进行:

def multi_stage_extraction(text):
    # 阶段1:实体识别
    entities = extract_entities(text)

    # 阶段2:实体链接(解决同义、歧义)
    linked = link_entities(entities, existing_kg)

    # 阶段3:关系提取
    relations = extract_relations(text, linked)

    # 阶段4:质量验证
    validated = validate_triples(linked, relations)

    return validated

六、实体链接(Entity Linking)

实体链接是决定图谱质量的关键步骤。它解决的主要问题:

  1. 同义合并:“Apple”、“Apple Inc.”、“苹果公司”指向同一实体
  2. 歧义消解:“苹果”是水果还是公司?根据上下文判断
  3. 别名管理:“埃隆·马斯克”、“Elon Musk”、“马斯克”

七、挑战与最佳实践

  1. 长文本处理:文档太长时需要分段处理
  2. 跨段落关联:同一实体的信息分散在多段中
  3. 隐含关系:有些关系不是显式表达的
  4. 领域术语:专业领域的实体识别需要领域知识

八、总结

实体抽取和关系提取是知识图谱构建的核心工序。

关键要点回顾:
- 两阶段流程:先实体识别,再关系提取
- 多阶段抽取方案比单阶段更可靠
- 实体链接解决同义合并和歧义消解
- 关系类型提示显着提升提取质量
- 质量验证是确保图谱准确性的关键

延伸阅读