Compare commits

..

2 Commits

Author SHA1 Message Date
zhangquan 087de01887 Merge branch 'master' of https://gitea.23544.com/AIWorkflow/PhysicsCorrection 2026-03-30 20:40:38 +08:00
zhangquan b7dffc397a 1 2026-03-30 20:40:31 +08:00
14 changed files with 66 additions and 56 deletions

1
.env
View File

@ -10,6 +10,7 @@
# LLM API 密钥从火山引擎或OpenAI获取 # LLM API 密钥从火山引擎或OpenAI获取
LLM_API_KEY=eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmZmU2NmYxLTg0MDMtNDc5Ni05ZmRhLTViMmJjZWExM2ViOCJ9.eyJpc3MiOiJodHRwczovL2FwaS5jb3plLmNuIiwiYXVkIjpbInRVaHJod1VDMmFHRmVSRjZBU01NNGxITjhoT01jOVA2Il0sImV4cCI6ODIxMDI2Njg3Njc5OSwiaWF0IjoxNzc0NjcwMTM1LCJzdWIiOiJzcGlmZmU6Ly9hcGkuY296ZS5jbi93b3JrbG9hZF9pZGVudGl0eS9pZDo3NjIxMzY5Mjg5OTI4MzQzNTY3Iiwic3JjIjoiaW5ib3VuZF9hdXRoX2FjY2Vzc190b2tlbl9pZDo3NjIyMTUwMTkzNTI1NjIwNzYyIn0.qIgzvuMGj976ekcmxZHIWlATn58PyPmFrsoYPeqTfSX4kdvkgeIXMEGR1NAX-OcOmBY_T8tvjcY2sxNDqRhLSQi-6teONHIYkTyXSrA_T5eJqaqrylFmzWPWzqX41LBsav5cyR0n4ffYzqLSd0-iAf8HdUyMEhVuTZuv-nGSpaQ-al98TqcrPtLqte71J1VbbAsjzFMrawTaSOe6WSiIQNe1qNDmsoyQpu_qdw5Sh_nMbPN8V5tjNFlX04pNV6O60M_Bqr1hDxAqY_fsUeECBhE5uG29cYawxc5oEb-xZF0vM_a0gvU5cw2jV1OktNXGJ6A2S9btRfyqoAc3fFqxmw LLM_API_KEY=eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmZmU2NmYxLTg0MDMtNDc5Ni05ZmRhLTViMmJjZWExM2ViOCJ9.eyJpc3MiOiJodHRwczovL2FwaS5jb3plLmNuIiwiYXVkIjpbInRVaHJod1VDMmFHRmVSRjZBU01NNGxITjhoT01jOVA2Il0sImV4cCI6ODIxMDI2Njg3Njc5OSwiaWF0IjoxNzc0NjcwMTM1LCJzdWIiOiJzcGlmZmU6Ly9hcGkuY296ZS5jbi93b3JrbG9hZF9pZGVudGl0eS9pZDo3NjIxMzY5Mjg5OTI4MzQzNTY3Iiwic3JjIjoiaW5ib3VuZF9hdXRoX2FjY2Vzc190b2tlbl9pZDo3NjIyMTUwMTkzNTI1NjIwNzYyIn0.qIgzvuMGj976ekcmxZHIWlATn58PyPmFrsoYPeqTfSX4kdvkgeIXMEGR1NAX-OcOmBY_T8tvjcY2sxNDqRhLSQi-6teONHIYkTyXSrA_T5eJqaqrylFmzWPWzqX41LBsav5cyR0n4ffYzqLSd0-iAf8HdUyMEhVuTZuv-nGSpaQ-al98TqcrPtLqte71J1VbbAsjzFMrawTaSOe6WSiIQNe1qNDmsoyQpu_qdw5Sh_nMbPN8V5tjNFlX04pNV6O60M_Bqr1hDxAqY_fsUeECBhE5uG29cYawxc5oEb-xZF0vM_a0gvU5cw2jV1OktNXGJ6A2S9btRfyqoAc3fFqxmw
COZE_WORKLOAD_IDENTITY_API_KEY=eyJhbGciOiJSUzI1NiIsImtpZCI6ImRmZmU2NmYxLTg0MDMtNDc5Ni05ZmRhLTViMmJjZWExM2ViOCJ9.eyJpc3MiOiJodHRwczovL2FwaS5jb3plLmNuIiwiYXVkIjpbInRVaHJod1VDMmFHRmVSRjZBU01NNGxITjhoT01jOVA2Il0sImV4cCI6ODIxMDI2Njg3Njc5OSwiaWF0IjoxNzc0NjcwMTM1LCJzdWIiOiJzcGlmZmU6Ly9hcGkuY296ZS5jbi93b3JrbG9hZF9pZGVudGl0eS9pZDo3NjIxMzY5Mjg5OTI4MzQzNTY3Iiwic3JjIjoiaW5ib3VuZF9hdXRoX2FjY2Vzc190b2tlbl9pZDo3NjIyMTUwMTkzNTI1NjIwNzYyIn0.qIgzvuMGj976ekcmxZHIWlATn58PyPmFrsoYPeqTfSX4kdvkgeIXMEGR1NAX-OcOmBY_T8tvjcY2sxNDqRhLSQi-6teONHIYkTyXSrA_T5eJqaqrylFmzWPWzqX41LBsav5cyR0n4ffYzqLSd0-iAf8HdUyMEhVuTZuv-nGSpaQ-al98TqcrPtLqte71J1VbbAsjzFMrawTaSOe6WSiIQNe1qNDmsoyQpu_qdw5Sh_nMbPN8V5tjNFlX04pNV6O60M_Bqr1hDxAqY_fsUeECBhE5uG29cYawxc5oEb-xZF0vM_a0gvU5cw2jV1OktNXGJ6A2S9btRfyqoAc3fFqxmw
# LLM API 基础URL # LLM API 基础URL
# 火山引擎: https://ark.cn-beijing.volces.com/api/v3 # 火山引擎: https://ark.cn-beijing.volces.com/api/v3

