附件上传 → 文档解析 → 文本存储 → NER提取 → 图数据库存储 → 前端展示
后端服务:
lingyue-ai: NER服务调用层lingyue-project: 项目、附件、规则管理lingyue-file: 文件存储lingyue-task: 异步任务管理Python服务:
ner-service: NER实体提取服务(支持rule/ollama/deepseek模式)数据存储:
graph_nodes(节点)、graph_edges(边)、graph_properties(属性)A. 用户输入类(6个):
B. 附件提取类-基础信息(7个):
C. 附件提取类-评审内容(18个):
D. 表格数据类(7个):
E. 计算类(2个):
采用三层提取策略:
适用于格式固定的字段:
2024年7月13日93.33分一级、二级ZGDIDBOY-0835.1.1.1适用于需要语义理解的字段:
适用于表格数据:
新增智报专用实体类型:
ENTITY_TYPES = {
# 基础类型(已有)
"DATE": "日期",
"NUMBER": "数值",
"ORG": "机构",
"LOC": "地点",
"PERSON": "人名",
# 智报专用类型(新增)
"SCORE": "评审得分",
"LEVEL": "级别",
"CERTIFICATE_CODE": "证书编号",
"REVIEW_CODE": "评审代码",
"COMPANY_ALIAS": "公司简称",
"PROJECT_NAME": "项目名称",
"REVIEW_ITEM": "评审项",
"SUGGESTION": "整改建议",
}
阶段1: 工作方案(001008)
↓ 提取:评审对象、简称、日期、范围、人员
阶段2: 核心要素评审记录表(001001)
↓ 提取:得分、各项评审内容、问题描述
阶段3: 复审问题建议表(001010)
↓ 提取:整改建议
阶段4: 其他附件(001003等)
↓ 提取:自评过程、工作依据等
-- 新增节点类型
NODE_NER_ENTITY = "NER_ENTITY" -- NER提取的实体
NODE_NER_RELATION = "NER_RELATION" -- 实体间关系
{
"id": 1001,
"node_type": "NER_ENTITY",
"node_key": "entity_20240101_001",
"node_name": "中国电建集团成都勘测设计研究院有限公司",
"properties": {
"entity_type": "ORG", // 实体类型
"entity_value": "成都院", // 实体值(简称)
"confidence": 0.95, // 置信度
"char_start": 100, // 文本起始位置
"char_end": 125, // 文本结束位置
"line": 5, // 所在行号
"context": "...评审对象:中国电建...", // 上下文
"source_attachment_id": 408, // 来源附件
"extract_method": "llm", // 提取方法
"extract_time": "2024-03-04 14:00:00"
}
}
-- 新增边类型
EDGE_HAS_NER_ENTITY = "HAS_NER_ENTITY" -- 附件→实体
EDGE_ENTITY_RELATION = "ENTITY_RELATION" -- 实体→实体关系
EDGE_ENTITY_TO_VALUE = "ENTITY_TO_VALUE" -- 实体→要素值(映射)
附件[408:工作方案] --[HAS_NER_ENTITY]--> 实体[成都院]
实体[成都院] --[ENTITY_RELATION:简称]--> 实体[中国电建集团成都院]
实体[成都院] --[ENTITY_TO_VALUE]--> 要素值[project.reviewObjectAlias]
NER实体 → 规则处理 → 要素值填充
示例:
NER提取: "2024年7月13日" (DATE)
↓
规则匹配: project.workStartAt
↓
创建VALUE节点: "2024年7月13日"
↓
关联到ELEMENT: workStartAt
位置:项目详情 → 附件管理 → NER分析
功能模块:
实体列表视图
文本标注视图
关系图谱视图
要素映射视图
<template>
<div class="ner-analysis">
<!-- 顶部统计 -->
<div class="stats-bar">
<el-statistic title="实体总数" :value="entityCount" />
<el-statistic title="关系数" :value="relationCount" />
<el-statistic title="已映射" :value="mappedCount" />
</div>
<!-- 实体列表 -->
<el-tabs v-model="activeTab">
<el-tab-pane label="实体列表" name="entities">
<entity-list :entities="entities" />
</el-tab-pane>
<el-tab-pane label="文本标注" name="annotation">
<text-annotation :text="sourceText" :entities="entities" />
</el-tab-pane>
<el-tab-pane label="关系图谱" name="graph">
<relation-graph :entities="entities" :relations="relations" />
</el-tab-pane>
<el-tab-pane label="要素映射" name="mapping">
<entity-mapping :entities="entities" :elements="elements" />
</el-tab-pane>
</el-tabs>
</div>
</template>
任务:
产出:
任务:
产出:
任务:
产出:
任务:
产出:
任务:
产出:
方案A:附件上传后自动触发
// AttachmentService.uploadAttachment()
@Transactional
public AttachmentUploadVO uploadAttachment(...) {
// ... 创建附件节点
// 触发NER提取(异步)
taskService.createNerTask(attachmentId);
return vo;
}
方案B:用户手动触发
// 新增API
@PostMapping("/{attachmentId}/ner")
public Result<?> triggerNer(@PathVariable Long attachmentId) {
nerService.extractEntities(attachmentId);
return Result.ok();
}
自动映射规则:
// 基于实体类型和上下文自动映射
if (entity.getType().equals("ORG") &&
entity.getContext().contains("评审对象")) {
mapToElement(entity, "project.reviewObject");
}
if (entity.getType().equals("DATE") &&
entity.getContext().contains("评审开始")) {
mapToElement(entity, "project.workStartAt");
}
手动映射接口:
@PostMapping("/entities/{entityId}/map")
public Result<?> mapEntityToElement(
@PathVariable Long entityId,
@RequestBody MapRequest request) {
nerService.mapEntity(entityId, request.getElementKey());
return Result.ok();
}
批量处理:
缓存策略:
增量更新:
风险:规则模式准确率不足,LLM成本高
应对:
风险:大文档NER耗时长
应对:
风险:NER结果与要素值不一致
应对:
文档版本:v1.0
创建时间:2024-03-04
维护者:开发团队