Skip to content

Update EFSA code and README#3

Open
T-Atlas wants to merge 11 commits into
cty1934:mainfrom
ICT-FinD-Lab:main
Open

Update EFSA code and README#3
T-Atlas wants to merge 11 commits into
cty1934:mainfrom
ICT-FinD-Lab:main

Conversation

@T-Atlas

@T-Atlas T-Atlas commented Jul 1, 2026

Copy link
Copy Markdown

Summary

  • Refactor prompt scripts for multi-company and multi-event tree-style EFSA extraction
  • Align event and sentiment labels with the bundled EFSA dataset
  • Support EFSA data.json batch loading and more robust LLM output parsing
  • Update README with ACL Anthology/arXiv links, citation, bilingual documentation, dataset details, and runtime requirements

Notes

  • This PR is opened from ICT-FinD-Lab/EFSA:main into cty1934/EFSA:main.
  • The fork is 11 commits ahead and 0 commits behind upstream main.

Verification

  • Compared upstream main against ICT-FinD-Lab:main with GitHub compare API
  • Confirmed changed files are README.md, README_zh.md, code/direct_prompt.py, and code/reasoning_prompt.py

Utopian-25 and others added 11 commits July 1, 2026 16:47
# 原始 `direct_prompt.py` 与优化版对比说明

## 一、原始 `direct_prompt.py` 存在的问题

原始代码实现方式较为简单直接,其整体流程如下:

```text
输入新闻
    │
    ▼
拼接固定 Prompt
    │
    ▼
调用大语言模型
    │
    ▼
输出一个四元组结果
```

整个流程属于**线性单路径推理(Linear Reasoning)**,只能针对一条固定新闻完成一次事件抽取。

### 主要缺陷

#### 1. 新闻内容固定

新闻文本直接写死在代码中,只能分析固定内容,无法适配不同新闻。

#### 2. 仅支持单公司分析

代码默认新闻中只包含一家企业。当新闻涉及多家公司时,仅能识别其中一个,其余公司信息全部被忽略。

#### 3. 仅生成一个事件结果

每条新闻只能得到一个四元组结果。当新闻包含多个事件时,仅保留其中一个事件,其余事件无法识别。

#### 4. 缺少异常处理

程序没有任何异常捕获机制。当模型返回内容格式不符合预期时,程序会直接报错退出,鲁棒性较差。

#### 5. 不支持批量处理

每分析一条新闻,都需要手动修改代码中的新闻内容,无法自动读取新闻数据集进行批量分析。

---

# 二、优化后的 `direct_prompt.py` 改进

优化后的代码进行了整体重构,采用**面向对象(Object-Oriented Programming,OOP)**设计,并引入了**树形展开(Tree Expansion)**推理机制。

整体处理流程如下:

```text
新闻
 │
 ├── 公司1
 │      ├── 一级事件1
 │      │      ├── 二级事件1
 │      │      └── 二级事件2
 │      │
 │      └── 一级事件2
 │             └── 二级事件3
 │
 └── 公司2
        └── 一级事件3
               └── 二级事件4
```

每一条完整路径都会生成对应的事件四元组,实现对新闻内容的全面覆盖。

---

## 具体改进

### 1. 多公司识别能力

原始版本:

```text
新闻
 │
 ▼
一个公司
```

优化后:

```text
新闻
 │
 ▼
所有公司
```

例如,一篇新闻同时提到:

- 浦东建设
- 上海建工

优化后的程序能够识别全部公司,并分别进行后续事件分析。

---

### 2. 多事件展开能力

对于每一家企业,不再只保留一个一级事件,而是识别所有相关一级事件。

例如:

```text
浦东建设
├── 项目中标
├── 财报发布
└── 股东减持
```

所有事件都会进入后续推理流程,不再遗漏任何有效信息。

---

### 3. 多子事件展开能力

针对每个一级事件,继续展开所有对应的二级事件。

例如:

