8 Коміти 24f1406736 ... d5620857e3

Автор SHA1 Опис Дата
  zhch158_admin d5620857e3 feat(新增本地银行流水处理器配置): 添加bank_statement_yusys_local配置以支持本地GLM-OCR处理 2 тижнів тому
  zhch158_admin cc7098b12b feat(增强调试功能): 更新流水线初始化和配置,支持多种调试模式覆盖配置文件设置 2 тижнів тому
  zhch158_admin d196e091a2 feat(新增银行交易流水场景配置): 添加bank_statement_yusys_local.yaml以支持银行流水OCR处理 2 тижнів тому
  zhch158_admin 9b8ee548e6 feat(新增本地OCR处理器配置): 添加yusys_ocr_local处理器配置以支持银行对账单场景 2 тижнів тому
  zhch158_admin 04a2536ac9 feat(新增本地PDF文件列表): 添加本地PDF文件列表以支持银行对账单和财务报告场景 2 тижнів тому
  zhch158_admin 5cdf258ef7 Add local daemon scripts for GLM-OCR and PaddleOCR-VL with image processing capabilities 2 тижнів тому
  zhch158_admin bf82a94c6f feat(添加llama.cpp配置说明文档): 新增文档以指导在Mac M4 Pro上使用llama-server加载本地GGUF模型进行OCR识别 2 тижнів тому
  zhch158_admin 13f0515435 feat(debug参数控制): 更新文档以反映新的调试参数设计,支持全局和模块级调试控制 2 тижнів тому
30 змінених файлів з 2369 додано та 131 видалено
  1. 262 63
      docs/ocr_tools/universal_doc_parser/debug参数控制.md
  2. 605 0
      docs/ocr_tools/universal_doc_parser/llama.cpp配置说明.md
  3. 210 2
      ocr_tools/daemons/README.md
  4. 1 0
      ocr_tools/daemons/curl_local_img.png
  5. 18 0
      ocr_tools/daemons/curl_local_ocr.sh
  6. 381 0
      ocr_tools/daemons/glmocr_local_daemon.sh
  7. 378 0
      ocr_tools/daemons/paddle_local_daemon.sh
  8. 22 0
      ocr_tools/daemons/payload.json
  9. 7 0
      ocr_tools/ocr_batch/pdf_list_local.txt
  10. 16 0
      ocr_tools/ocr_batch/processor_configs.yaml
  11. 200 0
      ocr_tools/universal_doc_parser/config/bank_statement_yusys_local.yaml
  12. 2 2
      ocr_tools/universal_doc_parser/core/layout_model_router.py
  13. 10 8
      ocr_tools/universal_doc_parser/core/pipeline_manager_v2.py
  14. 4 3
      ocr_tools/universal_doc_parser/core/pipeline_manager_v2_streaming.py
  15. 159 16
      ocr_tools/universal_doc_parser/main_v2.py
  16. 4 4
      ocr_validator/config/A用户_单元格扫描流水.yaml
  17. 7 0
      ocr_validator/config/B用户_扫描流水.yaml
  18. 6 6
      ocr_validator/config/乔_建设银行图.yaml
  19. 7 0
      ocr_validator/config/付_工商银行943825图.yaml
  20. 7 7
      ocr_validator/config/对公_招商银行图.yaml
  21. 7 0
      ocr_validator/config/山西云集科技有限公司.yaml
  22. 7 0
      ocr_validator/config/康强_北京农村商业银行.yaml
  23. 7 0
      ocr_validator/config/张_微信图.yaml
  24. 4 4
      ocr_validator/config/德_内蒙古银行照.yaml
  25. 7 0
      ocr_validator/config/提取自赤峰黄金2023年报.yaml
  26. 7 0
      ocr_validator/config/施博深.yaml
  27. 6 6
      ocr_validator/config/朱_中信银行图.yaml
  28. 7 6
      ocr_validator/config/湛_平安银行图.yaml
  29. 4 4
      ocr_validator/config/至远彩色_2023年报.yaml
  30. 7 0
      ocr_validator/config/许_民生银行图.yaml

+ 262 - 63
docs/ocr_tools/universal_doc_parser/debug参数控制.md

@@ -1,109 +1,308 @@
-## 当前问题诊断
+# Debug 参数控制设计文档 v2
 
-**共 5 处断裂点:**
+## 当前实现方案(v2 - 分模块控制)
 
-| # | 问题 | 位置 |
-|---|---|---|
-| 1 | `--debug` CLI 参数**从未传进** `pipeline.debug_mode` | main_v2.py:yaml 有 output 块时,`debug` 参数被完全丢弃 |
-| 2 | `_process_all_elements` 调用时**没有穿透** `debug_mode` | pipeline_manager_v2.py:element_processors 看不到 pipeline 层的 debug 状态 |
-| 3 | `element_processors` 构建子模块 `debug_options` 时**漏掉了 `enabled`** | 只注入了 `output_dir`/`prefix`,子模块即使拿到路径也不会输出 |
-| 4 | 新旧两套系统(`debug_mode` vs `debug_options.enabled`)**无统一激活入口** | base.py 做了兼容读取但没有统一写入 |
-| 5 | streaming 模式**完全不传播** `debug_mode` | `pipeline_manager_v2_streaming.py` |
+**设计理念**:提供全局和精细两种控制方式,命令行参数直接覆盖配置文件设置。
 
----
+### 控制层级
+
+```
+命令行参数(4个独立开关)
+    ↓ 调用
+_apply_debug_overrides() 函数
+    ↓ 直接修改
+pipeline.config['xxx']['debug_options']['enabled'] = True
+    ↓ 各模块读取
+layout_detector / table_classifier / wired_table 等使用各自的 debug_options
+```
 
-## 建议设计:单一开关 → 逐层传播
+---
 
-**核心原则**:命令行 `--debug` 是唯一的顶层开关,向下覆盖 yaml 配置。
+## 命令行参数设计
 
-### 第一层:main_v2.py — 写入 pipeline.debug_mode
+### 新增 4 个 debug 参数
 
 ```python
-# process_single_input 中,创建 pipeline 后立即覆盖 debug_mode
-pipeline = _create_pipeline(...)
+parser.add_argument("--debug", action="store_true",
+    help="开启全局debug模式(启用所有模块的调试输出)")
 
-# 命令行 --debug 优先级高于 yaml
-if debug:
-    pipeline.debug_mode = True
-    # 同步覆盖 output 配置
-    pipeline.config.setdefault('output', {})['debug_mode'] = True
+parser.add_argument("--debug-layout", action="store_true",
+    help="仅开启布局检测的debug输出")
+
+parser.add_argument("--debug-table", action="store_true",
+    help="仅开启表格识别的debug输出")
+
+parser.add_argument("--debug-ocr", action="store_true",
+    help="仅开启OCR识别的debug输出")
+```
+
+### 使用示例
+
+```bash
+# 1. 全局 debug(开发阶段完整调试)
+python main_v2.py -i test.pdf -c config.yaml --debug
+
+# 2. 定位布局问题(只看 layout 输出)
+python main_v2.py -i test.pdf -c config.yaml --debug-layout
+
+# 3. 定位表格问题(只看 table 输出)
+python main_v2.py -i test.pdf -c config.yaml --debug-table
+
+# 4. 组合使用(layout + table,不包括OCR)
+python main_v2.py -i test.pdf -c config.yaml --debug-layout --debug-table
+
+# 5. 正常运行(无 debug 输出)
+python main_v2.py -i test.pdf -c config.yaml
 ```
 
-### 第二层:pipeline_manager_v2.py — 向子模块传播
+---
+
+## 核心实现:`_apply_debug_overrides()` 函数
 
-在 `_process_all_elements` 调用处加入 debug 参数传递(pipeline → element_processors):
+### 函数签名
 
 ```python
-processed_elements, discarded_elements = self._process_all_elements(
-    ...
-    debug_mode=self.debug_mode,   # ← 补充这一项
-    output_dir=output_dir,
-)
+def _apply_debug_overrides(
+    pipeline,
+    debug: bool,
+    debug_layout: bool,
+    debug_table: bool,
+    debug_ocr: bool,
+    output_config: dict
+):
 ```
 
-### 第三层:element_processors.py — 构建 debug_options 时加上 enabled
+### 覆盖逻辑
 
 ```python
-# 当前(缺失 enabled)
-debug_opts_override = {'output_dir': output_dir, 'prefix': basename}
+# 1. 确定需要启用哪些模块的 debug
+enable_layout_debug = debug or debug_layout
+enable_table_debug = debug or debug_table
+enable_ocr_debug = debug or debug_ocr
 
-# 修正后
-debug_opts_override = {
-    'enabled': debug_mode,        # ← 缺失的这一项
-    'output_dir': output_dir,
-    'prefix': basename,
-}
+# 2. 更新 pipeline 全局状态
+if debug or debug_layout or debug_table or debug_ocr:
+    pipeline.debug_mode = True
+    output_config['debug_mode'] = True
+
+# 3. 直接修改配置文件中的 debug_options.enabled
+if enable_layout_debug:
+    config['layout_detection']['debug_options']['enabled'] = True
+
+if enable_table_debug:
+    config['table_classification']['debug_options']['enabled'] = True
+    config['table_recognition_wired']['debug_options']['enabled'] = True
+
+if enable_ocr_debug:
+    config['ocr_recognition']['debug_options']['enabled'] = True
 ```
 
-### yaml 配置建议:`debug_options` 应只配置**保存内容**,不配 `enabled`
+### 优先级规则
 
-各子模块的 `debug_options` 只负责描述"调试时保存什么",`enabled` 动态注入:
+| 条件 | 结果 |
+|------|------|
+| 命令行无任何 debug 参数 | 使用配置文件默认值(通常为 false) |
+| 命令行有 `--debug` | 所有模块 debug 启用 |
+| 命令行有 `--debug-layout` | 仅布局检测 debug 启用 |
+| 命令行有 `--debug-table` | 仅表格相关 debug 启用 |
+| 命令行有 `--debug-layout --debug-table` | 布局+表格 debug 启用,OCR 不启用 |
+
+---
+
+## YAML 配置文件规范
+
+### 推荐配置(enabled 默认 false)
+
+各模块的 `debug_options` 只描述"调试时保存什么内容",`enabled` 字段默认为 `false`,由命令行参数动态控制:
 
 ```yaml
-# ✅ 推荐:只描述保存项,enabled 由运行时控制
+# ✅ 推荐:enabled: false,由命令行控制
+layout_detection:
+  debug_options:
+    enabled: false              # 默认关闭,--debug 或 --debug-layout 时启用
+    output_dir: null            # null = 跟随主输出目录
+    prefix: ""                  # 文件名前缀
+
+table_classification:
+  debug_options:
+    enabled: false              # 默认关闭,--debug 或 --debug-table 时启用
+    output_dir: null
+    save_table_lines: true      # 描述保存哪些内容
+    image_format: "png"
+    prefix: ""
+
 table_recognition_wired:
   debug_options:
-    output_dir: null        # null = 随输出目录
+    enabled: false              # 默认关闭,--debug 或 --debug-table 时启用
+    output_dir: null
     save_table_lines: true
+    save_connected_components: true
     save_grid_structure: true
     save_text_overlay: true
+    image_format: "png"
+    prefix: ""
+```
 
-# ❌ 不推荐:hardcode enabled: true,会绕过命令行开关
+### ❌ 不推荐的配置
+
+```yaml
+# ❌ enabled: true 会导致永远开启,无法被命令行关闭
 table_recognition_wired:
   debug_options:
-    enabled: true           # 这会永远开启,无法被命令行关闭
+    enabled: true               # 硬编码为 true,会绕过命令行控制
 ```
 
 ---
 
-## 推荐的完整控制层级
+## 代码修改清单
 
+### 1. main_v2.py(已完成)
+
+**新增参数:**
+```python
+parser.add_argument("--debug", ...)
+parser.add_argument("--debug-layout", ...)
+parser.add_argument("--debug-table", ...)
+parser.add_argument("--debug-ocr", ...)
+```
+
+**新增函数:**
+```python
+def _apply_debug_overrides(pipeline, debug, debug_layout, debug_table, debug_ocr, output_config):
+    # 实现配置覆盖逻辑
 ```
-命令行 --debug
-    ↓ 写入
-pipeline.debug_mode(覆盖 yaml output.debug_mode)
-    ↓ 注入(已有)
-layout_detector.debug_mode
-    ↓ 注入(待补)
-element_processors(debug_mode=...)
-    ↓ 构建(待补 enabled 字段)
-wired_table.recognize(debug_options={'enabled': debug_mode, ...})
-table_classifier.classify(debug_options={'enabled': debug_mode, ...})
+
+**更新调用:**
+```python
+def process_single_input(..., debug, debug_layout, debug_table, debug_ocr, ...):
+    pipeline = _create_pipeline(...)
+    _apply_debug_overrides(pipeline, debug, debug_layout, debug_table, debug_ocr, output_config)
 ```
 
-`--log_level` 与 debug 是**独立维度**,不需要联动,按现有设计保持即可。
+### 2. 配置文件(需要更新)
+
+所有场景配置文件中的 `debug_options.enabled` 改为 `false`:
+- `bank_statement_yusys_v4.yaml`
+- `bank_statement_yusys_v3.yaml`
+- `bank_statement_mineru_vl.yaml`
+- 等其他配置文件...
 
 ---
 
-## 需要修改的文件清单
+## 优势对比
+
+### 新方案优势(v2)
+
+✅ **灵活性高**
+- 全局开关:`--debug` 一键全开
+- 精细控制:`--debug-layout` 定位特定问题
+- 组合使用:可以同时启用多个特定模块
 
