## 功能特性 - 建立统一的项目结构,包含 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 - 开发依赖
7.9 KiB
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 注册机制
决定: 显式导入注册,不使用动态发现。
# 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。
# 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. 依赖分组
决定: 按文件类型细分依赖分组。
[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 解析器加入解析器链,优先级最高,无参数控制。
# 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):
- 搭建核心模块(core/)
- 搭建基础架构(readers/base.py、utils/)
- 迁移各个 reader(docx/xlsx/pptx/pdf/html)
- 实现统一 CLI 入口
- 编写测试
- 更新文档
Open Questions
无。所有决策已与用户确认。