基于大模型的软件工程文档结构化提取系统
推荐使用
.venv环境。
DocStruct 当前聚焦于软件工程文档的结构化提取与离线评测。系统不包含知识库问答、向量检索、URL 导入、模型切换和前端联动能力。
- 支持
PDF、DOCX、MD、TXT文件上传 - 支持 5 类主干软件工程文档:
srs、api、hld、tc、dbdd unknown类型只保留解析后的原文、摘要与 IR,不执行结构化抽取- 解析结果同时保存
raw_text、summary和document_ir - 抽取结果统一输出五类主干对象和证据回溯
- 前端支持 PDF/DOCX 原文与结构化结果左右对照,并可点击提取项定位 bbox 证据
- 支持从文档前部确定性提取版本号等元数据
- 提供离线评测脚本,便于论文实验复现
DocStruct 不做通用 JSON 生成器,而是面向软件工程文档建立稳定抽取链路:
PDF / DOCX / MD / TXT
↓
Parser → Document IR
↓
LLM Summary
↓
Section-aware Chunking
↓
Map:逐块局部抽取
↓
Finalize:LLM 合并分块候选(失败则降级跳过)
↓
Reduce:确定性合并、去重、全局 ID
↓
Evidence Binding
↓
Final JSON
核心思想:
Document IR保存标题、段落、表格、页码、bbox、章节路径和阅读顺序summary在解析后由 LLM 基于文档内容和大纲生成,并作为分块抽取的全局上下文ExtractionContract控制每类文档抽什么,不使用任意动态 Schema- LLM 只负责 chunk 内局部语义抽取和候选合并,Reduce 尽量使用确定性逻辑
- Finalizer 在 Map 之后用一次额外 LLM 调用合并分块候选,失败时自动降级跳过
- 术语表、参考资料、附录等章节在分块时自动跳过
- finalizer 只合并分块候选,不再读取完整原文证据片段,避免退化为全文一次性抽取
- 每个对象通过 1-3 个
evidence_element_ids锚点绑定到原文元素,最终生成evidence - 前端通过
evidence.object_id/page/bbox将结构化对象映射回 PDF 页面证据 - Schema 只保留高价值事实字段,避免用派生分组或兜底字段稀释结果
- 系统仅面向中短文档,默认上限为
100000字符 - 超过上限的文档直接拒绝处理
- 当前后端只服务结构化提取主流程
- 知识库问答、向量库和跨文档长期记忆后续应作为独立模块设计
| 文档类型 | doc_type |
重点抽取内容 |
|---|---|---|
| 软件需求规格说明书 | srs |
角色、模块、需求、接口;需求内包含验收标准 |
| API 接口文档 | api |
接口、endpoint 元数据、请求响应产物 |
| 概要设计说明书 | hld |
架构风格、技术栈、模块职责 |
| 测试用例文档 | tc |
测试范围、测试用例、测试步骤 |
| 数据库设计文档 | dbdd |
数据库、表、字段定义 |
| 未知类型 | unknown |
仅保留基础元信息、原文、摘要和 IR |
class SrsExtractedDocument(SrsExtraction, BaseExtractedDocument):
doc_type: Literal[“srs”] = Field(default=”srs”)
system_name: str = Field(default=””)
target_users: list[str] = Field(default_factory=list)
functional_requirements: list[FunctionalReqItem] = Field(default_factory=list)
non_functional_requirements: list[NonFunctionalReqItem] = Field(default_factory=list)
business_flows: list[BusinessFlowItem] = Field(default_factory=list)
evidence: list[Evidence] = Field(default_factory=list)- 每类文档使用独立 typed schema:SRS 使用
functional_requirements/non_functional_requirements/business_flows,API 使用apis,HLD 使用modules/core_flows/design_decisions,TC 使用test_cases,DBDD 使用tables id是系统生成的稳定对象 ID,前缀映射如下:
| 槽位 | 前缀 | 示例 |
|---|---|---|
functional_requirements |
FREQ |
FREQ-001 |
non_functional_requirements |
NFR |
NFR-001 |
business_flows |
BFL |
BFL-001 |
apis |
APIS |
APIS-001 |
modules |
MOD |
MOD-001 |
core_flows |
CFL |
CFL-001 |
design_decisions |
DEC |
DEC-001 |
test_cases |
TC |
TC-001 |
tables |
TBL |
TBL-001 |
- 原文编号可作为
name后缀保留 - SRS 的验收标准不作为独立需求输出,局部验收条目写入对应功能需求的
criteria evidence保存对象到DocumentElement的回溯信息evidence_element_ids是少量定位锚点,不要求覆盖对象的每个字段或明细evidence不包含独立编号或章节路径;定位依赖object_id、element_id、page、bbox、text_span
pip install -r requirements.txt根目录创建 .env:
LLM_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
LLM_API_KEY=your_api_key
LLM_MODEL=deepseek-v4-flash
EXTRACTION_CHUNK_MAX_CHARS=5000
EXTRACTION_MAX_CHARS=100000
EXTRACTION_CONCURRENCY=3
PARSER_BACKEND=basic
DOCLING_ENABLE_OCR=false
DOCLING_ENABLE_TABLE_STRUCTURE=true
DOCLING_FORCE_BACKEND_TEXT=true| 配置项 | 说明 | 默认值 |
|---|---|---|
LLM_API_KEY |
大模型调用鉴权(回退 DASHSCOPE_API_KEY) |
无 |
LLM_BASE_URL |
OpenAI-Compatible 接口地址 | 无 |
LLM_MODEL |
结构化提取模型 | deepseek-v4-flash |
UPLOAD_DIR |
上传文件目录 | db/uploads |
DB_PATH |
SQLite 路径 | db/db.sqlite3 |
EXTRACTION_CHUNK_MAX_CHARS |
IR chunk 目标大小 | 5000 |
EXTRACTION_MAX_CHARS |
文档最大字符数上限 | 100000 |
EXTRACTION_THRESHOLD |
触发抽取的最小字符阈值 | 6000 |
EXTRACTION_CONCURRENCY |
分块并发抽取数 | 3 |
PARSER_BACKEND |
解析后端,当前默认 basic |
basic |
DOCLING_ENABLE_OCR |
Docling OCR 开关 | false |
DOCLING_ENABLE_TABLE_STRUCTURE |
Docling 表格结构识别开关 | true |
DOCLING_FORCE_BACKEND_TEXT |
Docling 优先使用 PDF 原生文本层 | true |
EXTRACTION_CHUNK_OVERLAP_CHARS |
相邻分块重叠字符数 | 300 |
LLM_MAX_TOKENS |
LLM 最大输出 token 数 | 16384 |
python main.py服务默认运行在 http://127.0.0.1:8001。
| 层次 | 技术 |
|---|---|
| 后端 | FastAPI + Tortoise-ORM + SQLite |
| 文档解析 | docling、pymupdf4llm、python-docx |
| 结构化抽取 | OpenAI-Compatible API + Pydantic |
| 评测 | Python 脚本 + JSON / Markdown 报告 |
| 前端 | React 19 + TypeScript + Vite + Tailwind CSS v4 + shadcn/ui + CodeMirror + pdfjs-dist + TanStack Query |
DocStruct/
├── main.py
├── core/
│ ├── parser.py
│ ├── parsers/
│ │ ├── __init__.py
│ │ └── docling_parser.py
│ ├── ir.py
│ ├── chunker.py
│ ├── extractor.py
│ ├── reducer.py
│ ├── config.py
│ ├── constants.py
│ ├── utils.py
│ ├── metadata.py
│ ├── schema_registry.py
│ ├── experiment_sdk.py
│ ├── document_service.py
│ └── llm.py
├── schemas/
│ ├── models.py
│ ├── dto.py
│ ├── constants.py
│ ├── extraction.py
│ ├── ir.py
│ └── docs/
│ ├── __init__.py
│ ├── base.py
│ ├── srs.py
│ ├── api.py
│ ├── design.py
│ ├── test.py
│ └── dbdd.py
├── scripts/
│ ├── ci_test.py
│ ├── evaluate.py
│ └── hooks/
├── tests/
│ ├── test_reducer.py
│ ├── test_extractor_prompts.py
│ ├── test_dto.py
│ ├── test_metadata.py
│ └── test_docling_parser.py
├── frontend/ (React + TypeScript + Vite)
├── experiments/ (offline evaluation)
├── static/ (example documents)
└── paper/ (thesis chapters)
文档上传
↓
Parser 生成 Markdown 与 Document IR
↓
LLM 生成 summary
↓
用户指定 doc_type
↓
构建 DocumentOutline 与 ExtractionContract
↓
按章节和元素边界生成 chunk
↓
并发 Map 抽取
↓
Finalize:LLM 合并分块候选(失败则降级跳过)
↓
Reduce 确定性合并、去重、重排全局 ID
↓
Evidence Binding 回填 page / bbox / text_span
↓
保存 extracted_data
raw_text 用于人类预览和修订,summary 用作分块抽取的全局背景,document_ir 是分块与证据绑定的机器可读来源。summary 不作为 evidence 来源,不会进入 allowed_evidence_element_ids。finalizer 只处理分块候选及其已有证据 ID,不重新读取完整 document_ir.elements。
| 方法 | 路径 | 说明 |
|---|---|---|
POST |
/api/upload |
上传文件并执行结构化抽取 |
GET |
/api/documents |
获取文档列表 |
GET |
/api/documents/{doc_id} |
获取文档详情 |
PATCH |
/api/documents/{doc_id} |
人工修订 raw_text、summary 或 extracted_data |
DELETE |
/api/documents/{doc_id} |
删除文档记录与原始文件 |
GET |
/api/documents/{doc_id}/chunks |
返回分块调试数据 |
POST |
/api/documents/{doc_id}/retry-extraction |
重新执行结构化抽取 |
GET |
/api/documents/{doc_id}/file |
下载原始上传文件 |
上传请求要求:
- 表单字段
file - 表单字段
doc_type
| 路径 | 用途 |
|---|---|
db/uploads/ |
上传的原始文件 |
db/db.sqlite3 |
主数据库 |
experiments/results/ |
评测输出结果 |
当前数据库模型使用 title、created_at、raw_text、summary 等字段,不兼容旧版 filename、upload_time、parsed_content 表结构。开发环境如保留旧 SQLite 文件,需要重建数据库或执行单独迁移。
建议至少覆盖以下场景:
- 上传
PDF / DOCX / MD / TXT,确认状态从uploaded/parsing/extracting变为completed或failed - 检查
raw_text是否正常生成 - 检查
summary是否生成,并确认摘要失败不会阻断结构化抽取 - 检查
document_ir是否包含elements、outline、section_path - 检查
extracted_data是否符合五类主干对象和evidence - 对使用 Docling 解析的 PDF,点击前端提取项,确认 PDF 跳转到对应页并高亮 bbox
- 对 basic parser 或非 PDF 文档,确认前端仍可展示文本证据且不会错误绘制 PDF 框
- 上传
unknown类型文档,确认只保留原文和 IR - 上传超长文档,确认返回明确错误
- 修改
raw_text、summary或extracted_data,确认PATCH生效 - 删除文档后确认数据库记录与上传文件一并清理
uv run python scripts/ci_test.py
uv run python scripts/ci_test.py --backend-only
uv run python scripts/ci_test.py --frontend-only后端检查当前包含:
- Python 编译检查
main.py导入与应用实例化检查