"""初中数学作业批改工作流状态定义 - 支持多学生多图片批改""" from typing import List, Optional, Literal from pydantic import BaseModel, Field from utils.file.file import File # === 基础数据结构 === class StudentHomework(BaseModel): """学生作业信息""" student_id: int = Field(..., description="学生ID") student_name: str = Field(default="", description="学生姓名") homework_images: List[str] = Field(default=[], description="该学生的作业图片URL列表") class ImageInfo(BaseModel): """图片信息""" width: int = Field(..., description="图片宽度(像素)") height: int = Field(..., description="图片高度(像素)") dpi: int = Field(default=72, description="图片DPI") class MarkPosition(BaseModel): """批改标记位置""" x: int = Field(..., description="标记中心X坐标(绝对像素)") y: int = Field(..., description="标记中心Y坐标(绝对像素)") class QuestionItem(BaseModel): """题目项(一体化识别结果)""" question_id: str = Field(..., description="题号(如10、10.1、10.2)") parent_id: str = Field(default="", description="母题题号(子题时填写)") is_sub_question: bool = Field(default=False, description="是否为子题") question_text: str = Field(default="", description="题目内容") student_answer: str = Field(default="", description="学生答案内容") answer_bbox: List[int] = Field(..., description="学生答案区域的边界框 [x1, y1, x2, y2](用于定位标注)") mark_position: MarkPosition = Field(..., description="批改标记应该标注的位置坐标") full_score: int = Field(default=10, description="满分") class CorrectAnswer(BaseModel): """标准答案项(从Word文档解析)""" question_id: str = Field(..., description="题号(如10、10.1、10.2)") parent_id: str = Field(default="", description="母题题号(子题时填写)") is_sub_question: bool = Field(default=False, description="是否为子题") question_text: str = Field(default="", description="题目内容/题干") correct_answer: str = Field(..., description="标准答案") full_score: int = Field(default=10, description="满分") answer_analysis: str = Field(default="", description="答案解析/解题步骤") class CorrectionResult(BaseModel): """批改结果""" question_id: str = Field(..., description="题号") parent_id: str = Field(default="", description="母题题号") status: Literal["correct", "incorrect", "partial"] = Field(..., description="批改状态") score: int = Field(default=0, description="得分") full_score: int = Field(default=10, description="满分") comment: str = Field(default="", description="批改评语") class Annotation(BaseModel): """完整批注""" question_id: str = Field(..., description="题号") parent_id: str = Field(default="", description="母题题号") status: Literal["correct", "incorrect", "partial"] = Field(..., description="批改状态") question_text: str = Field(default="", description="题目内容") student_answer: str = Field(default="", description="学生答案") answer_bbox: List[int] = Field(default=[], description="答案区域边界框") comment: str = Field(default="", description="批改评语") mark_position: MarkPosition = Field(..., description="批改标记位置") score: int = Field(default=0, description="得分") full_score: int = Field(default=10, description="满分") class SingleImageResult(BaseModel): """单张图片的批改结果""" image_index: int = Field(..., description="图片索引(从0开始)") image_info: ImageInfo = Field(..., description="图片信息") image_url: str = Field(default="", description="处理后的图片URL") annotations: List[Annotation] = Field(default=[], description="该图片的批注列表") class StudentResult(BaseModel): """单个学生的批改结果""" student_id: int = Field(..., description="学生ID") student_name: str = Field(default="", description="学生姓名") total_images: int = Field(..., description="该学生的总图片数") image_results: List[SingleImageResult] = Field(default=[], description="各图片的批改结果") overall_comment: str = Field(default="", description="该学生的整体评价") total_score: int = Field(default=0, description="该学生的总分") full_score: int = Field(default=100, description="该学生的满分") grade: str = Field(default="", description="该学生的等级") # === 全局状态 === class GlobalState(BaseModel): """工作流全局状态""" # 输入参数 student_homework: List[StudentHomework] = Field(default=[], description="学生作业列表") answer_doc_url: str = Field(default="", description="正确答案Word文件的URL(.docx格式)") comment_max_length: int = Field(default=100, description="评语最大字数") max_concurrent: int = Field(default=10, description="并行批改的最大数量") grade_standards: dict = Field(default={}, description="评价等级标准") # 中间状态 correct_answers: List[CorrectAnswer] = Field(default=[], description="从Word解析的标准答案列表") # 最终结果 student_results: List[StudentResult] = Field(default=[], description="各学生的批改结果列表") # === 图输入输出 === class GraphInput(BaseModel): """工作流输入""" student_homework: List[StudentHomework] = Field(..., description="学生作业列表,每个学生包含ID和作业图片") answer_doc_url: str = Field(default="", description="正确答案Word文件的URL(.docx格式,可选)") comment_max_length: int = Field(default=100, description="评语最大字数,默认100字") max_concurrent: int = Field(default=10, description="并行批改的最大数量,默认10") grade_standards: dict = Field( default={ "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": "大面积错误,核心知识点未掌握;大量空白、敷衍、抄袭;未达到基本完成要求"} }, description="评价等级标准,包含各等级的最低得分率百分比和描述" ) class GraphOutput(BaseModel): """工作流输出""" student_results: List[StudentResult] = Field(..., description="各学生的批改结果列表") # === 文档答案解析节点 === class DocExtractInput(BaseModel): """文档答案解析节点输入""" answer_doc_url: str = Field(default="", description="正确答案Word文件的URL(.docx格式,可选)") class DocExtractOutput(BaseModel): """文档答案解析节点输出""" correct_answers: List[CorrectAnswer] = Field(default=[], description="解析的标准答案列表") # === 子图状态定义(单图片处理流程) === class SubgraphState(BaseModel): """子图全局状态(处理单张图片)""" # 输入 homework_image: File = Field(..., description="当前处理的作业图片") correct_answers: List[CorrectAnswer] = Field(default=[], description="标准答案列表") image_index: int = Field(default=0, description="图片索引") comment_max_length: int = Field(default=100, description="评语最大字数") # 中间状态 image_info: ImageInfo = Field(default_factory=lambda: ImageInfo(width=0, height=0, dpi=72), description="图片信息") image_url: str = Field(default="", description="处理后的图片URL") question_items: List[QuestionItem] = Field(default=[], description="识别的题目项列表") correction_results: List[CorrectionResult] = Field(default=[], description="批改结果列表") annotations: List[Annotation] = Field(default=[], description="最终批注列表") class SubgraphInput(BaseModel): """子图输入""" homework_image: File = Field(..., description="当前处理的作业图片") correct_answers: List[CorrectAnswer] = Field(default=[], description="标准答案列表") image_index: int = Field(default=0, description="图片索引") comment_max_length: int = Field(default=100, description="评语最大字数") class SubgraphOutput(BaseModel): """子图输出""" image_result: SingleImageResult = Field(..., description="单张图片的批改结果") # === 子图节点输入输出 === # 1. 图像预处理节点 class ImagePreprocessInput(BaseModel): """图像预处理节点输入""" homework_image: File = Field(..., description="上传的作业图片") class ImagePreprocessOutput(BaseModel): """图像预处理节点输出""" image_info: ImageInfo = Field(..., description="图片信息") image_url: str = Field(..., description="处理后的图片URL") # 2. 一体化识别批改节点(合并识别+批改) class RecognizeAndCorrectInput(BaseModel): """一体化识别批改节点输入""" image_url: str = Field(..., description="图片URL") image_info: ImageInfo = Field(..., description="图片信息") correct_answers: List[CorrectAnswer] = Field(default=[], description="标准答案列表") comment_max_length: int = Field(default=100, description="评语最大字数") class RecognizeAndCorrectOutput(BaseModel): """一体化识别批改节点输出""" question_items: List[QuestionItem] = Field(default=[], description="识别的题目项列表") correction_results: List[CorrectionResult] = Field(default=[], description="批改结果列表") # 3. 批改判断节点 class CorrectionJudgeInput(BaseModel): """批改判断节点输入""" question_items: List[QuestionItem] = Field(default=[], description="题目项列表") correct_answers: List[CorrectAnswer] = Field(default=[], description="标准答案列表") comment_max_length: int = Field(default=100, description="评语最大字数") class CorrectionJudgeOutput(BaseModel): """批改判断节点输出""" correction_results: List[CorrectionResult] = Field(default=[], description="批改结果列表") # 4. 结果整合节点 class ResultMergeInput(BaseModel): """结果整合节点输入""" question_items: List[QuestionItem] = Field(default=[], description="题目项列表") correction_results: List[CorrectionResult] = Field(default=[], description="批改结果列表") class ResultMergeOutput(BaseModel): """结果整合节点输出""" annotations: List[Annotation] = Field(default=[], description="最终批注列表") # === 循环节点 === class ProcessImagesInput(BaseModel): """多学生作业处理循环节点输入""" student_homework: List[StudentHomework] = Field(default=[], description="学生作业列表") correct_answers: List[CorrectAnswer] = Field(default=[], description="标准答案列表") comment_max_length: int = Field(default=100, description="评语最大字数") max_concurrent: int = Field(default=10, description="并行批改的最大数量") grade_standards: dict = Field(default={}, description="评价等级标准") class ProcessImagesOutput(BaseModel): """多学生作业处理循环节点输出""" student_results: List[StudentResult] = Field(..., description="各学生的批改结果列表")