diff --git a/DOCKER_DEPLOY.md b/DOCKER_DEPLOY.md new file mode 100644 index 0000000..a51fa31 --- /dev/null +++ b/DOCKER_DEPLOY.md @@ -0,0 +1,308 @@ +# Docker 部署指南(硬编码 KEY 版本) + +## 📋 配置说明 + +本 Dockerfile 已经将所有 API 配置和密钥硬编码在镜像中: + +### 已硬编码的配置 + +| 配置项 | 值 | +|--------|-----| +| API 端点 | `https://api.coze.cn/v1` ✅(包含 /v1 前缀) | +| COZE_API_KEY | `Bearer eyJhbGci...` ✅ | +| LLM_API_KEY | `Bearer eyJhbGci...` ✅ | +| COZE_WORKSPACE_ID | `7622238752642957347` ✅ | +| LLM_MODEL_NAME | `doubao-seed-2-0-pro-260215` ✅ | + +### Workspace ID 来源 + +从 JWT Token 的 `sub` 字段提取: +``` +spiffe://api.coze.cn/workload_identity/id:7622238752642957347 +``` + +## 🚀 快速部署 + +### 步骤 1:构建镜像 + +```bash +# 进入项目根目录 +cd /workspace/projects + +# 构建镜像 +docker build -t math-grading:latest . +``` + +### 步骤 2:运行容器 + +```bash +# 运行容器(简单方式) +docker run -d \ + --name math-grading \ + -p 8000:8000 \ + --restart unless-stopped \ + math-grading:latest + +# 运行容器(完整方式,挂载日志和缓存) +docker run -d \ + --name math-grading \ + -p 8000:8000 \ + -v $(pwd)/logs:/app/work/logs \ + -v $(pwd)/cache:/tmp/homework_cache \ + --restart unless-stopped \ + math-grading:latest +``` + +### 步骤 3:验证服务 + +```bash +# 查看容器状态 +docker ps | grep math-grading + +# 查看日志 +docker logs -f math-grading + +# 检查健康状态 +curl http://localhost:8000/health +``` + +## 🧪 测试接口 + +### 测试 LLM 调用 + +```bash +curl -X POST http://localhost:8000/stream_run \ + -H "Content-Type: application/json" \ + -d '{ + "student_homework": [ + { + "student_id": 1, + "student_name": "测试学生", + "homework_images": [ + "https://dpcclass.oss-cn-beijing.aliyuncs.com/umsupload/2026/03/18/69baa4f5-4826-4901-00e1-c6e66f02947f.jpg?x-oss-process=image/resize,w_1000" + ] + } + ], + "answer_doc_url": "", + "comment_max_length": 50, + "max_concurrent": 1 + }' +``` + +### 测试多图片批改 + +```bash +curl -X POST http://localhost:8000/stream_run \ + -H "Content-Type: application/json" \ + -d '{ + "student_homework": [ + { + "student_id": 1, + "student_name": "张三", + "homework_images": [ + "https://dpcclass.oss-cn-beijing.aliyuncs.com/umsupload/2026/03/18/69baa4f5-4826-4901-00e1-c6e66f02947f.jpg?x-oss-process=image/resize,w_1000", + "https://dpcclass.oss-cn-beijing.aliyuncs.com/umsupload/2026/03/25/69c344ba-4826-4901-00e1-c6ff235a12b2.jpg?x-oss-process=image/resize,w_1000" + ] + } + ], + "answer_doc_url": "https://dpcclass.oss-cn-beijing.aliyuncs.com/umsupload/2026/03/25/69c353d0-4826-4901-00e1-c7081bcab988.docx", + "comment_max_length": 50, + "max_concurrent": 2 + }' +``` + +## 🔧 常用命令 + +### 容器管理 + +```bash +# 停止容器 +docker stop math-grading + +# 启动容器 +docker start math-grading + +# 重启容器 +docker restart math-grading + +# 删除容器 +docker rm -f math-grading + +# 删除镜像 +docker rmi math-grading:latest +``` + +### 日志查看 + +```bash +# 查看实时日志 +docker logs -f math-grading + +# 查看最近 100 行日志 +docker logs --tail 100 math-grading + +# 查看错误日志 +docker logs math-grading 2>&1 | grep ERROR +``` + +### 进入容器 + +```bash +# 进入容器 shell +docker exec -it math-grading /bin/bash + +# 查看环境变量 +docker exec math-grading env | grep COZE + +# 运行 Python 脚本 +docker exec math-grading python assets/check_env.py +``` + +## 📊 验证配置成功 + +### 成功标志 + +1. **容器启动成功** + ```bash + docker ps | grep math-grading + # 应该显示容器运行中(Up status) + ``` + +2. **日志无错误** + ```bash + docker logs math-grading | grep ERROR + # 不应该有认证错误(401)或端点错误(404) + ``` + +3. **健康检查通过** + ```bash + curl http://localhost:8000/health + # 应该返回 200 OK + ``` + +4. **LLM 调用成功** + ```bash + # 运行测试请求,日志应该显示: + # ✅ HTTP Request: POST https://api.coze.cn/v1/chat/completions "HTTP/1.1 200 OK" + ``` + +### 错误排查 + +#### 错误 1:容器无法启动 + +```bash +# 查看详细日志 +docker logs math-grading + +# 检查端口是否被占用 +netstat -tuln | grep 8000 +``` + +#### 错误 2:LLM 调用 401 错误 + +```bash +# 检查环境变量是否正确设置 +docker exec math-grading env | grep COZE_API_KEY + +# 应该看到: +# COZE_API_KEY=Bearer eyJhbGci... +``` + +#### 错误 3:LLM 调用 404 错误 + +```bash +# 检查 API 端点配置 +docker exec math-grading env | grep BASE_URL + +# 应该看到: +# COZE_INTEGRATION_BASE_URL=https://api.coze.cn/v1 +# 注意要有 /v1 前缀 +``` + +## 🔐 安全说明 + +### ⚠️ 密钥已硬编码 + +当前 Dockerfile 将 API Key 硬编码在镜像中: +- ✅ 优点:部署简单,无需额外配置 +- ❌ 缺点:密钥会暴露在镜像中,存在安全风险 + +### 安全建议 + +1. **私有仓库** + - 使用私有 Docker Registry(如阿里云容器镜像服务) + - 不要将镜像推送到公共仓库 + +2. **访问控制** + - 限制镜像访问权限 + - 使用 RBAC 控制镜像拉取 + +3. **定期更换密钥** + - 定期在 Coze 平台更换 API Key + - 重新构建镜像 + +4. **生产环境建议** + - 使用环境变量方式(推荐) + - 参考 `assets/docker-compose.yml` + +## 📝 配置说明 + +### 环境变量列表 + +| 变量名 | 说明 | 值 | +|--------|------|-----| +| `COZE_API_KEY` | Coze API 认证密钥 | Bearer eyJhbGci... | +| `LLM_API_KEY` | LLM API 认证密钥 | Bearer eyJhbGci... | +| `COZE_WORKSPACE_ID` | 工作区 ID | 7622238752642957347 | +| `COZE_INTEGRATION_BASE_URL` | API 基础 URL | https://api.coze.cn/v1 | +| `COZE_INTEGRATION_MODEL_BASE_URL` | 模型 API 基础 URL | https://api.coze.cn/v1 | +| `LLM_BASE_URL` | LLM API 基础 URL | https://api.coze.cn/v1 | +| `LLM_MODEL_NAME` | LLM 模型名称 | doubao-seed-2-0-pro-260215 | +| `COZE_INTEGRATION_API_KEY` | 集成 API 密钥 | Bearer eyJhbGci... | + +### 端口说明 + +| 端口 | 说明 | +|------|------| +| 8000 | HTTP 服务端口 | + +### 目录说明 + +| 目录 | 说明 | +|------|------| +| /app | 应用工作目录 | +| /app/work/logs/bypass | 日志目录 | +| /tmp/homework_cache | 缓存目录 | + +## 🎯 总结 + +### 一键部署命令 + +```bash +# 构建并运行 +docker build -t math-grading:latest . +docker run -d --name math-grading -p 8000:8000 --restart unless-stopped math-grading:latest + +# 查看日志 +docker logs -f math-grading + +# 测试接口 +curl -X POST http://localhost:8000/stream_run \ + -H "Content-Type: application/json" \ + -d '{"student_homework": [{"student_id": 1, "homework_images": ["https://example.com/image.jpg"]}]}' +``` + +### 预期结果 + +- ✅ 容器正常运行 +- ✅ 日志显示服务启动成功 +- ✅ LLM 调用返回 200 OK +- ✅ 批改功能正常工作 + +### 关键修复点 + +1. ✅ API 端点包含 `/v1/` 前缀 +2. ✅ COZE_WORKSPACE_ID 已配置 +3. ✅ API Key 已硬编码 +4. ✅ 健康检查已添加 +5. ✅ 日志和缓存目录已创建 diff --git a/Dockerfile b/Dockerfile index 7865026..253dc22 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,50 +6,40 @@ WORKDIR /app RUN sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list.d/debian.sources 2>/dev/null || \ sed -i 's/deb.debian.org/mirrors.aliyun.com/g' /etc/apt/sources.list -# 安装完整的系统依赖(包含所有 PyGObject 需要的库) +# 安装系统依赖 RUN apt-get update && apt-get install -y --no-install-recommends \ - # 编译工具 gcc \ g++ \ make \ pkg-config \ cmake \ - # D-Bus 相关 - dbus \ - dbus-x11 \ - libdbus-1-dev \ - libdbus-glib-1-dev \ - # GLib 和 GObject 相关 libglib2.0-dev \ - libgirepository1.0-dev \ - gobject-introspection \ - # Cairo 图形库 libcairo2-dev \ - libcairo-gobject2 \ - # GTK 相关(如果需要) - libgtk-3-dev \ - # 其他常用库 - libffi-dev \ libxml2-dev \ libxslt1-dev \ + curl \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* -# 设置 pip 国内镜像源 +# 设置 pip 国内镜像源和 API 配置(硬编码 KEY) ENV PIP_INDEX_URL=https://mirrors.aliyun.com/pypi/simple/ \ PIP_TRUSTED_HOST=mirrors.aliyun.com \ + # API 端点配置(包含 /v1 前缀) COZE_INTEGRATION_BASE_URL=https://api.coze.cn/v1 \ COZE_INTEGRATION_MODEL_BASE_URL=https://api.coze.cn/v1 \ - COZE_WORKLOAD_IDENTITY_API_KEY="Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmZmU2NmYxLTg0MDMtNDc5Ni05ZmRhLTViMmJjZWExM2ViOCJ9.eyJpc3MiOiJodHRwczovL2FwaS5jb3plLmNuIiwiYXVkIjpbIkZVS1kzOVR0dFlSdmlNaldGVmNjaUg0NWFPblp2TGxpIl0sImV4cCI6ODIxMDI2Njg3Njc5OSwiaWF0IjoxNzc0NjkyOTc0LCJzdWIiOiJzcGlmZmU6Ly9hcGkuY296ZS5jbi93b3JrbG9hZF9pZGVudGl0eS9pZDo3NjIyMjM4NzUyNjQyOTU3MzQ3Iiwic3JjIjoiaW5ib3VuZF9hdXRoX2FjY2Vzc190b2tlbl9pZDo3NjIyMjQ4Mjg1OTMxMDQ0ODkxIn0.XSJaTryHWYzQaHxd9g9rOX2Y3YRY8kGAlvSFH9UkWR9EFDfZESG1GFEdWDelYeoHBqtdiQhxTcYdGPA87_PweMfh0wJXTdCEzTDHAOlUUupJEKTpkUAMEoEZpYBrwKQxjzGglkMHUoXqM5I0tQsARaqZ-j-JOW9Y6fHot56squm8GSt7WZkVSj6ZC2Us4cpO_RIgsN_pBU0CFSlUpOU5AdQQ8LvHzp60-DGaXVU0mFIYKhnXKbTf3PSXpJlH-W78FULh2FcdOTxMIcNkL5nuIHGakoNNDxs-k5Ucp06kFEMcbvec4iB1njbkHrsilYeJOFRoqkXJpQujDngZxKecLA" \ - LLM_API_KEY="Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmZmU2NmYxLTg0MDMtNDc5Ni05ZmRhLTViMmJjZWExM2ViOCJ9.eyJpc3MiOiJodHRwczovL2FwaS5jb3plLmNuIiwiYXVkIjpbIkZVS1kzOVR0dFlSdmlNaldGVmNjaUg0NWFPblp2TGxpIl0sImV4cCI6ODIxMDI2Njg3Njc5OSwiaWF0IjoxNzc0NjkyOTc0LCJzdWIiOiJzcGlmZmU6Ly9hcGkuY296ZS5jbi93b3JrbG9hZF9pZGVudGl0eS9pZDo3NjIyMjM4NzUyNjQyOTU3MzQ3Iiwic3JjIjoiaW5ib3VuZF9hdXRoX2FjY2Vzc190b2tlbl9pZDo3NjIyMjQ4Mjg1OTMxMDQ0ODkxIn0.XSJaTryHWYzQaHxd9g9rOX2Y3YRY8kGAlvSFH9UkWR9EFDfZESG1GFEdWDelYeoHBqtdiQhxTcYdGPA87_PweMfh0wJXTdCEzTDHAOlUUupJEKTpkUAMEoEZpYBrwKQxjzGglkMHUoXqM5I0tQsARaqZ-j-JOW9Y6fHot56squm8GSt7WZkVSj6ZC2Us4cpO_RIgsN_pBU0CFSlUpOU5AdQQ8LvHzp60-DGaXVU0mFIYKhnXKbTf3PSXpJlH-W78FULh2FcdOTxMIcNkL5nuIHGakoNNDxs-k5Ucp06kFEMcbvec4iB1njbkHrsilYeJOFRoqkXJpQujDngZxKecLA" \ + COZE_INTEGRATION_CHAT_BASE_URL=https://api.coze.cn/v1 \ LLM_BASE_URL=https://api.coze.cn/v1 \ - LLM_MODEL_NAME=doubao-seed-2-0-pro-260215 \ + # API Key 配置(硬编码) COZE_API_KEY="Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmZmU2NmYxLTg0MDMtNDc5Ni05ZmRhLTViMmJjZWExM2ViOCJ9.eyJpc3MiOiJodHRwczovL2FwaS5jb3plLmNuIiwiYXVkIjpbIkZVS1kzOVR0dFlSdmlNaldGVmNjaUg0NWFPblp2TGxpIl0sImV4cCI6ODIxMDI2Njg3Njc5OSwiaWF0IjoxNzc0NjkyOTc0LCJzdWIiOiJzcGlmZmU6Ly9hcGkuY296ZS5jbi93b3JrbG9hZF9pZGVudGl0eS9pZDo3NjIyMjM4NzUyNjQyOTU3MzQ3Iiwic3JjIjoiaW5ib3VuZF9hdXRoX2FjY2Vzc190b2tlbl9pZDo3NjIyMjQ4Mjg1OTMxMDQ0ODkxIn0.XSJaTryHWYzQaHxd9g9rOX2Y3YRY8kGAlvSFH9UkWR9EFDfZESG1GFEdWDelYeoHBqtdiQhxTcYdGPA87_PweMfh0wJXTdCEzTDHAOlUUupJEKTpkUAMEoEZpYBrwKQxjzGglkMHUoXqM5I0tQsARaqZ-j-JOW9Y6fHot56squm8GSt7WZkVSj6ZC2Us4cpO_RIgsN_pBU0CFSlUpOU5AdQQ8LvHzp60-DGaXVU0mFIYKhnXKbTf3PSXpJlH-W78FULh2FcdOTxMIcNkL5nuIHGakoNNDxs-k5Ucp06kFEMcbvec4iB1njbkHrsilYeJOFRoqkXJpQujDngZxKecLA" \ - COZE_INTEGRATION_API_KEY="Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmZmU2NmYxLTg0MDMtNDc5Ni05ZmRhLTViMmJjZWExM2ViOCJ9.eyJpc3MiOiJodHRwczovL2FwaS5jb3plLmNuIiwiYXVkIjpbIkZVS1kzOVR0dFlSdmlNaldGVmNjaUg0NWFPblp2TGxpIl0sImV4cCI6ODIxMDI2Njg3Njc5OSwiaWF0IjoxNzc0NjkyOTc0LCJzdWIiOiJzcGlmZmU6Ly9hcGkuY296ZS5jbi93b3JrbG9hZF9pZGVudGl0eS9pZDo3NjIyMjM4NzUyNjQyOTU3MzQ3Iiwic3JjIjoiaW5ib3VuZF9hdXRoX2FjY2Vzc190b2tlbl9pZDo3NjIyMjQ4Mjg1OTMxMDQ0ODkxIn0.XSJaTryHWYzQaHxd9g9rOX2Y3YRY8kGAlvSFH9UkWR9EFDfZESG1GFEdWDelYeoHBqtdiQhxTcYdGPA87_PweMfh0wJXTdCEzTDHAOlUUupJEKTpkUAMEoEZpYBrwKQxjzGglkMHUoXqM5I0tQsARaqZ-j-JOW9Y6fHot56squm8GSt7WZkVSj6ZC2Us4cpO_RIgsN_pBU0CFSlUpOU5AdQQ8LvHzp60-DGaXVU0mFIYKhnXKbTf3PSXpJlH-W78FULh2FcdOTxMIcNkL5nuIHGakoNNDxs-k5Ucp06kFEMcbvec4iB1njbkHrsilYeJOFRoqkXJpQujDngZxKecLA" \ - COZE_WORKSPACE_ID=7622238752642957347 + LLM_API_KEY="Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmZmU2NmYxLTg0MDMtNDc5Ni05ZmRhLTViMmJjZWExM2ViOCJ9.eyJpc3MiOiJodHRwczovL2FwaS5jb3plLmNuIiwiYXVkIjpbIkZVS1kzOVR0dFlSdmlNaldGVmNjaUg0NWFPblp2TGxpIl0sImV4cCI6ODIxMDI2Njg3Njc5OSwiaWF0IjoxNzc0NjkyOTc0LCJzdWIiOiJzcGlmZmU6Ly9hcGkuY296ZS5jbi93b3JrbG9hZF9pZGVudGl0eS9pZDo3NjIyMjM4NzUyNjQyOTU3MzQ3Iiwic3JjIjoiaW5ib3VuZF9hdXRoX2FjY2Vzc190b2tlbl9pZDo3NjIyMjQ4Mjg1OTMxMDQ0ODkxIn0.XSJaTryHWYzQaHxd9g9rOX2Y3YRY8kGAlvSFH9UkWR9EFDfZESG1GFEdWDelYeoHBqtdiQhxTcYdGPA87_PweMfh0wJXTdCEzTDHAOlUUupJEKTpkUAMEoEZpYBrwKQxjzGglkMHUoXqM5I0tQsARaqZ-j-JOW9Y6fHot56squm8GSt7WZkVSj6ZC2Us4cpO_RIgsN_pBU0CFSlUpOU5AdQQ8LvHzp60-DGaXVU0mFIYKhnXKbTf3PSXpJlH-W78FULh2FcdOTxMIcNkL5nuIHGakoNNDxs-k5Ucp06kFEMcbvec4iB1njbkHrsilYeJOFRoqkXJpQujDngZxKecLA" \ + # Workspace ID 配置(硬编码,从 JWT Token 提取) + COZE_WORKSPACE_ID=7622238752642957347 \ + # LLM 模型配置 + LLM_MODEL_NAME=doubao-seed-2-0-pro-260215 \ + COZE_INTEGRATION_API_KEY="Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmZmU2NmYxLTg0MDMtNDc5Ni05ZmRhLTViMmJjZWExM2ViOCJ9.eyJpc3MiOiJodHRwczovL2FwaS5jb3plLmNuIiwiYXVkIjpbIkZVS1kzOVR0dFlSdmlNaldGVmNjaUg0NWFPblp2TGxpIl0sImV4cCI6ODIxMDI2Njg3Njc5OSwiaWF0IjoxNzc0NjkyOTc0LCJzdWIiOiJzcGlmZmU6Ly9hcGkuY296ZS5jbi93b3JrbG9hZF9pZGVudGl0eS9pZDo3NjIyMjM4NzUyNjQyOTU3MzQ3Iiwic3JjIjoiaW5ib3VuZF9hdXRoX2FjY2Vzc190b2tlbl9pZDo3NjIyMjQ4Mjg1OTMxMDQ0ODkxIn0.XSJaTryHWYzQaHxd9g9rOX2Y3YRY8kGAlvSFH9UkWR9EFDfZESG1GFEdWDelYeoHBqtdiQhxTcYdGPA87_PweMfh0wJXTdCEzTDHAOlUUupJEKTpkUAMEoEZpYBrwKQxjzGglkMHUoXqM5I0tQsARaqZ-j-JOW9Y6fHot56squm8GSt7WZkVSj6ZC2Us4cpO_RIgsN_pBU0CFSlUpOU5AdQQ8LvHzp60-DGaXVU0mFIYKhnXKbTf3PSXpJlH-W78FULh2FcdOTxMIcNkL5nuIHGakoNNDxs-k5Ucp06kFEMcbvec4iB1njbkHrsilYeJOFRoqkXJpQujDngZxKecLA" # 升级 pip 和构建工具 -RUN pip install --no-cache-dir --upgrade pip setuptools wheel meson ninja +RUN pip install --no-cache-dir --upgrade pip setuptools wheel # 复制并安装依赖 COPY requirements.txt . @@ -58,5 +48,12 @@ RUN pip install --no-cache-dir -r requirements.txt # 复制项目文件 COPY . . +# 创建必要的目录 +RUN mkdir -p /app/work/logs/bypass /tmp/homework_cache + +# 添加健康检查 +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:8000/health || exit 1 + EXPOSE 8000 -CMD ["python", "src/main.py", "-m", "http", "-p", "8000"] \ No newline at end of file +CMD ["python", "src/main.py", "-m", "http", "-p", "8000"] diff --git a/README_DOCKER.md b/README_DOCKER.md new file mode 100644 index 0000000..3275bf7 --- /dev/null +++ b/README_DOCKER.md @@ -0,0 +1,202 @@ +# 数学作业批改系统 - Docker 部署 + +## 📦 快速开始 + +### 一键部署 + +```bash +# 运行部署脚本 +./deploy.sh +``` + +### 手动部署 + +```bash +# 构建镜像 +docker build -t math-grading:latest . + +# 启动容器 +docker run -d \ + --name math-grading \ + -p 8000:8000 \ + -v $(pwd)/logs:/app/work/logs \ + -v $(pwd)/cache:/tmp/homework_cache \ + --restart unless-stopped \ + math-grading:latest +``` + +## 🧪 测试验证 + +```bash +# 运行测试脚本 +./test.sh +``` + +## 📝 配置说明 + +### 已硬编码的配置 + +| 配置项 | 值 | 说明 | +|--------|-----|------| +| API 端点 | `https://api.coze.cn/v1` | 包含 /v1 前缀 | +| COZE_API_KEY | `Bearer eyJhbGci...` | Coze API 认证密钥 | +| COZE_WORKSPACE_ID | `7622238752642957347` | 工作区 ID | +| LLM_MODEL_NAME | `doubao-seed-2-0-pro-260215` | LLM 模型名称 | + +### 配置来源 + +- **API Key**: 从原始 Dockerfile 中提取 +- **Workspace ID**: 从 JWT Token 的 `sub` 字段提取 + ``` + spiffe://api.coze.cn/workload_identity/id:7622238752642957347 + ``` + +## 🔧 常用命令 + +### 容器管理 + +```bash +# 查看状态 +docker ps | grep math-grading + +# 查看日志 +docker logs -f math-grading + +# 停止服务 +docker stop math-grading + +# 启动服务 +docker start math-grading + +# 重启服务 +docker restart math-grading + +# 进入容器 +docker exec -it math-grading /bin/bash +``` + +## 📊 API 接口 + +### 批改接口 + +**请求**: +```bash +curl -X POST http://localhost:8000/stream_run \ + -H "Content-Type: application/json" \ + -d '{ + "student_homework": [ + { + "student_id": 1, + "student_name": "张三", + "homework_images": [ + "https://example.com/image1.jpg", + "https://example.com/image2.jpg" + ] + } + ], + "answer_doc_url": "https://example.com/answers.docx", + "comment_max_length": 50, + "max_concurrent": 5 + }' +``` + +**参数说明**: +- `student_homework`: 学生作业列表 +- `answer_doc_url`: 答案文档 URL(可选) +- `comment_max_length`: 评语最大字数(默认 50) +- `max_concurrent`: 并发批改数量(默认 5) + +## 📁 目录结构 + +``` +. +├── Dockerfile # Docker 镜像配置(硬编码 KEY) +├── deploy.sh # 一键部署脚本 +├── test.sh # 快速测试脚本 +├── DOCKER_DEPLOY.md # 详细部署文档 +├── logs/ # 日志目录 +├── cache/ # 缓存目录 +└── src/ # 源代码 +``` + +## 🔍 故障排查 + +### 问题 1: 容器无法启动 + +```bash +# 查看详细日志 +docker logs math-grading + +# 检查端口占用 +netstat -tuln | grep 8000 +``` + +### 问题 2: LLM 调用失败 + +```bash +# 检查环境变量 +docker exec math-grading env | grep COZE + +# 应该看到: +# COZE_API_KEY=Bearer eyJhbGci... +# COZE_WORKSPACE_ID=7622238752642957347 +# COZE_INTEGRATION_BASE_URL=https://api.coze.cn/v1 +``` + +### 问题 3: 认证错误 (401) + +- 确认 COZE_API_KEY 和 COZE_WORKSPACE_ID 都已设置 +- 检查 Token 是否有效 + +### 问题 4: 端点错误 (404) + +- 确认 API 端点包含 /v1 前缀 +- 检查 COZE_INTEGRATION_BASE_URL 配置 + +## 📚 详细文档 + +- [完整部署指南](DOCKER_DEPLOY.md) +- [项目文档](AGENTS.md) +- [Docker 404 错误修复](assets/fix_docker_404.md) + +## ⚠️ 安全提示 + +当前 Dockerfile 将 API Key 硬编码在镜像中: +- ✅ 优点:部署简单,无需额外配置 +- ❌ 缺点:密钥会暴露在镜像中 + +### 安全建议 + +1. **使用私有仓库**:不要推送到公共 Docker Registry +2. **访问控制**:限制镜像拉取权限 +3. **定期更换密钥**:在 Coze 平台定期更换 API Key +4. **生产环境**:考虑使用环境变量方式(参考 docker-compose.yml) + +## 🎯 验证成功标志 + +部署成功后,应该看到: + +```bash +# 1. 容器运行中 +docker ps | grep math-grading +# Up 5 minutes + +# 2. 健康检查通过 +curl http://localhost:8000/health +# 200 OK + +# 3. 日志无错误 +docker logs math-grading | grep ERROR +# (无输出) + +# 4. LLM 调用成功 +# 日志显示: +# HTTP Request: POST https://api.coze.cn/v1/chat/completions "HTTP/1.1 200 OK" +``` + +## 📞 支持 + +如遇问题,请: +1. 查看日志: `docker logs -f math-grading` +2. 运行测试: `./test.sh` +3. 参考文档: `DOCKER_DEPLOY.md` diff --git a/assets/extract_workspace_id.py b/assets/extract_workspace_id.py new file mode 100644 index 0000000..a4c9f6b --- /dev/null +++ b/assets/extract_workspace_id.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +""" +从 JWT Token 中提取 Workspace ID +""" + +import json +import base64 +import sys + +def extract_workspace_id(token): + """ + 从 JWT Token 中提取 Workspace ID + + Args: + token: JWT Token(带或不带 "Bearer " 前缀) + + Returns: + workspace_id: 工作区 ID + """ + try: + # 移除 "Bearer " 前缀 + if token.startswith("Bearer "): + token = token[7:] + + # 分割 JWT + parts = token.split(".") + if len(parts) != 3: + print("❌ Token 格式错误:应该是 JWT 格式(header.payload.signature)") + return None + + # 解码 payload + payload_b64 = parts[1] + # 添加 padding + padding = 4 - len(payload_b64) % 4 + if padding != 4: + payload_b64 += "=" * padding + + payload = json.loads(base64.b64decode(payload_b64)) + + # 显示 Token 信息 + print("\n" + "=" * 80) + print("JWT Token 信息") + print("=" * 80) + print(f"Issuer (iss): {payload.get('iss')}") + print(f"Audience (aud): {payload.get('aud')}") + print(f"Subject (sub): {payload.get('sub')}") + print(f"Expires (exp): {payload.get('exp')}") + print(f"Issued At (iat): {payload.get('iat')}") + print(f"Source (src): {payload.get('src')}") + print() + + # 提取 workspace_id + sub = payload.get('sub', '') + + # 尝试不同的格式 + if 'workload_identity/id:' in sub: + workspace_id = sub.split('workload_identity/id:')[-1] + print(f"✅ 格式 1: workload_identity/id:{workspace_id}") + elif 'workspace/id:' in sub: + workspace_id = sub.split('workspace/id:')[-1] + print(f"✅ 格式 2: workspace/id:{workspace_id}") + else: + # 尝试直接使用 sub + workspace_id = sub + print(f"⚠️ 格式 3: 直接使用 sub: {workspace_id}") + + print() + print("=" * 80) + print(f"🔑 提取的 Workspace ID: {workspace_id}") + print("=" * 80) + print() + + return workspace_id + + except Exception as e: + print(f"❌ 解析 Token 失败: {str(e)}") + return None + +def main(): + print() + print("=" * 80) + print("Workspace ID 提取工具") + print("=" * 80) + print() + + # 方式 1:从环境变量读取 + token = None + + # 1. 尝试从命令行参数读取 + if len(sys.argv) > 1: + token = sys.argv[1] + # 2. 尝试从环境变量读取 + else: + token = __import__('os').environ.get('COZE_API_KEY') + + if not token: + print("❌ 未找到 Token") + print() + print("使用方法:") + print(" 方式 1: python extract_workspace_id.py 'Bearer eyJhbGci...'") + print(" 方式 2: COZE_API_KEY='Bearer eyJhbGci...' python extract_workspace_id.py") + print() + return 1 + + # 提取 workspace_id + workspace_id = extract_workspace_id(token) + + if workspace_id: + print() + print("✅ 提取成功!") + print() + print("下一步:") + print(f"1. 在 docker-compose.yml 中设置:") + print(f" COZE_WORKSPACE_ID={workspace_id}") + print() + return 0 + else: + print() + print("❌ 提取失败,请检查 Token 格式") + print() + return 1 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..a91391c --- /dev/null +++ b/deploy.sh @@ -0,0 +1,89 @@ +#!/bin/bash +# Docker 快速部署脚本 + +set -e + +echo "==========================================" +echo "数学作业批改系统 - Docker 部署脚本" +echo "==========================================" +echo "" + +# 检查 Docker 是否安装 +if ! command -v docker &> /dev/null; then + echo "❌ Docker 未安装,请先安装 Docker" + exit 1 +fi + +echo "✅ Docker 已安装" +echo "" + +# 检查旧容器 +if docker ps -a | grep -q math-grading; then + echo "⚠️ 检测到旧容器 math-grading" + read -p "是否删除旧容器? (y/n) " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "🗑️ 删除旧容器..." + docker rm -f math-grading + else + echo "❌ 取消部署" + exit 1 + fi +fi + +echo "🏗️ 开始构建镜像..." +docker build -t math-grading:latest . + +if [ $? -eq 0 ]; then + echo "✅ 镜像构建成功" +else + echo "❌ 镜像构建失败" + exit 1 +fi + +echo "" +echo "🚀 启动容器..." + +# 创建日志和缓存目录 +mkdir -p logs cache + +# 启动容器 +docker run -d \ + --name math-grading \ + -p 8000:8000 \ + -v $(pwd)/logs:/app/work/logs \ + -v $(pwd)/cache:/tmp/homework_cache \ + --restart unless-stopped \ + math-grading:latest + +if [ $? -eq 0 ]; then + echo "✅ 容器启动成功" +else + echo "❌ 容器启动失败" + exit 1 +fi + +echo "" +echo "⏳ 等待服务启动..." +sleep 5 + +echo "" +echo "==========================================" +echo "部署完成!" +echo "==========================================" +echo "" +echo "服务地址: http://localhost:8000" +echo "" +echo "常用命令:" +echo " 查看日志: docker logs -f math-grading" +echo " 停止服务: docker stop math-grading" +echo " 启动服务: docker start math-grading" +echo " 重启服务: docker restart math-grading" +echo "" +echo "测试接口:" +echo " curl -X POST http://localhost:8000/stream_run \\" +echo " -H 'Content-Type: application/json' \\" +echo " -d '{\"student_homework\": [{\"student_id\": 1, \"homework_images\": [\"https://example.com/image.jpg\"]}]}'" +echo "" +echo "详细文档: DOCKER_DEPLOY.md" +echo "" diff --git a/test.sh b/test.sh new file mode 100644 index 0000000..ab82944 --- /dev/null +++ b/test.sh @@ -0,0 +1,125 @@ +#!/bin/bash +# 快速测试脚本 + +echo "==========================================" +echo "数学作业批改系统 - 快速测试" +echo "==========================================" +echo "" + +# 检查容器是否运行 +if ! docker ps | grep -q math-grading; then + echo "❌ 容器未运行,请先启动容器" + echo "运行: docker start math-grading" + exit 1 +fi + +echo "✅ 容器正在运行" +echo "" + +# 测试 1: 健康检查 +echo "测试 1: 健康检查" +echo "----------------------------------------" +response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/health) +if [ "$response" = "200" ]; then + echo "✅ 健康检查通过 (200 OK)" +else + echo "❌ 健康检查失败 (HTTP $response)" +fi +echo "" + +# 测试 2: 环境变量检查 +echo "测试 2: 环境变量检查" +echo "----------------------------------------" +echo "检查关键环境变量..." + +# COZE_API_KEY +if docker exec math-grading env | grep -q "COZE_API_KEY=Bearer"; then + echo "✅ COZE_API_KEY 已设置" +else + echo "❌ COZE_API_KEY 未设置" +fi + +# COZE_WORKSPACE_ID +if docker exec math-grading env | grep -q "COZE_WORKSPACE_ID=7622238752642957347"; then + echo "✅ COZE_WORKSPACE_ID 已设置 (7622238752642957347)" +else + echo "❌ COZE_WORKSPACE_ID 未设置或值不正确" +fi + +# API 端点 +if docker exec math-grading env | grep -q "COZE_INTEGRATION_BASE_URL=https://api.coze.cn/v1"; then + echo "✅ API 端点已配置 (https://api.coze.cn/v1)" +else + echo "❌ API 端点未配置或缺少 /v1 前缀" +fi + +# LLM 模型 +if docker exec math-grading env | grep -q "LLM_MODEL_NAME=doubao-seed-2-0-pro-260215"; then + echo "✅ LLM 模型已配置 (doubao-seed-2-0-pro-260215)" +else + echo "❌ LLM 模型未配置" +fi +echo "" + +# 测试 3: LLM 调用测试 +echo "测试 3: LLM 调用测试" +echo "----------------------------------------" +echo "发送测试请求..." + +response=$(curl -s -X POST http://localhost:8000/stream_run \ + -H "Content-Type: application/json" \ + -d '{ + "student_homework": [ + { + "student_id": 1, + "student_name": "测试学生", + "homework_images": [ + "https://dpcclass.oss-cn-beijing.aliyuncs.com/umsupload/2026/03/18/69baa4f5-4826-4901-00e1-c6e66f02947f.jpg?x-oss-process=image/resize,w_1000" + ] + } + ], + "answer_doc_url": "", + "comment_max_length": 50, + "max_concurrent": 1 + }' \ + --max-time 120) + +if [ $? -eq 0 ]; then + echo "✅ 请求发送成功" + echo "" + echo "响应预览:" + echo "$response" | head -c 500 + echo "..." +else + echo "❌ 请求失败" +fi +echo "" + +# 测试 4: 日志检查 +echo "测试 4: 日志检查" +echo "----------------------------------------" +echo "检查最近日志中的错误..." + +errors=$(docker logs --tail 100 math-grading 2>&1 | grep -i "401\|404\|authentication is invalid" | wc -l) + +if [ "$errors" -eq 0 ]; then + echo "✅ 未发现认证错误或端点错误" +else + echo "❌ 发现 $errors 个错误" + echo "最近的错误:" + docker logs --tail 50 math-grading 2>&1 | grep -i "401\|404\|authentication is invalid" | tail -5 +fi +echo "" + +# 总结 +echo "==========================================" +echo "测试总结" +echo "==========================================" +echo "" +echo "如果所有测试都通过,说明部署成功!" +echo "" +echo "下一步:" +echo " 1. 查看详细日志: docker logs -f math-grading" +echo " 2. 测试完整功能: 使用上面的测试请求" +echo " 3. 部署到生产环境: 参考 DOCKER_DEPLOY.md" +echo ""