-| 文件 | 改动 |
-|---|---|
-| main_v2.py | `--debug` 写入 `pipeline.debug_mode` |
-| pipeline_manager_v2.py | `_process_all_elements` 传入 `debug_mode` |
-| core/element_processors.py | 接收并向子模块传递 `debug_mode`,构建 `debug_options` 时加 `enabled` |
-| pipeline_manager_v2_streaming.py | 同步补充 debug_mode 传播(与 v2 保持一致) |
-| yaml 配置文件 | 各模块 `debug_options.enabled` 改为 `false`(运行时由命令行控制) |
+✅ **实现简洁**
+- 无需逐层传递参数(避免穿透5层架构)
+- 直接修改配置字典(一次性生效)
+- 各模块独立读取(解耦合)
+
+✅ **易于扩展**
+- 新增模块只需在 `_apply_debug_overrides()` 添加一个 if 分支
+- 不需要修改处理流水线的参数传递链路
+
+✅ **用户体验好**
+- 减少不必要的 debug 输出(针对性调试)
+- 加快调试速度(只看关心的模块)
+
+### 旧方案问题(v1)
+
+❌ 需要逐层穿透传递 `debug_mode` 参数:
+```
+main_v2.py → pipeline → _process_all_elements → element_processors → 各子模块
+```
+
+❌ 参数传递链路长,容易在某一层断裂
+
+❌ 只有全局开关,无法精细控制
+
+---
+
+## Debug 输出内容
+
+### 布局检测 debug(--debug-layout)
+
+输出文件:
+- `{basename}_page_{n}_layout.png` - 布局检测框可视化
+
+### 表格识别 debug(--debug-table)
+
+**表格分类**:
+- `{basename}_page_{n}_table_{i}_classification.png` - 有线/无线分类结果
+
+**有线表格识别**:
+- `{basename}_page_{n}_table_{i}_lines.png` - UNet 表格线检测
+- `{basename}_page_{n}_table_{i}_cells.png` - 单元格连通域
+- `{basename}_page_{n}_table_{i}_grid.png` - 逻辑网格结构
+- `{basename}_page_{n}_table_{i}_text_overlay.png` - 文本填充结果
+
+### OCR 识别 debug(--debug-ocr)
+
+输出文件:
+- `{basename}_page_{n}_ocr.png` - OCR 检测框和识别结果
+
+---
+
+## 与日志系统的关系
+
+**Debug 输出** 和 **日志级别** 是两个独立维度:
+
+| 参数 | 控制内容 | 输出位置 |
+|------|---------|---------|
+| `--debug` 系列 | 可视化图片输出 | 文件系统(output_dir) |
+| `--log_level` | 日志消息详细程度 | 控制台 / 日志文件 |
+
+**可以组合使用:**
+```bash
+# 开启详细日志 + 表格 debug 图片
+python main_v2.py -i test.pdf -c config.yaml --log_level DEBUG --debug-table
+```
 
 ---
-## 结语
+
+## 未来扩展建议
+
+### 1. 支持环境变量控制
+
+```bash
+export DEBUG_LAYOUT=1
+export DEBUG_TABLE=1
+python main_v2.py -i test.pdf -c config.yaml
+```
+
+### 2. 支持配置文件全局 debug 开关
+
+```yaml
+global:
+  debug_mode: false         # 全局默认值
+  debug_modules: []         # 或指定 ["layout", "table"]
+```
+
+### 3. 支持 debug 输出到独立目录
+
+```bash
+python main_v2.py -i test.pdf -c config.yaml --debug --debug-dir ./debug_output/
+```
+
+---
+
+## 总结
+
+**当前实现(v2)的核心特点:**
+
+1. ✅ **命令行优先**:`--debug` 参数是唯一的顶层开关
+2. ✅ **直接覆盖**:通过 `_apply_debug_overrides()` 直接修改配置字典
+3. ✅ **精细控制**:支持全局和分模块两种模式
+4. ✅ **配置解耦**:YAML 只描述"保存什么",不控制"是否启用"
+5. ✅ **易于维护**:无需修改处理流水线的参数传递链路
+
+**最佳实践:**
+- 开发阶段:使用 `--debug` 全局调试
+- 生产环境:不加任何 debug 参数
+- 问题定位:使用 `--debug-layout` / `--debug-table` 精准排查

+ 605 - 0
docs/ocr_tools/universal_doc_parser/llama.cpp配置说明.md

