## 功能特性 - 建立统一的项目结构,包含 core/、readers/、utils/、tests/ 模块 - 迁移 lyxy-reader-office 的所有解析器(docx、xlsx、pptx、pdf) - 迁移 lyxy-reader-html 的所有解析器(html、url 下载) - 统一 CLI 入口为 lyxy_document_reader.py - 统一 Markdown 后处理逻辑 - 按文件类型组织 readers,每个解析器独立文件 - 依赖分组按文件类型细分(docx、xlsx、pptx、pdf、html、http) - PDF OCR 解析器优先,无参数控制 - 使用 logging 模块替代简单 print - 设计完整的单元测试结构 - 重写项目文档 ## 新增目录/文件 - core/ - 核心模块(异常体系、Markdown 工具、解析调度器) - readers/ - 格式阅读器(base.py + docx/xlsx/pptx/pdf/html) - utils/ - 工具函数(文件类型检测) - tests/ - 测试(conftest.py + test_core/ + test_readers/ + test_utils/) - lyxy_document_reader.py - 统一 CLI 入口 ## 依赖分组 - docx - DOCX 文档解析支持 - xlsx - XLSX 文档解析支持 - pptx - PPTX 文档解析支持 - pdf - PDF 文档解析支持(含 OCR) - html - HTML/URL 解析支持 - http - HTTP/URL 下载支持 - office - Office 格式组合(docx/xlsx/pptx/pdf) - web - Web 格式组合(html/http) - full - 完整功能 - dev - 开发依赖
261 lines
7.9 KiB
Markdown
261 lines
7.9 KiB
Markdown
## Context
|
||
|
||
当前存在两个独立的文档解析 skill:lyxy-reader-office(支持 docx/xlsx/pptx/pdf)和 lyxy-reader-html(支持 html/url)。两个项目存在大量重复代码(common.py 中的 Markdown 处理函数完全相同),维护成本高,且无法统一扩展。
|
||
|
||
本项目 lyxy-document 是一个新的 Python 项目,用于统一这两个 skill 的能力。项目当前只有基础结构,需要完整迁移两个 skill 的核心功能。
|
||
|
||
项目规范约束:
|
||
- 语言:仅中文(代码注释、文档、交流)
|
||
- Python:始终用 uv 运行
|
||
- 依赖:pyproject.toml 声明,使用 uv 安装
|
||
- 模块文件:150-300 行
|
||
- 错误:需自定义异常 + 清晰信息 + 位置上下文
|
||
- 测试:所有需求必须设计全面测试
|
||
|
||
## Goals / Non-Goals
|
||
|
||
**Goals:**
|
||
- 建立统一的项目结构,合并两个 skill 的能力
|
||
- 提供一致的 CLI 入口 lyxy_document_reader.py
|
||
- 按文件类型组织 readers,每个解析器独立文件
|
||
- 建立自定义异常体系
|
||
- 使用 logging 模块替代简单 print
|
||
- 迁移所有解析器,保持原有功能和解析器优先级
|
||
- PDF OCR 优先,无参数控制
|
||
- 按文件类型细分依赖分组
|
||
- 设计完整的测试覆盖
|
||
|
||
**Non-Goals:**
|
||
- 保持原有两个 skill 的兼容入口(不需要)
|
||
- 保留 --high-res 参数(取消)
|
||
- 重写或优化解析器逻辑(仅迁移,保持原样)
|
||
- 实现新的解析器(仅迁移现有)
|
||
- 迁移原有 references 文档(文档重写)
|
||
|
||
## Decisions
|
||
|
||
### 1. 目录结构
|
||
|
||
**决定:** 扁平化目录结构,不使用 src/lyxy_document/ 双层目录。
|
||
|
||
```
|
||
lyxy-document/
|
||
├── lyxy_document_reader.py # 统一 CLI 入口
|
||
├── core/ # 核心模块
|
||
├── readers/ # 格式阅读器
|
||
├── utils/ # 工具函数
|
||
└── tests/ # 测试
|
||
```
|
||
|
||
**替代方案:**
|
||
- src/lyxy_document/ 双层包结构(更标准的 Python 包结构)
|
||
- 所有文件平铺在根目录(过于混乱)
|
||
|
||
**理由:** 用户明确要求减少目录层级,扁平化结构更简单直接,符合当前项目的规模。
|
||
|
||
---
|
||
|
||
### 2. Reader 注册机制
|
||
|
||
**决定:** 显式导入注册,不使用动态发现。
|
||
|
||
```python
|
||
# readers/__init__.py
|
||
from .base import BaseReader
|
||
from .docx import DocxReader
|
||
from .xlsx import XlsxReader
|
||
from .pptx import PptxReader
|
||
from .pdf import PdfReader
|
||
from .html import HtmlReader
|
||
|
||
READERS = [
|
||
DocxReader,
|
||
XlsxReader,
|
||
PptxReader,
|
||
PdfReader,
|
||
HtmlReader,
|
||
]
|
||
```
|
||
|
||
**替代方案:**
|
||
- 通过文件名匹配自动发现(*_reader.py 自动导入)
|
||
- 入口点(entry points)插件机制
|
||
|
||
**理由:** 用户明确要求显式导入,不考虑动态发现。显式导入更清晰、更可预测,符合项目"未上线、无用户"的阶段特点。
|
||
|
||
---
|
||
|
||
### 3. 解析器组织
|
||
|
||
**决定:** 每个解析器独立文件,按文件类型分目录组织。
|
||
|
||
```
|
||
readers/
|
||
├── docx/
|
||
│ ├── docling.py
|
||
│ ├── unstructured.py
|
||
│ ├── markitdown.py
|
||
│ ├── pypandoc.py
|
||
│ ├── python_docx.py
|
||
│ └── native_xml.py
|
||
├── xlsx/
|
||
│ └── ...
|
||
├── pptx/
|
||
│ └── ...
|
||
├── pdf/
|
||
│ ├── docling_ocr.py
|
||
│ ├── unstructured_ocr.py
|
||
│ ├── docling.py
|
||
│ ├── unstructured.py
|
||
│ ├── markitdown.py
|
||
│ └── pypdf.py
|
||
└── html/
|
||
├── downloader.py # 不拆分
|
||
├── cleaner.py
|
||
├── trafilatura.py
|
||
├── domscribe.py
|
||
├── markitdown.py
|
||
└── html2text.py
|
||
```
|
||
|
||
**替代方案:**
|
||
- 所有解析器在同一文件(原有方式,文件太大)
|
||
- 按解析库类型分目录(不如按文件类型直观)
|
||
|
||
**理由:** 用户明确要求每个解析器拆分到单个脚本,方便对单个解析器进行优化。按文件类型分目录符合逻辑,下载器和清理器作为 html reader 的辅助模块,不拆分。
|
||
|
||
---
|
||
|
||
### 4. 异常体系
|
||
|
||
**决定:** 自定义异常继承自基异常 LyxyDocumentError。
|
||
|
||
```python
|
||
# core/exceptions.py
|
||
class LyxyDocumentError(Exception):
|
||
"""文档处理基异常"""
|
||
pass
|
||
|
||
class FileDetectionError(LyxyDocumentError):
|
||
"""文件类型检测失败"""
|
||
pass
|
||
|
||
class ReaderNotFoundError(LyxyDocumentError):
|
||
"""未找到适配的阅读器"""
|
||
pass
|
||
|
||
class ParseError(LyxyDocumentError):
|
||
"""解析失败"""
|
||
pass
|
||
|
||
class DownloadError(LyxyDocumentError):
|
||
"""下载失败"""
|
||
pass
|
||
```
|
||
|
||
**替代方案:**
|
||
- 直接使用内置异常(不够清晰)
|
||
- 更细粒度的异常(过度设计)
|
||
|
||
**理由:** 符合项目规范"错误需自定义异常 + 清晰信息 + 位置上下文",5 个异常覆盖主要场景。
|
||
|
||
---
|
||
|
||
### 5. 依赖分组
|
||
|
||
**决定:** 按文件类型细分依赖分组。
|
||
|
||
```toml
|
||
[project.optional-dependencies]
|
||
docx = [...]
|
||
xlsx = [...]
|
||
pptx = [...]
|
||
pdf = [...]
|
||
html = [...]
|
||
http = [...]
|
||
office = ["lyxy-document[docx,xlsx,pptx,pdf]"]
|
||
web = ["lyxy-document[html,http]"]
|
||
full = ["lyxy-document[office,web]"]
|
||
dev = [...]
|
||
```
|
||
|
||
**替代方案:**
|
||
- 按原有 skill 分组(office/html,不够细)
|
||
- 所有依赖在 dependencies(太重)
|
||
|
||
**理由:** 用户明确要求分组更细,按文件类型分组让用户可以按需安装,组合分组(office/web/full)提供便利性。
|
||
|
||
---
|
||
|
||
### 6. PDF OCR 策略
|
||
|
||
**决定:** OCR 解析器加入解析器链,优先级最高,无参数控制。
|
||
|
||
```python
|
||
# readers/pdf/__init__.py
|
||
PARSERS = [
|
||
("docling OCR", docling_ocr.parse),
|
||
("unstructured OCR", unstructured_ocr.parse),
|
||
("docling", docling.parse),
|
||
("unstructured", unstructured.parse),
|
||
("MarkItDown", markitdown.parse),
|
||
("pypdf", pypdf.parse),
|
||
]
|
||
```
|
||
|
||
**替代方案:**
|
||
- 保留 --high-res 参数(用户要求取消)
|
||
- OCR 单独接口(不必要)
|
||
- 后续智能决策(留待未来)
|
||
|
||
**理由:** 用户明确要求取消 --high-res 参数,OCR 与非 OCR 同级,OCR 优先。后续可考虑更智能的方式(如先判断是否需要 OCR)。
|
||
|
||
---
|
||
|
||
### 7. 模块文件行数
|
||
|
||
**决定:** 严格控制单文件行数在 150-300 行。
|
||
|
||
**替代方案:**
|
||
- 不限制行数(可能导致过大文件)
|
||
- 更严格限制(过度拆分)
|
||
|
||
**理由:** 符合项目规范"模块文件 150-300 行"。原有单个 parser.py 文件过大,通过拆分为独立解析器文件来满足要求。
|
||
|
||
---
|
||
|
||
### 8. 临时文件
|
||
|
||
**决定:** 正式代码使用系统临时目录(tempfile 标准库),项目 temp/ 目录仅用于开发过程中的临时文档。
|
||
|
||
**替代方案:**
|
||
- 所有临时文件放项目 temp/(用户明确表示不需要)
|
||
|
||
**理由:** 用户明确说明 temp/ 目录是开发过程中大模型或 AI 创建与代码无关的文档时使用,正式代码不使用这个目录。
|
||
|
||
## Risks / Trade-offs
|
||
|
||
| 风险 | 影响 | 缓解措施 |
|
||
|-----|------|---------|
|
||
| 解析器拆分为独立文件后,代码复用需仔细设计 | 中 | 通用工具函数(如 parse_with_markitdown、parse_with_docling)放在 core/markdown.py 或 utils/ 中复用 |
|
||
| PDF OCR 优先可能导致非 OCR 文档解析变慢 | 中 | 后续可考虑增加智能判断(检测文档是否包含文本层),当前按用户要求保持简单 |
|
||
| 依赖分组过细可能导致用户困惑 | 低 | 提供 office/web/full 组合分组,文档中说明清楚 |
|
||
| 没有向后兼容,原有 skill 调用会失效 | 无 | 用户明确表示不需要兼容,项目阶段"未上线、无用户" |
|
||
| 缺少 --high-res 参数,用户无法控制是否使用 OCR | 低 | 用户明确要求取消,后续可考虑更智能的方式 |
|
||
|
||
## Migration Plan
|
||
|
||
本项目是新项目,无现有用户,无需迁移。
|
||
|
||
实施步骤(详见 tasks.md):
|
||
1. 搭建核心模块(core/)
|
||
2. 搭建基础架构(readers/base.py、utils/)
|
||
3. 迁移各个 reader(docx/xlsx/pptx/pdf/html)
|
||
4. 实现统一 CLI 入口
|
||
5. 编写测试
|
||
6. 更新文档
|
||
|
||
## Open Questions
|
||
|
||
无。所有决策已与用户确认。
|