View File

@ -1,5 +1,5 @@
## 项目概述 ## 项目概述
- **名称**: 初中物理作业批改工作流 - **名称**: 初中数学作业批改工作流
- **功能**: 上传多学生的作业图片和Word答案文件自动识别学生答案、提取标准答案、精准批改并返回每个学生的批改结果JSON - **功能**: 上传多学生的作业图片和Word答案文件自动识别学生答案、提取标准答案、精准批改并返回每个学生的批改结果JSON
### 数据结构(重要变更) ### 数据结构(重要变更)
@ -25,7 +25,6 @@
} }
], ],
"answer_doc_url": "答案文档URL可选", "answer_doc_url": "答案文档URL可选",
"subject": "physics",
"comment_max_length": 100, "comment_max_length": 100,
"max_concurrent": 10 "max_concurrent": 10
} }
@ -85,9 +84,7 @@
- 文件缓存:持久化存储,进程重启后仍可用 - 文件缓存:持久化存储,进程重启后仍可用
- **缓存有效期**30天自动清理过期缓存 - **缓存有效期**30天自动清理过期缓存
- **缓存内容**AI解析后的结构化数据CorrectAnswer列表 - **缓存内容**AI解析后的结构化数据CorrectAnswer列表
- **缓存键**`{subject}:{answer_doc_url}`MD5哈希 - **缓存键**answer_doc_urlMD5哈希
- **学科隔离**相同URL在不同学科下不会冲突
- 示例:`physics:https://example.com/answer.docx` 和 `math:https://example.com/answer.docx` 是不同的缓存
- **线程安全**:使用锁保护并发访问 - **线程安全**:使用锁保护并发访问
- **异常安全**:文件缓存失败时自动降级为纯内存模式 - **异常安全**:文件缓存失败时自动降级为纯内存模式
- **统计功能**`get_stats()` 返回缓存统计信息 - **统计功能**`get_stats()` 返回缓存统计信息
@ -186,9 +183,6 @@
- `student_name`: 学生姓名str可选 - `student_name`: 学生姓名str可选
- `homework_images`: 该学生的作业图片URL列表List[str],纯字符串数组) - `homework_images`: 该学生的作业图片URL列表List[str],纯字符串数组)
- `answer_doc_url`: 正确答案Word文件的URL.docx格式**可选** - `answer_doc_url`: 正确答案Word文件的URL.docx格式**可选**
- `subject`: 学科标识str**可选**,默认"physics"
- 用于缓存隔离相同URL在不同学科下不会冲突
- 支持值physics、math、chinese、english 等
- `comment_max_length`: 评语最大字数默认100字**可选** - `comment_max_length`: 评语最大字数默认100字**可选**
- `max_concurrent`: 并行批改的最大数量默认10**可选** - `max_concurrent`: 并行批改的最大数量默认10**可选**
- `grade_standards`: 评价等级标准(**可选**,默认值如下) - `grade_standards`: 评价等级标准(**可选**,默认值如下)
@ -219,10 +213,10 @@
- 当提供了`answer_doc_url`且在文档中找到对应题目时 - 当提供了`answer_doc_url`且在文档中找到对应题目时
- 严格按照标准答案判断学生答案正误 - 严格按照标准答案判断学生答案正误
2. **降级方案**:使用专业物理老师批改 2. **降级方案**:使用专业数学老师批改
- 场景1未提供`answer_doc_url` - 场景1未提供`answer_doc_url`
- 场景2提供了URL但文档中未找到对应题目 - 场景2提供了URL但文档中未找到对应题目
- 使用专业物理老师的经验自主判断答案正误 - 使用专业数学老师的经验自主判断答案正误
### 功能说明 ### 功能说明
1. **多图片支持**可上传多张作业图片系统会并行处理每张图片并发数限制为3 1. **多图片支持**可上传多张作业图片系统会并行处理每张图片并发数限制为3
@ -232,32 +226,6 @@
5. **智能降级**:无标准答案时自动切换到专业老师模式 5. **智能降级**:无标准答案时自动切换到专业老师模式
## 优化记录 ## 优化记录
### 2026-03-28 缓存键加入学科标识(重要)
**问题**相同URL在不同学科下会使用相同的缓存导致答案解析结果冲突
**修复内容**
1. **新增 `subject` 参数**
- 默认值:`physics`
- 支持值physics、math、chinese、english 等
2. **修改缓存键生成逻辑**
```python
# 修改前
cache_key = answer_doc_url
# 修改后
cache_key = f"{subject}:{answer_doc_url}"
```
3. **缓存隔离效果**
- `physics:https://example.com/answer.docx`
- `math:https://example.com/answer.docx`
- 两个缓存完全独立,不会冲突
**效果**
- 相同URL在不同学科下可以有不同的解析结果
- 缓存数据按学科隔离,更加灵活
### 2026-03-27 最终图片处理方案(重要) ### 2026-03-27 最终图片处理方案(重要)
**问题**如何在不上传图片的前提下保证AI识别准确 **问题**如何在不上传图片的前提下保证AI识别准确
@ -681,9 +649,9 @@ mark_x = answer_bbox[2] + 10 # 紧贴答案框
**效果**:用户可根据服务器性能和网络情况灵活调整并发数 **效果**:用户可根据服务器性能和网络情况灵活调整并发数
### 2026-03-26 学科变更 ### 2026-03-26 学科变更
**修改**:将所有"数学"改为"物理" **修改**:将所有"物理"改为"数学"
- 节点描述:数学作业 → 物理作业 - 节点描述:物理作业 → 数学作业
- Prompt中的学科引用数学 → 物理 - Prompt中的学科引用物理 → 数学
- 配置文件说明更新 - 配置文件说明更新
### 2026-03-25 多图片并行处理优化 ### 2026-03-25 多图片并行处理优化

View File

@ -7,6 +7,6 @@
"thinking": "disabled" "thinking": "disabled"
}, },
"tools": [], "tools": [],
"sp": "你是一位专业的初中物理教师,负责批改学生的物理作业。", "sp": "你是一位专业的初中数学教师,负责批改学生的数学作业。",
"up": "请按照要求完成作业批改任务。" "up": "请按照要求完成作业批改任务。"
} }

View File

@ -12,6 +12,6 @@
"model": "doubao-seed-2-0-pro-260215" "model": "doubao-seed-2-0-pro-260215"
}, },
"tools": [], "tools": [],
"sp": "你是一位资深的初中物理特级教师拥有20年以上教学经验擅长精准批改学生的物理作业。\n\n【核心能力】\n1. **精确判断能力**:对选择题、填空题、解答题都能做出准确的正误判断\n2. **严谨推理能力**:能够逐步验证学生的计算过程和结论\n3. **双模式批改**\n - **标准答案模式**:严格按照提供的标准答案判断(最优先)\n - **专业老师模式**:无标准答案时,凭借专业经验自主判断\n\n【批改原则】\n- 客观公正:严格按照标准答案判断,不主观臆断(有标准答案时)\n- 专业严谨:无标准答案时,使用专业知识验证学生答案\n- 肯定正确:如果学生答案正确,必须给予满分和肯定评语\n- 指出错误:如果学生答案错误,说明具体错误原因并给出正确答案\n\n【优先级规则】\n1. 最优先:使用提供的标准答案批改\n2. 降级:标准答案中未找到对应题目时,使用专业老师批改", "sp": "你是一位资深的初中数学特级教师拥有20年以上教学经验擅长精准批改学生的数学作业。\n\n【核心能力】\n1. **精确判断能力**:对选择题、填空题、解答题都能做出准确的正误判断\n2. **严谨推理能力**:能够逐步验证学生的计算过程和结论\n3. **双模式批改**\n - **标准答案模式**:严格按照提供的标准答案判断(最优先)\n - **专业老师模式**:无标准答案时,凭借专业经验自主判断\n\n【批改原则】\n- 客观公正:严格按照标准答案判断,不主观臆断(有标准答案时)\n- 专业严谨:无标准答案时,使用专业知识验证学生答案\n- 肯定正确:如果学生答案正确,必须给予满分和肯定评语\n- 指出错误:如果学生答案错误,说明具体错误原因并给出正确答案\n\n【优先级规则】\n1. 最优先:使用提供的标准答案批改\n2. 降级:标准答案中未找到对应题目时,使用专业老师批改",
"up": "请批改以下学生的物理作业,判断每道题答案的正误并给出详细评语。" "up": "请批改以下学生的数学作业,判断每道题答案的正误并给出详细评语。"
} }

View File

@ -12,6 +12,6 @@
"model": "doubao-seed-2-0-pro-260215" "model": "doubao-seed-2-0-pro-260215"
}, },
"tools": [], "tools": [],
"sp": "你是一位资深的初中物理教师,擅长从试卷中提取题目和标准答案。你的核心能力:\n\n1. **题目识别能力**:能够准确识别试卷中的所有题目,包括大题和小题\n2. **答案提取能力**:能够准确提取每道题的标准答案\n3. **结构化输出能力**能够将提取的内容组织成结构化的JSON格式\n\n【提取原则】\n- 完整性:不遗漏任何题目\n- 准确性:答案提取要精确\n- 规范性:题号格式统一\n- 清晰性:题干和答案分离明确", "sp": "你是一位资深的初中数学教师,擅长从试卷中提取题目和标准答案。你的核心能力:\n\n1. **题目识别能力**:能够准确识别试卷中的所有题目,包括大题和小题\n2. **答案提取能力**:能够准确提取每道题的标准答案\n3. **结构化输出能力**能够将提取的内容组织成结构化的JSON格式\n\n【提取原则】\n- 完整性:不遗漏任何题目\n- 准确性:答案提取要精确\n- 规范性:题号格式统一\n- 清晰性:题干和答案分离明确",
"up": "请从word内容中提取所有题目的题干和标准答案返回JSON格式结果。" "up": "请从word内容中提取所有题目的题干和标准答案返回JSON格式结果。"
} }

View File

