【突发公共事件智能分析新范式:基于PERSIA框架与大模型的知识图谱构建实践】

从海量新闻文本中,如何快速、准确地识别关键人物、言论和行动?PERSIA框架为我们提供了一个全新的视角。

github项目地址:https://github.com/xy200303/PERSIA

1. 引言:突发公共事件分析的挑战

在信息爆炸的时代,突发公共事件(如疫情、自然灾害、安全事故等)相关信息呈现爆发式增长。传统的信息分析方法面临诸多挑战:

  • 信息碎片化:事件相关信息分散在不同媒体、不同时间段
  • 实体不统一:同一人物、机构在不同报道中的表述各异
  • 关系模糊:言论与行动之间的因果关系难以厘清
  • 分析滞后:人工分析耗时耗力,难以实时响应

为解决这些问题,我们提出了PERSIA框架(Person-centric Emergency Response Statement and Interaction Analysis Framework),并基于大语言模型和知识图谱技术,构建了一套端到端的智能分析系统。

2. PERSIA框架:人物中心的突发公共事件分析模型

2.1 框架核心思想

PERSIA框架的核心是以人物为中心 ,围绕"谁说了什么"、"谁做了什么"、"产生了什么影响"三个核心问题展开分析。这种设计源于对突发公共事件本质的深刻认识:事件由人物驱动,决策由言论体现,结果由行动塑造

2.2 四层分析结构

PERSIA框架采用四层递进的分析结构:

复制代码
第一层:人物识别与关联
    核心人物定位(决策者、专家、受影响者等)
    人物关系网络构建

第二层:言论抽取与分析
    结构化人物言论
    言论情境标注(时间、场合、对象、媒体)

第三层:行动追踪与关联
    行动类型分类(决策、执行、响应等)
    言论→行动关联分析

第四层:因果推理与事件链构建
    多级因果链挖掘
    时间序列重建

2.3 实体标准化体系

为了确保信息的一致性,我们定义了严格的实体标准化体系:

python 复制代码
# 实体标准化格式(必须严格遵守)

## 1. 机构/组织实体
【格式:[ORG]官方全称(机构类型)】
- 正确示例:[ORG]国家卫生健康委员会(政府机构)
- 错误示例:卫健委、国家卫健委

## 2. 人物实体
【格式:[PERSON]姓名(职位@所属机构)】
- 正确示例:[PERSON]马晓伟(主任@国家卫生健康委员会)
- 错误示例:马晓伟、卫健委主任马晓伟
- **注意**:无需额外标注人物角色,通过职位体现

## 3. 地区/位置实体
【格式:[LOC]标准名称(地区类型)】
- 正确示例:[LOC]湖北省(省级行政区)、[LOC]武汉市(城市)
- 错误示例:湖北、武汉

## 4. 言论实体
【格式:[STATEMENT]内容摘要[言论类型](时间)】
- 正确示例:[STATEMENT]优化疫情防控措施[政策声明](2023-01-08)
- 错误示例:将优化防控措施、政策声明
- **要求**:内容摘要20字以内,时间格式YYYY-MM-DD

## 5. 行动实体
【格式:[ACTION]行动描述[行动类型](时间)】
- 正确示例:[ACTION]实施"乙类乙管"政策[决策](2023-01-08)
- 错误示例:执行乙类乙管、决策行动
- **要求**:行动描述15字以内

## 6. 结果实体
【格式:[OUTCOME]结果描述[结果类型](时间)】
- 正确示例:[OUTCOME]疫情得到有效控制[积极结果](2023-03-15)
- 错误示例:控制疫情、结果

# 关系标签列表(只能使用以下标签)
## 主体关系
    BELONGS_TO      # 人物属于机构
    LEADS           # 人物领导机构
    COLLABORATES_WITH  # 人物间合作
## 言论关系
    SAID            # 说/表示
    ANNOUNCED       # 宣布/发布
    RESPONDED_TO    # 回应/答复
    REFUTED         # 反驳/否认
    SUGGESTED       # 建议/提议
    QUOTED          # 引用/援引
## 行动关系
    IMPLEMENTED     # 实施/执行
    PARTICIPATED_IN # 参与/出席
    CONDUCTED       # 开展/进行
    ISSUED          # 印发/下发
    INITIATED       # 发起/启动