@@ -0,0 +1,605 @@
+# llama.cpp 本地 GGUF 模型配置说明
+
+本文档说明如何在 Mac M4 Pro 上使用 llama-server 加载本地 GGUF 模型进行 OCR 识别。
+
+## 目录
+- [模型文件](#模型文件)
+- [可用模型](#可用模型)
+- [守护脚本](#守护脚本)
+- [参数配置](#参数配置)
+- [使用方法](#使用方法)
+- [常见问题](#常见问题)
+
+---
+
+## 模型文件
+
+模型文件位于 `~/Library/Caches/llama.cpp/` 目录:
+
+```bash
+[192.168.1.27:~/Library/Caches/llama.cpp]$ ll
+total 6374440
+# GLM-OCR 模型
+-rw-r--r--@   1 zhch158  staff       1399 Mar 14 22:51 manifest=ggml-org=GLM-OCR-GGUF=latest.json
+-rw-r--r--@   1 zhch158  staff  950433408 Mar 14 22:53 ggml-org_GLM-OCR-GGUF_GLM-OCR-Q8_0.gguf                  # 主模型 950MB
+-rw-r--r--@   1 zhch158  staff         66 Mar 14 22:53 ggml-org_GLM-OCR-GGUF_GLM-OCR-Q8_0.gguf.etag
+-rw-r--r--@   1 zhch158  staff  484403648 Mar 14 22:54 ggml-org_GLM-OCR-GGUF_mmproj-GLM-OCR-Q8_0.gguf            # 投影器 484MB
+-rw-r--r--@   1 zhch158  staff         66 Mar 14 22:54 ggml-org_GLM-OCR-GGUF_mmproj-GLM-OCR-Q8_0.gguf.etag
+
+# PaddleOCR-VL 模型
+-rw-r--r--@   1 zhch158  staff       1407 Mar 15 16:04 manifest=PaddlePaddle=PaddleOCR-VL-1.5-GGUF=latest.json
+-rw-r--r--@   1 zhch158  staff  935768992 Mar 15 16:05 PaddlePaddle_PaddleOCR-VL-1.5-GGUF_PaddleOCR-VL-1.5.gguf  # 主模型 936MB
+-rw-r--r--@   1 zhch158  staff         66 Mar 15 16:05 PaddlePaddle_PaddleOCR-VL-1.5-GGUF_PaddleOCR-VL-1.5.gguf.etag
+-rw-r--r--@   1 zhch158  staff  881770496 Mar 15 16:06 PaddlePaddle_PaddleOCR-VL-1.5-GGUF_PaddleOCR-VL-1.5-mmproj.gguf  # 投影器 882MB
+-rw-r--r--@   1 zhch158  staff         66 Mar 15 16:06 PaddlePaddle_PaddleOCR-VL-1.5-GGUF_PaddleOCR-VL-1.5-mmproj.gguf.etag
+```
+
+---
+
+## 可用模型
+
+我们提供两个 OCR 视觉语言模型的本地部署方案:
+
+| 模型 | 主模型大小 | 投影器大小 | 守护脚本 | 端口 | 适用场景 |
+|------|----------|-----------|---------|------|---------|
+| **GLM-OCR Q8_0** | 950MB | 484MB | `glmocr_local_daemon.sh` | 8080 | 通用 OCR、英文、图表识别 |
+| **PaddleOCR-VL-1.5** | 936MB | 882MB | `paddleocr_local_daemon.sh` | 8081 | 中文 OCR、表格识别 |
+
+**模型下载地址:**
+- GLM-OCR: https://huggingface.co/ggml-org/GLM-OCR-GGUF
+- PaddleOCR-VL: https://huggingface.co/PaddlePaddle/PaddleOCR-VL-1.5-GGUF
+
+---
+
+## 守护脚本
+
+我们提供两个独立的守护脚本来管理 llama-server 服务:
+
+### 1. GLM-OCR 守护脚本
+
+**位置:** `ocr_tools/daemons/glmocr_local_daemon.sh`
+
+**配置:**
+- 端口:8080
+- 日志:`~/workspace/logs/glmocr_llamaserver.log`
+- PID:`~/workspace/logs/glmocr_llamaserver.pid`
+
+**配置:**
+- 端口:8080
+- 日志:`~/workspace/logs/glmocr_llamaserver.log`
+- PID:`~/workspace/logs/glmocr_llamaserver.pid`
+
+**llama-server 启动命令:**
+```bash
+llama-server \
+  -m ~/Library/Caches/llama.cpp/ggml-org_GLM-OCR-GGUF_GLM-OCR-Q8_0.gguf \
+  --mmproj ~/Library/Caches/llama.cpp/ggml-org_GLM-OCR-GGUF_mmproj-GLM-OCR-Q8_0.gguf \
+  --host 0.0.0.0 \
+  --port 8080 \
+  --media-path /Users/zhch158/workspace \
+  -c 16384 \
+  -ngl 99 \
+  -t 8 \
+  -b 512 \
+  -ub 128 \
+  --temp 0
+```
+
+### 2. PaddleOCR-VL 守护脚本
+
+**位置:** `ocr_tools/daemons/paddleocr_local_daemon.sh`
+
+**配置:**
+- 端口:8081
+- 日志:`~/workspace/logs/paddleocr_llamaserver.log`
+- PID:`~/workspace/logs/paddleocr_llamaserver.pid`
+
+**llama-server 启动命令:**
+```bash
+llama-server \
+  -m ~/Library/Caches/llama.cpp/PaddlePaddle_PaddleOCR-VL-1.5-GGUF_PaddleOCR-VL-1.5.gguf \
+  --mmproj ~/Library/Caches/llama.cpp/PaddlePaddle_PaddleOCR-VL-1.5-GGUF_PaddleOCR-VL-1.5-mmproj.gguf \
+  --host 0.0.0.0 \
+  --port 8081 \
+  --media-path /Users/zhch158/workspace \
+  -c 16384 \
+  -ngl 99 \
+  -t 8 \
+  -b 512 \
+  -ub 128 \
+  --temp 0
+```
+
+### 两个脚本可同时运行
+
+两个脚本使用不同的端口(8080 vs 8081)和独立的日志文件,因此可以同时启动用于对比测试。
+
+---
+
+## 参数配置
+
+### llama-server 参数详解
+
+| 参数 | 值 | 说明 |
+|------|-----|------|
+| `-m` | 模型路径 | 主 GGUF 模型文件(Q8_0 量化) |
+| `--mmproj` | 投影器路径 | 多模态投影器(用于处理图像) |
+| `--host` | `0.0.0.0` | 服务监听地址(允许外部访问) |
+| `--port` | `8080`/`8081` | 服务端口 |
+| `--media-path` | `/Users/zhch158/workspace` | 图片基准目录(使用相对路径访问) |
+| `-c` | `16384` | **CONTEXT_SIZE** - 上下文窗口大小 |
+| `-ngl` | `99` | Metal GPU 层数(99 = 全部加速) |
+| `-t` | `8` | CPU 线程数(M4 Pro 推荐值) |
+| `-b` | `512` | 批处理大小 |
+| `-ub` | `128` | 微批处理大小 |
+| `--temp` | `0` | 温度参数(0 = 确定性输出) |
+
+### CONTEXT_SIZE 与 max_tokens 的关系
+
+**关键概念:**
+
+1. **CONTEXT_SIZE (`-c 16384`)** - llama-server 的上下文窗口
+   - 模型一次能处理的**总 token 数量**
+   - 包括:输入 prompt tokens + 输出 tokens + 图像 tokens
+
+2. **max_tokens (16384)** - OpenAI API 的生成参数
+   - 模型**输出**的最大 token 数量
+   - 在配置文件 `model_params.max_tokens` 中设置
+
+**重要规则:**
+```
+max_tokens <= CONTEXT_SIZE
+```
+
+**实际使用场景:**
+```
+总上下文 (16384) = 图像 tokens (~500-2000) 
+                  + Prompt tokens (~10) 
+                  + 生成输出 tokens (最多 16384)
+```
+
+**为什么设置为 16384:**
+- ✅ 满足复杂表格识别需求(可能输出数千 tokens)
+- ✅ M4 Pro 48GB 内存充足(Q8_0 模型 ~1GB + 16K KV cache ~2-4GB)
+- ✅ Metal GPU 加速,性能良好
+- ✅ 与配置文件的 max_tokens 参数对齐
+
+**性能调整建议:**
+- **8192**:平衡性能与长度(适合一般文档)
+- **16384**:处理复杂大表格(当前推荐设置)
+- **32768**:极限场景(需要更多内存,性能下降)
+
+---
+
+## 使用方法
+
+### 前置要求
+
+1. **安装 llama.cpp:**
+   ```bash
+   brew install llama.cpp
+   ```
+
+2. **确认 conda 环境:**
+   ```bash
+   conda activate mineru2
+   ```
+
+3. **验证模型文件:**
+   ```bash
+   ls -lh ~/Library/Caches/llama.cpp/
+   ```
+
+### 启动服务
+
+#### 启动 GLM-OCR 服务
+
+```bash
+cd /Users/zhch158/workspace/repository.git/ocr_platform/ocr_tools/daemons
+
+# 查看配置
+./glmocr_local_daemon.sh config
+
+# 启动服务
+./glmocr_local_daemon.sh start
+
+# 查看状态
+./glmocr_local_daemon.sh status
+
+# 测试 API
+./glmocr_local_daemon.sh test
+```
+
+#### 启动 PaddleOCR-VL 服务
+
+```bash
+# 查看配置
+./paddleocr_local_daemon.sh config
+
+# 启动服务
+./paddleocr_local_daemon.sh start
+
+# 查看状态
+./paddleocr_local_daemon.sh status
+
+# 测试 API
+./paddleocr_local_daemon.sh test
+```
+
+#### 同时启动两个服务(对比测试)
+
+```bash
+# 两个服务使用不同端口,可以同时运行
+./glmocr_local_daemon.sh start      # 端口 8080
+./paddleocr_local_daemon.sh start   # 端口 8081
+
+# 检查状态
+./glmocr_local_daemon.sh status
+./paddleocr_local_daemon.sh status
+```
+
+### 守护脚本命令
+
+所有脚本支持以下命令:
+
+| 命令 | 说明 |
+|------|------|
+| `start` | 启动服务 |
+| `stop` | 停止服务 |
+| `restart` | 重启服务 |
+| `status` | 显示服务状态(PID、端口、内存、最近日志) |
+| `logs` | 实时查看日志(tail -f) |
+| `config` | 显示当前配置和系统信息 |
+| `test` | 测试 /v1/models API 端点 |
+| `test-client` | 显示集成测试示例 |
+
+### API 访问方式
+
+#### GLM-OCR API (端口 8080)
+
+```bash
+# OpenAI 兼容端点
+curl -X POST http://localhost:8080/v1/chat/completions \
+  -H 'Content-Type: application/json' \
+  -d '{
+    "model": "glm-ocr",
+    "messages": [{
+      "role": "user",
+      "content": [
+        {"type": "text", "text": "Table Recognition:"},
+        {"type": "image_url", "image_url": {"url": "file://test.png"}}
+      ]
+    }],
+    "max_tokens": 16384
+  }'
+
+# Models 端点
+curl http://localhost:8080/v1/models
+```
+
+#### PaddleOCR-VL API (端口 8081)
+
+```bash
+# OpenAI 兼容端点
+curl -X POST http://localhost:8081/v1/chat/completions \
+  -H 'Content-Type: application/json' \
+  -d '{
+    "model": "paddleocr-vl",
+    "messages": [{
+      "role": "user",
+      "content": [
+        {"type": "text", "text": "Table Recognition:"},
+        {"type": "image_url", "image_url": {"url": "file://test.png"}}
+      ]
+    }],
+    "max_tokens": 16384
+  }'
+
+# Models 端点
+curl http://localhost:8081/v1/models
+```
+
+### 配置文件集成
+
+#### GLM-OCR 配置文件
+
+`config/bank_statement_yusys_local.yaml`:
+
+```yaml
+vl_recognition:
+  module: "glmocr"
+  api_url: "http://localhost:8080/v1/chat/completions"
+  model: "glm-ocr"
+  model_params:
+    max_tokens: 16384
+    temperature: 0.1
+```
+
+#### PaddleOCR-VL 配置文件
+
+`config/bank_statement_paddleocr_local.yaml`:
+
+```yaml
+vl_recognition:
+  module: "paddleocr"
+  api_url: "http://localhost:8081/v1/chat/completions"
+  model: "paddleocr-vl"
+  model_params:
+    max_tokens: 16384
+    temperature: 0.1
+```
+
+#### 使用配置文件处理文档
+
+```bash
+cd /Users/zhch158/workspace/repository.git/ocr_platform/ocr_tools/universal_doc_parser
+
+# 使用 GLM-OCR
+python parse.py --input document.pdf \
+  --config config/bank_statement_yusys_local.yaml --debug
+
+# 使用 PaddleOCR-VL
+python parse.py --input document.pdf \
+  --config config/bank_statement_paddleocr_local.yaml --debug
+```
+
+---
+
+## 常见问题
+
+---
+
+## 常见问题
+
+### Q1: llama-server 文件访问错误
+
+**问题描述:**
+```
+Error: file does not exist: file:///Users/...
+```
+
+**原因:**
+llama-server 的文件访问模型是 **基准目录 + 相对路径**,而非文件系统绝对路径。
+
+**解决方法:**
+1. 使用 `--media-path` 设置基准目录
+2. 图片路径使用**相对路径**:`file://test.png` 而非 `file:///Users/zhch158/workspace/test.png`
+
+**示例:**
+```bash
+# 正确配置
+--media-path /Users/zhch158/workspace
+# API 调用时使用
+"image_url": {"url": "file://test.png"}  # 相对于 media-path
+
+# 错误配置
+"image_url": {"url": "file:///Users/zhch158/workspace/test.png"}  # ❌ 绝对路径不工作
+```
+
+### Q2: 服务启动但 API 无响应
+
+**排查步骤:**
+
+1. **检查服务状态:**
+   ```bash
+   ./glmocr_local_daemon.sh status
+   ```
+
+2. **检查端口监听:**
+   ```bash
+   lsof -i :8080  # GLM-OCR
+   lsof -i :8081  # PaddleOCR-VL
+   ```
+
+3. **查看日志:**
+   ```bash
+   ./glmocr_local_daemon.sh logs
+   ```
+
+4. **测试 API:**
+   ```bash
+   curl http://localhost:8080/v1/models
+   ```
+
+**常见原因:**
+- 服务正在启动中(模型加载需要几秒到几十秒)
+- 端口被占用
+- llama-server 版本过旧(需要支持 `--media-path` 参数)
+
+### Q3: 两个模型如何选择?
+
+| 场景 | 推荐模型 | 理由 |
+|------|---------|------|
+| 中文为主的文档/表格 | PaddleOCR-VL | 中文识别准确率更高 |
+| 英文为主的文档 | GLM-OCR | 通用性好,速度快 |
+| 复杂图表识别 | GLM-OCR | 图表理解能力强 |
+| 需要对比测试 | 同时启动两个 | 使用不同端口并行测试 |
+
+**建议:**
+- 首次使用:同时启动两个服务,使用相同测试数据对比效果
+- 确定效果后:选择一个模型日常使用,节省内存
+
+### Q4: 内存占用过高怎么办?
+
+**当前配置占用(单个服务):**
+- 模型加载:~1GB (Q8_0 量化)
+- KV cache:~2-4GB (16K 上下文)
+- 总计:~3-5GB
+
+**优化方案:**
+
+1. **减少 CONTEXT_SIZE:**
+   ```bash
+   CONTEXT_SIZE="8192"  # 从 16384 降至 8192
+   ```
+
+2. **只运行一个服务:**
+   ```bash
+   # 停止不常用的服务
+   ./paddleocr_local_daemon.sh stop
+   ```
+
+3. **调整 batch_size:**
+   ```bash
+   BATCH_SIZE="256"     # 从 512 降至 256
+   UBATCH_SIZE="64"     # 从 128 降至 64
+   ```
+
+### Q5: 如何查看性能指标?
+
+**方法 1:查看日志中的性能数据**
+```bash
+tail -20 ~/workspace/logs/glmocr_llamaserver.log
+```
+
+**日志输出示例:**
+```
+prompt eval time  = 10448.11 ms / 2701 tokens (  3.87 ms per token, 387 tokens/s)
+generate eval time = 19105.23 ms / 3412 tokens (  5.60 ms per token, 178 tokens/s)
+total time         = 29614.40 ms
+```
+
+**方法 2:使用 status 命令**
+```bash
+./glmocr_local_daemon.sh status
+```
+
+显示内容包括:
+- PID 和运行状态
+- 端口监听状态
+- 内存使用量
+- 最近日志
+
+### Q6: 如何更新模型?
+
+**下载新模型:**
+```bash
+# 模型会自动下载到 ~/Library/Caches/llama.cpp/
+# 如果需要手动下载,可以从 HuggingFace 下载并放置到该目录
+```
+
+**更新脚本中的路径:**
+```bash
+# 编辑守护脚本
+vim /Users/zhch158/workspace/repository.git/ocr_platform/ocr_tools/daemons/glmocr_local_daemon.sh
+
+# 修改模型路径
+MODEL_PATH="$HOME/Library/Caches/llama.cpp/新模型文件名.gguf"
+MMPROJ_PATH="$HOME/Library/Caches/llama.cpp/新投影器文件名.gguf"
+```
+
+**重启服务:**
+```bash
+./glmocr_local_daemon.sh restart
+```
+
+### Q7: 文件路径包含中文字符无法识别
+
+**问题:**
+路径包含中文时,llama-server 可能无法正确解析。
+
+**解决方法:**
+1. 将测试图片移动到不含中文的路径
+2. 确保 `--media-path` 路径不包含中文
+3. 使用英文文件名
+
+**示例:**
+```bash
+# 推荐
+--media-path /Users/zhch158/workspace
+file://test_image.png
+
+# 避免
+--media-path /Users/zhch158/工作空间
+file://测试图片.png
+```
+
+### Q8: Metal GPU 加速未生效
+
+**检查方法:**
+```bash
+./glmocr_local_daemon.sh logs
+```
+
+**正常输出应包含:**
+```
+ggml_metal_init: found device: Apple M4 Pro
+ggml_metal_init: GPU enabled
+```
+
+**如果未启用:**
+1. 检查 llama.cpp 是否支持 Metal(`llama-server --version`)
+2. 确认 `-ngl 99` 参数存在于启动命令
+3. 重新安装 llama.cpp:`brew reinstall llama.cpp`
+
+---
+
+## 性能参考数据
+
+### Mac M4 Pro 48GB 测试结果
+
+**测试场景:** 银行流水表格识别(24 行记录)
+
+| 模型 | Prompt 速度 | 生成速度 | 总耗时 | 输出 Tokens |
+|------|-----------|---------|--------|-----------|
+| GLM-OCR Q8_0 | 387 tokens/s | 178 tokens/s | ~30s | 3412 |
+| PaddleOCR-VL-1.5 | (待测试) | (待测试) | (待测试) | (待测试) |
+
+**配置参数:**
+- CONTEXT_SIZE: 16384
+- GPU_LAYERS: 99 (Metal)
+- THREADS: 8
+- Temperature: 0
+
+---
+
+## 相关文档
+
+- [守护脚本 README](../../../ocr_tools/daemons/README.md) - 详细的守护脚本使用说明
+- [universal_doc_parser 配置指南](../../../ocr_tools/universal_doc_parser/README.md)
+- [debug 参数控制](./debug参数控制.md)
+- [GLM-OCR 官方文档](https://github.com/GLM-OCR/GLM-OCR)
+- [PaddleOCR-VL 官方文档](https://github.com/PaddlePaddle/PaddleOCR)
+- [llama.cpp 文档](https://github.com/ggerganov/llama.cpp)
+
+---
+
+## 总结
+
+### 关键要点
+
+1. **两个独立脚本** - GLM-OCR (8080) 和 PaddleOCR-VL (8081) 可同时运行
+2. **CONTEXT_SIZE 必须 >= max_tokens** - 当前都设置为 16384
+3. **文件路径使用相对路径** - 相对于 `--media-path` 设置的基准目录
+4. **Metal GPU 加速** - Mac M4 Pro 使用 `-ngl 99` 全层加速
+5. **确定性输出** - `--temp 0` 确保 OCR 结果一致性
+6. **OpenAI 兼容 API** - 完全兼容 OpenAI vision API 格式
+
+### 快速开始命令
+
+```bash
+# 1. 进入守护脚本目录
+cd /Users/zhch158/workspace/repository.git/ocr_platform/ocr_tools/daemons
+
+# 2. 启动服务(选择一个或同时启动)
+./glmocr_local_daemon.sh start
+./paddleocr_local_daemon.sh start
+
+# 3. 查看状态
+./glmocr_local_daemon.sh status
+
+# 4. 使用 OCR pipeline 处理文档
+cd ../universal_doc_parser
+python parse.py --input document.pdf --config config/bank_statement_yusys_local.yaml --debug
+```
+
+### 下一步
+
+- [ ] 启动服务并测试 API
+- [ ] 对比两个模型的识别效果
+- [ ] 根据实际需求调整参数(CONTEXT_SIZE、threads 等)
+- [ ] 将性能更好的模型集成到生产 pipeline
+
+---
+
+**文档更新日期:** 2026年3月15日

+ 210 - 2
ocr_tools/daemons/README.md

@@ -8,6 +8,8 @@
 
 ## 脚本列表
 
+### 远程 vLLM 服务(Linux/GPU)
+
 | 脚本文件 | 服务类型 | 默认端口 | 服务 URL |
 |---------|---------|---------|---------|
 | `mineru_vllm_daemon.sh` | MinerU vLLM | 8121 | http://localhost:8121 |
@@ -15,6 +17,13 @@
 | `paddle_vllm_daemon.sh` | PaddleOCR-VL vLLM | 8110 | http://localhost:8110 |
 | `dotsocr_vllm_daemon.sh` | DotsOCR vLLM | 8101 | http://localhost:8101 |
 
+### 本地 GGUF 模型服务(macOS/Metal)
+
+| 脚本文件 | 服务类型 | 默认端口 | 服务 URL |
+|---------|---------|---------|---------|
+| `glmocr_local_daemon.sh` | GLM-OCR Q8_0 (llama.cpp) | 8080 | http://localhost:8080 |
+| `paddleocr_local_daemon.sh` | PaddleOCR-VL-1.5 (llama.cpp) | 8081 | http://localhost:8081 |
+
 ## 脚本与客户端工具映射
 
 | 服务端脚本 | 客户端工具 | 服务类型 | 默认端口 | API 端点 |
@@ -210,14 +219,162 @@ cd ../dots.ocr_vl_tool
 python main.py --input document.pdf --output_dir ./output --ip localhost --port 8101
 ```
 
+### 5. glmocr_local_daemon.sh
+
+**服务类型**:GLM-OCR 本地 GGUF 模型服务(macOS/Metal)
+
+**配置参数**:
+- `CONDA_ENV`: conda 环境名称(默认: `mineru2`)
+- `PORT`: 服务端口(默认: `8080`)
+- `HOST`: 服务主机(默认: `0.0.0.0`)
+- `MODEL_PATH`: GGUF 模型路径(默认: `~/Library/Caches/llama.cpp/ggml-org_GLM-OCR-GGUF_GLM-OCR-Q8_0.gguf`)
+- `MMPROJ_PATH`: 多模态投影器路径(默认: `~/Library/Caches/llama.cpp/ggml-org_GLM-OCR-GGUF_mmproj-GLM-OCR-Q8_0.gguf`)
+- `CONTEXT_SIZE`: 上下文长度(默认: `16384`)
+- `GPU_LAYERS`: Metal GPU 层数(默认: `99`,全部)
+- `THREADS`: CPU 线程数(默认: `8`)
+
+**启动方法**:
+```bash
+./glmocr_local_daemon.sh start
+```
+
+**服务 URL**:
+- API 端点: `http://localhost:8080`
+- OpenAI 兼容 API: `http://localhost:8080/v1/chat/completions`
+- Models 端点: `http://localhost:8080/v1/models`
+
+**依赖环境**:
+- macOS (M4 Pro 推荐)
+- Homebrew 安装 llama.cpp: `brew install llama.cpp`
+- conda 环境: `mineru2`
+- 模型文件位于: `~/Library/Caches/llama.cpp/`
+
+**模型大小**:
+- 主模型: 950MB (GLM-OCR-Q8_0.gguf)
+- 多模态投影器: 484MB (mmproj-GLM-OCR-Q8_0.gguf)
+
+**客户端使用**:
+```bash
+# 使用配置文件调用服务
+cd ../universal_doc_parser
+python parse.py --input document.pdf \
+  --config config/bank_statement_yusys_local.yaml --debug
+
+# 或直接调用 API
+curl -X POST http://localhost:8080/v1/chat/completions \
+  -H 'Content-Type: application/json' \
+  -d '{
+    "model": "glm-ocr",
+    "messages": [{
+      "role": "user",
+      "content": [
+        {"type": "text", "text": "Table Recognition:"},
+        {"type": "image_url", "image_url": {"url": "file://test.png"}}
+      ]
+    }],
+    "max_tokens": 16384
+  }'
+```
+
+**特点**:
+- 🚀 本地运行,无需网络访问
+- 🍎 Metal GPU 加速(macOS)
+- 📊 OpenAI 兼容 API
+- 🎯 确定性输出(--temp 0)
+- 💾 低内存占用(GGUF Q8_0 量化)
+
+### 6. paddleocr_local_daemon.sh
+
+**服务类型**:PaddleOCR-VL-1.5 本地 GGUF 模型服务(macOS/Metal)
+
+**配置参数**:
+- `CONDA_ENV`: conda 环境名称(默认: `mineru2`)
+- `PORT`: 服务端口(默认: `8081`)
+- `HOST`: 服务主机(默认: `0.0.0.0`)
+- `MODEL_PATH`: GGUF 模型路径(默认: `~/Library/Caches/llama.cpp/PaddlePaddle_PaddleOCR-VL-1.5-GGUF_PaddleOCR-VL-1.5.gguf`)
+- `MMPROJ_PATH`: 多模态投影器路径(默认: `~/Library/Caches/llama.cpp/PaddlePaddle_PaddleOCR-VL-1.5-GGUF_PaddleOCR-VL-1.5-mmproj.gguf`)
+- `CONTEXT_SIZE`: 上下文长度(默认: `16384`)
+- `GPU_LAYERS`: Metal GPU 层数(默认: `99`,全部)
+- `THREADS`: CPU 线程数(默认: `8`)
+
+**启动方法**:
+```bash
+./paddleocr_local_daemon.sh start
+```
+
+**服务 URL**:
+- API 端点: `http://localhost:8081`
+- OpenAI 兼容 API: `http://localhost:8081/v1/chat/completions`
+- Models 端点: `http://localhost:8081/v1/models`
+
+**依赖环境**:
+- macOS (M4 Pro 推荐)
+- Homebrew 安装 llama.cpp: `brew install llama.cpp`
+- conda 环境: `mineru2`
+- 模型文件位于: `~/Library/Caches/llama.cpp/`
+
+**模型大小**:
+- 主模型: 936MB (PaddleOCR-VL-1.5.gguf)
+- 多模态投影器: 882MB (PaddleOCR-VL-1.5-mmproj.gguf)
+
+**客户端使用**:
+```bash
+# 使用配置文件调用服务
+cd ../universal_doc_parser
+python parse.py --input document.pdf \
+  --config config/bank_statement_paddleocr_local.yaml --debug
+
+# 或直接调用 API
+curl -X POST http://localhost:8081/v1/chat/completions \
+  -H 'Content-Type: application/json' \
+  -d '{
+    "model": "paddleocr-vl",
+    "messages": [{
+      "role": "user",
+      "content": [
+        {"type": "text", "text": "Table Recognition:"},
+        {"type": "image_url", "image_url": {"url": "file://test.png"}}
+      ]
+    }],
+    "max_tokens": 16384
+  }'
+```
+
+**特点**:
+- 🚀 本地运行,无需网络访问
+- 🍎 Metal GPU 加速(macOS)
+- 📊 OpenAI 兼容 API
+- 🎯 确定性输出(--temp 0)
+- 💾 低内存占用(GGUF 量化)
+
+**对比测试**:
+```bash
+# 可同时启动两个服务进行对比测试
+./glmocr_local_daemon.sh start      # 端口 8080
+./paddleocr_local_daemon.sh start   # 端口 8081
+
+# 检查状态
+./glmocr_local_daemon.sh status
+./paddleocr_local_daemon.sh status
+```
+
 ## 部署建议
 
 ### 1. 环境准备
 
+#### Linux/GPU 环境(vLLM 服务)
 - 确保所有依赖的 conda 环境已正确安装
 - 确保模型文件已下载并放置在正确位置
 - 确保 GPU 驱动和 CUDA 已正确安装
 
+#### macOS/Metal 环境(本地 GGUF 服务)
+- 安装 llama.cpp: `brew install llama.cpp`
+- 确保 conda 环境 `mineru2` 已创建
+- 模型文件自动下载到 `~/Library/Caches/llama.cpp/` 或手动下载:
+  - GLM-OCR: https://huggingface.co/ggml-org/GLM-OCR-GGUF
+  - PaddleOCR-VL: https://huggingface.co/PaddlePaddle/PaddleOCR-VL-1.5-GGUF
+- 推荐硬件: Mac M4 Pro 或更高(至少 16GB RAM)
+
 ### 2. 配置调整
 
 在部署前,请根据实际环境调整脚本中的配置参数:
@@ -258,6 +415,7 @@ WantedBy=multi-user.target
 
 ### 5. 日志管理
 
+#### vLLM 服务日志(Linux)
 所有服务的日志文件位于 `/home/ubuntu/zhch/logs/` 目录:
 
 - `mineru_vllm.log` - MinerU vLLM 服务日志
@@ -265,20 +423,34 @@ WantedBy=multi-user.target
 - `paddleocr_vl_vllm.log` - PaddleOCR-VL vLLM 服务日志
 - `vllm.log` - DotsOCR vLLM 服务日志
 
+#### 本地 GGUF 服务日志(macOS)
+日志文件位于 `~/workspace/logs/` 目录:
+
+- `glmocr_llamaserver.log` - GLM-OCR llama-server 日志
+- `glmocr_llamaserver.pid` - GLM-OCR 进程 PID
+- `paddleocr_llamaserver.log` - PaddleOCR-VL llama-server 日志
+- `paddleocr_llamaserver.pid` - PaddleOCR-VL 进程 PID
+
 建议定期清理或轮转日志文件。
 
 ## 故障排查
 
 ### 问题:服务启动失败
 
-**可能原因**:
+**可能原因(vLLM 服务)**:
 1. conda 环境未正确激活
 2. 依赖包未安装
 3. 模型文件不存在
 4. 端口已被占用
 5. GPU 不可用或配置错误
 
-**解决方法**:
+**可能原因(本地 GGUF 服务)**:
+1. llama-server 未安装或版本不兼容
+2. 模型文件不存在于 `~/Library/Caches/llama.cpp/`
+3. 端口已被占用
+4. conda 环境未激活
+
+**解决方法(vLLM)**:
 1. 使用 `./script_name.sh config` 检查配置
 2. 检查 conda 环境是否正确激活:`conda env list`
 3. 检查依赖是否安装:`which python`, `which mineru-vllm-server` 等
@@ -286,6 +458,26 @@ WantedBy=multi-user.target
 5. 检查端口占用:`netstat -tuln | grep :PORT`
 6. 检查 GPU 状态:`nvidia-smi`
 
+**解决方法(本地 GGUF)**:
+1. 检查 llama-server: `which llama-server`, `llama-server --version`
+2. 检查模型文件: `ls -lh ~/Library/Caches/llama.cpp/`
+3. 检查端口占用: `lsof -i :8080` 或 `lsof -i :8081`
+4. 查看日志: `./glmocr_local_daemon.sh logs`
+5. 手动下载模型(如果缓存目录为空)
+
+### 问题:llama-server 文件访问错误
+
+**可能原因**:
+1. 使用绝对路径而非相对路径
+2. --media-path 设置不正确
+3. 文件路径包含中文或特殊字符
+
+**解决方法**:
+1. 确保 --media-path 设置为基准目录(如 `/Users/zhch158/workspace`)
+2. 图片路径使用相对路径:`file://test.png` 而非 `file:///Users/...`
+3. 避免路径中包含中文字符
+4. 测试: `./glmocr_local_daemon.sh test`
+
 ### 问题:API 无响应
 
 **可能原因**:
@@ -333,9 +525,25 @@ WantedBy=multi-user.target
 
 ## 注意事项
 
+### vLLM 服务(Linux/GPU)
 1. **路径配置**:脚本中的路径(如模型路径、日志路径)需要根据实际部署环境调整
 2. **端口冲突**:确保不同服务使用不同的端口,避免冲突
 3. **GPU 资源**:合理分配 GPU 资源,避免多个服务竞争同一 GPU
 4. **日志管理**:定期清理日志文件,避免磁盘空间不足
 5. **服务监控**:建议使用监控工具(如 systemd、supervisor)管理服务,确保服务稳定运行
 
+### 本地 GGUF 服务(macOS/Metal)
+1. **端口分离**:GLM-OCR 使用 8080,PaddleOCR-VL 使用 8081,可同时运行
+2. **文件路径**:使用相对路径(相对于 `--media-path`),而非绝对路径
+3. **内存要求**:每个服务约占用 2-3GB 内存,确保足够 RAM
+4. **Metal 加速**:自动使用 Metal GPU,无需 CUDA 配置
+5. **模型下载**:首次运行可能需要下载模型文件(约 1.5-2GB)
+6. **确定性输出**:使用 `--temp 0` 确保 OCR 结果一致性
+7. **API 兼容性**:完全兼容 OpenAI vision API 格式
+
+### 模型选择建议
+- **GLM-OCR**: 适合通用 OCR 场景,特别是英文和图表识别
+- **PaddleOCR-VL**: 适合中文 OCR 场景,表格识别效果好
+- **对比测试**: 可同时运行两个服务,对比识别效果后选择
+- **资源限制**: 如果内存有限(<16GB),建议只运行一个服务
+

+ 1 - 0
ocr_tools/daemons/curl_local_img.png

@@ -0,0 +1 @@
+/Users/zhch158/workspace/data/流水分析/A用户_单元格扫描流水/bank_statement_yusys_v4/A用户_单元格扫描流水/A用户_单元格扫描流水_page_003.png

+ 18 - 0
ocr_tools/daemons/curl_local_ocr.sh

@@ -0,0 +1,18 @@
+# --media-path /Users/zhch158/workspace  # 设置基础目录
+# {"url": "file://test_ocr_image.png"}  # 相对于基础目录的相对路径
+curl http://127.0.0.1:8080/v1/chat/completions \
+  -H "Content-Type: application/json" \
+  -d '{
+    "model": "ocr-vl",
+    "messages": [
+      {
+        "role": "user",
+        "content": [
+          {"type": "text", "text": "Table Recognition:"},
+          {"type": "image_url", "image_url": {"url": "file://repository.git/ocr_platform/ocr_tools/daemons/curl_local_img.png"}}
+        ]
+      }
+    ],
+    "max_tokens": 16384,
+    "temperature": 0.1
+  }'

+ 381 - 0
ocr_tools/daemons/glmocr_local_daemon.sh

@@ -0,0 +1,381 @@
+#!/bin/bash
+# filepath: ocr_platform/ocr_tools/daemons/glmocr_local_daemon.sh
+# 对应: GLM-OCR 本地 llama-server 服务(macOS),使用 GGUF 格式模型
+# 适用于 Mac M4 Pro 48G,使用 Metal GPU 加速
+# 模型下载地址: https://huggingface.co/ggml-org/GLM-OCR-GGUF
+# 模型下载地址: https://huggingface.co/PaddlePaddle/PaddleOCR-VL-1.5-GGUF
+
+# curl -X POST http://localhost:8080/v1/chat/completions -d @payload.json
+
+LOGDIR="$HOME/workspace/logs"
+mkdir -p $LOGDIR
+PIDFILE="$LOGDIR/glmocr_llamaserver.pid"
+LOGFILE="$LOGDIR/glmocr_llamaserver.log"
+
+# 配置参数
+CONDA_ENV="mineru2"
+PORT="8080"
+HOST="0.0.0.0"
+
+# 本地 GGUF 模型路径
+MODEL_PATH="$HOME/Library/Caches/llama.cpp/ggml-org_GLM-OCR-GGUF_GLM-OCR-Q8_0.gguf"
+MMPROJ_PATH="$HOME/Library/Caches/llama.cpp/ggml-org_GLM-OCR-GGUF_mmproj-GLM-OCR-Q8_0.gguf"
+
+# llama-server 参数
+CONTEXT_SIZE="16384"         # 上下文长度(需 >= max_tokens,推荐 8192-16384)
+GPU_LAYERS="99"              # Metal GPU 层数(99 表示全部)
+THREADS="8"                  # CPU 线程数(M4 Pro 建议值)
+BATCH_SIZE="512"             # 批处理大小
+UBATCH_SIZE="128"            # 微批处理大小
+
+# conda 环境激活
+if [ -f "$HOME/anaconda3/etc/profile.d/conda.sh" ]; then
+    source "$HOME/anaconda3/etc/profile.d/conda.sh"
+    conda activate $CONDA_ENV
+elif [ -f "$HOME/miniconda3/etc/profile.d/conda.sh" ]; then
+    source "$HOME/miniconda3/etc/profile.d/conda.sh"
+    conda activate $CONDA_ENV
+elif [ -f "/opt/miniconda3/etc/profile.d/conda.sh" ]; then
+    source /opt/miniconda3/etc/profile.d/conda.sh
+    conda activate $CONDA_ENV
+else
+    echo "Warning: conda initialization file not found, trying direct path"
+    export PATH="/opt/miniconda3/envs/$CONDA_ENV/bin:$PATH"
+fi
+
+start() {
+    if [ -f $PIDFILE ] && kill -0 $(cat $PIDFILE) 2>/dev/null; then
+        echo "GLM-OCR llama-server 已在运行"
+        return 1
+    fi
+
+    echo "启动 GLM-OCR llama-server 守护进程..."
+    echo "Host: $HOST, Port: $PORT"
+    echo "主模型: $MODEL_PATH"
+    echo "多模态投影器: $MMPROJ_PATH"
+    echo "上下文长度: $CONTEXT_SIZE"
+    echo "GPU 层数: $GPU_LAYERS (Metal)"
+    echo "线程数: $THREADS"
+
+    # 检查模型文件是否存在
+    if [ ! -f "$MODEL_PATH" ]; then
+        echo "❌ 主模型文件不存在: $MODEL_PATH"
+        echo "请确认模型已下载到 llama.cpp 缓存目录"
+        return 1
+    fi
+
+    if [ ! -f "$MMPROJ_PATH" ]; then
+        echo "❌ 多模态投影器文件不存在: $MMPROJ_PATH"
+        echo "请确认 mmproj 文件已下载"
+        return 1
+    fi
+
+    # 检查 llama-server 命令
+    if ! command -v llama-server >/dev/null 2>&1; then
+        echo "❌ llama-server 未找到"
+        echo "请安装: brew install llama.cpp"
+        return 1
+    fi
+
+    echo "🔧 使用 llama-server: $(which llama-server)"
+    echo "🔧 llama.cpp 版本: $(llama-server --version 2>&1 | head -1 || echo 'Unknown')"
+
+    echo "💻 系统信息:"
+    echo "  架构: $(uname -m)"
+    echo "  系统: $(uname -s)"
+    echo "  内存: $(sysctl -n hw.memsize | awk '{printf "%.1f GB", $1/1024/1024/1024}')"
+
+    # 启动 llama-server
+    # --log-disable \
+    nohup llama-server \
+        -m "$MODEL_PATH" \
+        --mmproj "$MMPROJ_PATH" \
+        --host $HOST \
+        --port $PORT \
+        --media-path /Users/zhch158/workspace \
+        -c $CONTEXT_SIZE \
+        -ngl $GPU_LAYERS \
+        -t $THREADS \
+        -b $BATCH_SIZE \
+        -ub $UBATCH_SIZE \
+        --temp 0 \
+        > $LOGFILE 2>&1 &
+
+    echo $! > $PIDFILE
+    echo "✅ GLM-OCR llama-server 已启动,PID: $(cat $PIDFILE)"
+    echo "📋 日志文件: $LOGFILE"
+    echo "🌐 服务 URL: http://$HOST:$PORT"
+    echo "📖 OpenAI 兼容 API: http://localhost:$PORT/v1 (chat/completions, models)"
+    echo ""
+    echo "等待服务启动..."
+    sleep 5
+    status
+}
+
+stop() {
+    if [ ! -f $PIDFILE ]; then
+        echo "GLM-OCR llama-server 未在运行"
+        return 1
+    fi
+
+    PID=$(cat $PIDFILE)
+    echo "停止 GLM-OCR llama-server (PID: $PID)..."
+
+    kill $PID
+
+    for i in {1..30}; do
+        if ! kill -0 $PID 2>/dev/null; then
+            break
+        fi
+        echo "等待进程停止... ($i/30)"
+        sleep 1
+    done
+
+    if kill -0 $PID 2>/dev/null; then
+        echo "强制终止进程..."
+        kill -9 $PID
+    fi
+
+    rm -f $PIDFILE
+    echo "✅ GLM-OCR llama-server 已停止"
+}
+
+status() {
+    if [ -f $PIDFILE ] && kill -0 $(cat $PIDFILE) 2>/dev/null; then
+        PID=$(cat $PIDFILE)
+        echo "✅ GLM-OCR llama-server 正在运行 (PID: $PID)"
+        echo "🌐 服务 URL: http://$HOST:$PORT"
+        echo "📋 日志文件: $LOGFILE"
+
+        # 检查端口监听状态
+        if lsof -nP -iTCP:$PORT -sTCP:LISTEN >/dev/null 2>&1; then
+            echo "🔗 端口 $PORT 正在监听"
+        else
+            echo "⚠️  端口 $PORT 未在监听(服务可能正在启动)"
+        fi
+
+        # 检查 API 响应
+        if command -v curl >/dev/null 2>&1; then
+            if curl -s --connect-timeout 2 http://127.0.0.1:$PORT/v1/models > /dev/null 2>&1; then
+                echo "🎯 API 响应正常"
+            else
+                echo "⚠️  API 无响应(服务可能正在启动)"
+            fi
+        fi
+
+        # 显示进程内存使用
+        if command -v ps >/dev/null 2>&1; then
+            MEM=$(ps -o rss= -p $PID 2>/dev/null | awk '{printf "%.2f GB", $1/1024/1024}')
+            if [ -n "$MEM" ]; then
+                echo "💾 内存使用: $MEM"
+            fi
+        fi
+
+        if [ -f $LOGFILE ]; then
+            echo "📄 最近日志(最后 3 行):"
+            tail -3 $LOGFILE | sed 's/^/  /'
+        fi
+    else
+        echo "❌ GLM-OCR llama-server 未在运行"
+        if [ -f $PIDFILE ]; then
+            echo "删除过期的 PID 文件..."
+            rm -f $PIDFILE
+        fi
+    fi
+}
+
+logs() {
+    if [ -f $LOGFILE ]; then
+        echo "📄 GLM-OCR llama-server 日志:"
+        echo "====================="
+        tail -f $LOGFILE
+    else
+        echo "❌ 日志文件不存在: $LOGFILE"
+    fi
+}
+
+config() {
+    echo "📋 当前配置:"
+    echo "  Conda 环境: $CONDA_ENV"
+    echo "  Host: $HOST"
+    echo "  Port: $PORT"
+    echo "  主模型路径: $MODEL_PATH"
+    echo "  多模态投影器: $MMPROJ_PATH"
+    echo "  上下文长度: $CONTEXT_SIZE"
+    echo "  GPU 层数: $GPU_LAYERS"
+    echo "  线程数: $THREADS"
+    echo "  批处理大小: $BATCH_SIZE"
+    echo "  微批处理大小: $UBATCH_SIZE"
+    echo "  PID 文件: $PIDFILE"
+    echo "  日志文件: $LOGFILE"
+
+    echo ""
+    echo "📦 模型文件检查:"
+    if [ -f "$MODEL_PATH" ]; then
+        SIZE=$(du -h "$MODEL_PATH" | cut -f1)
+        echo "  ✅ 主模型存在 ($SIZE)"
+    else
+        echo "  ❌ 主模型不存在"
+    fi
+
+    if [ -f "$MMPROJ_PATH" ]; then
+        SIZE=$(du -h "$MMPROJ_PATH" | cut -f1)
+        echo "  ✅ 多模态投影器存在 ($SIZE)"
+    else
+        echo "  ❌ 多模态投影器不存在"
+    fi
+
+    echo ""
+    echo "🔧 环境检查:"
+    echo "  llama-server: $(which llama-server 2>/dev/null || echo '未安装')"
+    if command -v llama-server >/dev/null 2>&1; then
+        LLAMA_VERSION=$(llama-server --version 2>&1 | head -1 || echo 'Unknown')
+        echo "  版本: $LLAMA_VERSION"
+    fi
+    echo "  Conda: $(which conda 2>/dev/null || echo '未找到')"
+    echo "  当前 Python: $(which python 2>/dev/null || echo '未找到')"
+
+    echo ""
+    echo "💻 系统信息:"
+    echo "  架构: $(uname -m)"
+    echo "  系统版本: $(sw_vers -productVersion 2>/dev/null || echo 'Unknown')"
+    echo "  总内存: $(sysctl -n hw.memsize 2>/dev/null | awk '{printf "%.1f GB", $1/1024/1024/1024}' || echo 'Unknown')"
+    echo "  CPU 核心: $(sysctl -n hw.ncpu 2>/dev/null || echo 'Unknown')"
+}
+
+test_api() {
+    echo "🧪 测试 GLM-OCR llama-server API..."
+
+    if [ ! -f $PIDFILE ] || ! kill -0 $(cat $PIDFILE) 2>/dev/null; then
+        echo "❌ GLM-OCR llama-server 服务未在运行"
+        return 1
+    fi
+
+    if ! command -v curl >/dev/null 2>&1; then
+        echo "❌ curl 命令未找到"
+        return 1
+    fi
+
+    echo "📡 测试 /v1/models 端点..."
+    response=$(curl -s --connect-timeout 10 http://127.0.0.1:$PORT/v1/models)
+    if [ $? -eq 0 ]; then
+        echo "✅ Models 端点可访问"
+        echo "$response" | python -m json.tool 2>/dev/null || echo "$response"
+    else
+        echo "❌ Models 端点不可访问"
+    fi
+
+    echo ""
+    echo "📡 测试 /health 端点..."
+    health=$(curl -s --connect-timeout 5 http://127.0.0.1:$PORT/health)
+    if [ $? -eq 0 ]; then
+        echo "✅ Health 端点: $health"
+    else
+        echo "⚠️  Health 端点不可访问"
+    fi
+}
+
+test_client() {
+    echo "🧪 测试 GLM-OCR 与 llama-server 集成..."
+
+    if [ ! -f $PIDFILE ] || ! kill -0 $(cat $PIDFILE) 2>/dev/null; then
+        echo "❌ GLM-OCR llama-server 服务未在运行,请先启动: $0 start"
+        return 1
+    fi
+
+    CONFIG_FILE="/Users/zhch158/workspace/repository.git/ocr_platform/ocr_tools/universal_doc_parser/config/bank_statement_yusys_local.yaml"
+    
+    echo "📄 配置文件: $CONFIG_FILE"
+    echo ""
+    echo "确保配置文件中 vl_recognition.api_url 指向: http://localhost:$PORT/v1/chat/completions"
+    echo ""
+    echo "测试命令示例:"
+    echo "  cd /Users/zhch158/workspace/repository.git/ocr_platform/ocr_tools/universal_doc_parser"
+    echo "  conda activate mineru2"
+    echo "  python parse.py --input /path/to/test/image.png --config $CONFIG_FILE --debug"
+    echo ""
+    echo "或者使用 curl 直接测试 API:"
+    echo "  curl -X POST http://localhost:$PORT/v1/chat/completions \\"
+    echo "    -H 'Content-Type: application/json' \\"
+    echo "    -d '{"
+    echo "      \"model\": \"glm-ocr\","
+    echo "      \"messages\": ["
+    echo "        {"
+    echo "          \"role\": \"user\","
+    echo "          \"content\": ["
+    echo "            {\"type\": \"text\", \"text\": \"Table Recognition:\"},"
+    echo "            {\"type\": \"image_url\", \"image_url\": {\"url\": \"file:///path/to/image.png\"}}"
+    echo "          ]"
+    echo "        }"
+    echo "      ],"
+    echo "      \"max_tokens\": 4096"
+    echo "    }'"
+}
+
+usage() {
+    echo "GLM-OCR llama-server 服务守护进程(macOS)"
+    echo "==========================================="
+    echo "用法: $0 {start|stop|restart|status|logs|config|test|test-client}"
+    echo ""
+    echo "命令:"
+    echo "  start       - 启动 GLM-OCR llama-server 服务"
+    echo "  stop        - 停止 GLM-OCR llama-server 服务"
+    echo "  restart     - 重启 GLM-OCR llama-server 服务"
+    echo "  status      - 显示服务状态和资源使用"
+    echo "  logs        - 显示服务日志(跟踪模式)"
+    echo "  config      - 显示当前配置"
+    echo "  test        - 测试 /v1/models API 端点"
+    echo "  test-client - 显示如何测试与配置文件集成"
+    echo ""
+    echo "配置(编辑脚本修改):"
+    echo "  Host: $HOST"
+    echo "  Port: $PORT"
+    echo "  主模型: $MODEL_PATH"
+    echo "  多模态投影器: $MMPROJ_PATH"
+    echo "  上下文长度: $CONTEXT_SIZE"
+    echo "  GPU 层数: $GPU_LAYERS (Metal)"
+    echo ""
+    echo "示例:"
+    echo "  ./glmocr_local_daemon.sh start"
+    echo "  ./glmocr_local_daemon.sh status"
+    echo "  ./glmocr_local_daemon.sh logs"
+    echo "  ./glmocr_local_daemon.sh test"
+    echo ""
+    echo "前置要求:"
+    echo "  1. 安装 llama.cpp: brew install llama.cpp"
+    echo "  2. 模型文件位于: ~/Library/Caches/llama.cpp/"
+    echo "  3. conda 环境 mineru2 已配置"
+}
+
+case "$1" in
+    start)
+        start
+        ;;
+    stop)
+        stop
+        ;;
+    restart)
+        stop
+        sleep 3
+        start
+        ;;
+    status)
+        status
+        ;;
+    logs)
+        logs
+        ;;
+    config)
+        config
+        ;;
+    test)
+        test_api
+        ;;
+    test-client)
+        test_client
+        ;;
+    *)
+        usage
+        exit 1
+        ;;
+esac

+ 378 - 0
ocr_tools/daemons/paddle_local_daemon.sh

@@ -0,0 +1,378 @@
+#!/bin/bash
+# filepath: ocr_platform/ocr_tools/daemons/paddleocr_local_daemon.sh
+# 对应: PaddleOCR-VL 本地 llama-server 服务(macOS),使用 GGUF 格式模型
+# 适用于 Mac M4 Pro 48G,使用 Metal GPU 加速
+# 模型下载地址: https://huggingface.co/PaddlePaddle/PaddleOCR-VL-1.5-GGUF
+# curl -X POST http://localhost:8081/v1/chat/completions -d @payload.json
+
+LOGDIR="$HOME/workspace/logs"
+mkdir -p $LOGDIR
+PIDFILE="$LOGDIR/paddleocr_llamaserver.pid"
+LOGFILE="$LOGDIR/paddleocr_llamaserver.log"
+
+# 配置参数
+CONDA_ENV="mineru2"
+PORT="8081"
+HOST="0.0.0.0"
+
+# 本地 GGUF 模型路径
+MODEL_PATH="$HOME/Library/Caches/llama.cpp/PaddlePaddle_PaddleOCR-VL-1.5-GGUF_PaddleOCR-VL-1.5.gguf"
+MMPROJ_PATH="$HOME/Library/Caches/llama.cpp/PaddlePaddle_PaddleOCR-VL-1.5-GGUF_PaddleOCR-VL-1.5-mmproj.gguf"
+
+# llama-server 参数
+CONTEXT_SIZE="16384"         # 上下文长度(需 >= max_tokens,推荐 8192-16384)
+GPU_LAYERS="99"              # Metal GPU 层数(99 表示全部)
+THREADS="8"                  # CPU 线程数(M4 Pro 建议值)
+BATCH_SIZE="512"             # 批处理大小
+UBATCH_SIZE="128"            # 微批处理大小
+
+# conda 环境激活
+if [ -f "$HOME/anaconda3/etc/profile.d/conda.sh" ]; then
+    source "$HOME/anaconda3/etc/profile.d/conda.sh"
+    conda activate $CONDA_ENV
+elif [ -f "$HOME/miniconda3/etc/profile.d/conda.sh" ]; then
+    source "$HOME/miniconda3/etc/profile.d/conda.sh"
+    conda activate $CONDA_ENV
+elif [ -f "/opt/miniconda3/etc/profile.d/conda.sh" ]; then
+    source /opt/miniconda3/etc/profile.d/conda.sh
+    conda activate $CONDA_ENV
+else
+    echo "Warning: conda initialization file not found, trying direct path"
+    export PATH="/opt/miniconda3/envs/$CONDA_ENV/bin:$PATH"
+fi
+
+start() {
+    if [ -f $PIDFILE ] && kill -0 $(cat $PIDFILE) 2>/dev/null; then
+        echo "PaddleOCR-VL llama-server 已在运行"
+        return 1
+    fi
+
+    echo "启动 PaddleOCR-VL llama-server 守护进程..."
+    echo "Host: $HOST, Port: $PORT"
+    echo "主模型: $MODEL_PATH"
+    echo "多模态投影器: $MMPROJ_PATH"
+    echo "上下文长度: $CONTEXT_SIZE"
+    echo "GPU 层数: $GPU_LAYERS (Metal)"
+    echo "线程数: $THREADS"
+
+    # 检查模型文件是否存在
+    if [ ! -f "$MODEL_PATH" ]; then
+        echo "❌ 主模型文件不存在: $MODEL_PATH"
+        echo "请确认模型已下载到 llama.cpp 缓存目录"
+        return 1
+    fi
+
+    if [ ! -f "$MMPROJ_PATH" ]; then
+        echo "❌ 多模态投影器文件不存在: $MMPROJ_PATH"
+        echo "请确认 mmproj 文件已下载"
+        return 1
+    fi
+
+    # 检查 llama-server 命令
+    if ! command -v llama-server >/dev/null 2>&1; then
+        echo "❌ llama-server 未找到"
+        echo "请安装: brew install llama.cpp"
+        return 1
+    fi
+
+    echo "🔧 使用 llama-server: $(which llama-server)"
+    echo "🔧 llama.cpp 版本: $(llama-server --version 2>&1 | head -1 || echo 'Unknown')"
+
+    echo "💻 系统信息:"
+    echo "  架构: $(uname -m)"
+    echo "  系统: $(uname -s)"
+    echo "  内存: $(sysctl -n hw.memsize | awk '{printf "%.1f GB", $1/1024/1024/1024}')"
+
+    # 启动 llama-server
+    nohup llama-server \
+        -m "$MODEL_PATH" \
+        --mmproj "$MMPROJ_PATH" \
+        --host $HOST \
+        --port $PORT \
+        --media-path /Users/zhch158/workspace \
+        -c $CONTEXT_SIZE \
+        -ngl $GPU_LAYERS \
+        -t $THREADS \
+        -b $BATCH_SIZE \
+        -ub $UBATCH_SIZE \
+        --temp 0 \
+        > $LOGFILE 2>&1 &
+
+    echo $! > $PIDFILE
+    echo "✅ PaddleOCR-VL llama-server 已启动,PID: $(cat $PIDFILE)"
+    echo "📋 日志文件: $LOGFILE"
+    echo "🌐 服务 URL: http://$HOST:$PORT"
+    echo "📖 OpenAI 兼容 API: http://localhost:$PORT/v1 (chat/completions, models)"
+    echo ""
+    echo "等待服务启动..."
+    sleep 5
+    status
+}
+
+stop() {
+    if [ ! -f $PIDFILE ]; then
+        echo "PaddleOCR-VL llama-server 未在运行"
+        return 1
+    fi
+
+    PID=$(cat $PIDFILE)
+    echo "停止 PaddleOCR-VL llama-server (PID: $PID)..."
+
+    kill $PID
+
+    for i in {1..30}; do
+        if ! kill -0 $PID 2>/dev/null; then
+            break
+        fi
+        echo "等待进程停止... ($i/30)"
+        sleep 1
+    done
+
+    if kill -0 $PID 2>/dev/null; then
+        echo "强制终止进程..."
+        kill -9 $PID
+    fi
+
+    rm -f $PIDFILE
+    echo "✅ PaddleOCR-VL llama-server 已停止"
+}
+
+status() {
+    if [ -f $PIDFILE ] && kill -0 $(cat $PIDFILE) 2>/dev/null; then
+        PID=$(cat $PIDFILE)
+        echo "✅ PaddleOCR-VL llama-server 正在运行 (PID: $PID)"
+        echo "🌐 服务 URL: http://$HOST:$PORT"
+        echo "📋 日志文件: $LOGFILE"
+
+        # 检查端口监听状态
+        if lsof -nP -iTCP:$PORT -sTCP:LISTEN >/dev/null 2>&1; then
+            echo "🔗 端口 $PORT 正在监听"
+        else
+            echo "⚠️  端口 $PORT 未在监听(服务可能正在启动)"
+        fi
+
+        # 检查 API 响应
+        if command -v curl >/dev/null 2>&1; then
+            if curl -s --connect-timeout 2 http://127.0.0.1:$PORT/v1/models > /dev/null 2>&1; then
+                echo "🎯 API 响应正常"
+            else
+                echo "⚠️  API 无响应(服务可能正在启动)"
+            fi
+        fi
+
+        # 显示进程内存使用
+        if command -v ps >/dev/null 2>&1; then
+            MEM=$(ps -o rss= -p $PID 2>/dev/null | awk '{printf "%.2f GB", $1/1024/1024}')
+            if [ -n "$MEM" ]; then
+                echo "💾 内存使用: $MEM"
+            fi
+        fi
+
+        if [ -f $LOGFILE ]; then
+            echo "📄 最近日志(最后 3 行):"
+            tail -3 $LOGFILE | sed 's/^/  /'
+        fi
+    else
+        echo "❌ PaddleOCR-VL llama-server 未在运行"
+        if [ -f $PIDFILE ]; then
+            echo "删除过期的 PID 文件..."
+            rm -f $PIDFILE
+        fi
+    fi
+}
+
+logs() {
+    if [ -f $LOGFILE ]; then
+        echo "📄 PaddleOCR-VL llama-server 日志:"
+        echo "====================="
+        tail -f $LOGFILE
+    else
+        echo "❌ 日志文件不存在: $LOGFILE"
+    fi
+}
+
+config() {
+    echo "📋 当前配置:"
+    echo "  Conda 环境: $CONDA_ENV"
+    echo "  Host: $HOST"
+    echo "  Port: $PORT"
+    echo "  主模型路径: $MODEL_PATH"
+    echo "  多模态投影器: $MMPROJ_PATH"
+    echo "  上下文长度: $CONTEXT_SIZE"
+    echo "  GPU 层数: $GPU_LAYERS"
+    echo "  线程数: $THREADS"
+    echo "  批处理大小: $BATCH_SIZE"
+    echo "  微批处理大小: $UBATCH_SIZE"
+    echo "  PID 文件: $PIDFILE"
+    echo "  日志文件: $LOGFILE"
+
+    echo ""
+    echo "📦 模型文件检查:"
+    if [ -f "$MODEL_PATH" ]; then
+        SIZE=$(du -h "$MODEL_PATH" | cut -f1)
+        echo "  ✅ 主模型存在 ($SIZE)"
+    else
+        echo "  ❌ 主模型不存在"
+    fi
+
+    if [ -f "$MMPROJ_PATH" ]; then
+        SIZE=$(du -h "$MMPROJ_PATH" | cut -f1)
+        echo "  ✅ 多模态投影器存在 ($SIZE)"
+    else
+        echo "  ❌ 多模态投影器不存在"
+    fi
+
+    echo ""
+    echo "🔧 环境检查:"
+    echo "  llama-server: $(which llama-server 2>/dev/null || echo '未安装')"
+    if command -v llama-server >/dev/null 2>&1; then
+        LLAMA_VERSION=$(llama-server --version 2>&1 | head -1 || echo 'Unknown')
+        echo "  版本: $LLAMA_VERSION"
+    fi
+    echo "  Conda: $(which conda 2>/dev/null || echo '未找到')"
+    echo "  当前 Python: $(which python 2>/dev/null || echo '未找到')"
+
+    echo ""
+    echo "💻 系统信息:"
+    echo "  架构: $(uname -m)"
+    echo "  系统版本: $(sw_vers -productVersion 2>/dev/null || echo 'Unknown')"
+    echo "  总内存: $(sysctl -n hw.memsize 2>/dev/null | awk '{printf "%.1f GB", $1/1024/1024/1024}' || echo 'Unknown')"
+    echo "  CPU 核心: $(sysctl -n hw.ncpu 2>/dev/null || echo 'Unknown')"
+}
+
+test_api() {
+    echo "🧪 测试 PaddleOCR-VL llama-server API..."
+
+    if [ ! -f $PIDFILE ] || ! kill -0 $(cat $PIDFILE) 2>/dev/null; then
+        echo "❌ PaddleOCR-VL llama-server 服务未在运行"
+        return 1
+    fi
+
+    if ! command -v curl >/dev/null 2>&1; then
+        echo "❌ curl 命令未找到"
+        return 1
+    fi
+
+    echo "📡 测试 /v1/models 端点..."
+    response=$(curl -s --connect-timeout 10 http://127.0.0.1:$PORT/v1/models)
+    if [ $? -eq 0 ]; then
+        echo "✅ Models 端点可访问"
+        echo "$response" | python -m json.tool 2>/dev/null || echo "$response"
+    else
+        echo "❌ Models 端点不可访问"
+    fi
+
+    echo ""
+    echo "📡 测试 /health 端点..."
+    health=$(curl -s --connect-timeout 5 http://127.0.0.1:$PORT/health)
+    if [ $? -eq 0 ]; then
+        echo "✅ Health 端点: $health"
+    else
+        echo "⚠️  Health 端点不可访问"
+    fi
+}
+
+test_client() {
+    echo "🧪 测试 PaddleOCR-VL 与 llama-server 集成..."
+
+    if [ ! -f $PIDFILE ] || ! kill -0 $(cat $PIDFILE) 2>/dev/null; then
+        echo "❌ PaddleOCR-VL llama-server 服务未在运行,请先启动: $0 start"
+        return 1
+    fi
+
+    CONFIG_FILE="/Users/zhch158/workspace/repository.git/ocr_platform/ocr_tools/universal_doc_parser/config/bank_statement_paddleocr_local.yaml"
+    
+    echo "📄 配置文件: $CONFIG_FILE"
+    echo ""
+    echo "确保配置文件中 vl_recognition.api_url 指向: http://localhost:$PORT/v1/chat/completions"
+    echo ""
+    echo "测试命令示例:"
+    echo "  cd /Users/zhch158/workspace/repository.git/ocr_platform/ocr_tools/universal_doc_parser"
+    echo "  conda activate mineru2"
+    echo "  python parse.py --input /path/to/test/image.png --config $CONFIG_FILE --debug"
+    echo ""
+    echo "或者使用 curl 直接测试 API:"
+    echo "  curl -X POST http://localhost:$PORT/v1/chat/completions \\"
+    echo "    -H 'Content-Type: application/json' \\"
+    echo "    -d '{"
+    echo "      \"model\": \"paddleocr-vl\","
+    echo "      \"messages\": ["
+    echo "        {"
+    echo "          \"role\": \"user\","
+    echo "          \"content\": ["
+    echo "            {\"type\": \"text\", \"text\": \"Table Recognition:\"},"
+    echo "            {\"type\": \"image_url\", \"image_url\": {\"url\": \"file:///path/to/image.png\"}}"
+    echo "          ]"
+    echo "        }"
+    echo "      ],"
+    echo "      \"max_tokens\": 4096"
+    echo "    }'"
+}
+
+usage() {
+    echo "PaddleOCR-VL llama-server 服务守护进程(macOS)"
+    echo "==========================================="
+    echo "用法: $0 {start|stop|restart|status|logs|config|test|test-client}"
+    echo ""
+    echo "命令:"
+    echo "  start       - 启动 PaddleOCR-VL llama-server 服务"
+    echo "  stop        - 停止 PaddleOCR-VL llama-server 服务"
+    echo "  restart     - 重启 PaddleOCR-VL llama-server 服务"
+    echo "  status      - 显示服务状态和资源使用"
+    echo "  logs        - 显示服务日志(跟踪模式)"
+    echo "  config      - 显示当前配置"
+    echo "  test        - 测试 /v1/models API 端点"
+    echo "  test-client - 显示如何测试与配置文件集成"
+    echo ""
+    echo "配置(编辑脚本修改):"
+    echo "  Host: $HOST"
+    echo "  Port: $PORT"
+    echo "  主模型: $MODEL_PATH"
+    echo "  多模态投影器: $MMPROJ_PATH"
+    echo "  上下文长度: $CONTEXT_SIZE"
+    echo "  GPU 层数: $GPU_LAYERS (Metal)"
+    echo ""
+    echo "示例:"
+    echo "  ./paddleocr_local_daemon.sh start"
+    echo "  ./paddleocr_local_daemon.sh status"
+    echo "  ./paddleocr_local_daemon.sh logs"
+    echo "  ./paddleocr_local_daemon.sh test"
+    echo ""
+    echo "前置要求:"
+    echo "  1. 安装 llama.cpp: brew install llama.cpp"
+    echo "  2. 模型文件位于: ~/Library/Caches/llama.cpp/"
+    echo "  3. conda 环境 mineru2 已配置"
+}
+
+case "$1" in
+    start)
+        start
+        ;;
+    stop)
+        stop
+        ;;
+    restart)
+        stop
+        sleep 3
+        start
+        ;;
+    status)
+        status
+        ;;
+    logs)
+        logs
+        ;;
+    config)
+        config
+        ;;
+    test)
+        test_api
+        ;;
+    test-client)
+        test_client
+        ;;
+    *)
+        usage
+        exit 1
+        ;;
+esac