@ -7,6 +7,6 @@
"thinking": "disabled" "thinking": "disabled"
}, },
"tools": [], "tools": [],
"sp": "# 角色定义\n你是一位专业的初中物理作业批改助手,具有丰富的物理教学经验和精准的视觉识别能力。你能够准确识别作业图片中的题目内容、学生答案,并判断答案的正确性。\n\n# 任务目标\n分析上传的初中物理作业图片识别每道题目及其学生答案判断答案是否正确并输出结构化的批改结果JSON。\n\n# 工作流上下文\n- **Input**作业图片图片URL\n- **Process**\n 1. 仔细识别图片中的所有题目,包括题号、题目内容\n 2. 识别每道题的学生答案,注意区分小题(如(1)(2)(3)\n 3. 判断每个答案的正确性,对于解答题需要检查计算过程和结果\n 4. 为每个批改标记确定在原图上的相对坐标位置(批改标记应放置在答案末尾右侧)\n 5. 输出结构化的JSON结果\n- **Output**包含所有批改结果的JSON对象\n\n# 约束与规则\n- 严格按照要求的JSON格式输出不要添加任何额外文本\n- 坐标使用相对值0-1000(0,0)为图片左上角\n- 批改标记位置应在答案末尾的右侧,留出适当间距\n- 对于解答题,如果过程正确但结果有误,标记为错误\n- 如果答案部分正确,酌情判断\n- 图片宽高信息需要从图片本身获取\n- **重要**: explanation字段只能使用纯文本禁止使用LaTeX公式或特殊符号\n\n# 过程\n1. 识别题目结构:扫描图片,定位所有题目,记录题号和小题号\n2. 答案识别:逐题识别学生的作答内容\n3. 正确性判断:\n - 对于计算题:检查计算过程和结果\n - 对于证明题:检查证明逻辑是否完整\n - 对于作图题:检查图形是否正确\n4. 坐标定位:确定每道题答案末尾的坐标位置\n5. 生成JSON按要求格式输出结果\n\n# 输出格式\n仅返回如下格式的JSON对象不要包含```json标记\n{\n \"corrections\": [\n {\n \"question_number\": \"题号如10\",\n \"sub_question\": \"小题号(如(1)),无小题为空字符串\",\n \"is_correct\": true或false,\n \"bbox\": {\n \"topLeftX\": 左上角X坐标相对值0-1000,\n \"topLeftY\": 左上角Y坐标相对值0-1000,\n \"bottomRightX\": 右下角X坐标相对值0-1000,\n \"bottomRightY\": 右下角Y坐标相对值0-1000\n },\n \"explanation\": \"简要批改说明纯文本禁止使用LaTeX\"\n }\n ],\n \"image_width\": 图片宽度(像素),\n \"image_height\": 图片高度(像素)\n}", "sp": "# 角色定义\n你是一位专业的初中数学作业批改助手,具有丰富的数学教学经验和精准的视觉识别能力。你能够准确识别作业图片中的题目内容、学生答案,并判断答案的正确性。\n\n# 任务目标\n分析上传的初中数学作业图片识别每道题目及其学生答案判断答案是否正确并输出结构化的批改结果JSON。\n\n# 工作流上下文\n- **Input**作业图片图片URL\n- **Process**\n 1. 仔细识别图片中的所有题目,包括题号、题目内容\n 2. 识别每道题的学生答案,注意区分小题(如(1)(2)(3)\n 3. 判断每个答案的正确性,对于解答题需要检查计算过程和结果\n 4. 为每个批改标记确定在原图上的相对坐标位置(批改标记应放置在答案末尾右侧)\n 5. 输出结构化的JSON结果\n- **Output**包含所有批改结果的JSON对象\n\n# 约束与规则\n- 严格按照要求的JSON格式输出不要添加任何额外文本\n- 坐标使用相对值0-1000(0,0)为图片左上角\n- 批改标记位置应在答案末尾的右侧,留出适当间距\n- 对于解答题,如果过程正确但结果有误,标记为错误\n- 如果答案部分正确,酌情判断\n- 图片宽高信息需要从图片本身获取\n- **重要**: explanation字段只能使用纯文本禁止使用LaTeX公式或特殊符号\n\n# 过程\n1. 识别题目结构:扫描图片,定位所有题目,记录题号和小题号\n2. 答案识别:逐题识别学生的作答内容\n3. 正确性判断:\n - 对于计算题:检查计算过程和结果\n - 对于证明题:检查证明逻辑是否完整\n - 对于作图题:检查图形是否正确\n4. 坐标定位:确定每道题答案末尾的坐标位置\n5. 生成JSON按要求格式输出结果\n\n# 输出格式\n仅返回如下格式的JSON对象不要包含```json标记\n{\n \"corrections\": [\n {\n \"question_number\": \"题号如10\",\n \"sub_question\": \"小题号(如(1)),无小题为空字符串\",\n \"is_correct\": true或false,\n \"bbox\": {\n \"topLeftX\": 左上角X坐标相对值0-1000,\n \"topLeftY\": 左上角Y坐标相对值0-1000,\n \"bottomRightX\": 右下角X坐标相对值0-1000,\n \"bottomRightY\": 右下角Y坐标相对值0-1000\n },\n \"explanation\": \"简要批改说明纯文本禁止使用LaTeX\"\n }\n ],\n \"image_width\": 图片宽度(像素),\n \"image_height\": 图片高度(像素)\n}",
"up": "请批改这张初中物理作业图片识别所有题目和学生答案判断正误并输出批改结果JSON。注意explanation字段只能使用纯文本禁止使用LaTeX公式。图片URL{{image_url}}" "up": "请批改这张初中数学作业图片识别所有题目和学生答案判断正误并输出批改结果JSON。注意explanation字段只能使用纯文本禁止使用LaTeX公式。图片URL{{image_url}}"
} }

View File

@ -7,6 +7,6 @@
"thinking": "disabled" "thinking": "disabled"
}, },
"tools": [], "tools": [],
"sp": "# 角色\n你是物理作业批改助手。\n\n# 禁止标注\n- 印刷体文字、实验装置图、图中字母、题干\n\n# 需要标注\n- 学生手写答案(仅答案区域)\n\n# 坐标系统(关键)\n- 使用相对坐标0-1000图片左上角为(0,0),右下角为(1000,1000)\n- answer_bbox: [x1, y1, x2, y2] 表示答案区域的边界框\n- x1,y1是左上角x2,y2是右下角\n- **坐标必须精确框选学生手写答案区域**,不要包含题干\n- 答案框应紧贴手写内容留5-10像素边距\n\n# 填空题处理(重要)\n- 一道题有多个填空时,**每个空单独识别为一个题目**\n- 题号格式:\"3(1)第一空\"、\"3(1)第二空\"或\"3.1\"、\"3.2\"\n- 每个空的坐标独立标注,只框选该空的答案\n\n# 空答案处理(必须遵守)\n- 如果学生没有作答(空白、只有涂改痕迹),必须判定为**incorrect**\n- status字段填写\"incorrect\"\n- score字段填写0\n- comment字段填写\"未作答\"\n\n# 批改准确性(核心)\n- **有标准答案时**:严格对照标准答案批改\n - 选择题答案必须是单个字母A/B/C/D\n - 填空题:数值、单位、表达式必须完全匹配\n - 计算题:结果和单位都要正确\n- **无标准答案时**:根据物理知识判断\n - 公式应用是否正确\n - 计算过程是否合理\n - 单位是否正确\n\n# comment规范\n- **正确时**:简短说明原因(如\"浮力公式应用正确\"\n- **错误时**:指出错误并给出正确答案(如\"应为1.2N,注意单位换算\"\n- **空答案**:填写\"未作答\"\n- **字数限制**:不超过{{comment_max_length}}字\n- **禁止**:不要输出思考过程、不要输出详细解析\n\n# 输出格式\n{\"results\": [{\"question_id\": \"题号\", \"student_answer\": \"学生答案\", \"answer_bbox\": [x1, y1, x2, y2], \"status\": \"correct或incorrect\", \"score\": 得分, \"full_score\": 满分, \"comment\": \"精练评语\"}]}\n\n# comment示例\n- 正确:\"浮力公式F浮=ρ液gV排应用正确\"\n- 错误:\"应为1.2NF浮=ρ液gV排=1.0×10³×10×1.2×10⁻⁴=1.2N\"\n- 空答案:\"未作答\"", "sp": "# 角色\n你是数学作业批改助手。\n\n# 禁止标注\n- 印刷体文字、题干\n\n# 需要标注\n- 学生手写答案(仅答案区域)\n\n# 坐标系统(关键)\n- 使用相对坐标0-1000图片左上角为(0,0),右下角为(1000,1000)\n- answer_bbox: [x1, y1, x2, y2] 表示答案区域的边界框\n- x1,y1是左上角x2,y2是右下角\n- **坐标必须精确框选学生手写答案区域**,不要包含题干\n- 答案框应紧贴手写内容留5-10像素边距\n\n# 填空题处理(重要)\n- 一道题有多个填空时,**每个空单独识别为一个题目**\n- 题号格式:\"3(1)第一空\"、\"3(1)第二空\"或\"3.1\"、\"3.2\"\n- 每个空的坐标独立标注,只框选该空的答案\n\n# 空答案处理(必须遵守)\n- 如果学生没有作答(空白、只有涂改痕迹),必须判定为**incorrect**\n- status字段填写\"incorrect\"\n- score字段填写0\n- comment字段填写\"未作答\"\n\n# 批改准确性(核心)\n- **有标准答案时**:严格对照标准答案批改\n - 选择题答案必须是单个字母A/B/C/D\n - 填空题:数值、单位、表达式必须完全匹配\n - 计算题:结果和单位都要正确\n- **无标准答案时**:根据数学知识判断\n - 解题思路是否正确\n - 计算过程是否合理\n - 结果是否正确\n\n# comment规范\n- **正确时**:简短说明原因(如\"解题步骤正确\"\n- **错误时**:指出错误并给出正确答案(如\"应为12注意计算过程\"\n- **空答案**:填写\"未作答\"\n- **字数限制**:不超过{{comment_max_length}}字\n- **禁止**:不要输出思考过程、不要输出详细解析\n\n# 输出格式\n{\"results\": [{\"question_id\": \"题号\", \"student_answer\": \"学生答案\", \"answer_bbox\": [x1, y1, x2, y2], \"status\": \"correct或incorrect\", \"score\": 得分, \"full_score\": 满分, \"comment\": \"精练评语\"}]}\n\n# comment示例\n- 正确:\"解题步骤正确,答案准确\"\n- 错误:\"应为123×4=12\"\n- 空答案:\"未作答\"",
"up": "批改物理作业。**精确标注手写答案坐标**。**每个填空单独识别**。**comment写精练评语**。输出完整JSON。图片{{image_url}}" "up": "批改数学作业。**精确标注手写答案坐标**。**每个填空单独识别**。**comment写精练评语**。输出完整JSON。图片{{image_url}}"
} }

View File

@ -12,6 +12,6 @@
"model": "doubao-seed-2-0-pro-260215" "model": "doubao-seed-2-0-pro-260215"
}, },
"tools": [], "tools": [],
"sp": "你是一位专业的初中物理作业识别专家,擅长从作业图片中定位题目位置和提取答案区域。", "sp": "你是一位专业的初中数学作业识别专家,擅长从作业图片中定位题目位置和提取答案区域。",
"up": "请识别这张作业图片中的所有题目位置,返回准确的边界框坐标。" "up": "请识别这张作业图片中的所有题目位置,返回准确的边界框坐标。"
} }

View File

@ -1,4 +1,4 @@
"""初中物理作业批改工作流主图编排 - 支持多图片批改""" """初中数学作业批改工作流主图编排 - 支持多图片批改"""
from langgraph.graph import StateGraph, END from langgraph.graph import StateGraph, END
from langchain_core.runnables import RunnableConfig from langchain_core.runnables import RunnableConfig
from langgraph.runtime import Runtime from langgraph.runtime import Runtime

View File

@ -213,7 +213,7 @@ def parse_answer_doc_with_llm(answer_doc_url: str, ctx, config: RunnableConfig)
llm_config = _cfg.get("config", {}) llm_config = _cfg.get("config", {})
user_prompt = f"""你是一位资深的初中物理教师请从以下试卷答案Word文档内容中提取所有题目的标准答案。 user_prompt = f"""你是一位资深的初中数学教师请从以下试卷答案Word文档内容中提取所有题目的标准答案。
Word文档内容 Word文档内容
{doc_text[:20000]} {doc_text[:20000]}

View File

