# ASL 文献处理技术选型 > **文档版本?* V1.0 > **创建日期?* 2025-11-15 > **适用模块?* AI 智能文献(ASL? > **目标?* 定义初筛、全文复筛、全文提取的技术栈和实现路? --- ## 📋 文档概述 ASL 模块涉及三种不同的文献处理场景,每种场景有不同的技术特点和实现方案? | 场景 | 输入格式 | 核心技?| 主要挑战 | |------|---------|---------|---------| | **标题摘要初筛** | Excel 文件 | Excel 解析 + LLM 筛?| 批量处理效率 | | **全文复筛** | PDF 全文 | PDF 提取 + LLM 筛?| PDF 解析准确?| | **全文数据提取** | PDF 全文 | PDF 提取 + LLM 结构化提?| 表格、公式准确提?| --- ## 🎯 技术架构总览 ``` ┌─────────────────────────────────────────────────────────? ? ASL 文献处理流程 ? └─────────────────────────────────────────────────────────? ? ├─ 场景 1: 标题摘要初筛 ? └─ 用户上传 Excel ?解析 ?LLM 批量筛??导出结果 ? ├─ 场景 2: 全文复筛 ? └─ 用户上传 PDF ?PDF 提取 ?LLM 筛??复核 ? └─ 场景 3: 全文数据提取 └─ PDF ?提取 + 结构??LLM 提取数据 ?人工复核 ┌─────────────────────────────────────────────────────────? ? 技术栈分层架构(共享) ? ├─────────────────────────────────────────────────────────? ? 前端? React 19 + Ant Design 5 + xlsx/exceljs ? ├─────────────────────────────────────────────────────────? ? 后端? Node.js (Fastify) + TypeScript ? ├─────────────────────────────────────────────────────────? ? 文档处理? Python 微服?(extraction_service) ? ? ├─ PyMuPDF: 快?PDF 提取 ? ? ├─ Nougat: 英文科学文献高质量提?? ? ? └─ Language Detector: 自动语言检? ? ├─────────────────────────────────────────────────────────? ? LLM ? DeepSeek-V3 + Qwen3 / GPT-5 + Claude-4.5 ? ├─────────────────────────────────────────────────────────? ? 数据? PostgreSQL 15 (asl_schema) ? └─────────────────────────────────────────────────────────? ``` --- ## 📌 场景 1: 标题摘要初筛 ### 1.1 技术特? - **输入格式**: Excel 文件 (`.xlsx` / `.xls`) - **数据规模**: 50-500 篇文?批次 - **主要字段**: 标题、摘要、DOI、作者、发表年份、期? - **处理重点**: 批量高效处理,无需 PDF 解析 ### 1.2 技术选型 #### 前端:Excel 上传与解? | 技?| ?| 用?| 优势 | |------|-----|------|------| | **Excel 上传** | `antd Upload` | 文件上传组件 | 拖拽上传、进度条 | | **Excel 解析** | `xlsx` / `exceljs` | 前端解析 Excel | 纯前端处理,快速预?| | **模板验证** | 自定义逻辑 | 校验列名和数据格?| 提前发现格式错误 | **推荐方案:`xlsx` 库(SheetJS?* - ?支持 `.xlsx` ?`.xls` 格式 - ??JavaScript,前端直接解? - ?体积小(~600KB),性能? - ?支持大文件(1000+ 行) **代码示例?* ```typescript import * as XLSX from 'xlsx'; function parseExcel(file: File): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = (e) => { try { const data = new Uint8Array(e.target.result as ArrayBuffer); const workbook = XLSX.read(data, { type: 'array' }); // 读取第一个工作表 const sheetName = workbook.SheetNames[0]; const worksheet = workbook.Sheets[sheetName]; // 转换?JSON const jsonData = XLSX.utils.sheet_to_json(worksheet); // 映射为标准格? const literatures = jsonData.map((row: any) => ({ title: row['Title'] || row['标题'], abstract: row['Abstract'] || row['摘要'], doi: row['DOI'], authors: row['Authors'] || row['作?], year: row['Year'] || row['年份'], journal: row['Journal'] || row['期刊'], })); resolve(literatures); } catch (error) { reject(new Error('Excel 解析失败')); } }; reader.onerror = () => reject(new Error('文件读取失败')); reader.readAsArrayBuffer(file); }); } ``` #### 后端:批量筛选处? **处理流程?* ``` Excel 数据 ?批量分组?0-20 ?组)?并行调用 LLM ?汇总结? ``` **关键技术点?* 1. **批量分组**:避免单次请求过大,10-20 ?组最? 2. **并行处理**:使?`Promise.all` 并行调用 LLM 3. **进度推?*:WebSocket 实时推送处理进? 4. **断点续传**:支持任务中断后继续 **代码示例?* ```typescript async function batchScreening( literatures: Literature[], protocol: Protocol, progressCallback: (progress: number) => void ) { const batchSize = 15; const batches = chunk(literatures, batchSize); const results = []; for (let i = 0; i < batches.length; i++) { const batch = batches[i]; // 并行处理当前批次 const batchResults = await Promise.all( batch.map(lit => dualModelScreening(lit, protocol)) ); results.push(...batchResults); // 推送进? const progress = Math.round(((i + 1) / batches.length) * 100); progressCallback(progress); } return results; } ``` ### 1.3 数据? ``` 用户操作 前端处理 后端处理 LLM 处理 ? ? ? ? ├─ 上传 Excel ? ? ? ? └──────────────→│ ? ? ? ├─ 解析 Excel ? ? ? ├─ 验证格式 ? ? ? ├─ 显示预览 ? ? ? ? ? ? ? ├─ 提交筛选任? ? ? ? ? └───────────────→│ ? ? ? ├─ 保存任务 ? ? ? ├─ 分组?5 ?组) ? ? ? ? ? ? ? ├─ 批次 1 ? ? ? ? └──────────────→│ ? ? ? ├─ DeepSeek 筛? ? ? ? ├─ Qwen3 筛? ? ? ? ├─ 对比结果 ? ? ? ←──────────────? ? ? ├─ 保存结果 ? ? ? ? ? ? ? ├─ 批次 2... ? ? ? ? ? ? ? ←───────────────?返回完整结果 ? ? ←──────────────?显示结果 ? ? └─ 人工复核 ? ? ? ``` --- ## 📌 场景 2 & 3: 全文复筛与数据提? ### 2.1 技术特? - **输入格式**: PDF 文件(英文医学文献) - **文件特点**: - 科学论文格式(标题、摘要、引言、方法、结果、讨论、参考文献) - 包含复杂表格、公式、图? - 通常 10-30 ? - **处理重点**: 高准确率提取,保留结构和格式 ### 2.2 技术选型:PDF 提取 #### 核心方案:Nougat + PyMuPDF 顺序降级策略 ? **现有架构**(已实现,位?`extraction_service/`): ```python # 顺序降级策略 def extract_pdf(file_path: str): # Step 1: 检测语言 language = detect_language(file_path) # Step 2: 中文 PDF ?PyMuPDF(快速) if language == 'chinese': return extract_pdf_pymupdf(file_path) # Step 3: 英文 PDF ?尝试 Nougat if check_nougat_available(): result = extract_pdf_nougat(file_path) # 质量检查(阈?0.7? if result['quality_score'] >= 0.7: return result # ?Nougat 成功 # Step 4: 降级?PyMuPDF return extract_pdf_pymupdf(file_path) ``` #### 技术对? | 方案 | 优势 | 劣势 | 适用场景 | |------|------|------|---------| | **Nougat** ?| ?专为科学文献设计
?公式、表格准确率?br>?输出 Markdown 格式
?保留文档结构 | ?速度慢(1-2 分钟/20 页)
?需?GPU 加?br>?内存占用大(~4GB?| 英文医学文献全文提取 | | **PyMuPDF** | ?速度快(秒级?br>?内存占用?br>?部署简?| ?公式、表格易丢失
?纯文本输?br>?布局易混?| 中文文献、快速预?| | **Adobe API** | ?商业级准确率
?云端处理 | ?需付费
?网络依赖
?隐私风险 | 不推荐(成本高) | | **Tesseract OCR** | ?开源免?br>?支持多语言 | ?需要图像预处理
?准确率不稳定 | 扫描?PDF(备选) | **推荐方案:Nougat(主?+ PyMuPDF(降级) ?* #### Nougat 核心优势(医学文献场景) ``` ?专为科学文献设计 ├─ 训练数据:arXiv 论文 + 科学期刊 ├─ 公式识别:LaTeX 格式输出 ├─ 表格保留:Markdown 表格格式 └─ 结构化输出:章节、段落清? ?输出格式:Markdown ├─ 标题层级? ## ### ├─ 表格:| Header | Data | ├─ 公式?$ formula $$ └─ 引用:[1] [2] [3] ?质量评估机制 ├─ 自动质量评分?-1? ├─ 低质量自动降?PyMuPDF └─ 保证提取成功? ``` #### 实现细节 **服务架构?* ``` Node.js Backend (Port 3001) ? ├─ 调用 ExtractionClient.ts ? └─ HTTP 请求 ?Python 微服? ? Python Extraction Service (Port 8000) ? ├─ /api/extract/pdf ? ├─ detect_language() ? ├─ extract_pdf_nougat() ?Nougat Model ? └─ extract_pdf_pymupdf() ?PyMuPDF ? └─ /api/health └─ 检?Nougat 可用? ``` **Node.js 调用代码?* ```typescript import { extractionClient } from '@common/document/ExtractionClient'; async function extractLiteraturePDF(file: Buffer, filename: string) { try { // 方法 1: 自动选择(推荐) const result = await extractionClient.extractPdf( file, filename, 'auto' ); // 方法 2: 强制使用 Nougat // const result = await extractionClient.extractPdf(file, filename, 'nougat'); return { text: result.text, method: result.method, // "nougat" | "pymupdf" quality: result.metadata.quality_score, pageCount: result.metadata.page_count, hasTables: result.metadata.has_tables, hasFormulas: result.metadata.has_formulas }; } catch (error) { console.error('PDF extraction failed:', error); throw error; } } ``` **Python 提取代码?* ```python # extraction_service/services/nougat_extractor.py def extract_pdf_nougat(file_path: str) -> Dict[str, Any]: """ 使用 Nougat 提取 PDF 文本 命令行调用: nougat -o --markdown --no-skipping """ cmd = [ 'nougat', file_path, '-o', output_dir, '--markdown', # 输出 Markdown 格式 '--no-skipping' # 不跳过任何页? ] # 执行 Nougat(超?5 分钟? process = subprocess.Popen(cmd, ...) stdout, stderr = process.communicate(timeout=300) # 读取输出文件?mmd? markdown_text = read_output_file() # 质量评估 quality_score = evaluate_nougat_quality(markdown_text) return { "success": True, "method": "nougat", "text": markdown_text, "format": "markdown", "metadata": { "quality_score": quality_score, "has_tables": detect_tables(markdown_text), "has_formulas": detect_formulas(markdown_text) } } ``` ### 2.3 文本后处? **Nougat 输出优化?* ```typescript function postProcessNougatOutput(markdown: string): ProcessedText { return { // 原始 Markdown raw: markdown, // 章节分割 sections: extractSections(markdown), // {abstract, methods, results, ...} // 表格提取 tables: extractTables(markdown), // 公式提取 formulas: extractFormulas(markdown), // 纯文本(去除格式? plainText: markdownToPlainText(markdown), // 结构化数据(用于 LLM? structured: { title: extractTitle(markdown), abstract: extractAbstract(markdown), methodology: extractMethodology(markdown), results: extractResults(markdown), } }; } ``` --- ## 📌 场景 4: 文献下载(Unpaywall API)⭐ ### 3.1 技术背? **Unpaywall** 是一个免费的开放获取(Open Access)文?API,可以: - ?通过 DOI 查询文献是否有免费全? - ?获取合法?PDF 下载链接 - ?完全免费,无需付费 - ?数据库覆?3000+ 万篇文献 **官网**: https://unpaywall.org/products/api ### 3.2 技术选型 #### API 调用方式 **基础信息?* - **API 端点**: `https://api.unpaywall.org/v2/{doi}?email={your_email}` - **请求方法**: GET - **认证方式**: 无需 API Key,仅需提供邮箱 - **速率限制**: 100,000 ?天(免费? **示例请求?* ```bash curl "https://api.unpaywall.org/v2/10.1038/nature12373?email=YOUR_EMAIL" ``` **响应示例?* ```json { "doi": "10.1038/nature12373", "title": "The genome of the woodland strawberry", "is_oa": true, "oa_status": "gold", "best_oa_location": { "url": "https://www.nature.com/articles/nature12373.pdf", "url_for_pdf": "https://www.nature.com/articles/nature12373.pdf", "url_for_landing_page": "https://www.nature.com/articles/nature12373", "license": "cc-by", "version": "publishedVersion" }, "oa_locations": [...] } ``` #### Node.js 实现 **服务封装?* ```typescript // backend/src/common/literature/UnpaywallClient.ts import axios from 'axios'; import { config } from '../../config/env'; export interface UnpaywallResult { doi: string; title: string; isOA: boolean; // 是否开放获? oaStatus: string; // "gold" | "green" | "hybrid" | "bronze" | "closed" pdfUrl: string | null; // PDF 下载链接 landingPageUrl: string; // 文献页面链接 license: string | null; // 许可协议 version: string | null; // "publishedVersion" | "acceptedVersion" } class UnpaywallClient { private baseUrl = 'https://api.unpaywall.org/v2'; private email: string; constructor(email: string = config.unpaywallEmail) { this.email = email; } /** * 通过 DOI 查询文献信息 */ async getByDoi(doi: string): Promise { try { const url = `${this.baseUrl}/${doi}?email=${this.email}`; const response = await axios.get(url, { timeout: 10000, // 10 秒超? }); const data = response.data; // 获取最佳下载位? const bestOA = data.best_oa_location; return { doi: data.doi, title: data.title, isOA: data.is_oa, oaStatus: data.oa_status, pdfUrl: bestOA?.url_for_pdf || null, landingPageUrl: bestOA?.url_for_landing_page || data.doi_url, license: bestOA?.license || null, version: bestOA?.version || null, }; } catch (error) { if (axios.isAxiosError(error)) { if (error.response?.status === 404) { throw new Error(`DOI not found: ${doi}`); } } throw new Error(`Unpaywall API error: ${error.message}`); } } /** * 批量查询(带速率限制? */ async getBatch(dois: string[]): Promise { const results = []; for (const doi of dois) { try { const result = await this.getByDoi(doi); results.push(result); // 速率限制?00ms/请求 await new Promise(resolve => setTimeout(resolve, 100)); } catch (error) { console.error(`Failed to fetch ${doi}:`, error.message); results.push(null); // 失败项标记为 null } } return results.filter(r => r !== null); } /** * 下载 PDF 文件 */ async downloadPdf(pdfUrl: string, outputPath: string): Promise { try { const response = await axios.get(pdfUrl, { responseType: 'arraybuffer', timeout: 60000, // 1 分钟超时 }); const fs = require('fs'); fs.writeFileSync(outputPath, response.data); } catch (error) { throw new Error(`PDF download failed: ${error.message}`); } } } export const unpaywallClient = new UnpaywallClient(); ``` **环境变量配置?* ```env # .env UNPAYWALL_EMAIL=your-email@example.com ``` #### 业务集成 **场景 1:批量检查文献是否可下载** ```typescript async function checkLiteratureAvailability(literatures: Literature[]) { const dois = literatures .map(lit => lit.doi) .filter(doi => doi); // 过滤?DOI const results = await unpaywallClient.getBatch(dois); return literatures.map(lit => ({ ...lit, downloadable: results.find(r => r.doi === lit.doi)?.isOA || false, pdfUrl: results.find(r => r.doi === lit.doi)?.pdfUrl || null, })); } ``` **场景 2:用户点击下载全?* ```typescript async function downloadLiteratureFullText(doi: string) { // Step 1: 查询 Unpaywall const unpaywallResult = await unpaywallClient.getByDoi(doi); if (!unpaywallResult.pdfUrl) { throw new Error('该文献无免费全文'); } // Step 2: 下载 PDF const filename = `${doi.replace(/\//g, '_')}.pdf`; const outputPath = `./downloads/${filename}`; await unpaywallClient.downloadPdf(unpaywallResult.pdfUrl, outputPath); // Step 3: 提取文本(调?extraction_service? const extractionResult = await extractionClient.extractPdf( fs.readFileSync(outputPath), filename, 'auto' ); return { pdfPath: outputPath, text: extractionResult.text, method: extractionResult.method, }; } ``` ### 3.3 前端集成 **批量下载按钮?* ```typescript // 批量检查可下载? async function checkDownloadable(selectedRows: Literature[]) { setLoading(true); const results = await api.checkLiteratureAvailability(selectedRows); const downloadableCount = results.filter(r => r.downloadable).length; message.success(`发现 ${downloadableCount} 篇可下载全文`); setLiteratures(results); setLoading(false); } // 下载全文 async function downloadFullText(literature: Literature) { if (!literature.downloadable) { message.warning('该文献无免费全文'); return; } try { const result = await api.downloadLiteratureFullText(literature.doi); message.success('下载成功'); // 打开 PDF 查看? openPdfViewer(result.pdfPath); } catch (error) { message.error(`下载失败: ${error.message}`); } } ``` --- ## 🔍 补充技术点 ### 4.1 您提到的技术点总结 | 技术点 | 状?| 说明 | |--------|------|------| | ?Nougat 模型 | 已实?| `extraction_service/services/nougat_extractor.py` | | ?PyMuPDF | 已实?| `extraction_service/services/pdf_extractor.py` | | ?顺序降级策略 | 已实?| 英文→Nougat,中文→PyMuPDF | | 🆕 Unpaywall API | 需新增 | 本文档提供实现方?| | ?Excel 解析 | 需新增 | 使用 `xlsx` 库(前端?| ### 4.2 可能遗漏的技术点 ? #### ?)表格提取增? **问题**:Nougat 虽然保留表格结构,但 LLM 直接处理 Markdown 表格可能不准确? **解决方案:Table Transformer** ```python # 使用微软?Table Transformer 模型 # https://github.com/microsoft/table-transformer from transformers import TableTransformerForObjectDetection import torch def extract_tables_enhanced(pdf_path: str): """ 使用 Table Transformer 精确定位表格 """ model = TableTransformerForObjectDetection.from_pretrained( "microsoft/table-transformer-detection" ) # 检测表格位? tables = model.detect_tables(pdf_path) # 提取每个表格 for table in tables: table_image = crop_table(pdf_path, table.bbox) table_data = ocr_table(table_image) return structured_tables ``` **优先级:V2.0**(MVP 阶段 Nougat 足够? #### ?)引用解析与链接 **问题**:科学文献包含大量引?`[1] [2] [3]`,需要解析并链接到参考文献? **解决方案:GROBID** ```python # GROBID: 开源科学文献解析工? # https://github.com/kermitt2/grobid import requests def parse_references(pdf_path: str): """ 使用 GROBID 解析参考文? """ with open(pdf_path, 'rb') as f: files = {'input': f} response = requests.post( 'http://localhost:8070/api/processFulltextDocument', files=files ) # 返回结构化的引用列表 return response.json()['references'] ``` **优先级:V2.0**(非核心功能? #### ?)公式识别与渲染 **问题**:Nougat 输出 LaTeX 公式,前端需要渲染? **解决方案:KaTeX / MathJax** ```typescript // 前端渲染 LaTeX 公式 import katex from 'katex'; import 'katex/dist/katex.min.css'; function renderFormula(latex: string) { return katex.renderToString(latex, { throwOnError: false, displayMode: true, }); } ``` **优先级:MVP**(提升用户体验) #### ?)PDF 预览与标? **问题**:人工复核时需要查看原文,并高亮标注? **解决方案:PDF.js + Annotator.js** ```typescript // React 组件 import { Viewer } from '@react-pdf-viewer/core'; import '@react-pdf-viewer/core/lib/styles/index.css'; function PdfViewer({ pdfUrl, annotations }) { return ( ); } ``` **优先级:MVP**(核心功能) #### ?)文献去? **问题**:Excel 上传可能包含重复文献(同一篇文献不同版本)? **解决方案:基?DOI 和标题的去重** ```typescript function deduplicateLiteratures(literatures: Literature[]) { const seen = new Set(); return literatures.filter(lit => { // 优先使用 DOI if (lit.doi) { if (seen.has(lit.doi)) return false; seen.add(lit.doi); return true; } // 否则使用标题(标准化后) const normalizedTitle = normalizeTitle(lit.title); if (seen.has(normalizedTitle)) return false; seen.add(normalizedTitle); return true; }); } function normalizeTitle(title: string): string { return title .toLowerCase() .replace(/[^\w\s]/g, '') // 去除标点 .replace(/\s+/g, ' ') // 规范化空? .trim(); } ``` **优先级:MVP**(必须功能) #### ?)文献元数据补全 **问题**:Excel 上传的数据可能不完整(缺 DOI、年份等)? **解决方案:Crossref API** ```typescript // 通过标题查询 DOI async function enrichMetadata(literature: Literature) { if (literature.doi) return literature; // 已有 DOI // 调用 Crossref API const response = await axios.get( `https://api.crossref.org/works?query.title=${literature.title}` ); const match = response.data.message.items[0]; return { ...literature, doi: match.DOI, year: match['published-print']?.['date-parts'][0][0], journal: match['container-title'][0], }; } ``` **优先级:V1.0**(增强功能) #### ?)批处理进度持久? **问题**:批量筛选耗时长(1000 ?> 10 分钟),需支持断点续传? **解决方案:Redis + 任务队列** ```typescript // 使用 Bull 队列 import Queue from 'bull'; const screeningQueue = new Queue('literature-screening', { redis: { host: 'localhost', port: 6379 } }); // 添加任务 screeningQueue.add({ projectId: 'xxx', literatures: [...], protocol: {...} }); // 处理任务 screeningQueue.process(async (job) => { const { projectId, literatures, protocol } = job.data; for (let i = 0; i < literatures.length; i++) { // 处理单篇文献 await screenLiterature(literatures[i], protocol); // 更新进度 job.progress((i + 1) / literatures.length * 100); } }); ``` **优先级:V1.0**(体验优化) #### ?)错误处理与重试 **问题**:LLM 调用可能失败(网络、超时、限流)? **解决方案:指数退避重?* ```typescript async function retryWithBackoff( fn: () => Promise, maxRetries: number = 3 ): Promise { for (let i = 0; i < maxRetries; i++) { try { return await fn(); } catch (error) { if (i === maxRetries - 1) throw error; // 指数退避:1s, 2s, 4s const delay = Math.pow(2, i) * 1000; await new Promise(resolve => setTimeout(resolve, delay)); } } } ``` **优先级:MVP**(必须功能) --- ## 📊 技术选型总结 ### MVP 阶段必选技? | 层级 | 技?| 用?| |------|------|------| | **前端** | `xlsx` | Excel 解析 | | **前端** | `PDF.js` | PDF 预览 | | **前端** | `KaTeX` | 公式渲染 | | **后端** | `ExtractionClient` | 调用 Python 微服?| | **后端** | `UnpaywallClient` | 文献下载 | | **Python** | `Nougat` | 英文 PDF 提取 | | **Python** | `PyMuPDF` | 快?PDF 提取 | | **数据?* | `asl_schema` | 数据存储 | ### V1.0 增强技? | 技?| 用?| |------|------| | Crossref API | 元数据补?| | Bull Queue | 任务队列 | | Redis | 进度持久?| ### V2.0 高级技? | 技?| 用?| |------|------| | Table Transformer | 表格精确提取 | | GROBID | 引用解析 | | Semantic Scholar API | 学术图谱 | --- ## 📁 测试数据存放建议 根据 ASL 模块的文件夹结构,测试数据应该放在: ``` AIclinicalresearch/docs/03-业务模块/ASL-AI智能文献/ └── 05-测试文档/ ├── 01-测试计划.md ├── 02-标题摘要初筛测试用例.md └── 03-测试数据/ ?新建文件? ├── README.md ?说明文档 ├── screening-test-data/ ? ├── literature-list-199.xlsx ?199 篇文献列? ? ├── picos-criteria.txt ?PICOS 标准 ? └── expected-results.json ?预期结果(金标准? ├── pdf-samples/ ? ├── sample-rct-01.pdf ? ├── sample-cohort-01.pdf ? └── README.md └── extraction-test-data/ └── README.md ``` **推荐结构?* ``` 05-测试文档/ ├── 01-测试计划.md ├── 02-标题摘要初筛测试用例.md └── 03-测试数据/ ├── README.md ?重要!说明测试数据来源、版权、使用方? ├── screening/ ? ├── literature-list-199.xlsx ? ├── picos-criteria.txt ? ├── inclusion-criteria.txt ? ├── exclusion-criteria.txt ? └── gold-standard.json ?人工标注的正确答? └── pdf-extraction/ ├── sample-01-high-quality.pdf ├── sample-02-with-tables.pdf └── sample-03-chinese.pdf ``` **README.md 示例?* ```markdown # ASL 测试数据? ## 📋 数据说明 ### 1. 标题摘要初筛测试数据 - **文件**: `literature-list-199.xlsx` - **数量**: 199 篇英文医学文? - **字段**: 标题、摘要、DOI、作者、年份、期? - **来源**: [描述数据来源] - **版权**: [说明版权信息] ### 2. PICOS 标准 - **文件**: `picos-criteria.txt` - **内容**: Population, Intervention, Comparison, Outcome, Study Design - **纳入标准**: 5 ? - **排除标准**: 8 ? ### 3. 金标准(人工标注结果? - **文件**: `gold-standard.json` - **标注?*: [标注专家信息] - **标注时间**: [时间] - **预期准确?*: ?90% ## 🎯 使用方法 ### 运行测试 ```bash npm run test:asl:screening ``` ### 评估准确? ```bash npm run test:asl:evaluate -- --gold-standard gold-standard.json ``` ## 📊 预期结果 - 纳入: 45 ? - 排除: 132 ? - 不确? 22 ? ``` --- ## 📚 相关文档 - [质量保障与可追溯策略](./06-质量保障与可追溯策略.md) - [数据库设计](./01-数据库设?md) - [API 设计规范](./02-API设计规范.md) - [文档提取微服务](../../../../extraction_service/README.md) --- **更新日志**? - 2025-11-15: 创建文档,定义初筛、全文处理、文献下载技术选型