+ 22 - 0
ocr_tools/daemons/payload.json

@@ -0,0 +1,22 @@
+{
+	"model": "ocr-vl",
+	"messages": [
+		{
+			"role": "user",
+			"content": [
+				{
+					"type": "text",
+					"text": "Table Recognition:"
+				},
+				{
+					"type": "image_url",
+					"image_url": {
+						"url": "file://repository.git/ocr_platform/ocr_tools/daemons/curl_local_img.png"
+					}
+				}
+			]
+		}
+	],
+	"max_tokens": 16384,
+	"temperature": 0.1
+}

+ 7 - 0
ocr_tools/ocr_batch/pdf_list_local.txt

@@ -0,0 +1,7 @@
+# 文件名<TAB>","场景(bank_statement / financial_report)
+对公_招商银行图.pdf,bank_statement
+B用户_扫描流水.pdf,bank_statement
+康强_北京农村商业银行.pdf,bank_statement
+施博深.pdf,bank_statement
+山西云集科技有限公司.pdf,bank_statement
+许_民生银行图.pdf,bank_statement

+ 16 - 0
ocr_tools/ocr_batch/processor_configs.yaml

@@ -62,6 +62,22 @@ processors:
     venv: "conda activate mineru2"
     description: "YUSYS MinerU OCR 框架"
 