```text
经营问题
├── 项目中标
└── 开展合作
```

每个二级事件都会进一步进行情感分析,并生成对应四元组。

---

### 4. 完整的树形结构

最终形成完整的树状推理结果:

```text
公司1
├── 一级事件1
│      ├── 子事件1
│      └── 子事件2
│
└── 一级事件2
       └── 子事件3

公司2
└── 一级事件3
       └── 子事件4
```

所有叶节点都会生成对应四元组,实现完整的信息覆盖。

---

### 5. 完善的异常处理机制

程序在每个关键步骤均加入异常捕获机制:

```python
try:
    ...
except Exception:
    ...
```

当模型返回内容格式异常时:

- 自动切换备用解析方案;
- 保证程序继续运行;
- 避免整个流程中断,提高系统鲁棒性。

---

### 6. 结构化输出

优化后不再输出简单字符串,而是采用结构化数据保存分析结果,包括:

- 完整树形推理结构;
- 去重后的四元组列表;
- 中间推理结果;
- 统计信息。

结构化输出更加便于后续的数据分析、可视化展示以及模型评估。

---

### 7. 支持批量处理

程序支持直接读取新闻数据集,自动完成批量分析。

处理流程如下:

```text
新闻1
新闻2
新闻3
...
      │
      ▼
自动分析
      │
      ▼
统一保存结果
```

无需人工逐条修改新闻内容,大幅提高了分析效率。

---

# 三、总结

相比原始版本,优化后的 `direct_prompt.py` 从单路径推理升级为树形多路径推理,实现了以下能力:

- 支持多公司识别;
- 支持多一级事件提取;
- 支持多二级事件展开;
- 支持完整树形推理结构;
- 增加异常处理与容错机制;
- 提供结构化输出结果;
- 支持新闻批量处理。

整体而言,优化后的代码能够更完整地覆盖复杂金融新闻中的多主体、多事件信息,提高了事件抽取的完整性、系统鲁棒性以及代码的可维护性,更符合实际金融事件分析场景的应用需求。
# 原始 `reasoning_prompt.py` 与优化版对比说明

## 一、原始 `reasoning_prompt.py` 存在的问题

相比 `direct_prompt.py`,原始 `reasoning_prompt.py` 采用了**分步骤推理(Step-by-Step Reasoning)**的方式,将事件抽取过程划分为多个阶段:

```text
新闻
 │
 ▼
识别公司
 │
 ▼
识别一级事件
 │
 ▼
识别二级事件
 │
 ▼
判断情感极性
 │
 ▼
输出一个四元组
```

虽然相比直接提示进行了推理拆分,但整体仍属于**线性单路径推理(Linear Reasoning)**。每一步仅保留一个推理结果,因此无法完整表达复杂金融新闻中的多主体、多事件关系。

### 主要缺陷

#### 1. 每条新闻仅生成一个结果

虽然程序支持循环读取新闻文件,但每条新闻最终仍然只输出一个四元组。

对于包含多个公司或多个事件的新闻,大量有效信息无法保留。

---

#### 2. 每一步仅保留一个答案

程序在每一步均采用"选择一个答案"的方式进行推理,例如:

```text
公司?
    ↓
一个公司

一级事件?
    ↓
一个事件

二级事件?
    ↓
一个子事件
```

因此无法识别:

- 多家公司;
- 多个一级事件;
- 多个二级事件。

---

#### 3. 缺少异常处理机制

若模型在任一步骤返回格式不符合预期,例如:

- 公司名称格式错误;
- 一级事件解析失败;
- 二级事件为空;

程序会直接报错退出,缺乏必要的容错能力。

---

#### 4. 事件类别采用硬编码

一级事件和二级事件列表直接写入代码。

例如:

```python
event_list = [
    "经营问题",
    "财务问题",
    ...
]
```

这种实现方式扩展性较差,当事件体系发生变化时,需要修改源代码,不利于维护。

---

#### 5. 信息利用率较低

