ebe33392ca
Preserve document revisions and evidence, harden indexing and export behavior, add explicit material selection and direct-folder import, validate model JSON schemas with safe error messages, and retain student selection while loading scoring data.
1125 lines
46 KiB
Markdown
1125 lines
46 KiB
Markdown
# WiseGrade(智评)项目指南
|
||
|
||
当前源码的索引版本为 5、证据政策为 2,最新迁移为 `0009`。解析与评分改进见 [第一轮优化说明](OPTIMIZATION_2026-10-12.md),历史文档修订与失败恢复见 [第二轮优化说明](OPTIMIZATION_2026-10-13.md),实际 PDF 流程发现的目录权限问题及最新升级边界见 [PDF 实流程说明](PDF_TEST_2026-10-13.md)。旧部署记录不表示本轮改动已在正式环境应用。
|
||
|
||
本指南统一维护部署、教师使用、技术设计和开发说明;项目简介和快速启动见 [README](../README.md)。整理日期:2026-09-30;会话实现说明沿用 2026-09-27 的记录。
|
||
|
||
部署与会话章节描述已有实现;“业务规则与技术设计”保留原始设计基线,其中标为初稿、建议或验收目标的内容不代表已全部实现。当前实现与验证边界见 [交接记录](../HANDOFF.md),具体接口以代码和运行中的 `/docs` 为准。开发任务提示词集中在 [prompts](prompts/README.md),不作为功能完成证明。
|
||
|
||
## 目录
|
||
|
||
- [部署与配置](#deployment)
|
||
- [教师使用](#usage)
|
||
- [业务规则与技术设计](#design)
|
||
- [会话与业务 Agent 实现](#agent)
|
||
- [开发与测试](#development)
|
||
- [故障定位与恢复](#troubleshooting)
|
||
|
||
<a id="deployment"></a>
|
||
|
||
## 部署与配置
|
||
|
||
WiseGrade 是单机单管理员教师端。Web 服务仅绑定 `127.0.0.1`,默认端口为 `18080`,可通过 `.env` 中的 `WISEGRADE_WEB_PORT` 修改。学生原件通过只读目录挂载进入容器,不经过浏览器上传。以下命令除明确切换目录外,均从仓库根目录执行。
|
||
|
||
### 准备
|
||
|
||
- Docker Engine + Compose v2(Linux),或 Windows Docker Desktop(WSL2 后端,运行 Linux 容器)。
|
||
- 一个存放学生材料的宿主机目录;其下每个批次目录包含 `学号_姓名_选题题目` 子目录。
|
||
- `deepseek-flash` 可访问的 OpenAI 兼容 API 地址和密钥;该端点必须支持文本、图片输入及原生 `tool_calls`。若只做评分而不使用对话,仍可沿用已有 JSON 调用。
|
||
- 离线嵌入模型包。默认使用 BAAI/bge-m3。内存不足时可改用 BAAI/bge-small-zh-v1.5。
|
||
|
||
仓库的 `docs/` 同时存放项目文档与课程示例资料;其中 PDF、DOC、DOCX 是课程资料,不是待导入的学生原件。旧版 `.doc` 只用于课程模板,经 LibreOffice 转换;学生输入只接受 PDF、DOCX、PPTX。
|
||
LibreOffice 已安装在后端 Docker 镜像中。按本指南使用容器时,教师不需要在 Windows 或 Linux 宿主机单独安装 LibreOffice;仅在脱离容器运行后端时才需自行安装。
|
||
|
||
### Linux 启动
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
mkdir -p data/inbox data/processed data/exports models/bge-m3
|
||
```
|
||
|
||
编辑 `.env`:
|
||
|
||
```env
|
||
WISEGRADE_DATA_DIR=/absolute/path/to/WiseGradeData
|
||
DEEPSEEK_API_KEY=your-key
|
||
WISEGRADE_SECRET_KEY=用随机值替换且至少32位
|
||
EMBEDDING_MODEL_PATH=/models/bge-m3
|
||
```
|
||
|
||
也可以启动后在“模型设置”页面填写 API Key。密钥由后端加密保存,浏览器不会直接调用模型服务。`WISEGRADE_SECRET_KEY` 一旦用于加密设置,不要随意更换,否则已保存的密钥将无法解密。
|
||
|
||
```bash
|
||
docker compose up
|
||
docker compose ps
|
||
curl http://127.0.0.1:18080/api/health
|
||
```
|
||
|
||
打开 <http://127.0.0.1:18080>。`api` 启动时执行 Alembic 迁移并创建初始课程、目标、指标点和规则。评分任务由 `worker` 容器处理。
|
||
|
||
如 `18080` 端口已被占用,在 `.env` 中将 `WISEGRADE_WEB_PORT` 改为其他空闲端口,重新运行 `docker compose up -d`,并使用对应端口访问。
|
||
|
||
### Windows Docker Desktop 启动
|
||
|
||
1. 安装并启动 Docker Desktop,启用 WSL2 后端,保持 Linux 容器模式。
|
||
2. 在 PowerShell 中进入项目目录:`Copy-Item .env.example .env`。
|
||
3. 编辑 `.env` 中的 `WISEGRADE_DATA_DIR`,使用正斜杠绝对路径,例如 `D:/WiseGradeData`。不要在 `.env` 中添加引号。
|
||
4. 将离线模型目录放在 `models/bge-m3`,填写 API 配置和随机 `WISEGRADE_SECRET_KEY`。
|
||
5. 运行 `docker compose up`,访问 <http://127.0.0.1:18080>。
|
||
|
||
如果挂载失败,先确认 `WISEGRADE_DATA_DIR` 存在、Docker Desktop 已取得对应磁盘访问权,再运行 `docker compose config` 查看展开后的挂载路径。首次启动后若修改了 `.env` 中的 API Key 等配置,运行 `docker compose up -d --force-recreate api worker web`,使容器读取新值。当前仓库尚未在 Windows 实机验证上述路径和文件挂载。
|
||
|
||
### 离线模型包
|
||
|
||
应用镜像不在启动时下载 BGE-M3。将独立模型包解压到 `models/bge-m3/`,目录应包含模型权重、`config.json`、分词器文件及 SentenceTransformers 配置。容器中该目录只读映射为 `/models/bge-m3`。替代模型需同时修改:
|
||
|
||
```env
|
||
EMBEDDING_MODEL_NAME=BAAI/bge-small-zh-v1.5
|
||
EMBEDDING_MODEL_PATH=/models/bge-small-zh-v1.5
|
||
EMBEDDING_MODEL_VERSION=your-package-version
|
||
```
|
||
|
||
`EMBEDDING_MODEL_VERSION` 会写入每个评分批次快照。若模型目录缺失或内容不完整,导入仍可完成,知识库入库和评分会显示错误。可离线分发 `docker save` 生成的镜像包及单独的 `models-bge-m3.tar.zst`;目标机执行 `docker load -i docker-images.tar` 后解压模型包,运行相同的 Compose 配置。
|
||
也可以把模型复制进后端镜像,但当前 Dockerfile 和 Compose 采用只读目录挂载;若改成镜像内置模型,须同时调整构建上下文,并移除会遮盖镜像内 `/models` 的挂载。本机 BGE-M3 必需文件约 2.29 GB,后端镜像当前约 1.08 GB;直接内置后镜像未压缩体积预计约 3.37 GB。API 与 worker 可以共用镜像层,不必各存一份模型。独立模型包便于单独更新和分发。
|
||
|
||
扫描 PDF 的 OCR 使用 PaddleOCR。离线 OCR 包放在 `models/ocr/detection/` 和 `models/ocr/recognition/`,或修改 `OCR_MODEL_DIR` 指向包含这两个子目录的位置。没有 OCR 模型且 PDF 无可提取文字时,文件进入解析失败状态并显示原因;系统不会在解析时自动下载 OCR 模型。
|
||
|
||
下面这组 PP-OCRv5 mobile 中文识别模型已在 PaddleOCR 3.7.0 中验证。下载后将压缩包的顶层目录去掉,使 `detection/` 和 `recognition/` 内直接包含 `inference.yml`、`inference.json` 和 `inference.pdiparams`:
|
||
|
||
```bash
|
||
mkdir -p models/ocr/detection models/ocr/recognition
|
||
curl -fL -o models/ocr/PP-OCRv5_mobile_det_infer.tar https://paddle-model-ecology.bj.bcebos.com/paddlex/official_inference_model/paddle3.0.0/PP-OCRv5_mobile_det_infer.tar
|
||
curl -fL -o models/ocr/PP-OCRv5_mobile_rec_infer.tar https://paddle-model-ecology.bj.bcebos.com/paddlex/official_inference_model/paddle3.0.0/PP-OCRv5_mobile_rec_infer.tar
|
||
tar -xf models/ocr/PP-OCRv5_mobile_det_infer.tar --strip-components=1 -C models/ocr/detection
|
||
tar -xf models/ocr/PP-OCRv5_mobile_rec_infer.tar --strip-components=1 -C models/ocr/recognition
|
||
```
|
||
|
||
模型名称从各自的 `inference.yml` 读取,不能让 PaddleOCR 使用与离线权重不匹配的默认模型。模型目录经 Compose 只读挂载,放入权重后无需修改 `.env`。
|
||
|
||
<a id="usage"></a>
|
||
|
||
## 教师使用
|
||
|
||
### 课程、导入、评分与导出
|
||
|
||
1. 在“课程与规则”中查看初始课程版本和评分规则。已发布版本不能直接覆盖;复制草稿、编辑、保存并发布新版本。课程目标、指标点、选题方向、模板和三个 Prompt 都属于课程版本。
|
||
2. 在“知识库资料”中选课程版本并入库课程资料。入库使用本地嵌入模型和 PostgreSQL `pgvector`。
|
||
3. 在“目录扫描与导入”中浏览 `/data/inbox` 下的相对目录,选择学生总目录并扫描。修正学号、姓名、选题和材料类型;PPT 导出的 PDF 必须确认成“汇报PPT/PDF”。每类材料只选择一个文件。
|
||
确认导入后,材料自动进入后台解析、必要 OCR 和索引任务;评分前即可检索已完成索引的报告。失败任务可从索引任务列表重试。
|
||
4. 在“评分批次”中选择已发布课程和规则版本,创建并启动批次。每次评分建立独立批次;重试不会覆盖其他批次。
|
||
5. 在“评分与复核”中查看逐项模型原始分、原文证据、页码、页面图像和异常。教师可逐项填写分数、评语和修改说明;修改历史单独保留。
|
||
6. 在“导出任务”中按批次导出 Excel,按学生导出 PDF 反馈。缺失或无法评分的项目显示待处理,不自动补零。
|
||
|
||
成绩权重和评分依据见下文“业务规则与技术设计”。
|
||
|
||
### 会话、范围与操作确认
|
||
|
||
打开系统默认进入空白“会话”草稿,点击“新建”立即切换到另一空白草稿,不弹窗、不写数据库。可在输入区直接提问,并在首次发送前反复选择或清除课程版本、评分批次和学生项目。发送首条非空消息时,会话和消息在同一事务保存;此后范围固定且在界面显示为只读。旧版留下的空会话仍可编辑范围。选择批次或项目会自动带入所属课程版本,清除课程版本会同时清除下级选择。范围固定后,工具不能读取范围之外的对象。会话列表、消息、引用、待确认操作和运行状态保存在数据库,刷新后可查看历史会话;刷新或再次进入页面仍打开新草稿。
|
||
|
||
可先问“列出课程版本”“扫描 batch 目录”“查看批次 3 的失败评分项”“查询项目 12 的报告实验数据”。课程目标与规则来自数据库;指定批次时使用批次创建时的快照。课程资料和报告通过 BGE-M3/pgvector 检索,回答附资料名、页码或幻灯片编号、原文片段和来源链接。评分证据与 Agent 检索引用都使用 `KnowledgeDocument` 的文档 ID、页码,对应同一 `KnowledgePage`:`/api/knowledge/documents/{id}/pages/{page}` 返回该页完整提取文字,`/image` 返回存在的页面 PNG。评分引用另有“查看评分”链接。历史引用由迁移 `0005` 一次性改写为这些统一地址;会话读取不再猜测旧图像链接。实体 PDF、DOCX、PPTX 页面经解析后通常同时有文字和图像;数据库直接生成的课程目标、评分规则等虚拟资料没有实体页面图像,只提供文字。没有证据时应明确说明。正式回答上方的“工具调用”先整体折叠,展开后每次调用仍独立折叠,可逐项查看参数和结果;回答末尾的“引用”也默认整体折叠。会话页不显示全局后台进度卡片,正在运行的会话仍在列表中标记。
|
||
|
||
扫描只读,可直接执行。导入、课程资料入库、创建批次、启动或重试、教师复核、导出和索引重试会生成确认卡片。卡片列出目标、范围、现值、新值及异常;教师点击“确认执行”才写入。模型工具白名单没有确认工具,聊天文字也不能代替按钮。导入候选有重复、识别异常或待确认文件时,卡片禁用确认;教师在卡片中修正身份、类别和文件选择,再“更新预览”。导入确认同时授权后台解析、必要 OCR 和项目索引。
|
||
|
||
教师复核必须在当前消息明确写出分数,例如“给 8 分”。Agent 可草拟评语,确认卡片展示批次、学生、评分项、模型原分、教师原分、证据、异常、新分和修改原因。失败或无法评分的项也可人工复核;原始模型分、证据和教师修改历史保留。
|
||
|
||
<a id="design"></a>
|
||
|
||
## 业务规则与技术设计
|
||
|
||
以下保留初始业务约束、数据示例和设计要求。会话、持久任务及统一证据页的增量实现见[会话与业务 Agent 实现](#agent);原始页面清单和 API 初稿仅供理解设计。
|
||
|
||
### 项目概述
|
||
|
||
#### 项目目标
|
||
|
||
构建一个前后端分离的自动评分系统,对自动化专业综合实践课程中的以下材料进行内容评分:
|
||
|
||
1. 阶段报告 1
|
||
2. 阶段报告 2
|
||
3. 阶段报告 3
|
||
4. 最终报告
|
||
5. 汇报 PPT,或 PPT 导出的 PDF
|
||
|
||
系统输出可复核的逐项评分、原文证据、页码或幻灯片编号、缺失内容、评语和改进建议。
|
||
|
||
#### 非目标
|
||
|
||
第一版不实现:
|
||
|
||
- 学生账号和在线提交
|
||
- 现场口头答辩、答辩视频和语音评分
|
||
- 学生个人贡献度自动识别
|
||
- 代码仓库自动审查
|
||
- AI 生成内容检测
|
||
- 自动控制实验硬件的远程接入
|
||
|
||
课程评分表中的“汇报答辩 40 分”保留原名称,但本系统只依据 PPT 内容或 PPT PDF 内容评分,不评价现场口头答辩。
|
||
|
||
### 已确定的业务规则
|
||
|
||
#### 教师使用流程
|
||
|
||
系统只供教师使用,采用单机单管理员模式。教师不需要创建学生账号。
|
||
|
||
教师流程:
|
||
|
||
```text
|
||
选择课程和学期
|
||
-> 选择评分规则版本
|
||
-> 选择挂载数据目录中的学生总文件夹
|
||
-> 扫描学生子文件夹
|
||
-> 自动识别学生信息和五类材料
|
||
-> 教师确认导入结果
|
||
-> 批量解析和评分
|
||
-> 查看模型评分和证据
|
||
-> 修改分数和评语
|
||
-> 导出 Excel 成绩表和逐生 PDF 反馈
|
||
```
|
||
|
||
#### 学生目录结构
|
||
|
||
宿主机数据目录示例:
|
||
|
||
```text
|
||
WiseGradeData/
|
||
└── 2026春季第1批/
|
||
├── 1023009006_李傲琦_基于ROS2系统的自瞄移动机器人/
|
||
│ ├── 阶段报告1.pdf
|
||
│ ├── 阶段报告2.pdf
|
||
│ ├── 阶段报告3.pdf
|
||
│ ├── 自动化专业综合实践报告.pdf
|
||
│ └── 汇报PPT.pptx
|
||
└── 其他学生/
|
||
```
|
||
|
||
学生目录命名格式:
|
||
|
||
```text
|
||
学号_姓名_选题题目
|
||
```
|
||
|
||
文件夹名称只用于初步识别。系统还要从文件首页、标题和正文中抽取姓名、学号、项目题目和选题方向,并在不一致时提醒教师确认。
|
||
|
||
#### 支持文件
|
||
|
||
支持:
|
||
|
||
- PDF
|
||
- DOCX
|
||
- PPTX
|
||
- PPT 导出的 PDF
|
||
|
||
不支持旧版 `.doc` 作为学生输入。
|
||
|
||
PPT PDF 必须在导入确认阶段标记为“汇报材料”,以免与最终报告 PDF 混淆。
|
||
|
||
#### 成绩结构
|
||
|
||
总评成绩:
|
||
|
||
```text
|
||
总评 = 阶段报告1 × 10%
|
||
+ 阶段报告2 × 10%
|
||
+ 阶段报告3 × 10%
|
||
+ 期末成绩 × 70%
|
||
```
|
||
|
||
期末评分保持课程评分表结构:
|
||
|
||
| 评分项 | 分值 |
|
||
|---|---:|
|
||
| 任务完成度 | 20 |
|
||
| 报告规范性 | 20 |
|
||
| 汇报答辩(系统仅按 PPT 内容评价) | 40 |
|
||
| 结果分析 | 20 |
|
||
|
||
#### 评分批次
|
||
|
||
每次评分创建独立批次。批次之间不覆盖、不替代,也不设置唯一“正式批次”。每个批次均可查看、复核和导出。
|
||
|
||
批次必须保存:
|
||
|
||
- 课程版本
|
||
- 评分规则版本
|
||
- 课程目标版本
|
||
- `deepseek-flash` 模型配置快照
|
||
- 嵌入模型名称和版本
|
||
- 创建时间和结束时间
|
||
- 评分任务状态
|
||
|
||
### 课程和评分依据
|
||
|
||
初始资料位于 `docs/`:
|
||
|
||
- `课程导学-自动化专业综合实践.pdf`
|
||
- `自动化专业综合实践三融合选题课程概述.pdf`
|
||
- `大作业评分表-自动化专业综合实践.docx`
|
||
- `阶段报告1模板.doc`
|
||
- `阶段报告2模板.doc`
|
||
- `阶段报告3模板.doc`
|
||
- `阶段报告1.pdf`
|
||
- `阶段报告2.pdf`
|
||
- `阶段报告3.pdf`
|
||
- `自动化专业综合实践报告.pdf`
|
||
|
||
#### 课程目标和毕业要求指标点
|
||
|
||
系统初始知识库必须包含并建立关联:
|
||
|
||
- 课程目标 1:问题解决与研究能力
|
||
- 课程目标 2:知识运用与分析能力
|
||
- 课程目标 3:实验设计与操作能力
|
||
- 课程目标 4:数据处理与报告撰写能力
|
||
- 毕业要求指标点 2.3、2.4
|
||
- 毕业要求指标点 4.1、4.2、4.3、4.4
|
||
- 毕业要求指标点 11.1、11.2
|
||
|
||
#### 阶段报告要求
|
||
|
||
##### 阶段报告 1:复杂自动化工程问题解决方案论证报告
|
||
|
||
重点内容:
|
||
|
||
- 文献调研和技术现状
|
||
- 复杂工程问题分析
|
||
- 主方案和至少一套替代方案
|
||
- 方案原理、适用场景和优劣对比
|
||
- 数理或工程依据下的可行性论证
|
||
- 进度计划、资源配置、成本构成和经济性分析
|
||
|
||
初始评分框架:
|
||
|
||
| 评分项 | 分值 |
|
||
|---|---:|
|
||
| 复杂工程问题和需求分析 | 25 |
|
||
| 文献调研和技术现状总结 | 20 |
|
||
| 主方案和替代方案设计 | 25 |
|
||
| 可行性、风险和合理性论证 | 15 |
|
||
| 工程管理、进度和成本分析 | 10 |
|
||
| 报告完整性和规范性 | 5 |
|
||
| 合计 | 100 |
|
||
|
||
##### 阶段报告 2:实验系统设计与硬件搭建方案
|
||
|
||
重点内容:
|
||
|
||
- 硬件选型清单、参数和依据
|
||
- 系统架构图、硬件连接或接口关系
|
||
- 分步骤实验计划、测试方法和预期指标
|
||
- 安全风险、防护措施和操作规范
|
||
|
||
初始评分框架:
|
||
|
||
| 评分项 | 分值 |
|
||
|---|---:|
|
||
| 系统架构和实现思路 | 20 |
|
||
| 硬件选型及选型依据 | 20 |
|
||
| 系统框图、连接关系和接口设计 | 20 |
|
||
| 实验方案、测试方法和预期指标 | 20 |
|
||
| 安全风险和防护措施 | 10 |
|
||
| 阶段进展及下一阶段计划 | 10 |
|
||
| 合计 | 100 |
|
||
|
||
##### 阶段报告 3:软件设计与系统调试报告
|
||
|
||
重点内容:
|
||
|
||
- 软件逻辑、流程图或状态图
|
||
- 核心代码片段和实现说明
|
||
- 调试过程、故障现象、原因和解决方法
|
||
- 实验数据整理和初步分析
|
||
- 系统功能、性能指标和存在问题
|
||
|
||
初始评分框架:
|
||
|
||
| 评分项 | 分值 |
|
||
|---|---:|
|
||
| 软件逻辑和系统实现 | 20 |
|
||
| 核心代码和功能说明 | 20 |
|
||
| 调试过程、问题分析和解决方法 | 20 |
|
||
| 实验数据和初步分析 | 20 |
|
||
| 功能和性能指标验证 | 15 |
|
||
| 下一阶段计划及报告规范性 | 5 |
|
||
| 合计 | 100 |
|
||
|
||
阶段评分框架必须作为可编辑的初始规则导入,不能写死在业务代码中。
|
||
|
||
### 总体架构
|
||
|
||
```text
|
||
React 前端
|
||
|
|
||
FastAPI 后端
|
||
|-- 课程、版本和规则管理
|
||
|-- 挂载目录扫描和导入
|
||
|-- 文档解析和事实抽取
|
||
|-- RAG 检索
|
||
|-- 评分编排和批次管理
|
||
|-- 教师复核
|
||
|-- Excel/PDF 导出
|
||
|
|
||
PostgreSQL + pgvector
|
||
Redis + RQ
|
||
本地文件存储
|
||
本地 BGE-M3 嵌入模型
|
||
DeepSeek API(deepseek-flash)
|
||
```
|
||
|
||
采用模块化单体后端,不在第一版拆分成大量微服务。模块边界清晰,后续可以拆分。
|
||
|
||
#### 推荐技术栈
|
||
|
||
- 前端:React、TypeScript、Vite、Ant Design
|
||
- 后端:Python、FastAPI、SQLAlchemy、Pydantic
|
||
- 数据库:PostgreSQL、pgvector
|
||
- 后台任务:Redis、RQ
|
||
- PDF 解析:PyMuPDF
|
||
- DOCX 解析:python-docx
|
||
- PPTX 解析:python-pptx
|
||
- 页面转换:LibreOffice headless
|
||
- OCR:PaddleOCR,扫描 PDF 时作为回退
|
||
- 本地嵌入:`BAAI/bge-m3`
|
||
- 模型调用:OpenAI 兼容客户端,统一模型名 `deepseek-flash`
|
||
- Excel:openpyxl 或 pandas
|
||
- PDF 反馈:HTML/CSS + WeasyPrint 或 Playwright
|
||
- 部署:Docker Compose
|
||
|
||
### 宿主机目录挂载方案
|
||
|
||
原始学生材料不通过浏览器上传。前端只选择后端可见的挂载目录下的大文件夹,后端扫描和读取文件。
|
||
|
||
挂载路径与平台配置统一见[部署与配置](#deployment)。
|
||
|
||
#### 路径安全
|
||
|
||
- 后端只允许访问 `/data/inbox`
|
||
- 前端只提交挂载目录内的相对路径
|
||
- 禁止提交绝对路径
|
||
- 后端必须执行路径规范化和越界检查
|
||
- 原始输入目录只读挂载
|
||
- 解析结果、任务状态和导出文件写入独立目录
|
||
|
||
#### 文件变更检测
|
||
|
||
导入时保存:
|
||
|
||
- 相对路径
|
||
- 文件大小
|
||
- 修改时间
|
||
- SHA-256
|
||
- MIME 类型
|
||
- 解析状态
|
||
|
||
若文件哈希未变化,不重复解析和向量化。源文件变化时提示重新导入。
|
||
|
||
### 文档解析和结构化
|
||
|
||
#### 统一文档对象
|
||
|
||
每个文档解析为:
|
||
|
||
```json
|
||
{
|
||
"document_type": "stage1",
|
||
"source_path": "...",
|
||
"pages": [
|
||
{
|
||
"page_number": 1,
|
||
"text": "...",
|
||
"tables": [],
|
||
"images": [],
|
||
"rendered_image_path": "..."
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
必须保留页码、章节名、表格和图片关联。
|
||
|
||
#### 文件识别
|
||
|
||
支持通过以下信号识别材料类型:
|
||
|
||
- 文件名关键词
|
||
- 文件扩展名
|
||
- 文档标题
|
||
- 首页文本
|
||
- 章节标题
|
||
- PPT 页数和标题
|
||
|
||
识别结果包括:
|
||
|
||
```text
|
||
确定
|
||
不确定
|
||
缺失
|
||
重复
|
||
无法解析
|
||
```
|
||
|
||
不确定的文件进入教师确认界面。
|
||
|
||
#### PPT/PDF 视觉处理
|
||
|
||
- 提取 PPT 原生文字、表格和备注
|
||
- 将每一页渲染为图片
|
||
- PDF 每页保存页面图像
|
||
- 评分相关页面连同文字一起发送给 `deepseek-flash`
|
||
- 图片证据必须关联页码或幻灯片编号
|
||
|
||
### RAG 方案
|
||
|
||
#### 知识库分层
|
||
|
||
##### 公共课程知识库
|
||
|
||
- 课程导学
|
||
- 课程目标
|
||
- 毕业要求指标点
|
||
- 评分表
|
||
- 三个阶段模板
|
||
- 各选题方向要求
|
||
- 优秀报告
|
||
- 课程版本资料
|
||
|
||
##### 学生项目知识库
|
||
|
||
每个学生独立命名空间:
|
||
|
||
```text
|
||
course_2026/project_1023009006/
|
||
stage1/
|
||
stage2/
|
||
stage3/
|
||
final_report/
|
||
presentation/
|
||
```
|
||
|
||
禁止评分时检索其他学生的材料。
|
||
|
||
#### 文本切分
|
||
|
||
- 按标题和章节进行结构感知切分
|
||
- 普通文本块约 400~800 个中文字符
|
||
- 重叠约 80~120 个中文字符
|
||
- 表格单独保存
|
||
- 图片及图片说明单独保存
|
||
- 每块必须保留页码、页面类型、章节、文档类型和评分指标关联
|
||
- 目录页文本作为导航块单独存放,不与正文块混同
|
||
|
||
#### 检索
|
||
|
||
采用混合检索:
|
||
|
||
```text
|
||
pgvector 向量检索
|
||
+ 中文二元组关键词检索
|
||
+ 课程版本过滤
|
||
+ 学生项目过滤
|
||
+ 报告类型过滤
|
||
+ 阶段过滤
|
||
+ 页面类型过滤
|
||
```
|
||
|
||
过滤在取候选之前完成,两个召回各自独立进行,再按排名融合。默认每个评分项送模型 8~12 个候选证据块,跨目标合计上限 14 块。
|
||
|
||
#### 嵌入模型
|
||
|
||
默认使用本地 `BAAI/bge-m3`;离线包、替代模型和版本配置见[部署与配置](#deployment)。
|
||
|
||
### DeepSeek 接入
|
||
|
||
#### 统一模型
|
||
|
||
只使用一个模型配置:
|
||
|
||
```yaml
|
||
provider: deepseek
|
||
base_url: https://api.deepseek.com
|
||
model: deepseek-flash
|
||
```
|
||
|
||
同一个模型负责:
|
||
|
||
- 文本理解
|
||
- 图像理解
|
||
- 事实抽取
|
||
- 评分
|
||
- 评语生成
|
||
- PPT 分析
|
||
- 跨文档一致性检查
|
||
|
||
#### 密钥管理
|
||
|
||
教师可以在设置页面配置:
|
||
|
||
- API Key
|
||
- Base URL
|
||
- 模型名
|
||
- 并发数
|
||
- 超时时间
|
||
- 重试次数
|
||
|
||
安全要求:
|
||
|
||
- 前端不直接调用 DeepSeek
|
||
- API Key 只发送给后端
|
||
- 数据库保存加密后的密钥
|
||
- 日志禁止输出 API Key
|
||
- 支持环境变量初始化
|
||
|
||
#### 发送数据范围
|
||
|
||
原始文件保存在本地。模型请求只发送当前评分项所需的:
|
||
|
||
- 文本证据块
|
||
- 表格内容
|
||
- 必要的页面图片
|
||
- 课程评分标准
|
||
- 已抽取的项目事实
|
||
|
||
不把全部报告无条件重复发送给每一个评分项。
|
||
|
||
### 评分引擎
|
||
|
||
评分采用多阶段流程:
|
||
|
||
```text
|
||
文件导入
|
||
-> 文档解析
|
||
-> 项目事实抽取
|
||
-> 规则检查
|
||
-> 指标级 RAG 检索
|
||
-> 指标级评分
|
||
-> 跨文档一致性检查
|
||
-> 分数汇总
|
||
-> 教师复核
|
||
```
|
||
|
||
#### 事实抽取
|
||
|
||
抽取:
|
||
|
||
- 学号、姓名、项目名称
|
||
- 选题方向
|
||
- 功能目标
|
||
- 性能指标
|
||
- 硬件型号
|
||
- 软件技术
|
||
- 实验方案
|
||
- 实验数据
|
||
- 进度计划
|
||
- 成本信息
|
||
- 结论和改进方向
|
||
|
||
#### 评分输出
|
||
|
||
每个评分项必须返回结构化 JSON:
|
||
|
||
```json
|
||
{
|
||
"criterion_id": "stage1.solution_design",
|
||
"score": 17,
|
||
"max_score": 20,
|
||
"reason": "...",
|
||
"evidence": [
|
||
{
|
||
"document": "阶段报告1",
|
||
"page": 12,
|
||
"quote": "...",
|
||
"target_id": "t0"
|
||
}
|
||
],
|
||
"missing_evidence": ["..."],
|
||
"suggestion": "...",
|
||
"confidence": 0.84
|
||
}
|
||
```
|
||
|
||
模型不得对没有证据的内容自行假设。结构化输出必须经过 Pydantic 校验;失败时自动重试或进入人工处理。`score` 允许为 `null`:当规则要求正文证据而检索只能给出目录、摘要或章节标题时,模型必须返回 `null`,不得据此判断学生内容缺失。
|
||
|
||
每条引文必须通过三类校验:原文与页码一致、来源页面类型在该目标允许范围内、内容性判断不得只由目录、摘要或单独章节标题支撑。页面权限和状态转换由代码执行;模型的说明或推理不能改变它。
|
||
|
||
#### 页面类型与证据政策
|
||
|
||
每份资料的每一页都带一个页面类型,随页面持久化并可供检索、评分、复核和会话查询:
|
||
|
||
| 页面类型 | 含义 | 能否作为内容证据 |
|
||
|---|---|---|
|
||
| `cover` | 封面、标题页 | 否(结构目标可用) |
|
||
| `abstract` | 摘要、Abstract | 否(结构目标可用) |
|
||
| `toc` | 目录及其续页 | 否(结构目标可用) |
|
||
| `body` | 正文 | 是 |
|
||
| `appendix` | 附录 | 是 |
|
||
| `references` | 参考文献、致谢 | 否(结构目标可用) |
|
||
| `unknown` | 识别有疑义 | 否 |
|
||
|
||
判定结合页首标题、章节条目密度、点线与末尾页码、摘要/参考文献措辞、封面字段和页脚样式,并由文档级连续性识别跨页续目录、连续摘要和连续参考文献。它不依赖"目录"一词或固定页号;重复页眉页脚在建立索引前清理。识别依据(各项信号、置信度和分类器版本)与页面一起保存,便于教师核查。
|
||
|
||
目录与正文共处一页时标为混合页,只对该页按文本块区分:正文块仍作为正文检索,不被整页排除。
|
||
|
||
`required_evidence` 的目标带 `use` 属性,并随批次快照保存:
|
||
|
||
- `use: structure`:封面、目录、参考文献等页面可以满足,例如"章节""参考文献"。
|
||
- `use: content`:只允许正文和附录中的实质段落、数据或表格。
|
||
|
||
评分规则沿用历史上的字符串数组形式;读取时按下述语法转换,批次快照记录转换结果和 `POLICY_VERSION`,因此旧批次始终可按当时的规则重读。
|
||
|
||
#### 检索
|
||
|
||
每个必备证据目标分别检索:先限定课程版本、学生项目、材料类别和允许的页面类型,再取候选。**页面过滤发生在候选数量限制之前**,目录页不会因为得分高而挤掉正文页。
|
||
|
||
语义匹配和中文关键词匹配各自召回候选池,再按排名倒数(RRF)融合;不对未归一化的语义分和关键词分直接相加。
|
||
|
||
关键词召回不使用 PostgreSQL 的 `to_tsvector('simple', …)`:该配置没有中文分词,整个中文串会成为一个词元。在课程报告上实测,查询"文献调研"无法命中含"第2章文献调研与技术现状"的页面,因此现有实现里关键词分长期接近零。现在改用字符二元组加 ASCII/数字整词的重叠度,无需扩展且在各数据库上行为一致。
|
||
|
||
名额分配优先分散到不同页面;有了可靠章节路径后对同一章节设软上限;名额不足时回填同页或同节的其他正文片段,但不会放宽证据类型。
|
||
|
||
内容目标在正文中没有命中时,利用目录标题匹配实际正文标题,补检该章节及其相邻正文页。目录只作导航,最终内容引文必须落在实际正文页。
|
||
|
||
评分发送给模型的页面图像优先选择相关的不同正文页,而不是目录页;评分最终送模型的片段数量保持受控,Agent 搜索同样受控。
|
||
|
||
#### 待教师复核
|
||
|
||
"待教师复核"是与材料缺失、解析失败、模型调用失败并列的独立状态:
|
||
|
||
- 材料缺失、重复、待确认或解析失败 → `unscorable`
|
||
- 模型调用或校验失败 → `failed`
|
||
- 内容目标补检后仍只有目录或摘要命中 → `needs_review`
|
||
|
||
进入待复核时模型分为 `null`,总分保持待处理,系统不会自动给低分或断言学生未写。评分项记录缺失的正文证据目标和当时的检索范围,该记录在教师复核后仍然保留。批次状态为"已完成,有待复核项"(`review_required`),仪表盘的待处理数包含待复核项。
|
||
|
||
教师可对任何状态的评分项复核,包括待复核项;教师评分输入框不预填 0。
|
||
|
||
#### 跨文档一致性
|
||
|
||
至少检查:
|
||
|
||
- 阶段 1 计划是否在阶段 2、3 中落实
|
||
- 各报告中的项目名称、硬件型号和技术路线是否一致
|
||
- 实验数据是否前后一致
|
||
- 最终报告和 PPT 是否描述同一成果
|
||
- 指标是否存在明显夸大或矛盾
|
||
|
||
### 规则配置和版本管理
|
||
|
||
规则不能写死在 Prompt 或 Python 代码中。
|
||
|
||
#### 评分指标结构
|
||
|
||
```yaml
|
||
criterion_id: stage1.solution_feasibility
|
||
name: 方案合理性与可行性
|
||
max_score: 15
|
||
description: 评价方案是否具有工程依据、可实施性和风险分析
|
||
linked_outcomes: [2.3, 2.4, 4.1]
|
||
required_evidence:
|
||
- label: 主方案
|
||
use: content # 只能用正文或附录的实质段落证明
|
||
- label: 替代方案
|
||
use: content
|
||
- label: 章节
|
||
use: structure # 目录即可证明章节存在
|
||
score_levels:
|
||
- min: 12
|
||
max: 15
|
||
description: 论证充分,方案比较清晰,风险分析具体
|
||
- min: 6
|
||
max: 11
|
||
description: 基本完成论证,但证据或分析不充分
|
||
- min: 0
|
||
max: 5
|
||
description: 缺少核心论证
|
||
```
|
||
|
||
`required_evidence` 也接受历史上的字符串数组(`required_evidence: [主方案, 替代方案]`)。读取时按措辞判定 `use`:提到标题、封面、目录、章节、参考文献、格式、附录等报告包装字样的目标为 `structure`,其余为 `content`。判定结果与 `POLICY_VERSION` 一起写入批次快照,不修改已发布的规则文本。
|
||
|
||
#### 版本要求
|
||
|
||
课程、课程目标、毕业要求、模板、评分规则和 Prompt 都需要版本号。评分批次保存完整版本快照。
|
||
|
||
教师修改规则后创建新版本,不覆盖历史版本。
|
||
|
||
### 核心数据表
|
||
|
||
建议至少包含:
|
||
|
||
```text
|
||
courses
|
||
course_versions
|
||
course_objectives
|
||
graduation_indicators
|
||
topic_directions
|
||
rubric_versions
|
||
rubric_criteria
|
||
rubric_score_levels
|
||
knowledge_documents
|
||
knowledge_pages
|
||
knowledge_chunks
|
||
students
|
||
projects
|
||
submissions
|
||
submission_files
|
||
scoring_batches
|
||
criterion_scores
|
||
score_evidences
|
||
teacher_revisions
|
||
export_tasks
|
||
app_settings
|
||
audit_logs
|
||
```
|
||
|
||
#### 评分批次
|
||
|
||
```text
|
||
scoring_batches
|
||
id
|
||
course_version_id
|
||
rubric_version_id
|
||
model_name
|
||
model_config_snapshot
|
||
embedding_model_name
|
||
embedding_model_version
|
||
status
|
||
created_at
|
||
finished_at
|
||
```
|
||
|
||
#### 评分项结果
|
||
|
||
```text
|
||
criterion_scores
|
||
id
|
||
batch_id
|
||
project_id
|
||
criterion_id
|
||
model_score
|
||
teacher_score
|
||
final_score
|
||
model_comment
|
||
teacher_comment
|
||
confidence
|
||
status
|
||
```
|
||
|
||
```text
|
||
final_score = teacher_score(存在时)或 model_score
|
||
```
|
||
|
||
模型原始结果不得被教师修改覆盖。
|
||
|
||
### 前端页面
|
||
|
||
第一版页面建议:
|
||
|
||
1. 仪表盘
|
||
2. 课程管理
|
||
3. 课程版本和评分规则管理
|
||
4. 课程资料/知识库管理
|
||
5. 数据目录扫描与导入确认
|
||
6. 学生项目列表
|
||
7. 评分批次列表
|
||
8. 批量评分进度
|
||
9. 学生评分详情
|
||
10. 证据和页码查看
|
||
11. 教师复核
|
||
12. Excel/PDF 导出
|
||
13. 模型和系统设置
|
||
|
||
### 后端 API 初稿
|
||
|
||
#### 课程和规则
|
||
|
||
```text
|
||
GET /api/courses
|
||
POST /api/courses
|
||
GET /api/courses/{id}/versions
|
||
POST /api/courses/{id}/versions
|
||
GET /api/rubrics/{version_id}
|
||
PUT /api/rubrics/{version_id}
|
||
POST /api/rubrics/{version_id}/publish
|
||
```
|
||
|
||
#### 文件扫描和项目导入
|
||
|
||
```text
|
||
GET /api/storage/directories
|
||
GET /api/storage/directories/{relative_path}/children
|
||
POST /api/import/scan
|
||
POST /api/import/confirm
|
||
GET /api/projects
|
||
GET /api/projects/{id}
|
||
```
|
||
|
||
#### 评分
|
||
|
||
```text
|
||
POST /api/scoring-batches
|
||
GET /api/scoring-batches
|
||
GET /api/scoring-batches/{id}/progress
|
||
POST /api/scoring-batches/{id}/start
|
||
GET /api/scoring-batches/{id}/scores
|
||
GET /api/projects/{id}/scores?batch_id={batch_id}
|
||
PUT /api/criterion-scores/{id}/review
|
||
```
|
||
|
||
#### 导出
|
||
|
||
```text
|
||
POST /api/exports/excel
|
||
POST /api/exports/project-pdf
|
||
GET /api/exports/{id}
|
||
```
|
||
|
||
### 缺失和异常处理
|
||
|
||
材料状态:
|
||
|
||
```text
|
||
已提交
|
||
缺失
|
||
重复
|
||
无法解析
|
||
内容不完整
|
||
待人工确认
|
||
```
|
||
|
||
处理原则:
|
||
|
||
- 缺失阶段报告:该阶段进入待人工处理,不默认静默给零分
|
||
- 缺失 PPT:汇报材料项显示无法评分
|
||
- 缺少章节:按照对应评分项判断并扣分
|
||
- 文件损坏:进入人工确认列表
|
||
- DeepSeek 调用失败:自动有限重试,仍失败则标记评分项失败
|
||
- 评分批次允许部分完成,但导出时必须显示未完成项目
|
||
|
||
<a id="agent"></a>
|
||
|
||
## 会话与业务 Agent 实现
|
||
|
||
会话交互及确认规则见[教师使用](#usage)。以下记录数据、模型边界、接口与运行恢复机制。
|
||
|
||
### 数据与任务
|
||
|
||
迁移 `0003` 添加会话、消息、运行、待确认操作、索引任务和批次固定项目清单;`0004` 添加首次请求幂等键与有界工具进度记录;`0005` 回填完整资料页文字与相对图像路径,并将历史会话中的引用一次性转换为统一页面链接;`0006` 为页面和检索片段添加页面类型、混合页标记、章节路径,为评分证据添加所属证据目标与历史标记,并新增评分项的缺证据记录。检索片段的文字和 1024 维向量保存在 PostgreSQL 的 `knowledge_chunks`;供教师核对的完整页文字单独保存在 `knowledge_pages`,避免以有重叠的检索片段拼接页面。PNG 文件位于共享 `data/processed/`,数据库只存相对于处理目录的路径;学生原件仍只读挂载在 `/data/inbox`。新批次在创建时保存项目 ID 列表;评分、结果查询和导出都使用该列表。旧批次该字段为 `NULL`,界面显示“历史项目清单未知”,只能按课程版本当前项目兼容读取,不能声称还原了创建时的范围。批次状态为 `created`、`queued`、`running`、`partial`、`failed`、`completed`、`review_required`;`review_required` 表示评分已结束但仍有待教师复核项,与部分完成不同。重复启动已入队或运行中的批次返回冲突。重试保留已评分项和教师复核。
|
||
|
||
索引版本 `0006` 起为 3。分块方式已改变(目录页不再作为正文文本),因此旧版本的索引不会被就地当作已分类正文使用:文档保留原有 `index_version`,在重建完成前不参与检索,教师可先在上线前查看 `/api/knowledge/stale/{course_version_id}` 的待重建清单。重建任务类型为 `reindex`,只从已保存的解析结果重建,不重新读取或修改学生原件;重复执行会跳过已是最新版本和源文件已变化的文档,并记录到审计日志。
|
||
|
||
导入后的每个待解析文件对应持久 `IndexTask`。worker 复用评分的解析和 RAG 入库代码;同一文件用共享处理目录中的文件锁串行化,避免自动索引与评分竞争写入。任务有 queued/running/completed/failed 状态、错误和重试入口。课程资料入库在会话中也作为索引任务执行。若 Redis 不可用,导入数据仍保存,索引任务标记失败并可重试。原始学生文件只从只读 `/data/inbox` 读取,文件 SHA-256 在预览和确认时复查。
|
||
|
||
导入后的每个待解析文件对应持久 `IndexTask`。worker 复用评分的解析和 RAG 入库代码;同一文件用共享处理目录中的文件锁串行化,避免自动索引与评分竞争写入。任务有 queued/running/completed/failed 状态、错误和重试入口。课程资料入库在会话中也作为索引任务执行。若 Redis 不可用,导入数据仍保存,索引任务标记失败并可重试。原始学生文件只从只读 `/data/inbox` 读取,文件 SHA-256 在预览和确认时复查。
|
||
|
||
待确认操作保存不可变参数、20 分钟有效期、幂等键和预期数据状态。确认时再次比对课程发布状态、规则与项目范围、批次状态、成绩与文件哈希。状态变化返回“重新预览”;重复确认已完成操作返回原结果。执行与会话 ID 写入审计日志。服务异常的操作标记失败,需重新准备;若进程在执行中崩溃,卡片保留 `executing` 状态,管理员应先核查实际业务记录再处理,不应直接重复写入。
|
||
|
||
批次汇总从数据库和现有权重规则计算完成数、未完成数、状态数量、分布、模型原始分均值和教师最终分均值。只有完整项目参与成绩均值及分布;失败、无法评分和待处理项不补零。详情仍可按学生、批次和评分项状态筛选。
|
||
|
||
### 模型与安全边界
|
||
|
||
会话使用模型设置页的 OpenAI 兼容 `/chat/completions` 地址、模型和后端 API Key。适配层按 [DeepSeek 原生工具调用文档](https://api-docs.deepseek.com/guides/tool_calls) 发送 `tools`,在每轮前用不含学生资料的 `wisegrade_ping` 最小探针验证端点会返回 `tool_calls`。当前 `deepseek-flash` 端点支持自动工具选择,但拒绝强制 `tool_choice="required"`,因此探针采用 `auto` 并检查返回结构。模型不支持时会显示明确配置错误,不会伪装工具已执行。既有评分 `json_call` 保持原有 JSON 行为。
|
||
|
||
内容检索和结构查询使用与评分相同的证据政策。`search_project_material` 只返回正文和附录片段,`search_project_structure` 返回目录条目与章节结构并明确说明这些信息不能证明章节内容,`get_document_pages` 列出各页的页码、页面类型和章节,`search_document` 在单份资料内检索正文。工具返回页面类型和稳定的引用标识(`citation_id`),每项标注 `source_role` 为 `content` 或 `structure`。评分工具返回的旧引文标记为历史评分引用(`source` 为 `historical_score_evidence`),不会被当作报告正文证据。
|
||
|
||
工具输入由 Pydantic 校验,后端再校验课程、批次和项目关系。每条教师消息默认最多调用工具 32 次,成功、失败与跳过的尝试均计数;模型最多请求 33 轮,以便用完工具次数后还能生成回答。同一轮中工具名和经校验的参数(含默认分页值)完全相同时,后续调用会标为“已跳过”,模型收到前次调用标识并使用此前结果,不再执行工具;不同查询词、筛选或分页偏移仍会正常执行。可在 `.env` 中通过 `CHAT_MAX_TOOL_CALLS` 调整工具上限(1–64),修改后重新创建 API 容器。`get_scores` 每次最多返回一个项目的六项评分,响应给出 `next_criterion_offset` 和 `next_project_offset` 供继续查询;长结果保留结构化评分数据,不再变成无内容的截断提示。检索工具的运行记录保留每次调用找到的有界原文片段;回答引用对各次调用的证据去重后保存,不再只取前 20 条。工具没有任意 SQL、任意文件路径、HTTP 请求、代码执行或确认能力。检索片段只作为数据,不作为工具指令。模型只收到当前问题所需的规则摘要、检索片段或紧凑统计;API Key 不写入日志或会话表。
|
||
|
||
### API 与故障恢复
|
||
|
||
草稿仅存在于浏览器;`POST /api/conversations/first-message` 以 UUID 请求键原子创建会话、首条教师消息及运行,重复请求返回同一 ID。`POST /api/conversations` 保留给旧客户端。`GET /api/conversations` 返回列表和各会话运行标识;`GET /api/conversations/{id}` 恢复消息、卡片、工具记录和运行状态;`PUT /api/conversations/{id}/scope` 仅允许首条教师消息前编辑范围;`DELETE /api/conversations/{id}` 删除非运行中的会话。删除会话会清理消息、运行与待确认卡片,不回滚已经确认的业务操作,相关业务审计仍保留。`POST /api/conversations/{id}/messages/start` 在提交消息后启动独立后台运行;`GET /api/conversations/{id}/runs/{run_id}/events` 可按运行 ID 订阅持久 SSE 事件。旧版 `POST /api/conversations/{id}/messages` 继续提供 SSE 的 `run`、`tool`、`message`、`error`、`done` 事件。确认、取消和导入编辑分别为 `POST /api/conversations/{id}/actions/{action_id}/confirm|cancel|edit-import`。索引任务可通过 `GET /api/index-tasks`、`GET /api/index-tasks/{id}` 查询,失败任务可重试。Nginx 对消息流关闭代理缓冲并设置 900 秒读取超时。
|
||
|
||
回答生成由后台线程执行,页面切换或 SSE 断开不会停止运行;前端按运行 ID 订阅 SSE,并轮询列表和当前会话详情恢复进度,可同时运行多个不同会话。同一会话仍拒绝并发发送。刷新后默认打开新草稿,点击历史会话即可查看正在运行的工具状态或最终结果。运行中的会话删除返回冲突,应待完成后重试。服务进程异常留下的 `running` 记录超过 15 分钟,在读取会话时标为中断。模型失败时,已提交的教师消息仍保留,可在同一会话继续提问。工具记录保存有界查询词、筛选、分页及必要标识;教师复核评语和原因只显示省略标记,未知字段与密钥不保存。旧记录中未保存的检索查询词无法还原,界面明确标明;旧评分记录可从工具结果恢复实际分页偏移。检索工具记录有界原文片段,评分工具记录有界评分行,其他工具记录结构摘要。回答按安全 Markdown 渲染,HTML 不执行,链接仅允许 HTTP(S);工具过程在回答上方,引用在回答最后,二者默认折叠。待确认卡片始终独立显示。若嵌入模型或 OCR 缺失,任务保留具体失败原因;补齐离线模型后重试。若课程资料缺失、原件哈希变化、课程或批次状态变化,应重新预览。
|
||
|
||
<a id="development"></a>
|
||
|
||
## 开发与测试
|
||
|
||
### 本地开发和测试命令
|
||
|
||
Python 依赖由 `backend/pyproject.toml` 管理:
|
||
|
||
```bash
|
||
cd backend
|
||
uv sync --extra ocr --group dev
|
||
uv run alembic upgrade head
|
||
uv run uvicorn wisegrade.main:app --reload
|
||
```
|
||
|
||
测试从仓库根目录运行:
|
||
|
||
```bash
|
||
docker compose --profile test run --build --rm test
|
||
```
|
||
|
||
先验证无需真实 API Key 的测试可运行:`docker compose --profile test run --build --rm test pytest -q -m 'not integration'`。宿主机运行同一测试时,须把 `TEST_DATABASE_URL` 指向独立 PostgreSQL 测试库,并将 `EMBEDDING_MODEL_PATH` 指向本机离线模型目录。完整集成测试会访问真实模型,应单独执行和记录。
|
||
|
||
测试使用独立的 PostgreSQL 16 + pgvector 容器和 `test-redis`,不连接业务数据库或业务任务队列。每个数据库测试创建临时数据库,执行 Alembic 迁移后运行,结束时删除;`test-db` 的数据目录为 tmpfs,不会写入业务数据卷。测试会从 `.env` 读取真实 DeepSeek API Key,并加载 `models/bge-m3` 和 `models/ocr` 中的离线模型;完整流程测试会发起真实模型请求。在宿主机直接运行 `uv run pytest` 时,需先设置指向专用 `wisegrade_tests` 数据库的 `TEST_DATABASE_URL`(使用 `postgresql+psycopg` 驱动,账号需有创建数据库的权限),并提供可用的模型配置。
|
||
|
||
本地开发需提供 PostgreSQL、Redis 和与容器路径一致的挂载目录;可用 Compose 启动 `db` 和 `redis`。Vite 前端:
|
||
|
||
```bash
|
||
cd frontend
|
||
npm install
|
||
npm run dev
|
||
```
|
||
|
||
开发服务器将 `/api` 代理到 `localhost:8000`。集成测试从仓库 `docs/` 复制阶段报告 1、2、3 与最终报告,并生成一份汇报 PDF 测试材料。
|
||
|
||
### 测试要求
|
||
|
||
#### 单元测试
|
||
|
||
- 目录扫描
|
||
- 学号/姓名/题目解析
|
||
- 文件类型识别
|
||
- 路径越界防护
|
||
- 文件哈希检测
|
||
- 规则权重计算
|
||
- 缺失材料处理
|
||
- 分数汇总
|
||
- 评分 JSON 校验
|
||
- 版本绑定
|
||
|
||
#### 集成测试
|
||
|
||
- 从示例材料导入到项目生成
|
||
- PDF/DOCX/PPTX 解析
|
||
- PPT PDF 识别
|
||
- 知识库入库和检索
|
||
- 模型 API mock 调用
|
||
- 评分批次创建、运行和查询
|
||
- 教师修改分数
|
||
- Excel 导出
|
||
- PDF 反馈导出
|
||
|
||
#### 验收测试
|
||
|
||
至少使用当前 `docs/` 中的示例材料完成一次完整流程:
|
||
|
||
```text
|
||
扫描目录
|
||
-> 导入学生项目
|
||
-> 解析五类材料
|
||
-> 创建评分批次
|
||
-> mock 或真实调用 deepseek-flash
|
||
-> 查看逐项证据
|
||
-> 修改分数
|
||
-> 导出 Excel
|
||
-> 导出学生 PDF 反馈
|
||
```
|
||
|
||
不应在测试中提交真实 API Key。使用环境变量或 mock 服务。
|
||
|
||
### Git 开发约定
|
||
|
||
必须创建合理的 `.gitignore`,至少忽略:
|
||
|
||
```text
|
||
.env
|
||
data/processed/
|
||
data/exports/
|
||
models/
|
||
__pycache__/
|
||
.pytest_cache/
|
||
node_modules/
|
||
dist/
|
||
*.pyc
|
||
```
|
||
|
||
开发和测试过程中按阶段提交,推荐提交节点:
|
||
|
||
```text
|
||
chore: initialize project structure
|
||
feat: add backend foundation
|
||
feat: add course and rubric versioning
|
||
feat: add mounted directory scanning
|
||
feat: add document parsing pipeline
|
||
feat: add knowledge base and hybrid retrieval
|
||
feat: add deepseek-flash adapter
|
||
feat: add scoring batch orchestration
|
||
feat: add teacher review workflow
|
||
feat: add excel and pdf exports
|
||
test: add integration coverage
|
||
docs: add deployment and usage guides
|
||
```
|
||
|
||
每个提交应保持可理解、可回退、尽量可运行。禁止把 API Key、真实学生隐私数据和模型权重提交到 Git。
|
||
|
||
### 原始开发阶段(历史规划)
|
||
|
||
以下优先级保留为初始实施顺序,不是当前待办清单;接手事项见 [HANDOFF](../HANDOFF.md)。
|
||
|
||
#### P0:可运行骨架
|
||
|
||
- Git、README、Docker Compose
|
||
- 前端和后端健康检查
|
||
- 数据库迁移
|
||
- 单管理员设置
|
||
|
||
#### P1:课程和导入
|
||
|
||
- 课程版本和规则配置
|
||
- 挂载目录扫描
|
||
- 学生目录识别
|
||
- 文件识别和导入确认
|
||
|
||
#### P2:解析和知识库
|
||
|
||
- PDF/DOCX/PPTX 解析
|
||
- 页面渲染
|
||
- OCR 回退
|
||
- BGE-M3 嵌入
|
||
- pgvector 混合检索
|
||
|
||
#### P3:评分
|
||
|
||
- 事实抽取
|
||
- 指标级 RAG 评分
|
||
- 跨文档一致性检查
|
||
- 批次状态和失败重试
|
||
|
||
#### P4:复核和导出
|
||
|
||
- 教师修改分数和评语
|
||
- Excel 成绩表
|
||
- 逐生 PDF 反馈
|
||
- 审计日志
|
||
|
||
#### P5:验收和部署
|
||
|
||
- 示例材料完整验收
|
||
- Linux 部署文档
|
||
- Windows Docker Desktop 部署文档
|
||
- 离线模型包说明
|
||
|
||
### 第一版验收目标
|
||
|
||
以下是验收目标,不代表所有平台和场景已实测。
|
||
|
||
当以下条件全部满足时,第一版系统完成:
|
||
|
||
- Linux 上可以通过 Docker Compose 启动
|
||
- Windows Docker Desktop 有明确部署步骤
|
||
- 教师可以选择挂载目录下的大文件夹
|
||
- 系统可以识别每个学生目录
|
||
- 支持五类材料和 PPT PDF
|
||
- 规则、课程目标和课程版本可编辑
|
||
- 可创建独立评分批次
|
||
- 使用本地嵌入模型完成检索
|
||
- 使用 `deepseek-flash` 完成文本和图像评分
|
||
- 每项评分显示证据和页码/幻灯片编号
|
||
- 教师可以修改分数和评语
|
||
- 每个批次都可以导出 Excel 和逐生 PDF
|
||
- 示例材料可以完成端到端测试
|
||
- Git 历史包含清晰的阶段提交
|
||
|
||
<a id="troubleshooting"></a>
|
||
|
||
## 故障定位与恢复
|
||
|
||
- `docker compose logs api worker` 查看迁移、解析和任务异常。日志不输出 API Key。
|
||
- 模型请求失败后按配置有限重试,评分项显示失败原因;可修复配置后重试该批次。
|
||
- 对话若提示端点或模型不支持 `tool_calls`,在“模型设置”中选择支持工具调用的 OpenAI 兼容端点和模型。探针不发送课程或学生资料;普通模型对话仍有调用次数和超时限制。
|
||
- 导入后索引任务若显示失败,查看 `/api/index-tasks/{id}` 的错误并修复 Redis、离线 BGE-M3 或 OCR,再重试。批次若已入队或运行,重复点击启动会返回冲突。
|
||
- 原件哈希变化后必须重新扫描并确认导入,否则解析会报“源文件已变化”。
|
||
- 源文件和模型目录为只读挂载;解析产物在 `data/processed/`,导出在 `data/exports/`,均被 Git 忽略。
|
||
|
||
会话运行中断、待确认操作卡在 `executing`、旧批次范围兼容等情况,见[会话与业务 Agent 实现](#agent)。
|