+  yusys_ocr_local:
+    script: "/Users/zhch158/workspace/repository.git/ocr_platform/ocr_tools/universal_doc_parser/main_v2.py"
+    input_arg: "--input"
+    output_arg: "--output_dir"
+    scene_arg: "--scene"
+    extra_args:
+      - "--config=/Users/zhch158/workspace/repository.git/ocr_platform/ocr_tools/universal_doc_parser/config/bank_statement_yusys_local.yaml"
+      - "--pages=1-35"
+      - "--streaming"
+      - "--debug"
+      - "--log_level=DEBUG"
+    output_subdir: "bank_statement_yusys_local"
+    log_subdir: "logs/bank_statement_yusys_v4_local"
+    venv: "conda activate mineru2"
+    description: "YUSYS(local) Wired UNET OCR GLM-OCR"
+
   # -------------------------------------------------------------------------
   # PaddleOCR-VL 处理器
   # -------------------------------------------------------------------------

+ 200 - 0
ocr_tools/universal_doc_parser/config/bank_statement_yusys_local.yaml

@@ -0,0 +1,200 @@
+# 银行交易流水场景配置 - V4版本
+# Pipeline V3逻辑: 有线表格使用MinerU UNet, 无线表格/seal使用GLM-OCR VLM
+# llama-server -hf ggml-org/GLM-OCR-GGUF
+scene_name: "bank_statement_yusys_local"
+
+description: "银行流水V4: PP-DocLayoutV3 layout + PaddleOCR + MinerU UNet(有线表格)+ GLM-OCR VLM(无线表格/seal)"
+
+input:
+  supported_formats: [".pdf", ".png", ".jpg", ".jpeg", ".bmp", ".tiff"]
+  dpi: 200
+
+preprocessor:
+  module: "mineru"
+  orientation_classifier:
+    enabled: true
+    model_name: "paddle_orientation_classification"
+    model_dir: null  # 使用默认路径
+  unwarping:
+    enabled: false
+
+# ============================================================
+# Layout 检测配置 - 智能路由器(按场景直接选择模型)
+# ============================================================
+layout_detection:
+  module: "smart_router"
+  strategy: "scene"  # 按场景直接选择模型,不走ocr_eval
+
+  # 场景策略:指定场景直接选用的布局模型
+  scene_strategy:
+    bank_statement:
+      model: "docling"
+    financial_report:
+      model: "paddle_ppdoclayoutv3"
+  default_model: "docling"
+
+  # 配置多个模型
+  models:
+    docling:
+      module: "docling"
+      model_name: "docling-layout-old"
+      model_dir: "ds4sd/docling-layout-old"
+      device: "cpu"
+      conf: 0.3
+      num_threads: 4
+
+    paddle_ppdoclayoutv3:
+      module: "paddle"
+      model_name: "PP-DocLayoutV3"
+      model_dir: "PaddlePaddle/PP-DocLayoutV3_safetensors"
+      device: "cpu"
+      conf: 0.3
+      num_threads: 4
+      batch_size: 1
+  
+  # 后处理配置
+  post_process:
+    # 将大面积文本块转换为表格(后处理)
+    convert_large_text_to_table: true  # 是否启用
+    min_text_area_ratio: 0.25         # 最小面积占比(25%)
+    min_text_width_ratio: 0.4         # 最小宽度占比(40%)
+    min_text_height_ratio: 0.3        # 最小高度占比(30%)
+
+  # Debug 可视化配置
+  debug_options:
+    enabled: false              # 由命令行 --debug 统一控制,勿在此 hardcode true
+    output_dir: null             # 调试输出目录;null不输出
+    prefix: ""                  # 保存文件名前缀(如设置为页码)
+
+# ============================================================
+# OCR 识别配置
+# ============================================================
+ocr_recognition:
+  module: "mineru"
+  language: "ch"
+  det_threshold: 0.5
+  unclip_ratio: 1.5
+  enable_merge_det_boxes: false
+  batch_size: 8
+  device: "cpu"
+
+# ============================================================
+# 表格分类配置(自动区分有线/无线表格)
+# ============================================================
+table_classification:
+  enabled: true               # 启用自动表格分类
+  module: "paddle"            # 分类模型:paddle(MinerU PaddleTableClsModel)
+  confidence_threshold: 0.5   # 分类置信度阈值
+  batch_size: 16              # 批处理大小
+
+  # Debug 可视化配置
+  debug_options:
+    enabled: false              # 由命令行 --debug 统一控制,勿在此 hardcode true
+    output_dir: null             # 调试输出目录;null不输出
+    save_table_lines: true       # 保存表格线可视化(unet横线/竖线叠加)
+    image_format: "png"          # 可视化图片格式:png/jpg
+    prefix: ""                  # 保存文件名前缀(如设置为页码/表格序号)
+
+# ============================================================
+# 有线表格识别专用配置(MinerU UNet)
+# ============================================================
+table_recognition_wired:
+  use_wired_unet: true
+  upscale_ratio: 3.333
+  need_ocr: true
+  row_threshold: 10
+  col_threshold: 15
+  ocr_conf_threshold: 0.9       # 单元格 OCR 置信度阈值
+  cell_crop_margin: 2
+  use_custom_postprocess: true  # 是否使用自定义后处理(默认启用)
+
+  # 是否启用倾斜矫正
+  enable_deskew: true
+
+  # 🆕 启用多源单元格融合
+  use_cell_fusion: true
+  
+  # 融合引擎配置
+  cell_fusion:
+    # RT-DETR 模型路径(必需)
+    rtdetr_model_path: "/Users/zhch158/models/pytorch_models/Table/RT-DETR-L_wired_table_cell_det.onnx"
+    
+    # 融合权重
+    unet_weight: 0.6        # UNet 权重(结构性强)
+    rtdetr_weight: 0.4      # RT-DETR 权重(鲁棒性强)
+    
+    # 阈值配置
+    iou_merge_threshold: 0.7    # 高IoU合并阈值(>0.7则加权平均)
+    iou_nms_threshold: 0.5      # NMS去重阈值
+    rtdetr_conf_threshold: 0.5  # RT-DETR置信度阈值
+    
+    # 功能开关
+    enable_ocr_compensation: true      # 启用OCR边缘补偿
+
+  # Debug 可视化配置
+  debug_options:
+    enabled: false              # 由命令行 --debug 统一控制,勿在此 hardcode true
+    output_dir: null             # 调试输出目录;null不输出
+    save_table_lines: true       # 保存表格线可视化(unet横线/竖线叠加)
+    save_connected_components: true  # 保存连通域提取的单元格图
+    save_grid_structure: true    # 保存逻辑网格结构(row/col/rowspan/colspan)
+    save_text_overlay: true      # 保存文本填充覆盖图
+    image_format: "png"          # 可视化图片格式:png/jpg
+    prefix: ""                  # 保存文件名前缀(如设置为页码/表格序号)
+
+# ============================================================
+# VL识别配置 - 使用 GLM-OCR(无线表格 + seal识别)
+# ============================================================
+vl_recognition:
+  module: "glmocr"
+  api_url: "http://localhost:8080/v1/chat/completions"
+  api_key: null  # 可选,如需要可填写
+  model: "glm-ocr"
+  max_image_size: 3500  # GLM-OCR 推荐的最大图片尺寸
+  resize_mode: 'max'    # 缩放模式: 'max' 保持宽高比, 'fixed' 固定尺寸
+  verify_ssl: false
+  
+  # Task prompt mapping - 针对不同任务使用不同提示词
+  task_prompt_mapping:
+    text: "Text Recognition:"
+    table: "Table Recognition:"
+    formula: "Formula Recognition:"
+    seal: "Seal Recognition:"  # 印章识别的专用提示词
+  
+  # 模型参数
+  model_params:
+    connection_pool_size: 128  # HTTP 连接池大小(应 >= max_workers)
+    http_timeout: 300          # HTTP 请求超时时间(秒)
+    connect_timeout: 30        # 连接超时时间(秒)
+    retry_max_attempts: 2      # 最大重试次数
+    retry_backoff_base_seconds: 0.5
+    retry_backoff_max_seconds: 8.0
+    retry_jitter_ratio: 0.2
+    retry_status_codes: [429, 500, 502, 503, 504]
+    max_tokens: 16384
+    temperature: 0.1
+    top_p: 0.0001
+    top_k: 1
+    repetition_penalty: 1.1
+  
+  # 场景特定配置
+  table_recognition:
+
+# ============================================================
+# 输出配置
+# ============================================================
+output:
+  create_subdir: false
+  save_pdf_images: true
+  save_json: true
+  save_page_json: true
+  save_markdown: true
+  save_page_markdown: true
+  save_html: true
+  save_layout_image: true
+  save_ocr_image: true
+  draw_type_label: true
+  draw_bbox_number: true
+  save_enhanced_json: true
+  normalize_numbers: true
+  debug_mode: false

+ 2 - 2
ocr_tools/universal_doc_parser/core/layout_model_router.py

@@ -39,8 +39,8 @@ class SmartLayoutRouter(BaseLayoutDetector):
         self.scene_name = config.get('scene_name', None)
         self.scene_strategy = config.get('scene_strategy', {})
         self.default_model = config.get('default_model', None)
-        # 调试模式支持
-        self.debug_mode = config.get('debug_mode', False)
+        # 调试模式支持(兼容 debug_mode 和 debug_options.enabled 两种配置方式)
+        self.debug_mode = config.get('debug_mode', config.get('debug_options', {}).get('enabled', False))
         self.output_dir = config.get('output_dir', None)
         self.page_name = None  # 将在 detect 方法中设置
         # 分数差距阈值:当模型间分数差距小于此值时,优先选择 docling

+ 10 - 8
ocr_tools/universal_doc_parser/core/pipeline_manager_v2.py

@@ -99,14 +99,19 @@ class EnhancedDocPipeline:
     
     # ==================== 初始化 ====================
     
-    def __init__(self, config_path: str):
+    def __init__(self, config_path_or_dict, config_is_dict=False):
         """
         初始化流水线
         
         Args:
-            config_path: 配置文件路径
+            config_path_or_dict: 配置文件路径或配置字典
+            config_is_dict: 是否为配置字典(True 时 config_path_or_dict 是字典)
         """
