serve方式如何启用表格增强适配器.md 9.0 KB

您的理解完全正确! 🎯 这确实是两个独立进程,钩子不会起作用。让我提供正确的解决方案:


问题分析

# ❌ 进程1: 预加载适配器
python3 -c "import sys; sys.path.insert(0, 'zhch'); import paddlex_adapter_hook"

# ❌ 进程2: 启动服务 (全新进程,不会继承进程1的状态)
paddlex --serve --port 8111 --device "gpu:3" --pipeline ...

问题:

  • 进程1加载适配器后立即退出
  • 进程2是全新的Python进程,不会继承进程1的任何状态
  • 适配器未生效

✅ 正确方案:使用 sitecustomize.py 自动加载

Python会在启动时自动导入 sitecustomize.py,我们利用这个机制:

方案1:全局 sitecustomize.py(推荐)

创建 zhch/sitecustomize.py:

"""
PaddleX 适配器自动加载钩子
放置在 Python 搜索路径中,会在任何 Python 进程启动时自动执行
"""
import os
import sys

def load_paddlex_adapters():
    """自动加载 PaddleX 适配器"""
    # 只有设置了环境变量才激活
    if os.getenv("PADDLEX_ENABLE_TABLE_ADAPTER", "").lower() not in ("true", "1", "yes"):
        return
    
    try:
        # 确保适配器路径在 sys.path
        adapter_base = os.path.dirname(os.path.abspath(__file__))
        adapters_path = os.path.join(adapter_base, 'adapters')
        
        if adapters_path not in sys.path:
            sys.path.insert(0, adapters_path)
        
        # 延迟导入,只有在实际使用时才加载
        def activate_adapter():
            try:
                from adapters.table_recognition_adapter import apply_table_recognition_adapter
                if apply_table_recognition_adapter():
                    print("✅ [sitecustomize] Table recognition adapter activated")
            except Exception as e:
                print(f"⚠️  [sitecustomize] Failed to activate adapter: {e}")
        
        # 监听 PaddleX 导入事件
        import importlib.abc
        import importlib.machinery
        
        class AdapterLoader(importlib.abc.MetaPathFinder):
            def find_module(self, fullname, path=None):
                if fullname == "paddlex.inference.pipelines.table_recognition.pipeline_v2":
                    # 在目标模块导入后激活适配器
                    activate_adapter()
                return None
        
        sys.meta_path.insert(0, AdapterLoader())
        
    except Exception as e:
        print(f"⚠️  [sitecustomize] Error setting up adapter hook: {e}")

# 自动执行
load_paddlex_adapters()

修改启动脚本 start_table_recognition_service.sh:

#!/bin/bash
# filepath: zhch/start_table_recognition_service.sh

# 🎯 设置适配器激活环境变量
export PADDLEX_ENABLE_TABLE_ADAPTER="true"

# 🎯 关键:将 zhch 目录加入 PYTHONPATH,使 sitecustomize.py 被自动发现
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
export PYTHONPATH="$SCRIPT_DIR:$PYTHONPATH"

# 设置模型下载源
export PADDLE_PDX_MODEL_SOURCE="bos"

# 设置日志级别
export PYTHONWARNINGS="ignore::UserWarning"

# 获取参数
PORT=${1:-8111}
DEVICE=${2:-"gpu:3"}
PIPELINE_CONFIG=${3:-"zhch/my_config/table_recognition_v2.yaml"}

echo "🚀 Starting Table Recognition Service with Enhanced Adapter..."
echo "   - Port: $PORT"
echo "   - Device: $DEVICE"
echo "   - Pipeline: $PIPELINE_CONFIG"
echo "   - Adapter: ENABLED (via sitecustomize.py)"
echo "   - PYTHONPATH: $PYTHONPATH"

# 启动服务 (sitecustomize.py 会自动被加载)
nohup paddlex --serve \
    --port "$PORT" \
    --device "$DEVICE" \
    --pipeline "$PIPELINE_CONFIG" \
    > "table_recognition_service_$(date +%Y%m%d_%H%M%S).log" 2>&1 &

PID=$!
echo "✅ Service started! PID: $PID"
echo "📝 Logs: tail -f table_recognition_service_*.log"
echo ""
echo "🛑 To stop: kill $PID"

方案2:包装脚本启动(备选)

如果 sitecustomize.py 不生效,使用Python包装脚本:

创建 zhch/start_paddlex_with_adapter.py:

#!/usr/bin/env python3
"""
PaddleX 服务启动包装器 - 自动激活适配器
"""
import os
import sys
import subprocess

def main():
    # 🎯 在当前进程中激活适配器
    if os.getenv("PADDLEX_ENABLE_TABLE_ADAPTER", "").lower() in ("true", "1", "yes"):
        sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'adapters'))
        try:
            from table_recognition_adapter import apply_table_recognition_adapter
            if apply_table_recognition_adapter():
                print("✅ Adapter activated in wrapper process")
        except Exception as e:
            print(f"⚠️  Failed to activate adapter: {e}")
    
    # 🎯 解析命令行参数
    args = sys.argv[1:]  # 去掉脚本名
    
    # 🎯 导入 PaddleX CLI 主函数并在当前进程执行
    from paddlex.paddlex_cli import main as paddlex_main
    
    # 替换 sys.argv 以传递参数
    sys.argv = ['paddlex'] + args
    
    # 执行 PaddleX
    paddlex_main()

if __name__ == "__main__":
    main()

修改启动脚本使用包装器:

#!/bin/bash
# filepath: zhch/start_table_recognition_service.sh

export PADDLEX_ENABLE_TABLE_ADAPTER="true"
export PADDLE_PDX_MODEL_SOURCE="bos"
export PYTHONWARNINGS="ignore::UserWarning"

PORT=${1:-8111}
DEVICE=${2:-"gpu:3"}
PIPELINE_CONFIG=${3:-"zhch/my_config/table_recognition_v2.yaml"}

echo "🚀 Starting Table Recognition Service with Adapter Wrapper..."

# 🎯 使用包装脚本启动 (在同一进程中)
nohup python3 zhch/start_paddlex_with_adapter.py \
    --serve \
    --port "$PORT" \
    --device "$DEVICE" \
    --pipeline "$PIPELINE_CONFIG" \
    > "table_recognition_service_$(date +%Y%m%d_%H%M%S).log" 2>&1 &

PID=$!
echo "✅ Service started! PID: $PID"

方案3:修改适配器为延迟激活(最简单)

修改 zhch/adapters/table_recognition_adapter.py 的自动激活逻辑:

# 在文件末尾修改
def _auto_activate_on_module_import():
    """
    当 pipeline_v2 模块被导入时自动激活
    """
    if os.getenv("PADDLEX_ENABLE_TABLE_ADAPTER", "").lower() not in ("true", "1", "yes"):
        return
    
    import sys
    from importlib import import_module
    
    # 保存原始 __import__
    original_import = __builtins__.__import__
    
    def patched_import(name, *args, **kwargs):
        module = original_import(name, *args, **kwargs)
        
        # 检测目标模块导入
        if name == "paddlex.inference.pipelines.table_recognition.pipeline_v2":
            print("🎯 Detected pipeline_v2 import, activating adapter...")
            apply_table_recognition_adapter()
            # 恢复原始 __import__
            __builtins__.__import__ = original_import
        
        return module
    
    # 替换 __import__
    __builtins__.__import__ = patched_import

# 模块导入时执行
_auto_activate_on_module_import()

然后在启动脚本中:

#!/bin/bash
export PADDLEX_ENABLE_TABLE_ADAPTER="true"
export PYTHONPATH="$(pwd)/zhch/adapters:$PYTHONPATH"  # 确保适配器模块可被导入

# 预加载适配器模块 (会设置 import hook)
python3 -c "import table_recognition_adapter" 2>/dev/null || true

# 启动服务 (适配器会在 pipeline_v2 导入时自动激活)
nohup paddlex --serve \
    --port 8111 \
    --device "gpu:3" \
    --pipeline "zhch/my_config/table_recognition_v2.yaml" \
    > "service.log" 2>&1 &

推荐方案对比

方案 优势 劣势 推荐度
sitecustomize.py ✅ Python官方机制
✅ 对所有进程生效
⚠️ 可能影响其他Python程序 ⭐⭐⭐⭐
包装脚本 ✅ 同一进程
✅ 100%生效
⚠️ 需要维护包装脚本 ⭐⭐⭐⭐⭐
import hook ✅ 延迟激活
✅ 精确控制
⚠️ 仍需预加载 ⭐⭐⭐

我推荐使用方案2(包装脚本),因为它最可靠且易于调试。


更新 README.md

在您的 README.md 中添加:

# 表格识别服务启动(带增强适配器)

## 服务端启动

```bash
cd ~/zhch/PaddleX/zhch
conda activate paddle
export PADDLE_PDX_MODEL_SOURCE="bos"

# 使用包装脚本启动服务(自动激活适配器)
chmod +x start_table_recognition_service.sh
./start_table_recognition_service.sh 8111 "gpu:3" "zhch/my_config/table_recognition_v2.yaml"

# 查看日志
tail -f table_recognition_service_*.log
```

## 客户端调用

```bash
# macOS 客户端
python table_recognition_v2_client.py \
    --input_file "/Users/zhch158/workspace/data/至远彩色印刷工业有限公司/2023年度报告母公司.pdf" \
    --output_dir "./output" \
    --api_url "http://10.192.72.11:8111/table-recognition-v2"
```

## 禁用适配器

```bash
# 方式1: 不设置环境变量
unset PADDLEX_ENABLE_TABLE_ADAPTER
./start_table_recognition_service.sh

# 方式2: 直接使用原始命令
nohup paddlex --serve --port 8111 --device "gpu:3" --pipeline "zhch/my_config/table_recognition_v2.yaml" &
```

现在适配器会在同一个进程中生效了!🎯