556 lines
13 KiB
Markdown
Executable File
556 lines
13 KiB
Markdown
Executable File
# V2 版本使用指南
|
|
|
|
> 版本: v2.0
|
|
> 日期: 2026-03-18
|
|
> 用途: 使用 PlantConnectome 优化版提示词提取 70+ 维结构化信息
|
|
|
|
---
|
|
|
|
## 快速开始 (3步)
|
|
|
|
### 第1步: 准备环境
|
|
|
|
```bash
|
|
# 进入项目目录
|
|
cd /home/xia-lab/gene-family-mas
|
|
|
|
# 确保依赖已安装
|
|
pip install openai PyMuPDF -q
|
|
|
|
# 设置 API 密钥
|
|
export DASHSCOPE_API_KEY="your-api-key"
|
|
```
|
|
|
|
### 第2步: 准备文件
|
|
|
|
```bash
|
|
# 确保提示词文件存在
|
|
ls -la prompts/extraction_prompt_v2.md
|
|
|
|
# 准备 PDF 文件
|
|
# 将你的论文 PDF 放在 paper/ceshi/1pian/ 目录下
|
|
```
|
|
|
|
### 第3步: 运行提取
|
|
|
|
```bash
|
|
# 使用 V2 提示词提取
|
|
python3 scripts/extract_structured.py \
|
|
paper/ceshi/1pian/你的论文.pdf \
|
|
--output paper/ceshi/1pian/extracted/v2_result.json
|
|
```
|
|
|
|
---
|
|
|
|
## 详细使用说明
|
|
|
|
## 方法一: 使用现有提取脚本 (推荐)
|
|
|
|
### 1.1 基本用法
|
|
|
|
```bash
|
|
# 基本提取
|
|
python3 scripts/extract_structured.py \
|
|
paper/ceshi/1pian/苦荞SPL2019BMCplantbios12870-019-1916-6.pdf \
|
|
--output paper/ceshi/1pian/extracted/v2_extraction.json
|
|
```
|
|
|
|
### 1.2 修改脚本使用 V2 提示词
|
|
|
|
编辑 `scripts/extract_structured.py`,修改提示词加载部分:
|
|
|
|
```python
|
|
# 找到 call_llm_extract 函数
|
|
# 修改提示词加载路径
|
|
|
|
def call_llm_extract(pdf_text: str, api_key: str = "", model: str = DEFAULT_MODEL):
|
|
"""调用LLM提取结构化信息"""
|
|
|
|
# 加载 V2 提示词
|
|
prompt_path = Path(__file__).parent.parent / "prompts" / "extraction_prompt_v2.md"
|
|
with open(prompt_path, 'r', encoding='utf-8') as f:
|
|
prompt_template = f.read()
|
|
|
|
# 替换占位符
|
|
user_prompt = prompt_template.replace("{chunk_text}", pdf_text[:120000])
|
|
|
|
# 调用 API...
|
|
```
|
|
|
|
### 1.3 批量提取
|
|
|
|
```bash
|
|
# 创建批量提取脚本
|
|
for pdf in paper/ceshi/1pian/*.pdf; do
|
|
echo "处理: $pdf"
|
|
python3 scripts/extract_structured.py "$pdf" \
|
|
--output "paper/ceshi/1pian/extracted/$(basename $pdf .pdf)_v2.json"
|
|
done
|
|
```
|
|
|
|
---
|
|
|
|
## 方法二: 使用 Python 直接调用
|
|
|
|
### 2.1 完整示例代码
|
|
|
|
创建 `run_v2_extraction.py`:
|
|
|
|
```python
|
|
#!/usr/bin/env python3
|
|
"""
|
|
V2 版本提取示例
|
|
"""
|
|
import os
|
|
import json
|
|
from pathlib import Path
|
|
|
|
# 配置
|
|
API_KEY = os.getenv("DASHSCOPE_API_KEY", "")
|
|
MODEL = "kimi-k2.5" # 或 "qwen-max"
|
|
|
|
def extract_from_pdf(pdf_path: str, output_path: str = None):
|
|
"""
|
|
使用 V2 提示词从 PDF 提取结构化信息
|
|
|
|
Args:
|
|
pdf_path: PDF 文件路径
|
|
output_path: 输出 JSON 文件路径 (可选)
|
|
|
|
Returns:
|
|
提取结果字典
|
|
"""
|
|
from openai import OpenAI
|
|
import fitz # PyMuPDF
|
|
|
|
# 1. 读取 PDF
|
|
print(f"📄 读取 PDF: {pdf_path}")
|
|
doc = fitz.open(pdf_path)
|
|
text = ""
|
|
for page in doc:
|
|
text += page.get_text("text") + "\n"
|
|
doc.close()
|
|
print(f" 文本长度: {len(text)} 字符")
|
|
|
|
# 2. 加载 V2 提示词
|
|
prompt_path = "/home/xia-lab/gene-family-mas/prompts/extraction_prompt_v2.md"
|
|
with open(prompt_path, 'r', encoding='utf-8') as f:
|
|
prompt_template = f.read()
|
|
|
|
# 3. 替换占位符
|
|
user_prompt = prompt_template.replace("{chunk_text}", text[:120000])
|
|
|
|
# 4. 调用 API
|
|
print(f"🤖 调用 {MODEL} 提取...")
|
|
client = OpenAI(
|
|
api_key=API_KEY,
|
|
base_url="https://coding.dashscope.aliyuncs.com/v1"
|
|
)
|
|
|
|
response = client.chat.completions.create(
|
|
model=f"bailian/{MODEL}",
|
|
messages=[
|
|
{"role": "system", "content": "You are a specialized bioinformatics paper information extraction expert."},
|
|
{"role": "user", "content": user_prompt}
|
|
],
|
|
temperature=0.1,
|
|
max_tokens=6000
|
|
)
|
|
|
|
# 5. 解析结果
|
|
result_text = response.choices[0].message.content
|
|
|
|
# 清理 JSON
|
|
import re
|
|
result_text = re.sub(r'[\x00-\x08\x0b-\x0c\x0e-\x1f]', '', result_text)
|
|
if "```json" in result_text:
|
|
result_text = result_text.split("```json")[1]
|
|
if "```" in result_text:
|
|
result_text = result_text.split("```")[0]
|
|
result_text = result_text.strip()
|
|
|
|
try:
|
|
result = json.loads(result_text)
|
|
print("✅ 提取成功!")
|
|
except json.JSONDecodeError as e:
|
|
print(f"⚠️ JSON 解析失败: {e}")
|
|
result = {"error": "JSON解析失败", "raw": result_text[:500]}
|
|
|
|
# 6. 保存结果
|
|
if output_path:
|
|
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
|
with open(output_path, 'w', encoding='utf-8') as f:
|
|
json.dump(result, f, ensure_ascii=False, indent=2)
|
|
print(f"💾 结果已保存: {output_path}")
|
|
|
|
return result
|
|
|
|
# 使用示例
|
|
if __name__ == "__main__":
|
|
pdf_path = "paper/ceshi/1pian/苦荞SPL2019BMCplantbios12870-019-1916-6.pdf"
|
|
output_path = "paper/ceshi/1pian/extracted/v2_result.json"
|
|
|
|
result = extract_from_pdf(pdf_path, output_path)
|
|
|
|
# 打印统计
|
|
if "error" not in result:
|
|
entities = result.get("entities", [])
|
|
relations = result.get("relations", [])
|
|
print(f"\n📊 提取统计:")
|
|
print(f" 实体数量: {len(entities)}")
|
|
print(f" 关系数量: {len(relations)}")
|
|
```
|
|
|
|
### 2.2 运行代码
|
|
|
|
```bash
|
|
# 设置 API 密钥
|
|
export DASHSCOPE_API_KEY="your-api-key"
|
|
|
|
# 运行提取
|
|
python3 run_v2_extraction.py
|
|
```
|
|
|
|
---
|
|
|
|
## 方法三: 使用 Jupyter Notebook
|
|
|
|
### 3.1 创建 Notebook
|
|
|
|
```python
|
|
# Cell 1: 导入依赖
|
|
import os
|
|
import json
|
|
from openai import OpenAI
|
|
import fitz
|
|
|
|
# Cell 2: 配置
|
|
API_KEY = os.getenv("DASHSCOPE_API_KEY")
|
|
MODEL = "kimi-k2.5"
|
|
|
|
# Cell 3: 读取 PDF
|
|
pdf_path = "paper/ceshi/1pian/苦荞SPL2019BMCplantbios12870-019-1916-6.pdf"
|
|
doc = fitz.open(pdf_path)
|
|
text = ""
|
|
for page in doc:
|
|
text += page.get_text("text") + "\n"
|
|
doc.close()
|
|
print(f"文本长度: {len(text)} 字符")
|
|
|
|
# Cell 4: 加载 V2 提示词
|
|
with open("prompts/extraction_prompt_v2.md", 'r') as f:
|
|
prompt_template = f.read()
|
|
|
|
user_prompt = prompt_template.replace("{chunk_text}", text[:120000])
|
|
|
|
# Cell 5: 调用 API
|
|
client = OpenAI(api_key=API_KEY, base_url="https://coding.dashscope.aliyuncs.com/v1")
|
|
|
|
response = client.chat.completions.create(
|
|
model=f"bailian/{MODEL}",
|
|
messages=[
|
|
{"role": "system", "content": "You are a specialized bioinformatics paper information extraction expert."},
|
|
{"role": "user", "content": user_prompt}
|
|
],
|
|
temperature=0.1,
|
|
max_tokens=6000
|
|
)
|
|
|
|
# Cell 6: 解析结果
|
|
result_text = response.choices[0].message.content
|
|
result = json.loads(result_text) # 可能需要清理
|
|
|
|
# Cell 7: 查看结果
|
|
print(f"实体数量: {len(result.get('entities', []))}")
|
|
print(f"关系数量: {len(result.get('relations', []))}")
|
|
|
|
# Cell 8: 保存
|
|
with open("v2_result.json", 'w') as f:
|
|
json.dump(result, f, ensure_ascii=False, indent=2)
|
|
```
|
|
|
|
---
|
|
|
|
## 验证提取结果
|
|
|
|
### 检查实体
|
|
|
|
```python
|
|
import json
|
|
|
|
with open("paper/ceshi/1pian/extracted/v2_result.json", 'r') as f:
|
|
result = json.load(f)
|
|
|
|
entities = result.get("entities", [])
|
|
print(f"实体总数: {len(entities)}")
|
|
|
|
# 按类型分组
|
|
from collections import Counter
|
|
type_counts = Counter(e.get("type", "unknown") for e in entities)
|
|
print("\n实体类型分布:")
|
|
for t, c in type_counts.most_common():
|
|
print(f" {t}: {c}")
|
|
|
|
# 显示前5个实体
|
|
print("\n前5个实体:")
|
|
for e in entities[:5]:
|
|
print(f" - {e.get('display_name', e.get('name'))}")
|
|
```
|
|
|
|
### 检查关系
|
|
|
|
```python
|
|
relations = result.get("relations", [])
|
|
print(f"关系总数: {len(relations)}")
|
|
|
|
# 按类型分组
|
|
rel_counts = Counter(r.get("relationship", "unknown") for r in relations)
|
|
print("\n关系类型分布 (Top 10):")
|
|
for r, c in rel_counts.most_common(10):
|
|
print(f" {r}: {c}")
|
|
|
|
# 显示前5个关系
|
|
print("\n前5个关系:")
|
|
for r in relations[:5]:
|
|
print(f" [{r.get('source_type')}] {r.get('source')}")
|
|
print(f" {r.get('relationship')}")
|
|
print(f" [{r.get('target_type')}] {r.get('target')}")
|
|
```
|
|
|
|
---
|
|
|
|
## 常见问题
|
|
|
|
### Q1: API 密钥未设置
|
|
|
|
**错误**: `未设置API密钥: DASHSCOPE_API_KEY`
|
|
|
|
**解决**:
|
|
```bash
|
|
export DASHSCOPE_API_KEY="your-api-key"
|
|
# 或在代码中设置
|
|
os.environ["DASHSCOPE_API_KEY"] = "your-api-key"
|
|
```
|
|
|
|
### Q2: PyMuPDF 未安装
|
|
|
|
**错误**: `No module named 'fitz'`
|
|
|
|
**解决**:
|
|
```bash
|
|
pip install PyMuPDF
|
|
```
|
|
|
|
### Q3: JSON 解析失败
|
|
|
|
**错误**: `JSON解析失败`
|
|
|
|
**解决**: 检查并清理返回的文本
|
|
```python
|
|
import re
|
|
# 移除控制字符
|
|
text = re.sub(r'[\x00-\x08\x0b-\x0c\x0e-\x1f]', '', text)
|
|
# 提取 JSON 块
|
|
if "```json" in text:
|
|
text = text.split("```json")[1]
|
|
if "```" in text:
|
|
text = text.split("```")[0]
|
|
```
|
|
|
|
### Q4: Token 超限
|
|
|
|
**错误**: `max_tokens 超限`
|
|
|
|
**解决**: 截断 PDF 文本
|
|
```python
|
|
# 只取前 120000 字符
|
|
text = text[:120000]
|
|
```
|
|
|
|
### Q5: 提取结果为空
|
|
|
|
**检查**:
|
|
1. PDF 是否可读
|
|
2. 提示词是否正确加载
|
|
3. API 调用是否成功
|
|
4. 返回内容是否包含 JSON
|
|
|
|
---
|
|
|
|
## 高级用法
|
|
|
|
### 批量处理多篇论文
|
|
|
|
```python
|
|
import os
|
|
import glob
|
|
from pathlib import Path
|
|
|
|
def batch_extract(pdf_dir: str, output_dir: str):
|
|
"""批量提取多篇论文"""
|
|
pdf_files = glob.glob(f"{pdf_dir}/*.pdf")
|
|
|
|
for pdf_path in pdf_files:
|
|
filename = Path(pdf_path).stem
|
|
output_path = f"{output_dir}/{filename}_v2.json"
|
|
|
|
print(f"\n{'='*60}")
|
|
print(f"处理: {filename}")
|
|
|
|
try:
|
|
result = extract_from_pdf(pdf_path, output_path)
|
|
|
|
if "error" not in result:
|
|
entities = len(result.get("entities", []))
|
|
relations = len(result.get("relations", []))
|
|
print(f"✅ 成功: {entities} 实体, {relations} 关系")
|
|
else:
|
|
print(f"❌ 失败: {result.get('error')}")
|
|
|
|
except Exception as e:
|
|
print(f"❌ 错误: {e}")
|
|
|
|
# 使用
|
|
batch_extract(
|
|
"paper/ceshi/1pian",
|
|
"paper/ceshi/1pian/extracted"
|
|
)
|
|
```
|
|
|
|
### 对比 V1 和 V2 结果
|
|
|
|
```python
|
|
def compare_results(v1_path: str, v2_path: str):
|
|
"""对比 V1 和 V2 提取结果"""
|
|
|
|
with open(v1_path) as f:
|
|
v1 = json.load(f)
|
|
with open(v2_path) as f:
|
|
v2 = json.load(f)
|
|
|
|
# 统计
|
|
v1_entities = len(v1.get("entities", []))
|
|
v2_entities = len(v2.get("entities", []))
|
|
v1_relations = len(v1.get("relations", []))
|
|
v2_relations = len(v2.get("relations", []))
|
|
|
|
print("V1 vs V2 对比:")
|
|
print(f" 实体: {v1_entities} → {v2_entities} ({v2_entities/v1_entities:.1f}x)")
|
|
print(f" 关系: {v1_relations} → {v2_relations} (提升: {v2_relations - v1_relations})")
|
|
```
|
|
|
|
---
|
|
|
|
## 最佳实践
|
|
|
|
### 1. 预处理 PDF
|
|
|
|
```python
|
|
# 清理 PDF 文本
|
|
def clean_pdf_text(text: str) -> str:
|
|
"""清理 PDF 提取的文本"""
|
|
# 移除多余空白
|
|
text = re.sub(r'\n+', '\n', text)
|
|
# 移除页眉页脚
|
|
lines = [l for l in text.split('\n') if not l.strip().isdigit()]
|
|
return '\n'.join(lines)
|
|
```
|
|
|
|
### 2. 后处理结果
|
|
|
|
```python
|
|
def post_process(result: dict) -> dict:
|
|
"""后处理提取结果"""
|
|
# 去重实体
|
|
entities = result.get("entities", [])
|
|
seen = set()
|
|
unique_entities = []
|
|
for e in entities:
|
|
key = (e.get("name"), e.get("type"))
|
|
if key not in seen:
|
|
seen.add(key)
|
|
unique_entities.append(e)
|
|
result["entities"] = unique_entities
|
|
|
|
# 去重关系
|
|
relations = result.get("relations", [])
|
|
seen = set()
|
|
unique_relations = []
|
|
for r in relations:
|
|
key = (r.get("source"), r.get("relationship"), r.get("target"))
|
|
if key not in seen:
|
|
seen.add(key)
|
|
unique_relations.append(r)
|
|
result["relations"] = unique_relations
|
|
|
|
return result
|
|
```
|
|
|
|
### 3. 质量检查
|
|
|
|
```python
|
|
def quality_check(result: dict) -> dict:
|
|
"""检查提取质量"""
|
|
issues = []
|
|
|
|
# 检查实体
|
|
entities = result.get("entities", [])
|
|
if len(entities) < 10:
|
|
issues.append(f"实体数量过少: {len(entities)}")
|
|
|
|
for e in entities:
|
|
if not e.get("type"):
|
|
issues.append(f"实体缺少类型: {e.get('name')}")
|
|
if not e.get("display_name"):
|
|
issues.append(f"实体缺少display_name: {e.get('name')}")
|
|
|
|
# 检查关系
|
|
relations = result.get("relations", [])
|
|
if len(relations) < 5:
|
|
issues.append(f"关系数量过少: {len(relations)}")
|
|
|
|
for r in relations:
|
|
if not r.get("source_type"):
|
|
issues.append(f"关系缺少source_type")
|
|
if not r.get("target_type"):
|
|
issues.append(f"关系缺少target_type")
|
|
|
|
return {
|
|
"quality_score": max(0, 100 - len(issues) * 10),
|
|
"issues": issues
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 总结
|
|
|
|
### 最简单的使用方式
|
|
|
|
```bash
|
|
# 1. 设置密钥
|
|
export DASHSCOPE_API_KEY="your-key"
|
|
|
|
# 2. 运行提取
|
|
python3 scripts/extract_structured.py \
|
|
paper/ceshi/1pian/你的论文.pdf \
|
|
--output paper/ceshi/1pian/extracted/v2_result.json
|
|
|
|
# 3. 查看结果
|
|
cat paper/ceshi/1pian/extracted/v2_result.json | jq '.entities | length'
|
|
cat paper/ceshi/1pian/extracted/v2_result.json | jq '.relations | length'
|
|
```
|
|
|
|
### 关键文件
|
|
|
|
| 文件 | 路径 | 说明 |
|
|
|------|------|------|
|
|
| V2 提示词 | `prompts/extraction_prompt_v2.md` | PlantConnectome优化版 |
|
|
| 提取脚本 | `scripts/extract_structured.py` | 主提取程序 |
|
|
| 使用指南 | `docs/V2_USAGE_GUIDE.md` | 本文档 |
|
|
|
|
---
|
|
|
|
*文档版本: v1.0*
|
|
*更新日期: 2026-03-18*
|