-        self.config = ConfigManager.load_config(config_path)
+        if config_is_dict:
+            self.config = config_path_or_dict
+        else:
+            self.config = ConfigManager.load_config(config_path_or_dict)
+            
         self.scene_name = self.config.get('scene_name', 'unknown')
         self.debug_mode = self.config.get('output', {}).get('debug_mode', False)
         
@@ -429,11 +434,8 @@ class EnhancedDocPipeline:
                     except Exception as e:
                         logger.warning(f"⚠️ Pre-OCR text box detection for layout evaluation failed: {e}")
             
-            # 启用调试模式时,设置调试信息(参考 layout_model_router.py 的实现)
-            if self.debug_mode:
-                # 对于 SmartLayoutRouter 或普通 layout detector,都设置这些属性
-                if hasattr(self.layout_detector, 'debug_mode'):
-                    self.layout_detector.debug_mode = self.debug_mode  # type: ignore
+            # 注入每页运行时信息(output_dir/page_name 仅在 layout detector 自身 debug 开启时才有意义)
+            if hasattr(self.layout_detector, 'debug_mode') and self.layout_detector.debug_mode:  # type: ignore
                 if output_dir and hasattr(self.layout_detector, 'output_dir'):
                     self.layout_detector.output_dir = output_dir  # type: ignore
                 if page_name and hasattr(self.layout_detector, 'page_name'):

+ 4 - 3
ocr_tools/universal_doc_parser/core/pipeline_manager_v2_streaming.py

@@ -56,15 +56,16 @@ class StreamingDocPipeline(EnhancedDocPipeline):
     - 最后统一生成完整Markdown(跨页表格合并)
     """
     
-    def __init__(self, config_path: str, output_dir: str):
+    def __init__(self, config_path_or_dict, output_dir: str, config_is_dict=False):
         """
         初始化流式处理流水线
         
         Args:
-            config_path: 配置文件路径
+            config_path_or_dict: 配置文件路径或配置字典
             output_dir: 输出目录(用于立即保存每页结果)
+            config_is_dict: 是否为配置字典(True 时 config_path_or_dict 是字典)
         """
-        super().__init__(config_path)
+        super().__init__(config_path_or_dict, config_is_dict=config_is_dict)
         self.output_dir = Path(output_dir)
         self.output_dir.mkdir(parents=True, exist_ok=True)
         

+ 159 - 16
ocr_tools/universal_doc_parser/main_v2.py

@@ -90,14 +90,45 @@ def _handle_dry_run(args: argparse.Namespace) -> bool:
     return False
 
 
-def _create_pipeline(streaming: bool, config_path: str, output_dir: str):
-    """创建并初始化处理流水线"""
+def _create_pipeline(
+    streaming: bool,
+    config_path: str,
+    output_dir: str,
+    debug: bool = False,
+    debug_layout: bool = False,
+    debug_table: bool = False,
+    debug_ocr: bool = False
+):
+    """
+    创建并初始化处理流水线(应用 debug 覆盖)
+    
+    Args:
+        streaming: 是否使用流式处理模式
+        config_path: 配置文件路径
+        output_dir: 输出目录
+        debug: 全局 debug 开关
+        debug_layout: 布局检测 debug 开关
+        debug_table: 表格识别 debug 开关
+        debug_ocr: OCR 识别 debug 开关
+        
+    Returns:
+        初始化后的 pipeline 实例
+    """
+    # 1. 先加载配置
+    from core.config_manager import ConfigManager
+    config = ConfigManager.load_config(config_path)
+    
+    # 2. 应用 debug 覆盖(在创建 pipeline 之前)
+    if debug or debug_layout or debug_table or debug_ocr:
+        _apply_debug_overrides_to_config(config, debug, debug_layout, debug_table, debug_ocr)
+    
+    # 3. 创建 pipeline(adapter 会读取到已修改的 config)
     if streaming:
         logger.info("🔄 Using streaming processing mode (memory-efficient)")
-        pipeline = StreamingDocPipeline(config_path, output_dir)
+        pipeline = StreamingDocPipeline(config, output_dir, config_is_dict=True)
     else:
         logger.info("🔄 Using batch processing mode (all pages in memory)")
-        pipeline = EnhancedDocPipeline(config_path)
+        pipeline = EnhancedDocPipeline(config, config_is_dict=True)
     
     return pipeline
 
@@ -122,6 +153,88 @@ def _get_default_output_config(debug: bool) -> dict:
     }
 
 
+def _apply_debug_overrides_to_config(
+    config: dict,
+    debug: bool,
+    debug_layout: bool,
+    debug_table: bool,
+    debug_ocr: bool
+):
+    """
+    应用命令行 debug 参数覆盖配置文件设置(在创建 pipeline 之前)
+    
+    优先级规则:
+    1. --debug: 启用所有模块的 debug
+    2. --debug-layout/--debug-table/--debug-ocr: 精细控制各模块
+    3. 配置文件的 debug_options 只提供默认值
+    
+    Args:
+        config: 配置字典(会被直接修改)
+        debug: 全局 debug 开关
+        debug_layout: 布局检测 debug 开关
+        debug_table: 表格识别 debug 开关
+        debug_ocr: OCR 识别 debug 开关
+    """
+    # 确定需要启用哪些模块的 debug
+    enable_layout_debug = debug or debug_layout
+    enable_table_debug = debug or debug_table
+    enable_ocr_debug = debug or debug_ocr
+    
+    # 1. 布局检测 debug
+    if enable_layout_debug:
+        if 'layout_detection' in config:
+            if 'debug_options' not in config['layout_detection']:
+                config['layout_detection']['debug_options'] = {}
+            config['layout_detection']['debug_options']['enabled'] = True
+            logger.info("✅ 启用布局检测 debug 输出")
+    
+    # 2. 表格分类 debug
+    if enable_table_debug:
+        if 'table_classification' in config:
+            if 'debug_options' not in config['table_classification']:
+                config['table_classification']['debug_options'] = {}
+            config['table_classification']['debug_options']['enabled'] = True
+            logger.info("✅ 启用表格分类 debug 输出")
+    
+    # 3. 有线表格识别 debug
+    if enable_table_debug:
+        if 'table_recognition_wired' in config:
+            if 'debug_options' not in config['table_recognition_wired']:
+                config['table_recognition_wired']['debug_options'] = {}
+            config['table_recognition_wired']['debug_options']['enabled'] = True
+            logger.info("✅ 启用有线表格识别 debug 输出")
+    
+    # 4. OCR 识别 debug(如果有 debug_options)
+    if enable_ocr_debug:
+        if 'ocr_recognition' in config:
+            if 'debug_options' not in config['ocr_recognition']:
+                config['ocr_recognition']['debug_options'] = {}
+            config['ocr_recognition']['debug_options']['enabled'] = True
+            logger.info("✅ 启用 OCR 识别 debug 输出")
+    
+    # 5. 更新输出配置
+    if enable_layout_debug or enable_ocr_debug or enable_table_debug:
+        output_config = config.get('output', {})
+        output_config['debug_mode'] = True
+        if enable_layout_debug or enable_ocr_debug:
+            output_config.setdefault('save_layout_image', True)
+            output_config.setdefault('save_ocr_image', True)
+    
+    # 输出当前 debug 状态
+    if debug:
+        logger.info("🐛 全局 Debug 模式已启用(所有模块)")
+    else:
+        debug_modules = []
+        if debug_layout:
+            debug_modules.append("布局检测")
+        if debug_table:
+            debug_modules.append("表格识别")
+        if debug_ocr:
+            debug_modules.append("OCR识别")
+        if debug_modules:
+            logger.info(f"🐛 Debug 模式已启用: {', '.join(debug_modules)}")
+
+
 def setup_logging(log_level: str = "INFO", log_file: Optional[str] = None):
     """设置日志"""
     logger.remove()
@@ -148,6 +261,9 @@ def process_single_input(
     config_path: Path,
     output_dir: Path,
     debug: bool = False,
+    debug_layout: bool = False,
+    debug_table: bool = False,
+    debug_ocr: bool = False,
     scene: Optional[str] = None,
     page_range: Optional[str] = None,
     streaming: bool = False
@@ -159,7 +275,10 @@ def process_single_input(
         input_path: 输入路径
         config_path: 配置文件路径
         output_dir: 输出目录
-        debug: 是否开启debug模式
+        debug: 全局debug开关(启用所有模块debug)
+        debug_layout: 仅启用布局检测debug
+        debug_table: 仅启用表格识别debug
+        debug_ocr: 仅启用OCR识别debug
         scene: 场景类型覆盖
         page_range: 页面范围(如 "1-5,7,9-12")
         streaming: 是否使用流式处理模式(按页处理,立即保存,节省内存)
@@ -168,16 +287,17 @@ def process_single_input(
         处理结果和输出路径
     """
     try:
-        # 创建流水线
-        pipeline = _create_pipeline(streaming, str(config_path), str(output_dir))
+        # 创建流水线(debug 覆盖已在 _create_pipeline 中应用)
+        pipeline = _create_pipeline(
+            streaming, 
+            str(config_path), 
+            str(output_dir),
+            debug=debug,
+            debug_layout=debug_layout,
+            debug_table=debug_table,
+            debug_ocr=debug_ocr
+        )
         output_config = pipeline.config.get('output', {}) or _get_default_output_config(debug)
-
-        # 命令行 --debug 优先级最高:覆盖 yaml 中的所有 debug 设置
-        if debug:
-            pipeline.debug_mode = True
-            output_config['debug_mode'] = True
-            output_config.setdefault('save_layout_image', True)
-            output_config.setdefault('save_ocr_image', True)
         
         use_context = not streaming and hasattr(pipeline, '__enter__')
         if use_context:
@@ -324,9 +444,14 @@ def main():
   # 处理图片目录
   python main_v2.py -i ./images/ -c config/bank_statement_paddle_vl.yaml
   
-  # 开启debug模式(输出可视化图片)
+  # 开启全局debug模式(所有模块输出可视化图片)
   python main_v2.py -i doc.pdf -c config.yaml --debug
   
+  # 开启特定模块的debug(精细控制)
+  python main_v2.py -i doc.pdf -c config.yaml --debug-layout        # 仅布局debug
+  python main_v2.py -i doc.pdf -c config.yaml --debug-table         # 仅表格debug
+  python main_v2.py -i doc.pdf -c config.yaml --debug-layout --debug-table  # 组合
+  
   # 指定输出目录
   python main_v2.py -i doc.pdf -c config.yaml -o ./my_output/
   
@@ -365,7 +490,22 @@ def main():
     parser.add_argument(
         "--debug",
         action="store_true",
-        help="开启debug模式(输出layout和OCR可视化图片)"
+        help="开启全局debug模式(启用所有模块的调试输出)"
+    )
+    parser.add_argument(
+        "--debug-layout",
+        action="store_true",
+        help="仅开启布局检测的debug输出"
+    )
+    parser.add_argument(
+        "--debug-table",
+        action="store_true",
+        help="仅开启表格识别的debug输出"
+    )
+    parser.add_argument(
+        "--debug-ocr",
+        action="store_true",
+        help="仅开启OCR识别的debug输出"
     )
     parser.add_argument(
         "--log_level",
@@ -407,6 +547,9 @@ def main():
         config_path=Path(args.config),
         output_dir=Path(args.output_dir),
         debug=args.debug,
+        debug_layout=args.debug_layout,
+        debug_table=args.debug_table,
+        debug_ocr=args.debug_ocr,
         scene=args.scene,
         page_range=args.pages,
         streaming=args.streaming

+ 4 - 4
ocr_validator/config/A用户_单元格扫描流水.yaml

@@ -18,11 +18,11 @@ document:
       description: "YUSYS-OCR框架 v3.0"
       enabled: true
 
-    # bank_statement_yusys_v2
+    # bank_statement_yusys_local
     - tool: "mineru"
-      result_dir: "bank_statement_yusys_v2"
-      image_dir: "mineru_vllm_results/{{name}}"
-      description: "YUSYS统一OCR框架"
+      result_dir: "bank_statement_yusys_local"
+      image_dir: "bank_statement_yusys_local/{{name}}"
+      description: "YUSYS-OCR框架(local) GLM-OCR"
       enabled: true
 
      # MinerU

+ 7 - 0
ocr_validator/config/B用户_扫描流水.yaml

@@ -11,6 +11,13 @@ document:
       description: "YUSYS-OCR框架 v4.0 GLM-OCR"
       enabled: true
 
+    # bank_statement_yusys_local
+    - tool: "mineru"
+      result_dir: "bank_statement_yusys_local"
+      image_dir: "bank_statement_yusys_local/{{name}}"
+      description: "YUSYS-OCR框架(local) GLM-OCR"
+      enabled: true
+
     # bank_statement_yusys_v3
     - tool: "mineru"
       result_dir: "bank_statement_yusys_v3"

+ 6 - 6
ocr_validator/config/乔_建设银行图.yaml

@@ -18,12 +18,12 @@ document:
       description: "YUSYS-OCR框架 v3.0"
       enabled: true
 
-    # # bank_statement_yusys_v2
-    # - tool: "mineru"
-    #   result_dir: "bank_statement_yusys_v2"
-    #   image_dir: "mineru_vllm_results/{{name}}"
-    #   description: "YUSYS统一OCR框架"
-    #   enabled: true
+    # bank_statement_yusys_local
+    - tool: "mineru"
+      result_dir: "bank_statement_yusys_local"
+      image_dir: "bank_statement_yusys_local/{{name}}"
+      description: "YUSYS-OCR框架(local) GLM-OCR"
+      enabled: true
 
     #  # MinerU
     # - tool: "mineru"

+ 7 - 0
ocr_validator/config/付_工商银行943825图.yaml

@@ -16,6 +16,13 @@ document:
       result_dir: "bank_statement_yusys_v3"
       image_dir: "bank_statement_yusys_v3/{{name}}"
       description: "YUSYS-OCR框架 v3.0"
+
+      enabled: true
+    # bank_statement_yusys_local
+    - tool: "mineru"
+      result_dir: "bank_statement_yusys_local"
+      image_dir: "bank_statement_yusys_local/{{name}}"
+      description: "YUSYS-OCR框架(local) GLM-OCR"
       enabled: true
 
     # bank_statement_mineru_vl

+ 7 - 7
ocr_validator/config/对公_招商银行图.yaml

@@ -11,6 +11,13 @@ document:
       description: "YUSYS-OCR框架 v4.0 GLM-OCR"
       enabled: true
 
+    # bank_statement_yusys_local
+    - tool: "mineru"
+      result_dir: "bank_statement_yusys_local"
+      image_dir: "bank_statement_yusys_local/{{name}}"
+      description: "YUSYS-OCR框架(local) GLM-OCR"
+      enabled: true
+
     # bank_statement_yusys_v3
     - tool: "mineru"
       result_dir: "bank_statement_yusys_v3"
@@ -18,13 +25,6 @@ document:
       description: "YUSYS-OCR框架 v3.0"
       enabled: true
 
-    # bank_statement_yusys_v2
-    - tool: "mineru"
-      result_dir: "bank_statement_yusys_v2"
-      image_dir: "mineru_vllm_results/{{name}}"
-      description: "YUSYS统一OCR框架"
-      enabled: true
-
      # MinerU
     - tool: "mineru"
       result_dir: "mineru_vllm_results"

+ 7 - 0
ocr_validator/config/山西云集科技有限公司.yaml

@@ -11,6 +11,13 @@ document:
       description: "YUSYS-OCR框架 v4.0 GLM-OCR"
       enabled: true
 
+    # bank_statement_yusys_local
+    - tool: "mineru"
+      result_dir: "bank_statement_yusys_local"
+      image_dir: "bank_statement_yusys_local/{{name}}"
+      description: "YUSYS-OCR框架(local) GLM-OCR"
+      enabled: true
+
     # bank_statement_yusys_v3
     - tool: "mineru"
       result_dir: "bank_statement_yusys_v3"

+ 7 - 0
ocr_validator/config/康强_北京农村商业银行.yaml

@@ -12,6 +12,13 @@ document:
       description: "YUSYS-OCR框架 v4.0 GLM-OCR"
       enabled: true
 
+    # bank_statement_yusys_local
+    - tool: "mineru"
+      result_dir: "bank_statement_yusys_local"
+      image_dir: "bank_statement_yusys_local/{{name}}"
+      description: "YUSYS-OCR框架(local) GLM-OCR"
+      enabled: true
+
     # bank_statement_yusys_v3
     - tool: "mineru"
       result_dir: "bank_statement_yusys_v3"

+ 7 - 0
ocr_validator/config/张_微信图.yaml

@@ -18,6 +18,13 @@ document:
       description: "YUSYS-OCR框架 v3.0"
       enabled: true
 
+    # bank_statement_yusys_local
+    - tool: "mineru"
+      result_dir: "bank_statement_yusys_local"
+      image_dir: "bank_statement_yusys_local/{{name}}"
+      description: "YUSYS-OCR框架(local) GLM-OCR"
+      enabled: true
+
     # bank_statement_mineru_vl
     - tool: "mineru"
       result_dir: "bank_statement_mineru_vl"

+ 4 - 4
ocr_validator/config/德_内蒙古银行照.yaml

@@ -19,11 +19,11 @@ document:
       description: "YUSYS-OCR框架 v3.0"
       enabled: true
 
-    # bank_statement_yusys_v2
+    # bank_statement_yusys_local
     - tool: "mineru"
-      result_dir: "bank_statement_yusys_v2"
-      image_dir: "mineru_vllm_results/{{name}}"
-      description: "YUSYS统一OCR框架"
+      result_dir: "bank_statement_yusys_local"
+      image_dir: "bank_statement_yusys_local/{{name}}"
+      description: "YUSYS-OCR框架(local) GLM-OCR"
       enabled: true
 
      # MinerU

+ 7 - 0
ocr_validator/config/提取自赤峰黄金2023年报.yaml

@@ -18,6 +18,13 @@ document:
       description: "YUSYS-OCR框架 v3.0"
       enabled: true
 
+    # bank_statement_yusys_local
+    - tool: "mineru"
+      result_dir: "bank_statement_yusys_local"
+      image_dir: "bank_statement_yusys_local/{{name}}"
+      description: "YUSYS-OCR框架(local) GLM-OCR"
+      enabled: true
+
      # MinerU
     - tool: "mineru"
       result_dir: "mineru_vllm_results"

+ 7 - 0
ocr_validator/config/施博深.yaml

@@ -11,6 +11,13 @@ document:
       description: "YUSYS-OCR框架 v4.0 GLM-OCR"
       enabled: true
 
+    # bank_statement_yusys_local
+    - tool: "mineru"
+      result_dir: "bank_statement_yusys_local"
+      image_dir: "bank_statement_yusys_local/{{name}}"
+      description: "YUSYS-OCR框架(local) GLM-OCR"
+      enabled: true
+
     # bank_statement_yusys_v3
     - tool: "mineru"
       result_dir: "bank_statement_yusys_v3"

+ 6 - 6
ocr_validator/config/朱_中信银行图.yaml

@@ -18,12 +18,12 @@ document:
       description: "YUSYS-OCR框架 v3.0"
       enabled: true
 
-    # # bank_statement_yusys_v2
-    # - tool: "mineru"
-    #   result_dir: "bank_statement_yusys_v2"
-    #   image_dir: "mineru_vllm_results/{{name}}"
-    #   description: "YUSYS统一OCR框架"
-    #   enabled: true
+    # bank_statement_yusys_local
+    - tool: "mineru"
+      result_dir: "bank_statement_yusys_local"
+      image_dir: "bank_statement_yusys_local/{{name}}"
+      description: "YUSYS-OCR框架(local) GLM-OCR"
+      enabled: true
 
     #  # MinerU
     # - tool: "mineru"

+ 7 - 6
ocr_validator/config/湛_平安银行图.yaml

@@ -18,12 +18,13 @@ document:
       description: "YUSYS-OCR框架 v3.0"
       enabled: true
 
-    # # bank_statement_yusys_v2
-    # - tool: "mineru"
-    #   result_dir: "bank_statement_yusys_v2"
-    #   image_dir: "mineru_vllm_results/{{name}}"
-    #   description: "YUSYS统一OCR框架"
-    #   enabled: true
+    # bank_statement_yusys_local
+    - tool: "mineru"
+      result_dir: "bank_statement_yusys_local"
+      image_dir: "bank_statement_yusys_local/{{name}}"
+      description: "YUSYS-OCR框架(local) GLM-OCR"
+      enabled: true
+
 
     #  # MinerU
     # - tool: "mineru"

+ 4 - 4
ocr_validator/config/至远彩色_2023年报.yaml

@@ -18,11 +18,11 @@ document:
       description: "YUSYS-OCR框架 v3.0"
       enabled: true
 
-    # bank_statement_yusys_v2
+    # bank_statement_yusys_local
     - tool: "mineru"
-      result_dir: "bank_statement_glm_vl"
-      image_dir: "bank_statement_glm_vl/{{name}}"
-      description: "YUSYS统一OCR框架"
+      result_dir: "bank_statement_yusys_local"
+      image_dir: "bank_statement_yusys_local/{{name}}"
+      description: "YUSYS-OCR框架(local) GLM-OCR"
       enabled: true
 
      # MinerU

+ 7 - 0
ocr_validator/config/许_民生银行图.yaml

@@ -11,6 +11,13 @@ document:
       description: "YUSYS-OCR框架 v4.0 GLM-OCR"
       enabled: true
 
+    # bank_statement_yusys_local
+    - tool: "mineru"
+      result_dir: "bank_statement_yusys_local"
+      image_dir: "bank_statement_yusys_local/{{name}}"
+      description: "YUSYS-OCR框架(local) GLM-OCR"
+      enabled: true
+
     # bank_statement_yusys_v3
     - tool: "mineru"
       result_dir: "bank_statement_yusys_v3"