# fileparser **Repository Path**: lkp_ksbk/fileparser ## Basic Information - **Project Name**: fileparser - **Description**: python 后端 文件解析服务 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-23 - **Last Updated**: 2026-07-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 文档转换与图片理解服务(v1 / v2) 将 Word / PDF / DOC 文档转换为结构化分块文本。 v2 额外支持 **图片提取 → VLM 四分类 → 专业描述生成 → 占位符替换**,并提供独立的单图分析接口。 --- ## 目录 1. [功能概览](#1-功能概览) 2. [目录结构](#2-目录结构) 3. [服务流程图](#3-服务流程图) 4. [快速开始](#4-快速开始) 5. [接口说明](#5-接口说明) 6. [结果输出说明](#6-结果输出说明) 7. [配置说明(config.yaml)](#7-配置说明configyaml) 8. [图片处理流程(v2)](#8-图片处理流程v2) 9. [PDF 支持说明](#9-pdf-支持说明) 10. [DOC 支持说明](#10-doc-支持说明) 11. [补充说明](#11-补充说明) --- ## 1. 功能概览 | 能力 | v1 | v2 | |------|:--:|:--:| | DOCX 加载(本地路径 / URL) | ✅ | ✅ | | 标题层级提取与分块(`split_level`) | ✅ | ✅ | | 表格转 Markdown | ✅ | ✅ | | JSON 结果输出 | ✅ | ✅ | | PDF 支持(基于 TOC 分块) | — | ✅ | | DOC 支持(自动转 DOCX) | — | ✅ | | 图片提取与占位符插入 | — | ✅ | | VLM 四分类 + 描述生成 | — | ✅ | | 独立单图分析接口(分类 + 描述) | — | ✅ | | 相同图片 Hash 缓存(避免重复调用) | — | ✅ | | 图片分类结果落盘(used / discarded) | — | ✅ | | LLM 调用日志落盘(llm.json) | — | ✅ | | 历史结果自动清理(TTL) | ✅ | ✅ | --- ## 2. 目录结构 ``` workspace/ ├── main.py # FastAPI 服务入口 ├── config.yaml # VLM / LOG / DOC 配置 ├── requirements.txt # Python 依赖 ├── start_server.sh # 启动脚本 ├── start_docker.sh # Docker 启动脚本 ├── src/ │ ├── docx_processor_v1/ # v1 实现 │ │ ├── converter.py │ │ ├── downloader.py │ │ ├── heading_extractor.py │ │ ├── chunk_processor.py │ │ ├── table_processor.py │ │ └── text_processor.py │ ├── docx_processor_v2/ # v2 实现(含图片理解) │ │ ├── converter.py │ │ ├── downloader.py │ │ ├── heading_extractor.py │ │ ├── chunk_processor.py │ │ ├── table_processor.py │ │ ├── text_processor.py │ │ ├── image_processor.py # 图片分类与描述 │ │ ├── pdf_processor.py # PDF 处理 │ │ ├── doc_processor.py # DOC → DOCX 转换 │ │ └── prompt.py # VLM 提示词构建 │ ├── image_service_v2/ # v2 单图分析接口实现 │ │ ├── __init__.py │ │ └── service.py │ └── utils/ │ ├── config.py # YAML 配置加载 │ ├── errors.py # 自定义异常 │ ├── logger.py # 日志工具 │ └── vlm_client.py # VLM 客户端(分类 + 描述) ├── scripts/ │ └── test_convert_api.py # API 测试脚本 ├── docs/ │ ├── service_flow.mmd # 流程图源文件 │ └── service_flow.png # 流程图 ├── result/ # 运行时生成的结果目录 └── log/ # 日志目录(按天切分) ``` --- ## 3. 服务流程图 ![服务流程图](docs/service_flow.png) --- ## 4. 快速开始 ### 4.1 安装依赖 ```bash pip install -r requirements.txt ``` > DOC 转换还需要系统安装 **LibreOffice**(`soffice` 命令可用)。 ### 4.2 启动服务 ```bash # 方式一:直接启动(默认端口 8000) python main.py # 方式二:使用启动脚本(支持 PORT 环境变量) PORT=8000 bash start_server.sh # 方式三:Docker bash start_docker.sh ``` ### 4.3 快速测试 ```bash # 上传文件(v2) python scripts/test_convert_api.py \ --version v2 --file data/files/xxx.docx --split-level 2 --upload # 传本地路径(v2) python scripts/test_convert_api.py \ --version v2 --file /absolute/path/to/file.docx --split-level 2 # 传远程 URL(v1) python scripts/test_convert_api.py \ --version v1 --file-url http://example.com/doc.docx --split-level 1 ``` --- ## 5. 接口说明 ### 5.1 健康检查 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/v1/health` | v1 健康检查 | | GET | `/v2/health` | v2 健康检查 | ```json { "status": "healthy", "version": "v2" } ``` ### 5.2 文档转换 | 方法 | 路径 | 说明 | |------|------|------| | POST | `/v1/convert` | v1 转换(仅 DOCX) | | GET | `/v2/convert` | v2 转换(仅支持 `file_url` 查询参数) | | POST | `/v2/convert` | v2 转换(DOCX / PDF / DOC + 图片理解) | | POST | `/v2/image/analyze` | v2 单图分析(仅上传 `jpg/jpeg/png`,一次完成分类 + 描述) | > **Content-Type**: `multipart/form-data` #### 请求参数 | 参数 | 类型 | 必填 | 说明 | |------|------|:----:|------| | `split_level` | int (Form) | ✅ | 分块层级,≥ 0;`0` = 不分块 | | `file` | UploadFile (File) | 二选一 | 上传文档文件(`.docx` / `.pdf` / `.doc`) | | `file_url` | str (Form) | 二选一 | 文档本地绝对路径或网络 URL | | `file_id` | str (Form) | ❌ | 任务唯一标识,不填则自动生成 | #### 请求示例 **cURL — 上传文件:** ```bash curl -X POST http://localhost:8000/v2/convert \ -F "file=@/path/to/document.docx" \ -F "split_level=2" ``` **cURL — 传路径 / URL:** ```bash curl -X POST http://localhost:8000/v2/convert \ -F "file_url=/absolute/path/to/file.docx" \ -F "split_level=2" \ -F "file_id=your-uuid" ``` **cURL — GET 传路径 / URL:** ```bash curl "http://localhost:8000/v2/convert?file_url=/absolute/path/to/file.docx&split_level=2&file_id=your-uuid" ``` **Python requests — 上传文件:** ```python import requests resp = requests.post( "http://localhost:8000/v2/convert", data={"split_level": 2, "file_id": "your-uuid"}, files={"file": ("doc.docx", open("doc.docx", "rb"))}, ) print(resp.json()) ``` **Python requests — 传路径:** ```python import requests resp = requests.post( "http://localhost:8000/v2/convert", data={"split_level": 2, "file_url": "/absolute/path/to/file.docx"}, ) print(resp.json()) ``` **Python requests — GET 传路径:** ```python import requests resp = requests.get( "http://localhost:8000/v2/convert", params={ "split_level": 2, "file_url": "/absolute/path/to/file.docx", "file_id": "your-uuid", }, ) print(resp.json()) ``` #### 成功响应 > **注意**:`body` 为 JSON 字符串,需要客户端再 `json.loads` 一次。 ```json { "status_code": 200, "body": "{\"code\":200,\"message\":\"completed\",\"data\":[{\"path\":[\"一级标题\",\"二级标题\"],\"content\":\"...\"}]}" } ``` | 字段 | 说明 | |------|------| | `status_code` | HTTP 状态码 | | `body` | JSON 字符串,结构见下 | `body` 解析后结构: ```json { "code": 200, "message": "completed", "data": [ { "path": ["一级标题", "二级标题"], "content": "..." } ] } ``` | 字段 | 说明 | |------|------| | `code` | 业务状态码 | | `message` | 结果信息 | | `data` | 分块数组,每项含 `path` 和 `content`(v2 已替换图片描述) | #### 错误响应 ```json { "status_code": 400, "body": "{\"code\":400,\"message\":\"不支持的文件格式\",\"data\":null}" } ``` | 状态码 | 场景 | |--------|------| | 400 | 未提供 `file` 或 `file_url` | | 400 | 文件格式不支持 | | 400 | DOC 转换失败 | | 500 | 其他内部异常 | ### 5.3 单图分析 | 方法 | 路径 | 说明 | |------|------|------| | POST | `/v2/image/analyze` | 上传单张图片,返回分类结果与描述结果 | > **Content-Type**: `multipart/form-data` #### 请求参数 | 参数 | 类型 | 必填 | 说明 | |------|------|:----:|------| | `file` | UploadFile (File) | ✅ | 上传图片文件,仅支持 `.jpg` / `.jpeg` / `.png` | | `file_id` | str (Form) | ❌ | 请求唯一标识,不填则自动生成 | #### 请求示例 **cURL:** ```bash curl -X POST http://localhost:8000/v2/image/analyze \ -F "file=@/path/to/demo.png" \ -F "file_id=image-test-001" ``` **Python requests:** ```python import requests resp = requests.post( "http://localhost:8000/v2/image/analyze", data={"file_id": "image-test-001"}, files={"file": ("demo.png", open("demo.png", "rb"), "image/png")}, ) print(resp.json()) ``` #### 成功响应 ```json { "status_code": 200, "body": "{\"code\":200,\"message\":\"completed\",\"data\":{\"request_id\":\"image-test-001\",\"file_name\":\"demo.png\",\"image_path\":\"result/20260325093000_image-test-001/image/source.png\",\"category_id\":3,\"category_name\":\"技术方案/架构设计类\",\"category_description\":\"技术方案、系统架构图、流程图、组件/模块关系、部署拓扑\",\"discarded\":false,\"description\":\"系统架构图\\n1 应用层\\n - APP等渠道端\\n\\n流转关系:\\n1 → 2\"}}" } ``` `body` 解析后结构: ```json { "code": 200, "message": "completed", "data": { "request_id": "image-test-001", "file_name": "demo.png", "image_path": "result/20260325093000_image-test-001/image/source.png", "category_id": 3, "category_name": "技术方案/架构设计类", "category_description": "技术方案、系统架构图、流程图、组件/模块关系、部署拓扑", "discarded": false, "description": "..." } } ``` | 字段 | 说明 | |------|------| | `request_id` | 请求唯一标识 | | `file_name` | 上传的原始文件名 | | `image_path` | 结果目录中的原图保存路径 | | `category_id` | 分类 ID,来源于 `config.yaml` 的 `VLM.categories` | | `category_name` | 分类名称 | | `category_description` | 分类说明 | | `discarded` | 是否命中 `discard_category_ids` | | `description` | 图片描述;若命中丢弃类则为空字符串 | #### 错误响应 ```json { "status_code": 400, "body": "{\"code\":400,\"message\":\"不支持的图片格式:仅支持 .jpg / .jpeg / .png\",\"data\":null}" } ``` | 状态码 | 场景 | |--------|------| | 400 | 未上传 `file` | | 400 | 图片格式不支持 | | 500 | 图片分类失败 | | 500 | 图片描述失败 | ### 5.4 兼容旧路径(已弃用) `GET /`、`GET /health`、`POST /convert` 仍可访问,等同于 v1 接口,标记为 deprecated。 --- ## 6. 结果输出说明 所有输出落在: ``` result/{YYYYMMDDHHMMSS}_{file_id}/outline/ ``` 单图分析接口输出落在: ``` result/{YYYYMMDDHHMMSS}_{file_id}/image/ ``` | 文件 / 目录 | 说明 | |-------------|------| | `chunks/` | 分块纯文本文件(`chunk_0.txt` ...) | | `chunks.json` | 最终分块 JSON(v2 已替换图片描述) | | `chunks_pre.json` | 图片替换前的分块 JSON(含占位符) | | `images/used/` | 分类为保留类且描述成功的图片 | | `images/used/*.txt` | 对应图片的分类与描述文本 | | `images/discarded/` | 分类为丢弃类或描述失败的图片 | | `llm.json` | VLM 请求日志(JSON Lines 格式) | | `image/source.jpg|jpeg|png` | 单图分析接口保存的原始图片 | | `image/result.json` | 单图分析接口最终结果 | | `image/description.txt` | 单图分析接口生成的描述文本(仅非丢弃类) | | `image/llm.json` | 单图分析接口的 VLM 请求日志(JSON Lines 格式) | > 历史结果目录超过 TTL 后会在服务启动时自动清理,默认 **72 小时**,可通过环境变量 `RESULT_TTL_HOURS` 调整。 --- ## 7. 配置说明(config.yaml) ### 7.1 VLM 配置 ```yaml VLM: model: qwen2.5-vl-32b-instruct # 视觉语言模型名称 url: https://dashscope.aliyuncs.com/... # OpenAI 兼容 API 地址 api_key: sk-xxx # API Key timeout: 1800 # 单次请求超时(秒) max_tokens: 8192 # 最大生成 token 数 retry_times: 3 # 网络/HTTP 重试次数(总尝试 = retry_times + 1) temperature: 0.2 # 生成温度 description_max_chars: 100 # 图片描述最大字数 vlm_concurrency: 4 # 图片处理并发数 discard_category_ids: [4] # 需要丢弃的分类 ID 列表 categories: # 分类体系定义(不可为空) - id: 1 name: 产品设计/UI原型类 description: 产品设计说明、UI原型、交互稿、视觉/页面布局 hints: [...] examples: [...] - id: 2 name: 功能需求类 description: 功能需求截图/示意、操作步骤说明、需求图示 hints: [...] examples: [...] - id: 3 name: 技术方案/架构设计类 description: 技术方案、系统架构图、流程图、组件/模块关系、部署拓扑 hints: [...] examples: [...] - id: 4 name: 其他 description: 不属于以上类别 ``` > `categories` 为空会直接导致服务异常退出,确保分类体系可控。 ### 7.2 日志配置 ```yaml LOG: debug_file: false # 是否生成 debug 级别日志文件(排查问题时改为 true) ``` ### 7.3 DOC 转换配置 ```yaml DOC: enabled: true # 是否启用 DOC 支持 convert_timeout: 1800 # LibreOffice 转换超时(秒) soffice_path: # soffice 路径(留空则自动检测) max_concurrency: 1 # 最大并发转换数(避免 LibreOffice 冲突) ``` --- ## 8. 图片处理流程(v2) v2 对文档中的每张图片执行以下流程: ``` 提取图片 → SHA-256 去重 → VLM 分类(四分类) ├── 类别 1/2/3 → VLM 描述生成 → 替换占位符 └── 类别 4(其他)→ 丢弃占位符 ``` **关键机制:** - **重试与退避**:每次 VLM 请求最多尝试 `retry_times + 1` 次,失败后按指数退避等待(1s → 2s → 4s → 5s 上限)。 - **Hash 缓存**:相同 SHA-256 的图片复用已有分类 / 描述结果,避免重复调用。 - **并发控制**:`vlm_concurrency` 控制并行处理的图片数量,降低 API 限流风险。 - **分类重试**:分类结果解析失败时,会重新请求 VLM(最多 `retry_times + 1` 次),兜底从文本中提取数字。 - **结果归档**:保留类图片移至 `images/used/`,丢弃类移至 `images/discarded/`,并为保留类生成 `.txt` 描述文件。 **图片描述替换格式:** ``` <图片1描述>xxx<图片1描述结束> ``` 编号按 **图片在最终内容中出现的顺序** 自动生成。 --- ## 9. PDF 支持说明 - **标题解析**:仅依赖 PDF 自带 TOC(目录),无 TOC 则无法分块。 - **表格解析**:依赖 Camelot,对扫描件 / 图片型 PDF 支持有限。 - **图片定位**:图片与文本位置关系难以精确还原,当前按页插入占位符。 - **文本顺序**:PDF 为版式格式,导出文本可能出现换行 / 顺序偏差。 --- ## 10. DOC 支持说明 - `.doc` 会先通过 LibreOffice 转换为 `.docx`,再按 DOCX 流程处理。 - 需要系统安装 LibreOffice,路径可在 `config.yaml` 的 `DOC.soffice_path` 中指定。 - 转换后的 `source.doc` / `source.docx` 保存在结果目录,便于排查。 - 支持 `max_concurrency` 限制同时转换数量,并为每次转换创建独立 LibreOffice Profile 避免冲突。 --- ## 11. 补充说明 - v1 / v2 并行共存。 - 日志按天切分,输出在 `log/` 目录。 - 如需扩展分类类别、调整提示词或描述格式,请修改 `config.yaml` 或 `src/docx_processor_v2/prompt.py`。