最终仅保存一个事件四元组:

```text
(公司,一级事件,二级事件,情感)
```

新闻中其他公司、其他事件以及推理过程中产生的大量中间信息均被丢弃,导致信息利用率较低。

---

# 二、优化后的 `reasoning_prompt.py` 改进

优化后的代码在保留树形展开思想的基础上,对推理流程进行了进一步优化,实现了更加完整的**多步骤树形推理链(Tree-Structured Reasoning Chain)**。

整体推理流程如下:

```text
新闻
 │
 ▼
Step 1
识别所有公司
 │
 ▼
Step 2
提取每家公司所有一级事件
 │
 ▼
Step 3
展开所有二级事件
 │
 ▼
Step 4
判断每个事件情感极性
 │
 ▼
生成全部四元组
```

相比原始版本,实现了更加完整的事件推理过程。

---

## 具体改进

### 1. 分步骤推理链

整个分析流程被划分为四个独立阶段,每一步均具有明确的输入和输出。

```text
Step 1
识别所有公司
        │
        ▼
Step 2
提取每家公司所有一级事件
        │
        ▼
Step 3
展开所有二级事件
        │
        ▼
Step 4
判断每个事件情感极性
```

各步骤之间层层递进,形成完整的推理链。

---

### 2. 备选降级方案

为了提高程序鲁棒性,每一步均设计了备用解析策略。

例如:

```text
Step 1

主方案:
识别所有公司

        │
失败
        ▼

备用方案:
至少提取一个公司

        │
继续后续分析
```

即使模型输出格式异常,程序仍能够继续完成推理流程,避免整体中断。

---

### 3. 详细日志记录

程序在推理过程中实时输出各阶段处理结果,例如:

```text
Step 1

识别公司:

浦东建设
上海建工

-------------------

Step 2

浦东建设:

项目中标
财报发布

上海建工:

签订合同
```

完整记录:

- 当前推理步骤;
- 已识别公司;
- 每家公司对应事件;
- 每个事件对应子事件。

便于调试、定位问题以及分析模型行为。

---

### 4. 统计信息记录

程序自动统计整个推理过程中的分析结果,包括:

- 四元组总数量;
- 去重前数量;
- 去重后数量;
- 每家公司对应事件数量;
- 每类事件出现次数。

例如:

```text
总四元组:15

去重前:15

去重后:12
```

便于后续评估模型抽取效果。

---

### 5. 行业信息补充

程序支持增加:

```text
公司
      │
      ▼
行业
```

映射关系。

例如:

```text
浦东建设
      │
      ▼
建筑工程
```

原始四元组:

```text
(公司,一级事件,二级事件,情感)
```

可扩展为:

```text
(公司,行业,一级事件,二级事件,情感)
```

进一步丰富事件表示能力。

---

### 6. JSON 格式化输出

最终结果统一采用 JSON 格式保存,例如:

```json
{
  "tree": "...",
  "quadruples": [...],
  "statistics": {...}
}
```

输出内容包括:

- 完整树形推理结构;
- 所有四元组;
- 去重结果;
- 统计信息;
- 中间推理结果。

相比字符串输出,更方便后续:

- 数据分析;
- 可视化展示;
- 下游模型训练;
- 数据存储与共享。

---

# 三、总结

优化后的 `reasoning_prompt.py` 在原始版本基础上进行了全面升级,主要体现在以下几个方面:

- 构建了完整的四阶段推理链,实现分步骤树形推理;
- 引入备用降级方案,提高程序鲁棒性;
- 增加详细日志记录,方便调试与过程追踪;
- 支持统计信息记录,便于分析模型性能;
- 支持公司—行业映射,实现四元组向五元组扩展;
- 采用 JSON 格式输出,提升数据结构化程度。

