Files
WiseGrade/HANDOFF.md
T

147 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# WiseGrade(智评)交接说明
更新日期:2026-09-30。先阅读 [项目指南](docs/GUIDE.md)(部署、使用、技术设计与会话实现);本文件记录当前实现、验证边界和接手后的优先事项。
## 当前状态
- 已实现《自动化专业综合实践》的教师端闭环:课程与规则版本、挂载目录扫描和导入、文档解析、课程及项目知识库、独立评分批次、逐项复核、Excel 成绩表和逐生 PDF 反馈。
- 当前源码默认进入空白“会话”草稿;首次发送才原子保存会话与消息,并支持范围编辑、删除、独立后台运行与工具过程恢复。沿用原有持久多轮会话、固定类型工具白名单、DeepSeek 原生工具调用、教师确认卡片、导入后自动索引任务和批次固定项目清单。旧页面继续可用。模型不能确认操作;会话、运行和待确认操作由迁移 `0003` 和 `0004` 持久化。
- 前端为 React/TypeScript/Vite/Ant Design;后端为 FastAPI/SQLAlchemy;PostgreSQL + pgvector 存储数据,Redis + RQ 执行评分任务,Nginx 提供前端并代理 `/api`。
- Compose Web 默认绑定 `127.0.0.1:18080`,本机预览也使用该端口。本机 `.env` 已生成随机加密密钥并填入 DeepSeek API Key,但不在 Git 中。
- 业务数据库已升级到 `0003`,初始课程及其版本已写入 PostgreSQL。本机已从 `docs/` 示例报告导入 1 个验收项目;汇报 PPTX 由示例报告内容生成,并非学生原始 PPT。
- `docs/阶段报告3.pdf` 已纳入示例材料和端到端测试。
- 本机已下载 BGE-M3 到 `models/bge-m3/`,固定 Hugging Face 修订版 `5617a9f61b028005a4858fdac845db406aefb181`;模型目录不提交到 Git。
- 本机已下载 PP-OCRv5 mobile 检测与中文识别模型到 `models/ocr/`;下载与解压命令见部署指南。
## 代码入口与数据流
| 位置 | 作用 |
| --- | --- |
| `compose.yaml`、`.env.example` | 五个容器及环境变量;原件目录以只读方式挂载到 `/data/inbox` |
| `backend/wisegrade/main.py`、`api.py` | FastAPI 启动、初始化课程、教师端 API |
| `backend/wisegrade/models.py`、`migrations/`、`seed.py` | 数据模型、Alembic 迁移、初始课程和规则 |
| `backend/wisegrade/storage.py`、`parsing.py` | 相对路径安全检查、目录识别、文件哈希、PDF/DOCX/PPTX 解析与页面资产 |
| `backend/wisegrade/rag.py`、`llm.py`、`scoring.py` | 本地嵌入与混合检索、DeepSeek 适配、批次评分 |
| `backend/wisegrade/settings_store.py`、`exports.py` | API Key 加密保存、Excel/PDF 导出 |
| `backend/wisegrade/services.py`、`indexing.py` | REST 与 Agent 共用导入、入库、建批、复核服务;持久后台索引任务 |
| `backend/wisegrade/agent_tools.py`、`actions.py`、`chat_api.py` | 类型化工具注册、确认边界、会话与 SSE 编排 |
| `frontend/src/App.tsx`、`frontend/src/api.ts` | 教师端页面及 API 调用 |
| `frontend/src/Chat.tsx` | 会话恢复、引用、进度和确认卡片 |
| `backend/tests/` | 路径、规则、版本、解析、模型输出、复核、导出及示例材料流程测试 |
教师选择 `/data/inbox` 下的相对目录后,后端扫描 `学号_姓名_选题题目` 子目录;教师确认材料类型后保存路径、大小、时间和 SHA-256。解析页面图像进入 `data/processed/`,完整页文字、知识片段及其向量进入 PostgreSQL。评分证据和 Agent 检索引用共用文档 ID、页码和资料页链接。评分时复制课程、规则和模型配置快照到独立批次,由 RQ worker 处理。模型原始评分、证据和教师修改记录分别保留;导出文件写入 `data/exports/`。原件不经过浏览器上传。
## 启动与检查
首次部署复制 `.env.example` 为 `.env`,设置学生目录 `WISEGRADE_DATA_DIR`、随机 `WISEGRADE_SECRET_KEY`、DeepSeek 地址及密钥,并把离线 BGE-M3 模型放在 `models/bge-m3/`。扫描版 PDF 另需 `models/ocr/detection/` 和 `models/ocr/recognition/`。模型目录和 `.env` 均不提交到 Git。详情及 Windows 路径写法见 [部署指南](docs/GUIDE.md)。
```bash
docker compose up
docker compose ps -a
curl http://127.0.0.1:18080/api/health
```
上面的 `curl` 使用默认 Web 端口;若 `.env` 中修改了 `WISEGRADE_WEB_PORT`(默认 `18080`),应使用对应端口。本机预览地址为 <http://127.0.0.1:18080>。`api` 启动时自动运行 Alembic 迁移。排障先看 `docker compose logs api worker web`。
Python 开发统一使用 `uv`:
```bash
cd backend
uv sync --extra ocr --group dev
uv run pytest -q
```
前端构建:`cd frontend && npm ci && npm run build`。宿主机缺少 LibreOffice 时,DOCX/PPTX 渲染测试会跳过;生产容器镜像已包含 LibreOffice。
## 已验证与尚未验证
- 部署镜像内完整后端测试:**19 通过、0 跳过**,包括 DOCX/PPTX 转换与页面图像测试。宿主机运行结果为 18 通过、1 跳过,因为宿主机未安装 LibreOffice;部署容器内为 25.2.3.2。另已用课程 DOCX 和验收 PPTX 实际验证页数、文字及页面图片。
- 2026-09-25:后端测试已改用独立 PostgreSQL + pgvector,并通过真实 DeepSeek 和本地 BGE-M3 模型测试。19 个其他测试通过;使用课程示例材料的完整流程单独重跑通过,22 个评分项全部完成,教师复核和导出检查通过。测试命令见 `docs/GUIDE.md`。
- 2026-09-25 旧版部署时:前端生产构建通过;Compose 的 PostgreSQL、Redis、API、worker、Web 启动,首页、深链接、`/api/health`、课程及仪表盘 API 返回正常。数据库当时为 `0002`;全新 SQLite 数据库迁移至 `0002` 也已验证。
- 2026-09-26 当前源码:宿主机独立 PostgreSQL 测试执行 `uv run pytest -q -m 'not integration'`,**24 通过、1 跳过、2 个真实集成测试未运行**;使用既有测试镜像加只读源码挂载执行 Compose 同组测试,**25 通过、2 个真实集成测试未运行**。单独构造旧版 `0002` 表结构,在临时 PostgreSQL 数据库升级 `0003` 通过;新数据库迁移也由测试覆盖。前端 `npm run build` 和新 Nginx 配置 `nginx -t` 通过。
- 2026-09-26 最小真实 DeepSeek `tool_calls` 兼容性探针通过,完整工具白名单也返回 `list_courses` 原生调用;该端点拒绝 `tool_choice="required"`,适配层已使用 `auto` 并校验返回的工具调用。未在该探针发送学生原件。
- 完整 Compose 测试首次运行 **25 通过、1 失败**:真实模型证据匹配导致示例流程首轮批次 `partial`。测试改为按既有重试规则继续未完成项后,单独重跑示例流程 **1 通过**(11 分 48 秒);成功项和教师复核结果由生产评分代码保留。此次未重新运行一次完整的所有测试组合,不应把第一次完整命令写成全部通过。
- 测试 Compose 现增设独立 `test-redis`,与业务队列隔离。隔离前完整测试曾把 5 个测试索引作业送入业务 Redis;旧业务 worker 因缺少新模块执行失败,未写入业务数据库,测试作业已从业务 Redis 失败队列和作业键清理。
- 2026-09-26 业务容器已更新:迁移前的 PostgreSQL 自定义格式备份保存在本机 `data/backups/wisegrade-pre-0003-20260926.dump`(权限 `600`,不提交);业务库 Alembic 为 `0003 (head)`。API、worker、Web 已重新创建,健康检查正常。通过 Web 入口验证 `/api/health`、会话列表、课程、项目、批次、索引任务及前端资源均返回 200;worker 可导入索引和评分任务;运行中的 API 对已配置模型的原生 `tool_calls` 探针通过。业务 Redis 队列为空。
- 后端标准 Dockerfile 构建再次卡在 Debian 软件源下载。由于 `backend/pyproject.toml`、`backend/uv.lock` 和 Dockerfile 自上一版镜像以来未变化,本机从已有 `wisegrade-api` 镜像叠加当前 `backend/` 源码构建 `wisegrade-backend-updated:20260926`,并用于 API 和 worker;关键源码与迁移文件的镜像内外哈希已核对。Web 则通过标准 Dockerfile 完整构建。此本机后端镜像依赖旧镜像层,供其他机器分发前仍需在软件源正常时完成一次标准 Dockerfile 构建。
- `docker compose --profile test run --build --rm test pytest -q -m 'not integration'` 曾启动测试镜像重建,但 Debian 软件源下载 LibreOffice 依赖在约 24 分钟后仍未完成,故中止;随后用已有测试镜像加最新源码只读挂载执行相同测试。当时尚未在浏览器中实际点击旧版会话主页、刷新恢复、确认卡片及深链接;目前这些路径由代码检查、后端 API 测试、HTTP 冒烟检查和前端 TypeScript/生产构建覆盖。
- RQ worker 曾在空闲期间因 Redis 连接超时退出,日志显示正常退出码 0;已在 `compose.yaml` 设置 `restart: unless-stopped` 并重新启动。接手后应观察是否反复超时。
- 使用 `docs/` 的四份示例报告及生成的汇报 PPTX,经目录扫描、确认、课程资料入库、真实 BGE-M3/pgvector 检索和真实 DeepSeek API 完成批次 `3`:**22/22 项成功**。首轮有 1 项引文不匹配,重试后成功;保留了成功项。项目索引重建后有 316 段、平均 506 字,向量均为 1024 维;77 条评分证据均能匹配对应文档页的原文和页面图片。
- 对批次 `3` 完成教师复核和修改历史验证;Excel 成绩表及 11 页 PDF 反馈均可下载且可解析,文件位于 `data/exports/`。批次 `2` 保留为首次真实运行的对照记录(16 项成功、6 项证据不匹配)。
- 将 `docs/阶段报告1.pdf` 的一页栅格化为无文本层 PDF 后,离线 PaddleOCR 提取 496 字,识别到目录和章节标题。真实扫描仪产生的不同质量扫描件尚未实测。Windows Docker Desktop 尚未在 Windows 机器上实测。
## 接手后优先完成
1. 在浏览器中复查新版会话草稿、范围选择、并行运行、删除确认、移动端布局及确认卡片。
2. 使用独立提供、已脱敏的学生目录再次验收五类原始材料,尤其是学生原始 PPTX 和真实扫描仪 PDF;当前验收使用仓库示例报告与生成材料。
3. 在目标 Windows Docker Desktop 环境验证目录挂载、模型路径和完整启动。
4. 持续查看 worker 日志;若 Redis 空闲超时重复出现,再定位 Docker 后端休眠或网络原因。不要仅凭容器显示“已启动”判断评分任务可用。
当前代码位于 `main` 分支,阶段提交使用 Conventional Commits。交接前工作区应保持干净;`.env`、`models/`、`data/`、缓存和构建产物由 `.gitignore` 排除。
## 2026-09-30 目录、摘要与正文证据治理
### 本次改动
新增 `backend/wisegrade/pages.py`(页面分类、混合页切块、真实章节路径)和 `backend/wisegrade/evidence.py`(证据政策)。`rag.py` 重写为按目标检索:页面类型过滤在候选上限之前完成,语义与中文二元组关键词各自召回后按排名倒数融合,名额先分散到不同页面并对同章节设软上限。`scoring.py` 按必备证据目标分别检索,正文补检走目录导航,内容目标缺失时进入 `needs_review`。迁移 `0006` 添加页面类型、章节、证据目标与历史标记、评分项缺证据记录列,并按已保存的解析结果重新分类页面;索引版本升到 3。
关键词召回不再使用 PostgreSQL `to_tsvector('simple', …)`。在隔离测试库中对真实页面实测:该配置把整个中文串当成一个词元,查询"文献调研"无法命中含"第2章文献调研与技术现状"的页面(`文献调研`、`串级PID 结构`、`视觉伺服与闭环控制` 均不匹配),因此旧实现的关键词分实际上长期接近零。现改为字符二元组加 ASCII/数字整词的重叠度。
### 修复的缺陷
批次 3 的 77 条评分引文中 14 条来自目录页(涉及 9 个评分项),会话 6 的 20 条引用中 5 条指向阶段报告 1 的目录页。根因有两层:目录页文本被当作正文片段参与检索并占用候选名额;评分提示词只要求引文原文和页码一致,没有任何来源类型约束。现在目录页作为导航块单独存放,内容目标只接受正文和附录,引文必须标注它要支持的证据目标。
### 本次验证(隔离环境)
隔离 PostgreSQL + pgvector 测试容器执行 `pytest -q -m 'not integration'`:**59 通过、2 个真实集成测试未运行**。新增 `backend/tests/test_evidence_pages.py` 覆盖阶段报告 1 目录与第 6–7 页文献调研正文的区分、阶段报告 3 目录与第 11–12 页测试正文的区分、最终报告摘要/跨页目录/正文/附录的区分、混合页与目录续页与重复页眉与识别不确定页、内容性判断拒绝目录与摘要与单独标题、结构性判断仍可用目录、正文补检不足时模型分为 null 且状态为待教师复核且教师可复核且缺证据记录保留、Agent 引用页面类型标记与历史评分引用标记、检索过滤先于候选上限、学生项目与课程版本隔离、批次快照记录证据政策。前端 `npm run build` 通过(仍有 Vite 大块提示)。
真实报告的分类结果已逐页核对:阶段报告 1 第 1 页封面、第 2–3 页目录、第 4–21 页正文、第 22 页参考文献;阶段报告 3 第 2–3 页目录、第 11–12 页为测试正文;最终报告第 2–3 页摘要、第 4–5 页跨页目录、第 49 页参考文献、第 50–53 页附录。
### 未验证与待业务环境执行
- 使用真实 DeepSeek 的完整评分批次**未**在本次运行。需要模型的部分(评分调用、事实抽取、一致性检查、Agent 回答)只用测试替身验证了编排与校验逻辑,尚未用真实模型端到端复跑。
- 未对业务数据库执行迁移、重建索引或重新评分,也未运行 PDF/Excel 导出(导出代码已更新,但未实际生成文件)。
- 未在 Windows Docker Desktop 实测。
业务环境应用步骤与批次 3 复核清单见 [docs/EVIDENCE_POLICY_BATCH3.md](docs/EVIDENCE_POLICY_BATCH3.md)。
## 2026-09-27 会话更新
新版 UI 的入口仍使用 `?page=chat`,旧 API 与历史记录保持可读。前端按运行 ID 订阅 SSE,并依靠会话列表和详情轮询恢复后台 Agent 运行;服务进程中断后的运行在 15 分钟超时后标为中断。正式 API 启动时会自动应用 `0004` 迁移。此处的测试结果以本次实际命令为准,历史验证记录仍保留在上方。
本次验证:隔离 PostgreSQL + pgvector 测试容器中运行 `pytest -q -m 'not integration'`,32 通过、2 个真实集成测试未运行;前端 `npm run build` 通过,Playwright Chromium `npm run test:e2e` 3 通过(API 使用浏览器路由模拟)。浏览器测试覆盖草稿、范围、历史切换、删除及错误、Markdown/XSS、引用、工具展开、并行运行切换与 390px 布局。后续已在业务数据库应用 `0004`,并通过 Web 入口用真实 DeepSeek 完成只读会话、SSE、刷新恢复和界面删除验收;评分等写入流程未因本次更新重新执行。前端产物有 Vite 大块警告。
## 2026-09-27 正式启动复验
从当前源码成功执行 `docker compose build api worker web` 与 `docker compose up -d`。业务 PostgreSQL 迁移从 `0003` 到 `0004`,迁移前备份保存在本机 `data/backups/wisegrade-pre-0004-20260927-020450.dump`(权限 `600`,不提交)。Compose 已调整为 API 健康后启动 Web 和 worker,服务设置自动重启。Web `/api/health` 为 200,API、数据库、Web 均健康,worker 在运行,Nginx 检查通过。经 Web 入口实际提交两条无学生资料的只读消息,DeepSeek 调用成功;SSE 收到 `run`、`tool`、`message`、`done`,首事件约 764 毫秒;刷新后打开新草稿,历史回答仍可查看。测试会话已通过界面删除,业务会话数恢复到原先 4 条。`docker compose stop` 后执行 `docker compose up -d --wait` 冷启动复验通过,五个正式服务恢复健康,Web `/api/health` 为 200。`docker compose up` 现在可直接用于正式启动,访问本机配置端口。
## 2026-09-27 会话引用与工具过程修复
正式回答上方的工具过程现在先整体折叠,每次调用再单独折叠;引用移至回答最后并整体折叠。会话页面移除了全局“后台进度”卡片。`get_scores` 改为按项目和评分项分页,每次最多一个项目、六项评分,并提供后续偏移量;长结果不再以空泛的截断提示替代全部评分。新检索调用会在运行记录中保存最多六条有界原文片段;回答引用对全部工具结果去重,不再只保存前 20 条。
本地业务数据库 6 号会话曾为定位问题补入来源引用和当前评分重查值;用户计划自行删除该会话,界面不保留针对这条旧记录的提示或特殊展示。操作前备份保存在 `data/backups/wisegrade-pre-chat-citation-20260927.dump`(权限 `600`,不提交)。新调用会保存实际每次找到的片段。
后续排查确认“旧结果未保存”来自 6 号会话三条已持久化的工具摘要,而非前端旧版本;这些摘要现已改为简短的评分项数量,会话本身仍在。
隔离 PostgreSQL 测试库执行 `pytest -q -m 'not integration'`:35 通过、2 个集成测试未运行。前端 `npm run build` 通过,Chromium `npm run test:e2e`:3 通过。API 与 Web 镜像已重新构建并以 `docker compose up -d --wait api web` 更新;新评分工具对限定范围返回六项,序列化后 6,820 字符。用真实项目检索“视觉检测 Otsu 光照鲁棒性 第48页”返回六条带原文与链接的结果,其中包含第 48 页。前端产物仍有 Vite 大块警告。
## 2026-09-27 工具参数与重复调用
7 号会话的四次 `get_scores` 实际分别读取评分项偏移量 0、6、12、18,旧工具记录只保存了批次与项目 ID,造成界面上看似相同参数。新记录保存经校验的默认值、查询词、筛选及分页参数;旧评分记录从结果恢复实际偏移量。7 号会话旧检索记录的查询词没有持久化,无法准确还原,界面会显示“历史调用未保存查询词”。同一轮内同名工具的相同有效参数只执行一次,后续调用标为“已跳过”,不同分页或查询正常执行。复核评语与原因继续省略,未知字段及密钥不保存。
隔离 PostgreSQL 测试容器执行 `pytest -q -m 'not integration'`:37 通过、2 个集成测试未运行;末次后端微调另运行针对性测试,1 通过。前端 `npm run build` 通过,Playwright Chromium `npm run test:e2e`:3 通过。测试覆盖注册工具参数字段、查询词与敏感文本边界、相同参数跳过、不同分页和查询正常执行,以及浏览器中旧记录偏移量恢复与参数展示。API 与 Web 镜像已构建并更新,两个容器均健康;正式页面通过全新 Chromium 会话打开 7 号会话,四次评分调用显示偏移量 0、6、12、18,旧检索调用显示未保存查询词提示。
## 2026-09-27 引用链接标签
检索引用的 `url` 指向页面文字 JSON 接口,`image_url` 指向页面图像;评分引用现在分别保存评分页 `url` 和证据 `image_url`。界面按目标显示“查看页面文字”“查看评分”“查看图像”。本阶段尚未迁移历史数据,后续由 `0005` 一次性迁移,见下文。后端评分引用针对性测试 1 通过,前端构建通过,Playwright 3 通过。API 与 Web 容器已更新并健康;正式页面检查 7 号会话 74 个图像链接,均显示“查看图像”。
## 2026-09-27 评分引用文字页补全
评分证据 `ScoreEvidence` 原已保存 `document_id` 和 `page`,前一版遗漏了这条来源链。该阶段 `project_result` 将文档 ID 交给 `get_scores`,新回答引用保存 `text_url`;历史会话曾在读取时匹配评分证据,但不改写数据库。隔离测试容器 `pytest -q -m 'not integration'` 为 38 通过、2 个集成测试未运行;前端构建和 Playwright 3 项测试通过。API 与 Web 容器已更新并健康。正式会话 7 的 74 条引用在浏览器中均显示文字和图像链接;其中 41 条旧评分引用经后端恢复。随后 `0005` 已将这一读取时匹配改为一次性数据迁移。
## 2026-09-27 统一资料页与评分证据
迁移 `0005` 新增 `knowledge_pages`,以文档 ID 和页码保存完整提取文字、处理目录内的相对 PNG 路径。`knowledge_chunks` 继续保存检索片段和 pgvector 向量;原始学生文件仍只读挂载在 `/data/inbox`,PNG 实体位于 `data/processed/`。检索、评分模型的页面图像、评分证据、评分复核和 Agent 引用现在都从同一资料页取得文字与图像地址。`/api/knowledge/documents/{id}/pages/{page}` 返回完整页文字,末尾 `/image` 返回 PNG。数据库生成的课程目标与评分规则没有原始实体图像,因而只提供文字链接。迁移直接改写已保存的会话引用和工具结果;会话 API 不再做旧图像直链的读取时补全。
迁移前备份为本机 `data/backups/wisegrade-pre-0005-20260927-174222.dump`(权限 `600`,不提交)。先将备份还原到隔离库演练迁移:157 个资料页、155 个图像页、0 个旧图像直链留在会话或工具记录、161 条评分证据均有资料页;随后正式数据库升级到 `0005`,155 个 PNG 路径均能找到实体文件,161 条证据原文均存在于对应完整页,199 条历史会话引用都有统一的文字和图像链接。Web 入口健康与页面接口均返回 200,抽查图像为 `image/png`;真实 Chromium 打开 7 号会话检查 74 张引用卡片,均有文字和图像链接,390px 页面无横向溢出。独立 PostgreSQL 测试容器 `pytest -q -m 'not integration'`:40 通过、2 个真实集成测试未运行;前端 `npm run build` 与 Playwright 3 项通过。API、worker、Web 镜像从当前源码重新构建并更新。前端构建仍有 Vite 大块提示。