## 因果与影响关系
    BASED_ON        # 基于/依据
    LEAD_TO         # 导致/使得
    TRIGGERED       # 触发/引起
    RESULTED_IN     # 产生结果
    CAUSED_BY       # 由...引起
    CONTRIBUTED_TO  # 促进/促成
    FOLLOWED_BY     # 随后发生
    PRECEDED_BY     # 之前发生

3. 基于大模型的PERSIA信息抽取

3.1 系统架构

我们构建了基于大语言模型的智能抽取系统,整体架构如下:

3.2 提示词工程

关键的一步是设计高质量的提示词,引导大模型按照PERSIA框架进行信息抽取:

python 复制代码
NER_PERSIA_PROMPT= """
# 角色
你是PERSIA框架专家------人物中心化的突发公共事件信息抽取系统。

# 核心约束
1. **标准化输出**:必须严格按照以下标准化格式输出
2. **禁止自创**:不能创建任何未定义的实体类型和关系标签
3. **简洁格式**:去除复杂嵌套格式,使用统一标准格式

# 实体标准化格式(必须严格遵守)

## 1. 机构/组织实体
【格式:[ORG]官方全称(机构类型)】
- 正确示例:[ORG]国家卫生健康委员会(政府机构)
- 错误示例:卫健委、国家卫健委

## 2. 人物实体
【格式:[PERSON]姓名(职位@所属机构)】
- 正确示例:[PERSON]马晓伟(主任@国家卫生健康委员会)
- 错误示例:马晓伟、卫健委主任马晓伟
- **注意**:无需额外标注人物角色,通过职位体现

## 3. 地区/位置实体
【格式:[LOC]标准名称(地区类型)】
- 正确示例:[LOC]湖北省(省级行政区)、[LOC]武汉市(城市)
- 错误示例:湖北、武汉

## 4. 言论实体
【格式:[STATEMENT]内容摘要[言论类型](时间)】
- 正确示例:[STATEMENT]优化疫情防控措施[政策声明](2023-01-08)
- 错误示例:将优化防控措施、政策声明
- **要求**:内容摘要20字以内,时间格式YYYY-MM-DD

## 5. 行动实体
【格式:[ACTION]行动描述[行动类型](时间)】
- 正确示例:[ACTION]实施"乙类乙管"政策[决策](2023-01-08)
- 错误示例:执行乙类乙管、决策行动
- **要求**:行动描述15字以内

## 6. 结果实体
【格式:[OUTCOME]结果描述[结果类型](时间)】
- 正确示例:[OUTCOME]疫情得到有效控制[积极结果](2023-03-15)
- 错误示例:控制疫情、结果

# 关系标签列表(只能使用以下标签)
## 主体关系
    BELONGS_TO      # 人物属于机构
    LEADS           # 人物领导机构
    COLLABORATES_WITH  # 人物间合作
## 言论关系
    SAID            # 说/表示
    ANNOUNCED       # 宣布/发布
    RESPONDED_TO    # 回应/答复
    REFUTED         # 反驳/否认
    SUGGESTED       # 建议/提议
    QUOTED          # 引用/援引
## 行动关系
    IMPLEMENTED     # 实施/执行
    PARTICIPATED_IN # 参与/出席
    CONDUCTED       # 开展/进行
    ISSUED          # 印发/下发
    INITIATED       # 发起/启动
## 因果与影响关系
    BASED_ON        # 基于/依据
    LEAD_TO         # 导致/使得
    TRIGGERED       # 触发/引起
    RESULTED_IN     # 产生结果
    CAUSED_BY       # 由...引起
    CONTRIBUTED_TO  # 促进/促成
    FOLLOWED_BY     # 随后发生
    PRECEDED_BY     # 之前发生
# 输出格式
每行一个三元组,严格格式:
[实体类型]实体标准描述 <SEP> 关系标签 <SEP> [实体类型]实体标准描述
**注意**:不要添加任何注释、层级标注或其他文本

# 抽取流程规范

## 第一步:识别所有实体
1. 提取机构实体:[ORG]全称(类型)
2. 提取人物实体:[PERSON]姓名(职位@机构)
3. 提取言论实体:[STATEMENT]摘要[类型](时间)
4. 提取行动实体:[ACTION]描述[类型](时间)
5. 提取结果实体:[OUTCOME]描述[类型](时间)

## 第二步:建立标准关系
1. 人物与机构:BELONGS_TO/LEADS
2. 人物与言论:SAID/ANNOUNCED/SUGGESTED
3. 言论与行动:LEAD_TO/TRIGGERED
4. 行动与结果:RESULTED_IN
5. 时间顺序:FOLLOWED_BY/PRECEDED_BY

## 第三步:严格格式化输出
- 每行一个三元组
- 使用标准实体格式
- 使用预定义关系标签
- 确保时间信息准确
- 任何行动、言论都必须指明是谁做的,谁说的

# 严格禁止行为
1. 禁止使用简称(必须用全称)
2. 禁止省略职位/机构信息
3. 禁止自创关系标签(只能用预定义标签)
4. 禁止合并多个关系(每行只一个关系)
5. 禁止输出非标准化实体(只能用定义的类型)
6. 禁止添加注释、说明或其他文本
7. 禁止使用复杂嵌套格式(如@场合>对象#媒体)
8. 禁止输出层级标记(如// 第一层)
9. 禁止出现没有主体的事件或者言论实体

# 标准化示例

## 示例1:简单情况
输入:"卫健委表示,将优化防控措施。"
正确输出:
[ORG]国家卫生健康委员会(政府机构) <SEP> SAID <SEP> [STATEMENT]将优化疫情防控措施[政策声明](当日)

错误输出:
卫健委 <SEP> 说 <SEP> 优化防控措施
// 言论层
[PERSON]马晓伟(主任@国家卫生健康委员会)(决策者) <SEP> ANNOUNCED <SEP> [STATEMENT]优化措施[声明]

## 示例2:复杂情况
输入:"钟南山院士建议老年人打疫苗。北京市随即开展了接种工作。"
正确输出:
[PERSON]钟南山(院士@中国工程院) <SEP> SUGGESTED <SEP> [STATEMENT]建议老年人接种疫苗[专家建议](当日)
[STATEMENT]建议老年人接种疫苗[专家建议](当日) <SEP> LEAD_TO <SEP> [ACTION]开展老年人疫苗接种[执行](当日)
[ORG]北京市政府(地方政府) <SEP> CONDUCTED <SEP> [ACTION]开展老年人疫苗接种[执行](当日)

错误输出:
[PERSON]钟南山(院士@中国工程院)(专家) <SEP> 建议 <SEP> [STATEMENT]老年人打疫苗[建议]
[ACTION]接种工作[执行](当日) <SEP> 由 <SEP> [ORG]北京(政府)

## 示例3:完整事件
输入:"2023年1月8日,国家卫健委主任马晓伟宣布实施'乙类乙管'政策。钟南山院士表示支持这一调整。"
正确输出:
[PERSON]马晓伟(主任@国家卫生健康委员会) <SEP> ANNOUNCED <SEP> [STATEMENT]实施"乙类乙管"政策[政策声明](2023-01-08)
[PERSON]钟南山(院士@中国工程院) <SEP> RESPONDED_TO <SEP> [STATEMENT]实施"乙类乙管"政策[政策声明](2023-01-08)
[PERSON]钟南山(院士@中国工程院) <SEP> SAID <SEP> [STATEMENT]支持政策调整[观点表达](2023-01-09)

# 完整处理流程示例

输入文本:"在国务院联防联控机制新闻发布会上,国家卫健委主任马晓伟宣布,自2023年1月8日起对新型冠状病毒感染实施'乙类乙管'。钟南山院士随后表示支持。"

**标准化输出**:
[PERSON]马晓伟(主任@国家卫生健康委员会) <SEP> BELONGS_TO <SEP> [ORG]国家卫生健康委员会(政府机构)
[PERSON]马晓伟(主任@国家卫生健康委员会) <SEP> ANNOUNCED <SEP> [STATEMENT]实施"乙类乙管"政策[政策声明](2023-01-08)
[ORG]国务院联防联控机制(协调机构) <SEP> IMPLEMENTED <SEP> [ACTION]实施"乙类乙管"政策[决策](2023-01-08)
[PERSON]钟南山(院士@中国工程院) <SEP> RESPONDED_TO <SEP> [STATEMENT]实施"乙类乙管"政策[政策声明](2023-01-08)
[PERSON]钟南山(院士@中国工程院) <SEP> SAID <SEP> [STATEMENT]支持"乙类乙管"政策[观点表达](2023-01-09)
[ACTION]实施"乙类乙管"政策[决策](2023-01-08) <SEP> RESULTED_IN <SEP> [OUTCOME]疫情防控措施优化[积极结果](2023-01-20)

# 错误检查清单
在输出前检查:
1. 所有实体是否都是预定义类型?
2. 所有关系标签是否都在列表中?
3. 是否每行只有一个三元组?
4. 是否所有机构都用全称?
5. 是否所有人物都有职位@机构?
6. 是否所有言论都有[类型](时间)?
7. 是否所有行动都有[类型](时间)?
8. 是否没有添加任何注释或说明?

# 最终输出要求
只输出符合以下格式的三元组,每行一个:
[类型]描述 <SEP> 关系 <SEP> [类型]描述

不需要说明、不需要注释、不需要层级标记
严格按照标准化格式
只使用预定义的实体类型和关系标签

现在开始,严格按照标准化要求抽取信息。
"""
"""

3.3 异步批量处理

为处理大量新闻文本,我们实现了高效的异步批量处理机制:

python 复制代码
#利用大模型进行并发抽取,结果保存到./outputs/file,支持断点恢复
CSV_PATH = "./dataset/sentences.csv"
MAX_CONCURRENT = 20  # 最大并发数
OUTPUT_PATH = "./outputs/file"
LOG_DIR = "./logs"
os.makedirs(LOG_DIR,exist_ok=True)
os.makedirs(OUTPUT_PATH, exist_ok=True)
llm_c = LLMClient(system_prompt=NER_PERSIA_PROMPT,max_concurrent_requests=MAX_CONCURRENT,history_max=-1)

async def process_text(id,text,pbar: tqdm = None):
    try:
        file_path = os.path.join(OUTPUT_PATH,str(id)+".txt")
        res=await llm_c.allm_call(text)
        with open(file_path,"w",encoding="utf-8") as f:
            f.write(res)
        if pbar:
            pbar.update(1)
            pbar.set_postfix_str(f"处理完成: {id}")
        return True
    except Exception as e:
        log_file=os.path.join(LOG_DIR,str(id)+".log")
        with open(log_file,"a",encoding="utf-8") as f:
            f.write(str(e))
        if pbar:
            pbar.set_postfix_str(f"处理失败: {id} - {str(e)[:30]}")
        return False

async def main():
    # 读取csv新闻文件
    data = pd.read_csv("./dataset/sentences.csv")
    print("数据统计:")
    print(f"总行数: {len(data)}")
    print(f"唯一文档数: {data['id'].nunique()}")
    print("前几个ID:", data['id'].unique()[:5])
    # 根据id进行聚合,组成完成的新闻描述
    group_data = data.groupby("id")
    tasks=[]
    print("\n📁 正在准备处理任务...")
    pbar = tqdm(total=len(group_data), desc="处理分组")
    for index,group in group_data:
        file_path = os.path.join(OUTPUT_PATH,str(index)+".txt")
        if os.path.exists(file_path):
            pbar.update(1)
            continue
        text = "".join(group["sentence"].tolist())
        tasks.append(asyncio.create_task(process_text(index,text,pbar=pbar)))
    # 等待所有任务完成
    results = await asyncio.gather(*tasks, return_exceptions=True)
    pbar.close()
    print("\n📊 处理结果统计:")
    success=0
    for result in results:
        if result:
            success += 1
    print("成功:",success)

if __name__ == "__main__":
    asyncio.run(main())

4. 三元组解析与知识图谱构建

4.1 三元组解析器

大模型输出的是标准化的文本三元组,我们需要将其解析为结构化数据:

python 复制代码
import json
import re
from typing import List, Dict, Any
import networkx as nx
import matplotlib.pyplot as plt
from pyvis.network import Network
import pandas as pd
def parse_triples(output_text: str) -> List[Dict]:
    """解析三元组文本为结构化数据"""
    triples = []
    lines = output_text.strip().split('\n')
    for i, line in enumerate(lines):
        if '<SEP>' not in line:
            continue

        parts = line.split(' <SEP> ')
        if len(parts) != 3:
            continue
        head, relation, tail = parts
        # 解析实体格式:[TYPE]名称(描述)
        head_match = re.match(r'\[(\w+)\](.*?)\((.*?)\)', head)
        tail_match = re.match(r'\[(\w+)\](.*?)\((.*?)\)', tail)

        if head_match and tail_match:
            triple = {
                'id': i,
                'head': {
                    'id': f"entity_{i}_head",
                    'name': head_match.group(2).strip(),
                    'type': head_match.group(1),
                    'description': head_match.group(3),
                    'label': f"{head_match.group(2)} ({head_match.group(1)})"
                },
                'relation': relation,
                'tail': {
                    'id': f"entity_{i}_tail",
                    'name': tail_match.group(2).strip(),
                    'type': tail_match.group(1),
                    'description': tail_match.group(3),
                    'label': f"{tail_match.group(2)} ({tail_match.group(1)})"
                }
            }
            triples.append(triple)
    return triples

4.2 知识图谱构建

基于解析的三元组,我们构建知识图谱网络:

python 复制代码
import hashlib
from pyvis.network import Network
import webbrowser
def truncate_text(text, max_length=15):
    """截断长文本,添加省略号"""
    if len(text) <= max_length:
        return text
    return text[:max_length] + "..."
def get_entity_hash(name):
    return name
    """为实体生成哈希键,确保相同实体得到相同哈希"""
    # 对名称进行标准化处理
    clean_name = name.strip()
    # 生成哈希值(使用MD5,取前8位)
    hash_obj = hashlib.md5(clean_name.encode())
    hash_hex = hash_obj.hexdigest()
    # 返回哈希键
    return f"{hash_hex}"

def get_shape_by_type(entity_type):
    # 根据类型设置不同形状
    if entity_type == 'PERSON':
        shape = 'dot'
        size = 25
    elif entity_type == 'ORG':
        shape = 'diamond'
        size = 30
    elif entity_type == 'LOC':
        shape = 'triangle'
        size = 30
    else:  # STATEMENT
        shape = 'square'
        size = 35
    shape="dot"
    return shape,size
# 提取[]中的内容
def extract_brackets_content(text):
    print(text)
    """提取[]括号中的内容"""
    pattern = r'\[(.*?)\]'  # 匹配[]中的内容,非贪婪匹配
    matches = re.findall(pattern, text)
    if len(matches) == 0:
        return text
    return matches[0]
def build_network(triples):
    # 创建网络图
    net = Network(height="600px", width="100%", directed=True)
    # 节点颜色映射
    node_colors = {
        'PERSON': '#FF6B6B',
        'ORG': '#4ECDC4',
        'LOC': '#FFD166',
        'STATEMENT': '#95D2B3'
    }
    # 按名称合并相同实体
    # 使用名称作为节点的唯一标识
    entity_id_map = {}  # 映射:name -> node_id
    # 先收集所有实体
    all_entities = set()
    for item in triples:
        head_entity_name = item['head']['name']
        tail_entity_name = item['tail']['name']
        head_entity_id = item['head']['id']
        tail_entity_id = item['tail']['id']
        head_entity_type = item['head']['type']
        tail_entity_type = item['tail']['type']
        if head_entity_name not in all_entities:
            all_entities.add(head_entity_name)
            entity_id_map[get_entity_hash(head_entity_name)] = head_entity_id
            shape,size=get_shape_by_type(head_entity_type)
            if head_entity_type=="STATEMENT":
                head_entity_name=extract_brackets_content(head_entity_name)
            net.add_node(
                head_entity_id,
                label=head_entity_name,
                title=f"{head_entity_name}<br>类型: {head_entity_type}",
                color=node_colors.get(head_entity_type, '#CCCCCC'),
                shape=shape,
                size=size
            )
        if tail_entity_name not in all_entities:
            all_entities.add(tail_entity_name)
            entity_id_map[get_entity_hash(tail_entity_name)] = tail_entity_id
            shape,size=get_shape_by_type(tail_entity_type)
            if tail_entity_type=="STATEMENT":
                tail_entity_name=extract_brackets_content(tail_entity_name)
            net.add_node(
                tail_entity_id,
                label=tail_entity_name,
                title=f"{tail_entity_name}<br>类型: {tail_entity_type}",
                color=node_colors.get(tail_entity_type, '#CCCCCC'),
                shape=shape,
                size=size
            )
    # 添加边
    for item in triples:
        head_name = item['head']['name']
        tail_name = item['tail']['name']

        head_id = entity_id_map[get_entity_hash(head_name)]
        tail_id = entity_id_map[get_entity_hash(tail_name)]

        net.add_edge(
            head_id,
            tail_id,
            label=item['relation'],
            arrows='to'
        )
    return net

5. 知识图谱可视化

5.1 可视化设计原则

我们设计了多层次的可视化方案:

  1. 节点编码:不同实体类型使用不同颜色和形状
  2. 关系可视化:不同类型的关系使用不同线型和颜色
  3. 时间维度:通过节点大小、颜色深浅表示时间先后
  4. 重要性排序:通过节点中心性计算,突出关键实体

5.2 交互式可视化实现

使用PyVis库实现交互式知识图谱可视化:

python 复制代码
#可视化单个数据
net=build_network(triples_list[2])
# 保存HTML文件
net.save_graph("knowledge_graph.html")
# 在浏览器中打开
webbrowser.open("knowledge_graph.html")
print("\n🌐 已在浏览器中打开知识图谱")

5.3 可视化效果展示



附录:代码资源

完整代码已开源在GitHub:https://github.com/xy200303/PERSIA

核心文件结构

复制代码
C:.
│  config.yaml
│  examples.py
│  knowledge_graph.html
│  PERSIA实体抽取.py
│  requirements.txt
│  大模型知识图谱抽取.ipynb
├─.idea
│  │  .gitignore
│  │  misc.xml
│  │  modules.xml
│  │  workspace.xml
│  │  突发公共事件抽取.iml
│  │
│  └─inspectionProfiles
│          profiles_settings.xml
├─config
│  │  config.py
│  │  __init__.py
│  │
│  └─__pycache__
│          config.cpython-311.pyc
│          __init__.cpython-311.pyc
│
├─dataset
│      sentences.csv
│
├─lib
│  ├─bindings
│  │      utils.js
│  │
│  ├─tom-select
│  │      tom-select.complete.min.js
│  │      tom-select.css
│  │
│  └─vis-9.1.2
│          vis-network.css
│          vis-network.min.js
│
├─llm
│  │  client.py
│  │  __init__.py
│  │
│  └─__pycache__
│          client.cpython-311.pyc
│          __init__.cpython-311.pyc
│
├─logs
├─models
│  │  __init__.py
│  │
│  └─__pycache__
│          __init__.cpython-311.pyc
│
├─outputs
│  └─file
└─prompts
    │  __init__.py
    │
    └─__pycache__
            __init__.cpython-311.pyc

快速开始

bash 复制代码
# 安装依赖
pip install -r requirements.txt

# 运行示例
python PERSIA实体抽取.py

本博客介绍了PERSIA框架的理论基础、技术实现和应用案例。希望通过这个框架,能够为突发公共事件的分析和理解提供新的工具和思路。欢迎在评论区交流讨论!

相关推荐
旺财矿工1 天前
AI 智能体 OpenClaw 2.6.6 Win11 安装与快速上手教程
人工智能·自动化·openclaw·小龙虾·龙虾
0xR3lativ1ty1 天前
Transformer自注意力为何除以根号dk
人工智能·深度学习·transformer
无籽西瓜a1 天前
RAG 中的幻觉是什么?原因分析与防范措施
人工智能·ai·rag
大囚长1 天前
AI是人类灭绝的前奏
人工智能
小妖同学学AI1 天前
抛弃传统数据库!Qdrant用Rust重写AI记忆,大模型知识库迎来性能革命!
数据库·人工智能·rust
星爷AG I1 天前
20-3 长时记忆(AGI基础理论)
人工智能·agi
guslegend1 天前
第2节:规范驱动开发SDD,让AI永远在轨道上
人工智能·ai编程
智枢圈1 天前
RAG检索增强生成
人工智能
中微子1 天前
养虾小妙招:如何用 OpenClaw 把 Claude Code 调教成你的专属打工仔
linux·人工智能
一切皆是因缘际会1 天前
通用人工智能底层原理:从记忆结构视角解析大模型行为与意识涌现
人工智能·安全·ai·架构·系统架构