总体而言,原始版本仍属于**线性单路径推理**,只能得到单一分析结果;优化后的版本则采用**树形多路径推理**,能够完整覆盖新闻中的多个公司、多个事件及其情感关系,并提供更加完善的日志记录、异常处理和结构化输出能力,更适用于复杂金融新闻事件抽取与情感分析任务。
Merged after fixing prompt parsing, EFSA label alignment, data.json batch loading, and regression coverage.
Merged after addressing README review feedback, adding bilingual documentation, arXiv link, citation update, dataset count clarification, and runtime requirements.
Copilot AI review requested due to automatic review settings July 1, 2026 10:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR modernizes the EFSA prompt-based extraction scripts for multi-company/multi-event “tree-style” analysis, improves robustness when parsing LLM outputs and loading EFSA data.json, and refreshes the repository documentation (English + Simplified Chinese) with paper metadata and clearer usage guidance.

Changes:

  • Refactors direct_prompt.py and reasoning_prompt.py into class-based analyzers with structured multi-step extraction and more robust output parsing/deduplication.
  • Adds batch loading support for EFSA-style JSON (data.json) and structured output export.
  • Expands README documentation (EN/ZH) with citation, links, dataset details, and runtime notes.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 11 comments.

File Description
README.md Expanded paper metadata, dataset info, and runtime requirements/usage notes.
README_zh.md Added bilingual (ZH) README mirroring the updated EN documentation.
code/direct_prompt.py Refactored into a multi-company, multi-event extraction flow with parsing utilities.
code/reasoning_prompt.py Refactored into a tree-style reasoning analyzer plus batch JSON/text loading and structured outputs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread code/reasoning_prompt.py
Comment on lines +355 to +359
{
'index': idx,
'data_id': record.get('data_id'),
'news_text': record.get('content', '').strip()
}
Comment thread code/reasoning_prompt.py
Comment on lines +117 to +126
try:
companies = _parse_items(response)
if companies:
return companies
except:
pass

# 备选:直接按逗号分割
companies = _parse_items(response)
return companies if companies else [self._fallback_extract_company(news_text)]
Comment thread code/reasoning_prompt.py
Comment on lines +150 to +160
# 解析事件列表
try:
events = _parse_items(response, coarse_events)
if events:
return events
except:
pass

# 备选:按逗号分割并过滤
events = _parse_items(response, coarse_events)
return events if events else [self._fallback_extract_coarse_event(news_text, company)]
Comment thread code/reasoning_prompt.py
Comment on lines +192 to +202
# 解析事件列表
try:
events = _parse_items(response, fine_events)
if events:
return events
except:
pass

# 备选:按逗号分割并过滤
events = _parse_items(response, fine_events)
return events if events else [self._fallback_extract_fine_event(news_text, company, coarse_event)]
Comment thread code/reasoning_prompt.py
import re
from typing import List, Tuple, Dict, Any

os.environ["CUDA_VISIBLE_DEVICES"] = "0"
Comment thread README.md

The prompt scripts currently call `.half().cuda()`, so running them as-is requires an NVIDIA GPU with CUDA. CPU-only execution requires changing the model loading code accordingly.

The prompt scripts load a local or Hugging Face-compatible model through `transformers`. Replace `model-name` in the scripts with the actual model path or model identifier before running. The selected model must provide a `model.chat(tokenizer, prompt, history=...)` interface; a generic `AutoModel` without this chat API is not sufficient.
Comment thread code/direct_prompt.py
Comment on lines +10 to +17
def _dedupe_preserve_order(items):
seen = set()
deduped = []
for item in items:
if item not in seen:
seen.add(item)
deduped.append(item)
return deduped
Comment thread README.md
Comment on lines +44 to +48
Install the required dependencies before running the code:

```bash
pip install torch transformers
```
Comment thread README_zh.md

## 环境依赖

运行代码前请安装所需依赖:
Comment thread README_zh.md
pip install torch transformers
```

Prompt 脚本通过 `transformers` 加载本地模型或 Hugging Face 兼容模型。运行前请将脚本中的 `model-name` 替换为实际模型路径或模型标识符。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants