## 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 无。所有决策已与用户确认。