392 lines
18 KiB
Markdown
392 lines
18 KiB
Markdown
## 项目概述
|
||
- **名称**: 初中物理作业批改工作流
|
||
- **功能**: 上传多张作业图片和Word答案文件,自动识别学生答案、提取标准答案、精准批改并返回批改结果JSON
|
||
|
||
### 节点清单
|
||
| 节点名 | 文件位置 | 类型 | 功能描述 | 分支逻辑 | 配置文件 |
|
||
|-------|---------|------|---------|---------|---------|
|
||
| doc_extract | `nodes/doc_extract_node.py` | agent | 从Word文件(.docx)提取题干和标准答案;如无URL则返回空列表 | - | `config/doc_extract_llm_cfg.json` |
|
||
| process_images | `nodes/process_images_node.py` | looparray | 循环调用子图处理每张作业图片,生成最终批改结果 | - | - |
|
||
|
||
**类型说明**: task(普通任务节点) / agent(大模型节点) / condition(条件分支) / looparray(列表循环) / loopcond(条件循环)
|
||
|
||
## 子图清单
|
||
| 子图名 | 文件位置 | 功能描述 | 被调用节点 |
|
||
|-------|---------|------|---------|
|
||
| single_image_subgraph | `graphs/loop_graph.py` | 处理单张图片的完整批改流程(预处理→识别批改→整合→包装) | process_images |
|
||
|
||
### 子图内部节点
|
||
| 节点名 | 文件位置 | 类型 | 功能描述 |
|
||
|-------|---------|------|---------|
|
||
| image_preprocess | `nodes/image_preprocess_node.py` | task | 下载图片、自动旋转(横向→纵向)、缩放到固定宽度1000px、上传对象存储 |
|
||
| recognize_and_correct | `nodes/recognize_and_correct_node.py` | agent | **一体化识别批改**:合并识别题目和批改为一次LLM调用 |
|
||
| result_merge | `nodes/result_merge_node.py` | task | 将识别结果和批改结果合并为最终批注 |
|
||
| wrap_result | `graphs/loop_graph.py` | task | 包装子图结果为SingleImageResult输出 |
|
||
|
||
## 技能使用
|
||
- 节点 `recognize_and_correct` 使用大语言模型技能(多模态,识别+批改合并)
|
||
- 模型:`doubao-seed-2-0-pro-260215`(旗舰视觉模型,推理能力强,输出简洁)
|
||
- 节点 `doc_extract` 使用大语言模型技能
|
||
- 模型:`doubao-seed-2-0-pro-260215`(旗舰模型,复杂推理能力强)
|
||
- 使用 python-docx 解析 Word 文档
|
||
|
||
## 工作流程(多图片批改架构)
|
||
|
||
```
|
||
┌─────────────────────┐
|
||
│ doc_extract │
|
||
│ (Word答案解析) │
|
||
└──────────┬──────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ process_images │
|
||
│ (多图片循环处理) │
|
||
│ 生成最终批改结果 │
|
||
└─────────────────────┘
|
||
```
|
||
|
||
### 子图内部流程(处理单张图片 - 优化版)
|
||
|
||
```
|
||
┌─────────────────────┐
|
||
│ image_preprocess │
|
||
│ (图像预处理) │
|
||
└──────────┬──────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│recognize_and_correct│ ← 合并节点:识别+批改一次完成
|
||
│ (一体化识别批改) │
|
||
└──────────┬──────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ result_merge │
|
||
│ (结果整合) │
|
||
└──────────┬──────────┘
|
||
│
|
||
▼
|
||
┌─────────────────────┐
|
||
│ wrap_result │
|
||
│ (包装输出) │
|
||
└─────────────────────┘
|
||
```
|
||
|
||
## 核心功能:多图片批改机制
|
||
|
||
### 输入参数
|
||
- `homework_images`: 上传的作业图片列表(List[File],支持多张图片)
|
||
- `answer_doc_url`: 正确答案Word文件的URL(.docx格式,**可选**)
|
||
- `comment_max_length`: 评语最大字数(默认100字,**可选**)
|
||
- `max_concurrent`: 并行批改的最大数量(默认10,**可选**)
|
||
- `grade_standards`: 评价等级标准(**可选**,默认值如下)
|
||
```json
|
||
{
|
||
"A+": {"min_percentage": 95, "description": "答案全部正确,步骤完整规范,逻辑严谨;书写/格式整洁,无错别字、无遗漏;完成度100%,态度认真,质量上乘"},
|
||
"A": {"min_percentage": 90, "description": "答案完全正确,无任何错误;步骤合理、格式规范,无原则性问题;完成度100%,满足全部要求"},
|
||
"B": {"min_percentage": 80, "description": "存在少量非关键性错误,或步骤略有缺失;整体思路基本正确,仅细节、格式、计算等小问题;完成大部分内容,整体合格但不够严谨"},
|
||
"C": {"min_percentage": 70, "description": "错误较多,部分核心题目作答错误;步骤不完整、逻辑不够清晰;完成度一般,有明显应付、漏答情况"},
|
||
"D": {"min_percentage": 0, "description": "大面积错误,核心知识点未掌握;大量空白、敷衍、抄袭;未达到基本完成要求"}
|
||
}
|
||
```
|
||
|
||
### 输出结果
|
||
- `final_result`: 最终批改结果JSON(包含多图片)
|
||
- `total_images`: 总图片数
|
||
- `image_results`: 各图片的批改结果列表
|
||
- `overall_comment`: 整体评价(根据得分率生成)
|
||
- `total_score`: 总得分
|
||
- `full_score`: 总满分
|
||
- `grade`: 等级评定
|
||
|
||
### 批改优先级(严格按照以下顺序)
|
||
1. **最优先**:使用Word文档中的标准答案批改
|
||
- 当提供了`answer_doc_url`且在文档中找到对应题目时
|
||
- 严格按照标准答案判断学生答案正误
|
||
|
||
2. **降级方案**:使用专业物理老师批改
|
||
- 场景1:未提供`answer_doc_url`
|
||
- 场景2:提供了URL但文档中未找到对应题目
|
||
- 使用专业物理老师的经验自主判断答案正误
|
||
|
||
### 功能说明
|
||
1. **多图片支持**:可上传多张作业图片,系统会并行处理每张图片(并发数限制为3)
|
||
2. **Word答案提取**:从.docx文件中提取题干和标准答案
|
||
3. **子图循环处理**:使用子图封装单图片处理流程,主图调用子图处理每张图片
|
||
4. **批改结果JSON**:返回包含所有图片批改结果的结构化JSON
|
||
5. **智能降级**:无标准答案时自动切换到专业老师模式
|
||
|
||
## 优化记录
|
||
### 2026-03-26 填空题拆分优化(重要)
|
||
**问题**:一道题有多个填空时,被合并成一个答案,批改标记无法精准定位
|
||
|
||
**修复内容**:
|
||
1. **优化Prompt**:
|
||
- 明确要求:一道题有多个填空时,**每个空单独识别为一个题目**
|
||
- 题号格式:\"3(1)第一空\"、\"3(1)第二空\"、\"4(2)第一空\"、\"4(2)第二空\"
|
||
- 每个空单独批改,单独打分
|
||
|
||
2. **示例说明**:
|
||
```
|
||
❌ 错误:3(1) → "4、1"(合并)
|
||
✅ 正确:3(1)第一空 → "4"
|
||
3(1)第二空 → "1"
|
||
```
|
||
|
||
3. **参数传递优化**:
|
||
- comment_max_length参数正确传递到Jinja2模板
|
||
- 确保LLM生成符合长度要求的comment
|
||
|
||
**效果**:
|
||
- 识别数量从9个增加到13个
|
||
- 每个填空都有独立的批改标记
|
||
- 批改标记精准定位到每个答案位置
|
||
|
||
### 2026-03-26 JSON解析优化(重要)
|
||
**问题**:LLM输出可能不完整(被max_completion_tokens截断),导致JSON解析失败
|
||
|
||
**修复内容**:
|
||
1. **新增fix_incomplete_json函数**:
|
||
- 自动检测缺失的括号(}和])
|
||
- 自动补全缺失的括号,使JSON完整
|
||
- 示例:`{"results": [{"id": 1}` → 自动补全为 `{"results": [{"id": 1}]}`
|
||
|
||
2. **增强JSON解析流程**:
|
||
- 第一步:尝试直接解析
|
||
- 第二步:尝试修复不完整的JSON(补全括号)
|
||
- 第三步:尝试提取JSON对象
|
||
- 第四步:尝试修复提取的JSON
|
||
|
||
3. **移除错误的截断逻辑**:
|
||
- 不再在解析后截断comment(可能破坏转义字符)
|
||
- 完全依赖LLM遵守comment_max_length限制
|
||
- 通过Prompt明确要求LLM控制comment长度
|
||
|
||
4. **参数正确传递**:
|
||
- comment_max_length参数正确传递到Prompt
|
||
- LLM根据该参数生成符合长度的comment
|
||
|
||
**效果**:
|
||
- JSON解析成功率大幅提升
|
||
- 能够处理不完整的JSON输出
|
||
- comment长度由LLM控制,避免截断破坏格式
|
||
|
||
### 2026-03-26 识别优化:禁止标注实验装置图(重要)
|
||
**问题**:
|
||
1. 在实验装置图(如弹簧测力计、烧杯等)上标注了批改气泡
|
||
2. 坐标定位不够精准
|
||
|
||
**修复内容**:
|
||
1. **Prompt优化**:
|
||
- 明确禁止标注实验装置图、示意图、电路图
|
||
- 明确禁止标注图中标注的字母(如A、B、C、D、E、F、G)
|
||
- 强调只标注学生手写答案
|
||
|
||
2. **工程规范优化**:
|
||
- 从config文件读取sp和up(符合工程规范)
|
||
- 使用Jinja2模板渲染Prompt
|
||
- 代码中只保留动态部分构建(标准答案、图片尺寸等)
|
||
|
||
3. **识别流程优化**:
|
||
- 找题号 → 找学生答案 → 框选答案 → 判断正误
|
||
- 强调学生答案的特征:手写、填写空白处、计算结果
|
||
|
||
**效果**:不再误标注实验装置图,只标注学生手写答案
|
||
|
||
### 2026-03-26 新增并行数量控制参数
|
||
**优化前**:硬编码并发数限制为3,不够灵活
|
||
**优化后**:添加max_concurrent参数,默认值10,用户可自定义
|
||
|
||
**具体优化**:
|
||
1. **新增参数**:`max_concurrent`(可选,默认10)
|
||
2. **修改位置**:
|
||
- `GraphInput.max_concurrent: Optional[int] = 10`
|
||
- `GlobalState.max_concurrent: int = 10`
|
||
- `ProcessImagesInput.max_concurrent: int`
|
||
3. **使用方式**:
|
||
```json
|
||
{
|
||
"homework_images": [...],
|
||
"max_concurrent": 5 // 最多同时处理5张图片
|
||
}
|
||
```
|
||
|
||
**效果**:用户可根据服务器性能和网络情况灵活调整并发数
|
||
|
||
### 2026-03-26 学科变更
|
||
**修改**:将所有"数学"改为"物理"
|
||
- 节点描述:数学作业 → 物理作业
|
||
- Prompt中的学科引用:数学 → 物理
|
||
- 配置文件说明更新
|
||
|
||
### 2026-03-25 多图片并行处理优化
|
||
**优化前**:多图片串行处理,总时间 = 单张图片时间 × 图片数量
|
||
**优化后**:多图片并行处理(并发数限制为3),总时间大幅缩短
|
||
|
||
**具体优化**:
|
||
1. **并行处理架构**:使用 `ThreadPoolExecutor` 并行调用子图处理每张图片
|
||
- 最多同时处理3张图片
|
||
- 结果按 `image_index` 正确排序,保证顺序一致性
|
||
2. **性能提升**:
|
||
- 3张图片:时间减少约66%(从3份时间 → 1份时间)
|
||
- 5张图片:时间减少约80%(从5份时间 → 约2份时间,分两批并行)
|
||
3. **质量保证**:
|
||
- 每张图片独立处理,互不影响
|
||
- 识别逻辑、批改逻辑完全相同,质量不受影响
|
||
|
||
### 2026-03-26 坐标定位修复(重要)
|
||
**问题**:坐标定位特别不准,批改标记位置错误
|
||
**原因**:Y坐标修正逻辑错误,导致坐标被错误缩放
|
||
|
||
**修复内容**:
|
||
1. **坐标系统重构**:从绝对坐标改为相对坐标(0-1000)系统
|
||
- AI返回相对坐标(0-1000),(0,0)为图片左上角,(1000,1000)为右下角
|
||
- 代码将相对坐标转换为绝对坐标:`绝对X = 相对X × width / 1000`,`绝对Y = 相对Y × height / 1000`
|
||
|
||
2. **Prompt优化**:
|
||
- 明确要求AI返回相对坐标(0-1000)
|
||
- 添加坐标系统说明和示例
|
||
|
||
3. **转换逻辑修正**:
|
||
- 移除错误的Y坐标修正(`Y × height_ratio`)
|
||
- 实现正确的相对坐标到绝对坐标转换
|
||
|
||
**效果**:坐标定位准确,批改标记位置正确
|
||
|
||
### 2026-03-26 题目和答案识别优化(重要)
|
||
**问题**:
|
||
1. 无法准确区分"题干"和"学生答案"
|
||
2. 批改气泡不在学生答案位置
|
||
3. 题干位置被误标注为答案
|
||
|
||
**修复内容**:
|
||
1. **Prompt重写**:
|
||
- 明确定义"题干"和"学生答案"的区别
|
||
- 强调只标注学生手写答案,不标注印刷体题干
|
||
- 添加识别流程指导
|
||
|
||
2. **坐标定位优化**:
|
||
- 自动计算mark_position:答案框右侧30像素,垂直居中
|
||
- 添加边界检查,确保不超出图片范围
|
||
- 不再依赖AI返回的mark_position(可能不准确)
|
||
|
||
3. **识别指导**:
|
||
- 题号识别:如1、2、3、(1)、(2)等
|
||
- 答案定位:学生手写内容(不是印刷体)
|
||
- bbox框选:准确框选学生答案区域
|
||
|
||
**效果**:更准确地区分题干和答案,批改气泡位置更精准
|
||
|
||
### 2026-03-25 批改速度优化
|
||
**优化前**:每张图片需要3次LLM调用(识别+批改+整体评价)
|
||
**优化后**:每张图片只需1次LLM调用
|
||
|
||
**具体优化**:
|
||
1. **合并识别和批改**:将`homework_recognize`和`correction_judge`合并为`recognize_and_correct`节点
|
||
- 识别题目、学生答案、坐标的同时进行批改
|
||
- 减少一次LLM调用,速度提升约50%
|
||
|
||
2. **简化整体评价**:不再调用LLM生成整体评价
|
||
- 使用规则直接生成评价内容
|
||
- 根据得分率和错误数量生成个性化评语
|
||
- 减少一次LLM调用
|
||
|
||
3. **子图节点精简**:从5个节点减少到4个节点
|
||
- 移除:homework_recognize、correction_judge
|
||
- 新增:recognize_and_correct(合并节点)
|
||
- 保留:image_preprocess、result_merge、wrap_result
|
||
|
||
**效果**:
|
||
- LLM调用次数:每张图片从3次减少到1次
|
||
- 预计批改时间减少约60%
|
||
|
||
### 2026-03-25 新增输入参数控制
|
||
1. **新增 `comment_max_length` 参数**:控制评语最大字数,默认100字
|
||
2. **新增 `grade_standards` 参数**:自定义评价等级标准
|
||
- 支持自定义各等级的最低得分率百分比
|
||
- 支持自定义各等级的描述
|
||
- 默认标准:A+(≥95%)、A(≥90%)、B(≥80%)、C(≥70%)、D(<70%)
|
||
3. **使用方式**:
|
||
```json
|
||
{
|
||
"homework_images": [...],
|
||
"comment_max_length": 50, // 评语最多50字
|
||
"grade_standards": {
|
||
"A+": {"min_percentage": 98, "description": "完美"},
|
||
"A": {"min_percentage": 90, "description": "优秀"},
|
||
...
|
||
}
|
||
}
|
||
```
|
||
|
||
### 2026-03-25 评语优化与整体评价
|
||
1. **评语具体化**:批改评语要求具体说明对错原因
|
||
- 正确时:说明为什么正确
|
||
- 错误时:指出错误原因并给出正确答案
|
||
- 部分正确时:说明哪些对了哪些错了
|
||
- 字数限制:50字以内,最多不超过100字
|
||
- 不要显示思考过程,只输出结果
|
||
2. **评语示例**:
|
||
- 选择题正确:答案为B,与标准答案一致,正确。
|
||
- 填空题错误:答案应为8+√7和8-√7,学生只写了一个,不完整。
|
||
- 解答题正确:解题过程完整,步骤清晰,结果正确。
|
||
- 计算题错误:计算过程有误,正确答案是m=2,建议检查移项步骤。
|
||
3. **整体评价**:根据所有批改内容自动生成简短的整体评价
|
||
- 调用LLM生成个性化评价
|
||
- 评价不超过50字
|
||
- 包含主要问题或优点
|
||
- 给出简短建议
|
||
4. **HTML报告优化**:在统计总览后显示整体评价区域
|
||
|
||
### 2026-03-25 自动旋转功能
|
||
1. **新增横向图片自动旋转**:如果上传的图片宽度大于高度(横向图片),系统会自动旋转-90度使其变为纵向
|
||
2. **旋转时机**:在图像预处理阶段,下载图片后、缩放前进行旋转
|
||
3. **旋转方向**:逆时针旋转90度(rotate(-90)),确保文字方向正确
|
||
4. **日志记录**:添加详细的旋转日志,便于调试
|
||
|
||
### 2026-03-25 多图片批改功能
|
||
1. **新增多图片支持**:从单图片批改升级为支持多图片批量批改
|
||
2. **新增子图架构**:创建 `loop_graph.py` 封装单图片处理流程
|
||
3. **新增循环节点**:创建 `process_images_node.py` 循环调用子图处理每张图片
|
||
4. **重构状态定义**:
|
||
- `GraphInput.homework_image` → `homework_images: List[File]`
|
||
- 新增 `SubgraphState`、`SubgraphInput`、`SubgraphOutput` 子图状态
|
||
- 新增 `SingleImageResult` 单图片批改结果
|
||
- 新增 `FinalResult.image_results` 多图片结果列表
|
||
5. **重构HTML生成**:支持生成包含所有图片批改标注的HTML报告
|
||
6. **优化主图编排**:简化为三节点线性流程(doc_extract → process_images → html_generate)
|
||
|
||
### 2026-03-25 双模式批改机制
|
||
1. **新增智能降级逻辑**:优先使用标准答案,无标准答案时自动切换专业老师模式
|
||
2. **修改state.py**:`answer_doc_url`改为可选字段,支持不提供答案URL的场景
|
||
3. **升级correction_judge_node**:实现题目分离逻辑,有标准答案和无标准答案分别处理
|
||
4. **更新Prompt**:批改节点支持两种模式(标准答案模式 + 专业老师模式)
|
||
5. **优化doc_extract_node**:无URL时返回空列表,不中断工作流
|
||
|
||
### 2026-03-25 Word答案解析功能
|
||
1. 新增 `doc_extract_node` 节点:从Word文件(.docx)提取题干和标准答案
|
||
2. 使用 python-docx 提取 Word 文档内容
|
||
3. 并行处理架构:图像识别与答案解析同时进行
|
||
4. 基于 Word 中的标准答案进行精准批改
|
||
|
||
### 2026-03-25 OCR识别能力优化
|
||
1. 问题:识别节点把学生答案中的"8"错认成"9",导致误判
|
||
2. 优化识别节点prompt:增加OCR识别特别提示,强调区分8和9、6和0、1和7等相似字符
|
||
3. 效果:第7题正确识别为"8+√7,8-√7",满分通过
|
||
|
||
### 2026-03-25 批改能力升级
|
||
1. 升级批改节点模型:`doubao-seed-1-6-vision-250815` → `doubao-seed-2-0-pro-260215`
|
||
2. 原因:较小模型对选择题判断准确率不足
|
||
3. 效果:选择题判断准确率大幅提升,推理过程更严谨
|
||
|
||
### 2026-03-24 重构(学习豆包APP方式)
|
||
1. 从8个节点简化为5个节点(现调整为子图+主图架构)
|
||
2. 采用一体化识别:AI识别answer_bbox,代码计算mark_position
|
||
3. 实现精准坐标计算,Y坐标与答案垂直中心完美对齐
|
||
|
||
## TODO
|
||
- 提供真实的多页作业图片进行完整流程测试
|
||
- 优化HTML报告的图片展示布局
|
||
- 支持PDF格式答案文档
|