feat: separate contents, abstract and body evidence
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
# 证据政策上线与批次 3 复核清单
|
||||
|
||||
本文记录把页面类型证据政策应用到**业务环境**的步骤,以及批次 3 需要教师复核的项目。文中的业务数据操作**均未执行**,请由教师确认后按顺序操作。
|
||||
|
||||
## 一、诊断基线
|
||||
|
||||
在业务数据库只读检查确认:
|
||||
|
||||
- 批次 3 的 77 条评分引文中,**14 条来自目录页**,涉及 **9 个评分项**。
|
||||
- 会话 6 保存的 20 条引用中,**5 条**指向阶段报告 1 的目录页。
|
||||
|
||||
这些数字是诊断基线。本次改动不自动改写批次 3 的既有分数,也不改写会话 6 的历史消息。
|
||||
|
||||
## 二、改动后的行为
|
||||
|
||||
| 情形 | 改动前 | 改动后 |
|
||||
|---|---|---|
|
||||
| 目录页命中 | 作为正文证据送入模型并计入候选 | 只作导航,不进入内容证据 |
|
||||
| 内容目标证据 | 目录、摘要、章节标题均可 | 仅正文与附录的实质段落、数据、表格 |
|
||||
| 结构目标证据 | 与其他证据等同 | 封面、目录、参考文献可用,明确标注来源 |
|
||||
| 正文补检不足 | 仍可能给出较低模型分 | `needs_review`,模型分为 `null`,总分待处理 |
|
||||
| 历史引文 | 与正文证据无区别 | 标记"历史评分引用" |
|
||||
|
||||
## 三、业务环境应用步骤
|
||||
|
||||
以下步骤**尚未执行**。
|
||||
|
||||
### 1. 备份
|
||||
|
||||
```bash
|
||||
docker compose exec -T db pg_dump -U wisegrade -Fc wisegrade > data/backups/wisegrade-pre-0006-$(date +%Y%m%d-%H%M%S).dump
|
||||
chmod 600 data/backups/wisegrade-pre-0006-*.dump
|
||||
```
|
||||
|
||||
### 2. 隔离库演练(推荐)
|
||||
|
||||
把备份还原到隔离数据库,先在隔离库执行升级和重建,核对页面分类与检索结果,再动业务库。`backend/tests/test_evidence_pages.py` 已用真实报告覆盖这几类情形。
|
||||
|
||||
### 3. 应用迁移
|
||||
|
||||
迁移在 API 容器启动时自动执行(`alembic upgrade head`)。如需手工执行:
|
||||
|
||||
```bash
|
||||
docker compose exec api alembic upgrade head
|
||||
docker compose exec -T db psql -U wisegrade -c "SELECT version_num FROM alembic_version;"
|
||||
# 期望:0006
|
||||
```
|
||||
|
||||
`0006` 会:
|
||||
|
||||
- 为 `knowledge_pages` 添加 `page_type`、`mixed`、`section`、`chapter`、`confidence`、`classification`;
|
||||
- 为 `knowledge_chunks` 添加 `chapter`、`page_type`、`block_type`;
|
||||
- 为 `score_evidences` 添加 `target_id`、`target_label`、`page_type`、`source_role`、`historical`;
|
||||
- 为 `criterion_scores` 添加 `evidence_gaps`;
|
||||
- 把**已有**评分引文全部标记为 `historical = true`;
|
||||
- 按已保存的解析结果重新分类所有页面,写入审计日志。
|
||||
|
||||
重新分类不需要嵌入模型,因此在迁移中直接完成。`downgrade` 可撤销上述列。
|
||||
|
||||
### 4. 重建索引
|
||||
|
||||
分块方式已改变,旧索引不会被当作已分类正文使用。查看待重建清单:
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:18080/api/knowledge/stale/1 | python -m json.tool
|
||||
```
|
||||
|
||||
重建(课程版本 1):
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://127.0.0.1:18080/api/knowledge/reindex/1
|
||||
docker compose logs -f worker
|
||||
```
|
||||
|
||||
重建任务可重复执行:已是最新版本的文档跳过,源文件哈希变化的文档跳过并说明原因,失败项记录具体错误。结束后再次查询 `/api/knowledge/stale/1`,`documents` 应为空。
|
||||
|
||||
### 5. 核对页面分类
|
||||
|
||||
```bash
|
||||
curl -s "http://127.0.0.1:18080/api/knowledge/documents/<document_id>/pages" | python -m json.tool
|
||||
```
|
||||
|
||||
预期(仓库示例报告):阶段报告 1 第 1 页 `cover`、第 2–3 页 `toc`、第 4–21 页 `body`、第 22 页 `references`;阶段报告 3 第 2–3 页 `toc`、第 11–12 页 `body`;最终报告第 2–3 页 `abstract`、第 4–5 页 `toc`、第 49 页 `references`、第 50–53 页 `appendix`。分类有疑义的页面为 `unknown`,不会当作正文。
|
||||
|
||||
### 6. 新批次验证
|
||||
|
||||
新建一个评分批次(不要重跑批次 3),确认真实模型下的表现:已评分项的引文来源为正文或附录;只检索到目录的内容项进入"待教师复核"。
|
||||
|
||||
## 四、批次 3 复核清单
|
||||
|
||||
只读检查得到的 9 个涉及目录页引文的评分项。**建议教师逐项核对**,不需要重跑批次:
|
||||
|
||||
| 处理方式 | 说明 |
|
||||
|---|---|
|
||||
| 查看该评分项证据 | 界面中旧引文显示"历史评分引用"标签,不能当作报告正文证据 |
|
||||
| 在原始报告中定位正文 | 用引用卡片上的"查看页面文字"/"查看图像"跳转,或按下方章节提示查找 |
|
||||
| 填写教师复核分 | 输入框不预填 0;填写后状态变为"教师已复核",模型原始分与检索记录保留 |
|
||||
|
||||
复核时可直接检查的正文位置(仓库示例报告):
|
||||
|
||||
- **阶段报告 1 第 6–7 页**:文献调研与技术现状正文,含控制方法对比表和技术现状总结。涉及 `stage1.literature`。
|
||||
- **阶段报告 3 第 11–12 页**:系统测试与验证正文,含自主巡线测试、控制实时性、光照影响。涉及 `stage3.data`、`stage3.verification`。
|
||||
- **最终报告第 6 页起**:正文各章;第 50–53 页为附录,可用作内容证据。
|
||||
- **最终报告第 2–3 页为摘要**:可用于"摘要称……"这类明确归属的概述,不能单独证明方法已实现或指标已达标。
|
||||
|
||||
教师也可选择保留原有分数:本次改动不自动改写批次 3,历史分数在导出和界面中维持原值,仅在引文旁标明其来源类型。
|
||||
|
||||
## 五、注意事项
|
||||
|
||||
- 迁移和重建只改动页面分类、索引与引文元数据,**不改动**学生原件、评分批次、教师复核记录和会话历史。
|
||||
- 重建过程可重试;失败项保留具体错误,重试入口在项目的"解析与索引任务"中。
|
||||
- 迁移后旧评分引文的原始分数不会改变,但界面与导出会标明它们是历史引用。
|
||||
- 若教师希望批次 3 的引文重新落在正文页,需要新建批次并重新评分;该操作会消耗模型调用,请先确认。
|
||||
+78
-11
@@ -502,22 +502,24 @@ course_2026/project_1023009006/
|
||||
- 重叠约 80~120 个中文字符
|
||||
- 表格单独保存
|
||||
- 图片及图片说明单独保存
|
||||
- 每块必须保留页码、章节、文档类型和评分指标关联
|
||||
- 每块必须保留页码、页面类型、章节、文档类型和评分指标关联
|
||||
- 目录页文本作为导航块单独存放,不与正文块混同
|
||||
|
||||
#### 检索
|
||||
|
||||
采用混合检索:
|
||||
|
||||
```text
|
||||
PostgreSQL 全文检索
|
||||
+ pgvector 向量检索
|
||||
pgvector 向量检索
|
||||
+ 中文二元组关键词检索
|
||||
+ 课程版本过滤
|
||||
+ 学生项目过滤
|
||||
+ 报告类型过滤
|
||||
+ 阶段过滤
|
||||
+ 页面类型过滤
|
||||
```
|
||||
|
||||
默认每个评分项取 8~12 个候选证据块,去重后送入模型。
|
||||
过滤在取候选之前完成,两个召回各自独立进行,再按排名融合。默认每个评分项送模型 8~12 个候选证据块,跨目标合计上限 14 块。
|
||||
|
||||
#### 嵌入模型
|
||||
|
||||
@@ -622,7 +624,8 @@ model: deepseek-flash
|
||||
{
|
||||
"document": "阶段报告1",
|
||||
"page": 12,
|
||||
"quote": "..."
|
||||
"quote": "...",
|
||||
"target_id": "t0"
|
||||
}
|
||||
],
|
||||
"missing_evidence": ["..."],
|
||||
@@ -631,7 +634,60 @@ model: deepseek-flash
|
||||
}
|
||||
```
|
||||
|
||||
模型不得对没有证据的内容自行假设。结构化输出必须经过 Pydantic 校验;失败时自动重试或进入人工处理。
|
||||
模型不得对没有证据的内容自行假设。结构化输出必须经过 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。
|
||||
|
||||
#### 跨文档一致性
|
||||
|
||||
@@ -656,10 +712,12 @@ 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
|
||||
@@ -672,6 +730,8 @@ score_levels:
|
||||
description: 缺少核心论证
|
||||
```
|
||||
|
||||
`required_evidence` 也接受历史上的字符串数组(`required_evidence: [主方案, 替代方案]`)。读取时按措辞判定 `use`:提到标题、封面、目录、章节、参考文献、格式、附录等报告包装字样的目标为 `structure`,其余为 `content`。判定结果与 `POLICY_VERSION` 一起写入批次快照,不修改已发布的规则文本。
|
||||
|
||||
#### 版本要求
|
||||
|
||||
课程、课程目标、毕业要求、模板、评分规则和 Prompt 都需要版本号。评分批次保存完整版本快照。
|
||||
@@ -692,6 +752,7 @@ rubric_versions
|
||||
rubric_criteria
|
||||
rubric_score_levels
|
||||
knowledge_documents
|
||||
knowledge_pages
|
||||
knowledge_chunks
|
||||
students
|
||||
projects
|
||||
@@ -838,7 +899,11 @@ GET /api/exports/{id}
|
||||
|
||||
### 数据与任务
|
||||
|
||||
迁移 `0003` 添加会话、消息、运行、待确认操作、索引任务和批次固定项目清单;`0004` 添加首次请求幂等键与有界工具进度记录;`0005` 回填完整资料页文字与相对图像路径,并将历史会话中的引用一次性转换为统一页面链接。检索片段的文字和 1024 维向量保存在 PostgreSQL 的 `knowledge_chunks`;供教师核对的完整页文字单独保存在 `knowledge_pages`,避免以有重叠的检索片段拼接页面。PNG 文件位于共享 `data/processed/`,数据库只存相对于处理目录的路径;学生原件仍只读挂载在 `/data/inbox`。新批次在创建时保存项目 ID 列表;评分、结果查询和导出都使用该列表。旧批次该字段为 `NULL`,界面显示“历史项目清单未知”,只能按课程版本当前项目兼容读取,不能声称还原了创建时的范围。批次状态为 `created`、`queued`、`running`、`partial`、`failed`、`completed`;重复启动已入队或运行中的批次返回冲突。重试保留已评分项和教师复核。
|
||||
迁移 `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 在预览和确认时复查。
|
||||
|
||||
@@ -850,6 +915,8 @@ GET /api/exports/{id}
|
||||
|
||||
会话使用模型设置页的 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 与故障恢复
|
||||
|
||||
Reference in New Issue
Block a user