# Docker 部署问题排查指南

## 问题描述
在 Docker 环境中部署工作流时，出现以下错误：
```
S3对象不存在: <!DOCTYPE HTML><html><head>...<title>404 Not Found</title>...
```

## 诊断步骤

### 1. 运行 URL 诊断脚本

在 Docker 容器内运行：
```bash
python scripts/test_urls.py
```

**预期结果**：所有 URL 应该显示 `✅ 有效`

**如果显示 `❌ 无效`**：
- 检查 Docker 容器网络配置
- 检查 DNS 解析
- 检查防火墙规则

### 2. 检查 Docker 网络配置

```bash
# 检查容器是否可以访问外网
docker exec <container_id> ping -c 3 8.8.8.8

# 检查 DNS 解析
docker exec <container_id> nslookup dpcclass.oss-cn-beijing.aliyuncs.com

# 检查是否能访问 OSS
docker exec <container_id> curl -I https://dpcclass.oss-cn-beijing.aliyuncs.com
```

### 3. 检查 LLM 服务网络

LLM 服务可能在隔离的网络环境中运行，无法直接访问外部 URL。

**验证方法**：
- 查看 LLM 服务的网络配置
- 确认 LLM 服务可以访问外部 URL
- 联系 LLM 服务提供商确认网络策略

## 解决方案

### 方案 1：配置 Docker 网络使用宿主机网络

```bash
docker run --network host ...
```

**优点**：
- 容器使用宿主机的网络栈
- 可以直接访问宿主机的网络资源

**缺点**：
- 端口可能冲突
- 安全性降低

### 方案 2：配置 DNS 解析

在 Dockerfile 中：
```dockerfile
RUN echo "nameserver 8.8.8.8" > /etc/resolv.conf
```

或在 docker-compose.yml 中：
```yaml
services:
  app:
    dns:
      - 8.8.8.8
      - 114.114.114.114
```

### 方案 3：添加 HTTP 代理

如果需要通过代理访问外网：

```bash
# 在容器内设置环境变量
export HTTP_PROXY=http://proxy.example.com:8080
export HTTPS_PROXY=http://proxy.example.com:8080
```

或在 Dockerfile 中：
```dockerfile
ENV HTTP_PROXY=http://proxy.example.com:8080
ENV HTTPS_PROXY=http://proxy.example.com:8080
```

### 方案 4：下载图片到本地（推荐）

如果 LLM 服务无法访问外部 URL，可以在预处理阶段下载图片：

```python
import requests
import tempfile
import os
from pathlib import Path

def download_image_to_local(image_url: str) -> str:
    """
    下载图片到本地临时目录
    返回本地文件路径
    """
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
    }
    
    response = requests.get(image_url, headers=headers, timeout=30)
    response.raise_for_status()
    
    # 保存到临时目录
    tmp_dir = Path("/tmp/homework_images")
    tmp_dir.mkdir(parents=True, exist_ok=True)
    
    # 生成文件名
    import hashlib
    filename = hashlib.md5(image_url.encode()).hexdigest() + ".jpg"
    local_path = tmp_dir / filename
    
    # 写入文件
    with open(local_path, "wb") as f:
        f.write(response.content)
    
    return str(local_path)
```

### 方案 5：使用内网 OSS（最佳）

如果有内网 OSS，可以：
1. 将图片从公网 OSS 复制到内网 OSS
2. 使用内网 OSS URL
3. 配置内网 OSS 访问权限

## 代码改进

我已经增强了 URL 验证逻辑：

### 1. 改进的图片 URL 验证（`recognize_and_correct_node.py`）
```python
# 检查 HTTP 状态码（只接受 200 和 206）
if status_code not in (200, 206):
    raise ValueError(f"HTTP status code: {status_code}")

# 检查 Content-Type
content_type = response.headers.get('Content-Type', '')
if not content_type.startswith('image/'):
    logger.warning(f"Unexpected Content-Type: {content_type}")

# 检查是否为 HTML 错误页面
if b'<html' in preview_lower:
    # 提取具体错误信息（404/403/500）
    raise ValueError(f"URL returned HTML page ({error_msg})")
```

### 2. 改进的 Word 文档下载验证（`doc_extract_node.py`）
```python
# 验证 URL 格式
if not url.startswith(('http://', 'https://')):
    raise ValueError(f"Invalid URL format")

# 检查 HTTP 状态码
if status_code != 200:
    raise ValueError(f"HTTP Error {status_code}: {url}")

# 检查是否为有效的 docx
if not response.content.startswith(b'PK'):
    raise ValueError("Not a valid docx file")
```

## 临时解决方案

如果遇到无法解决的访问问题，可以：

### 1. 使用本地图片测试
```json
{
  "student_homework": [
    {
      "student_id": 1,
      "student_name": "测试",
      "homework_images": ["file:///tmp/test_image.jpg"]
    }
  ],
  "answer_doc_url": ""
}
```

### 2. 禁用答案文档
```json
{
  "answer_doc_url": "",
  ...
}
```

系统会自动切换到"专业老师批改模式"，不依赖标准答案。

## 联系支持

如果以上方案都无法解决问题，请提供：
1. Docker 容器网络配置
2. `python scripts/test_urls.py` 的输出
3. Docker 容器日志
4. LLM 服务网络配置