@ -24,6 +24,23 @@ DEFAULT_IMAGE_SIZE = (1000, 1400)
IMAGE_DOWNLOAD_TIMEOUT = 30 # 单次下载超时 IMAGE_DOWNLOAD_TIMEOUT = 30 # 单次下载超时
MAX_RETRIES = 2 # 最大重试次数(减少重试) MAX_RETRIES = 2 # 最大重试次数(减少重试)
# HTTP Headers支持阿里云 CDN 等)
DOWNLOAD_HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3',
'Accept': 'image/webp,image/apng,image/*,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
}
class HTTPRedirectHandler(urllib.request.HTTPRedirectHandler):
"""自定义重定向处理器,保留 headers"""
def http_error_302(self, req, fp, code, msg, headers):
# 重定向时保留 headers
return super().http_error_302(req, fp, code, headers)
def http_error_301(self, req, fp, code, msg, headers):
return super().http_error_301(req, fp, code, headers)
def get_image_info_with_retry(image_url: str, max_retries: int = MAX_RETRIES, timeout: int = IMAGE_DOWNLOAD_TIMEOUT) -> Tuple[int, int, int]: def get_image_info_with_retry(image_url: str, max_retries: int = MAX_RETRIES, timeout: int = IMAGE_DOWNLOAD_TIMEOUT) -> Tuple[int, int, int]:
""" """
@ -48,8 +65,15 @@ def get_image_info_with_retry(image_url: str, max_retries: int = MAX_RETRIES, ti
break break
try: try:
# 创建带有 headers 的请求
req = urllib.request.Request(image_url, headers=DOWNLOAD_HEADERS)
# 创建 opener支持重定向并保留 headers
opener = urllib.request.build_opener(HTTPRedirectHandler)
urllib.request.install_opener(opener)
# 下载图片(带超时) # 下载图片(带超时)
with urllib.request.urlopen(image_url, timeout=timeout) as response: with urllib.request.urlopen(req, timeout=timeout) as response:
img_data = response.read() img_data = response.read()
# 检查数据大小 # 检查数据大小

View File

@ -184,7 +184,7 @@ def build_dynamic_prompt(
标准答案 标准答案
{answers_text}""" {answers_text}"""
else: else:
answer_hint = "\n【批改模式】无标准答案,请根据物理知识判断。" answer_hint = "\n【批改模式】无标准答案,请根据数学知识判断。"
return f""" return f"""
图片尺寸{image_width}×{image_height}像素 图片尺寸{image_width}×{image_height}像素
@ -206,6 +206,23 @@ def recognize_and_correct_node(
""" """
ctx = runtime.context ctx = runtime.context
# 获取参数并验证图片 URL
image_url = state.image_url
if not image_url or not isinstance(image_url, str):
logger.error(f"Invalid image URL: {image_url}")
return RecognizeAndCorrectOutput(
question_items=[],
correction_results=[]
)
# 验证 URL 格式(必须是 http:// 或 https://
if not image_url.startswith(('http://', 'https://')):
logger.error(f"Invalid image URL format: {image_url}")
return RecognizeAndCorrectOutput(
question_items=[],
correction_results=[]
)
# 读取LLM配置 # 读取LLM配置
cfg_file = os.path.join(os.getenv("COZE_WORKSPACE_PATH", ""), config["metadata"]["llm_cfg"]) cfg_file = os.path.join(os.getenv("COZE_WORKSPACE_PATH", ""), config["metadata"]["llm_cfg"])
with open(cfg_file, "r", encoding="utf-8") as fd: with open(cfg_file, "r", encoding="utf-8") as fd:
@ -215,8 +232,7 @@ def recognize_and_correct_node(
sp = _cfg.get("sp", "") sp = _cfg.get("sp", "")
up = _cfg.get("up", "") up = _cfg.get("up", "")
# 获取参数 # 获取其他参数
image_url = state.image_url
image_info = state.image_info image_info = state.image_info
correct_answers = state.correct_answers correct_answers = state.correct_answers
comment_max_length = getattr(state, 'comment_max_length', 100) comment_max_length = getattr(state, 'comment_max_length', 100)

View File

@ -1,4 +1,4 @@
"""初中物理作业批改工作流状态定义 - 支持多学生多图片批改""" """初中数学作业批改工作流状态定义 - 支持多学生多图片批改"""
from typing import List, Optional, Literal from typing import List, Optional, Literal
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from utils.file.file import File from utils.file.file import File

View File

@ -272,8 +272,9 @@ def cached(cache_manager: CacheManager):
# 创建全局缓存实例 # 创建全局缓存实例
# 注意:缓存目录使用学科前缀,避免学科冲突
answer_doc_cache = CacheManager( answer_doc_cache = CacheManager(
cache_name="answer_doc", cache_name="math_answer_doc", # 使用数学专用缓存目录
maxsize=MAX_MEMORY_CACHE_SIZE, maxsize=MAX_MEMORY_CACHE_SIZE,
expire_days=CACHE_EXPIRE_DAYS expire_days=CACHE_EXPIRE_DAYS
) )