本节至第 9 节保留 2026-07-31 非 OCR 链路的原始测试记录。2026-08-01 新增的 GalGameService 集成结果见第 10 节;其中已完成的 GalGame 流程结论取代第 8 节对应的 历史待办状态。
本轮规定的非 OCR 全流程已经通过:
- 用户可经 Gateway 注册并登录;
- Gateway 可动态内省 Access Token,重复内省不会延长固定过期时间;
- 用户可上传含内嵌文字的 PDF;
- FileService 可在
enableOcr=false时提取并交付规范化纯文本; - KnowledgeService 可经 Gateway 读取文本、构建分层知识图谱并写入 Neo4j;
- Knowledge API 与 Neo4j 的章节、知识点和关系数量一致;
- 最终容器完成 50 个真实 HTTP 契约断言,覆盖新增的 400/401/404/415/416/ 422/429 分支和统一错误信封。
最终样例得到 7 个真实章节、243 个去重知识点和 207 条
PREREQUISITE 关系。所有知识点初始掌握度为 0,先修子图为 DAG,每个知识点至少有
一个可回到原文的来源位置。
本结论不包含 OCR 功能正确性。OCRService 镜像已成功构建,但本轮没有启动 OCR 容器、调用 OCR 接口、下载或运行 OCR 模型,也没有测试识别准确率。
| 项目 | 值 |
|---|---|
| 执行时间 | 2026-07-31 11:50(Asia/Shanghai) |
| Git 分支 | main |
| 基线 HEAD | 2b1b03e82513aa00ef6b5ac1095f97f680915801 |
| 基线提交 | 2b1b03e Remix OCRService Bugs |
| 测试对象 | 上述 HEAD 加当前工作区的冲突合并、适配及修复;尚未提交 |
| Docker Engine | 29.4.3,overlayfs |
| Docker Desktop 数据盘 | D:\DockerData\DockerDesktopWSL\disk\docker_data.vhdx |
| 测试资料 | .artifacts/integration/农业生态学题库.pdf |
| 文件大小 | 1,528,551 bytes |
| 文件 SHA-256 | ECDA0AA80E2380564374D0B7373864708446FB5B3EA9E97C0D94E27384E53F0B |
已确认 C:\Users\Arabid\AppData\Local\Docker\wsl 下没有 Docker VHDX;当前镜像和卷数据
位于 D:\DockerData。容器内部显示的 /var/lib/docker 是 Docker Desktop 虚拟机内
路径,不表示数据重新写回 C 盘。
为避免把未执行的功能写成“已测试”,本报告采用四种证据:
| 层级 | 含义 |
|---|---|
| E2E | 真实 HTTP、Gateway、服务进程和数据库共同参与 |
| 契约测试 | 进程内测试请求/响应、错误映射、授权或算法不变量 |
| 构建验证 | Release 编译或容器镜像构建成功,不代表功能正确 |
| 未测试 | 本轮明确不启动或缺少上游服务,不能宣称通过 |
docs/contract.md 中本轮实际执行的注册、登录、上传、非 OCR 文本提取、构图与图读取
主路径,以及 Gateway 信任边界,均已按下列 E2E 或契约测试回对。未在证据表中列出的
错误状态不宣称逐一经过 E2E。GalGameService、RenderService、事件总线和 OCR 的未来
接口仍保留为 URGENT/后续契约,不属于本轮“功能通过”结论。
| 组件 | 命令或证据 | 结果 |
|---|---|---|
| AuthService | dotnet build GalGame.AuthService.csproj -c Release |
通过,0 warning / 0 error |
| AuthService | dotnet test Tests/GalGame.AuthService.Tests.csproj -c Release |
8/8 通过 |
| UserService | dotnet test Tests/GalGame.UserService.Tests.csproj -c Release |
32/32 通过,含主项目 0 warning 构建 |
| FileService | dotnet build GalGame.FileService.csproj -c Release |
通过,0 warning / 0 error |
| KnowledgeService | dotnet test KnowledgeService.Tests/KnowledgeService.Tests.csproj -c Release |
105/105 通过 |
| Gateway | TypeScript tsc |
通过 |
| Gateway | Vitest | 12 files,166/166 通过 |
| 最终 HTTP 契约矩阵 | .artifacts/integration/Test-ContractNegatives.ps1 |
50 个断言通过,ocrInvoked=false |
| Compose | docker compose -f compose.integration.yaml config --quiet |
通过 |
| 默认集成栈 | docker compose ... up -d --wait |
7 个组件全部 healthy |
已存在并验证可构建的镜像:
galreview-integration-auth-service:latestgalreview-integration-user-service:latestgalreview-integration-file-service:latestgalreview-integration-knowledge-service:latestgalreview-integration-gateway:latestgalreview-integration-ocr-service:latestmongo:8.0neo4j:2026.06.0
最终运行组件为 Gateway、AuthService、UserService、FileService、KnowledgeService、
MongoDB 和 Neo4j,均为 healthy。ocr-service 运行数量为 0。
Gateway 最终日志未再出现 MaxListenersExceededWarning。测试和运行日志仍可见
http-proxy-middleware 依赖触发的 Node DEP0060 util._extend 弃用提示;它没有造成
测试失败或请求错误,后续应通过升级第三方依赖消除。
针对故意发送的畸形 JSON/query,ASP.NET ExceptionHandlerMiddleware 会先记录通用
“unhandled exception”诊断行,随后自定义处理器明确记录 Invalid ... request 并返回
已断言的 400;按各服务自定义未处理错误标记复查为
NO_UNEXPECTED_SERVICE_LOG_HITS,不存在启动失败或服务级未处理异常。
入口脚本:
backend\FileService\Test-GatewayFileFlow.ps1 `
-FilePath .artifacts\integration\农业生态学题库.pdf `
-ContinueKnowledgeGraph `
-SubjectHint AGRONOMY `
-VerifyNeo4j密钥由运行中的本地容器环境提供,测试输出和本文均未打印真实值。
| 断言 | 结果 |
|---|---|
POST /api/v1/auth/registrations 创建用户和会话 |
通过 |
POST /api/v1/auth/sessions 使用同一用户登录 |
通过 |
| 返回 Access Token | 通过 |
两次 INTERNAL introspection 均为 active=true |
通过 |
| 内省用户与注册用户相同 | 通过 |
内省前后 expiresAt 不变 |
通过 |
GET /api/v1/users/me 返回同一用户资料 |
通过 |
| 管理员登录返回小写 UUID v4,随后访问受保护用户列表 | 201 / 200,通过 |
最终容器还实际验证:Auth 畸形 JSON、缺失当前密码、null 刷新令牌均返回冻结信封;
管理员 null 用户名返回 401;multi-use 邀请缺少 maxUses 返回 400;密码重置确认
前五次无效证据返回 422,第六次返回 429 RATE_LIMITED。Auth 的 UserService
管理员资料查询响应校验器以 8/8 单测覆盖有效空数组、有效资料、畸形 JSON、缺失/
null data、非空 meta、空 traceId 和越权资料;最终真实管理员列表请求为 200。
资料更新路由的畸形/空值和 SubjectCode 边界均经 Gateway 或 UserService 测试验证,
响应含 data: null、空 details 和非空 traceId,没有落入 500。小写
agronomy 被规范化为 AGRONOMY,连字符 bio-chem 被 400 拒绝;INTERNAL
profile lookup 的缺失/null、空数组和去重前 501 项分别得到 400/400/200/400。
偏好接口在最终容器栈中经 Gateway 验证:畸形 JSON,以及缺失 required
reducedMotion 均为 400 VALIDATION_ERROR;可解析但越界的目标时长为
422 BUSINESS_RULE_VIOLATION。UserService 全量测试为 32/32。
| 断言 | 观测值 |
|---|---|
| multipart 上传 | 通过 |
| IngestionJob | SUCCEEDED |
enableOcr |
false |
ocrUsed |
false |
| parser | files-text-v1 |
| 规范化文本长度 | 26,139 UTF-16 code units |
| 文本 SHA-256 | 5b233c1cb5ea7e7cd0cc37a4958bd9eb7ee8593ef39603a4cc72adeacc86ae1b |
sourceMap |
20 个有序、非重叠区间 |
blocks |
20 个,文本均与声明区间逐字一致 |
| owner、UTF-8、NFC、LF、checksum、offset | 全部通过 |
FileService 使用 standalone MongoDB,因此完成发布采用:
暂存完整文本且 Material=PROCESSING
→ IngestionJob=SUCCEEDED
→ Material=READY
这会允许极短的 SUCCEEDED + PROCESSING 可观察窗口,但不会出现
READY 早于 SUCCEEDED,也不会出现没有完整文本的 READY。启动恢复逻辑可从已暂存
文本完成最后发布。
另以未知扩展名 utf8-fallback.bin 和 application/octet-stream 执行非 OCR
上传/解析:任务为 SUCCEEDED,enableOcr=false、ocrUsed=false,得到 85 个
UTF-16 code unit、1 个来源区间和 1 个块,checksum 与原文重新计算结果一致。这证明
契约中的 UTF-8 文本兜底不是仅有代码分支而未执行。
最终容器还验证了空 multipart 上传和非法 ocrMode 均返回
400 VALIDATION_ERROR。非法模式请求在创建解析任务前即被拒绝,没有启动或调用
OCRService。
同一矩阵还验证:
.json + application/json在写入前返回415 MEDIA_TYPE_UNSUPPORTED;- 上传与列表查询的
" agronomy "/agronomy均规范化并命中同一资料; - 非法整型 query 返回统一
400 VALIDATION_ERROR; - 不可满足的 Binary Range 返回空体 416;
application/pdf即使上传文件名为.bin,仍按 PDF 解析并在不使用 OCR 时成功;- 只含 script/style 的 HTML 解析任务进入 FAILED,未发布空的 READY 文档;
- 跨 owner 删除、owner 删除和重复删除依次为 404/204/404。
最终标识仅用于复现本次本地结果:
| 项目 | 值 |
|---|---|
| materialId | 7a7c9820-cf5a-43ff-a29a-b7aaa1283ad1 |
| buildId | 639b9d21-5f6b-43ae-b37f-59c0e47da44a |
| graphId | 0bc21eaa-1a67-4bd7-96b6-ca64fad482eb |
| 构建状态 | SUCCEEDED |
| 章节 | 7 |
| 知识点 | 243 |
| 关系 | 207 |
| 先修关系 | 207 |
| 章节切分模式 | HEADING_RULES |
章节标题:
- 第一章 绪论
- 第二章 种群与群落
- 第三章 农业生态系统
- 第四章 农业生态系统的物质循环
- 第五章 能量流动
- 第六章 农业生态系统的评价与优化
- 第七章 生态农业与可持续发展
额外不变量:
- Knowledge API 计数与 Neo4j 计数均为
1 graph / 7 chapters / 243 points / 207 relations; PREREQUISITE子图通过拓扑排序,无环;- 所有初始 mastery score 为 0;
- 每个知识点具有有效、非空来源位置;
- 章节响应只使用
AUTO / HEADING_RULES / MARKDOWN / DELIMITER / FIXED_WINDOW契约枚举; - 相同 UUID D
Idempotency-Key和相同请求复用原 build; - 相同 key 携带不同
subjectHint返回409 IDEMPOTENCY_KEY_REUSED。
端点级负向验证还覆盖:数字 enum token、畸形 JSON、缺失 mastery graphId 和缺失
INTERNAL PlanGraph snapshotVersion 均返回统一 400;非法连字符 SubjectCode 返回
400 SUBJECT_CODE_INVALID;缺失 review evidence required 字段返回
400 REVIEW_EVIDENCE_INVALID。KnowledgeService 最终全量测试为 105/105。
完整机器可读结果位于
.artifacts/integration/knowledge-flow-report.json;最终 HTTP 契约矩阵位于
.artifacts/integration/contract-negative-report.json,只保存用例名、状态和错误码,
不保存令牌或服务密钥。
从 Gateway 容器网络直接向服务发送仅用于验证的请求,结果如下:
| 目标 | 调用身份 | 结果 |
|---|---|---|
| File extracted-text | KnowledgeService |
200 |
| File extracted-text | AuthService |
403 |
| Knowledge PlanGraph | GalGameService |
通过 allowlist,随后因虚构资源返回 404 |
| Knowledge PlanGraph | RenderService |
403 |
| Knowledge review evidence | RenderService |
通过 allowlist,随后因合成数据返回领域校验 400 |
| Knowledge review evidence | GalGameService |
403 |
Gateway 契约测试还验证:
- AuthService 连接失败、5 秒超时、任意非
200、密钥错配403、5xx、 非 JSON 或畸形200均映射为503 SERVICE_UNAVAILABLE; - 只有包含完整字段和正确契约类型的
active=false映射为用户侧401 TOKEN_EXPIRED;极简{active:false}、非法 UUID 或非法 DateTime 均映射为503; X-Correlation-Id会传给内省请求并保留为响应traceId;POST /api/v1/knowledge-graph-builds使用 generation 限流,GET 状态轮询使用 general 限流;- 文件本体硬上限为 10 MiB,Gateway/File multipart 整包前置上限为 11 MiB, 恰好 10 MiB 的文件不会因协议开销被提前拒绝。
- 代理连接、DNS、管道和超时错误统一返回
503 SERVICE_UNAVAILABLE;只有不可解析 的上游 HTTP 消息返回502 UPSTREAM_CONTRACT_INVALID,真实连接拒绝、挂起超时 和畸形响应测试均通过。
| 问题 | 修复与验证 |
|---|---|
| OCR 更新与旧 stash 同路径覆盖,GitHub Desktop 无法恢复 | 按服务语义合并;保留当前 OCR/Mongo FileService;无未解决冲突 |
| GET 构图状态轮询错误消耗 generation 配额并返回 429 | POST 精确使用 generation,GET 使用 general;Gateway 测试和 E2E 通过 |
| PDF 每页仅一行导致 4 个固定窗口、4 点、0 边 | v2 内联章节和连续题号规则;最终 7 章、243 点、207 边 |
| 页眉污染知识点与伪依赖 | 版权页眉清理;真实样例未生成页眉标题或页眉摘要 |
HEADINGRULES/FIXEDWINDOW 不符合契约枚举 |
显式映射为 HEADING_RULES/FIXED_WINDOW;单测与 E2E 通过 |
| 构图幂等冲突码被 repository 抢先替换 | 统一为 IDEMPOTENCY_KEY_REUSED;单测与 E2E 通过 |
| 图指纹遗漏学科和切分参数 | 纳入最终 subject、全部公开 SegmentationOptions;针对性测试通过 |
| Gateway 把 Auth 配置/上游故障伪装成无效令牌 | 非规范内省统一 503;UUID v4、UTC 时间、成功信封及故障映射正反例均覆盖 |
Gateway 未分类代理错误返回 502 INTERNAL_ERROR,且超时可能先断开 socket |
传输故障统一 503 SERVICE_UNAVAILABLE,仅畸形 HTTP 为 502 UPSTREAM_CONTRACT_INVALID;Gateway 全量 166/166 通过 |
| Gateway/File 把 multipart 整包和文件本体都限为 10 MiB | 整包 11 MiB,文件本体仍硬限 10 MiB;边界测试通过 |
| File extracted-text 接受任意服务名 | 默认精确 allowlist 仅 KnowledgeService;运行时 200/403 验证通过 |
| File 先发布 READY 后写 SUCCEEDED | 改为暂存文本、SUCCEEDED、READY 的可恢复顺序 |
| Token 内省会滑动延长 Access/Refresh 到期时间 | 改为固定绝对有效期;E2E 两次内省到期时间相同 |
| 固定管理员 ID 不是 UUID v4,严格内省会使管理员路由返回 503 | 改为稳定小写 UUID v4;管理员登录和受保护用户列表经最终 Gateway 容器通过 |
| User PATCH/PUT 的空或畸形 JSON 被全局异常处理器映射为 500 | 独立更新 handler 将解析与字段错误统一映射为 400 VALIDATION_ERROR;最终 Gateway 容器请求通过 |
| User 偏好 PUT 的畸形 JSON 落入 500,数字字符串被宽松接收,required bool 缺失不可区分 | 独立偏好 handler 区分 400 缺失/传输/类型错误与 422 业务越界;User 全量 32/32 和最终 Gateway 请求通过 |
File 非法 ocrMode 使用非统一错误码 |
统一为 400 VALIDATION_ERROR;最终容器请求通过且未调用 OCR |
| Gateway 代理数量触发 MaxListeners 警告 | 阈值按有限路由表设置;最终日志不再出现警告 |
| 未知扩展名被入口过滤,无法进入 UTF-8 兜底 | 允许 text/* 或 application/octet-stream 进入文本解析;真实 E2E 通过 |
| File 仅凭 MIME 接受、却只按扩展名分派解析器 | MIME 与扩展名统一决定解析器;.bin + application/pdf 的真实非 OCR 任务成功 |
| File 可把空结构解析结果发布为 READY | 完成态前校验非空 text/sourceMap/blocks 与全部区间;空 HTML 的真实任务进入 FAILED |
| File SubjectCode 与删除接口泄漏/规范化不一致 | 上传和列表统一 Trim+大写;跨 owner、已删除统一 404,活动/竞态为 409 |
| Knowledge 绑定错误可能绕过统一信封 | 启用 ThrowOnBadRequest;数字 enum、畸形 JSON、缺失 query 的真实端点均为统一 400 |
| Auth 将畸形 UserService 200 误作空结果或客户端 400 | 严格校验成功信封并映射 502 UPSTREAM_CONTRACT_INVALID;8/8 契约测试和真实有效响应通过 |
- OCR 未测试:仅证明 Dockerfile 可构建;未启动、未调用、未评估准确率。
- GalGameService / RenderService 未参加全流程(截至 2026-07-31):本轮只验证 KnowledgeService 的 INTERNAL allowlist 和进程内计划/掌握度测试。题目生成、剧情生成、运行时会话和真实 evidence 回传仍为对应负责人的 URGENT 集成项。GalGameService 后续集成结果见 第 10 节;RenderService 与真实游玩回传仍未覆盖。
- 异步事件未测试:当前闭环使用同步 INTERNAL HTTP;消息 broker、重试和 DLQ 仍是后续统一基础设施契约。
- File access grant 未测试且不属于当前可执行契约:现有占位映射没有 grant token 或服务端过期校验,已从当前接口目录移出;完整短期下载授权由 FileService/Gateway 负责人作为 URGENT 项实现。
- 生产密钥与生产数据库未测试:本轮使用本地开发配置。
DEEPSEEK_API_KEY和BitchSDAU没有注入、读取或记录,它们不是确定性提取和构图链路的依赖。 - Mongo 跨集合原子提交:standalone 部署没有事务;当前通过固定顺序、条件更新和
启动恢复避免错误
READY。若生产要求完全不可观察的单一完成态,应将 Mongo 部署为 replica set 并使用事务。
交付时以下检查均已实际通过:
git diff --check
git diff --name-only --diff-filter=U
git stash list
docker compose -f compose.integration.yaml config --quiet
docker compose -f compose.integration.yaml ps原冲突 stash 已在语义合并后清除;其不可变备份保存在分支
codex/stash-backup-20260731(96d4d24d2c9ecfb97408f73a3b5e72810923def0),
便于需要时审计。git diff --name-only --diff-filter=U 和 stash 列表均为空;
Compose 配置校验为 0,七个默认组件全部 healthy,OCR 运行数为 0。
在保留前一轮注册、登录、上传、非 OCR 提取和 KnowledgeService 构图能力的基础上, 本轮完成了复习计划到 GalGame 游戏包的本地集成验证。Assessment 与 Learning 两条计划 均可经 Gateway 提交生成任务、取得清单及内容,并通过游戏包校验接口。
由于 Windows 排除端口范围与当时的默认映射冲突,本轮曾使用一组仅限主机侧的临时端口。
该临时映射现已废止,不得复制到当前配置;当前宿主 published 默认值以
contract.md 第 9.5 节为准,实际值可由 .env 的 *_HOST_PORT 覆盖:
| 入口 | 当前默认地址 |
|---|---|
| Gateway | 旧临时映射已废止;当前为 http://127.0.0.1:5000 |
| KnowledgeService 诊断入口 | 旧临时映射已废止;当前为 http://127.0.0.1:5104 |
当时的默认主机映射在本机无法绑定;当前应在 5000-5300 内选择未被保留的宿主端口,
不应为绕过冲突而修改容器 target。
最终 auth、file、user、knowledge、galgame、gateway、mongo、neo4j
八个容器均为 healthy。
最终自动化回归结果:GalGameService Release 测试 258/258 通过,Gateway 的
12/12 个测试文件、172/172 个用例通过,Gateway TypeScript 构建退出码为 0。
Compose 已成功构建全部应用镜像并启动上述八个组件。
Docker Desktop 数据盘已核对为
D:\DockerData\DockerDesktopWSL\disk\docker_data.vhdx,本轮镜像和卷未落回 C 盘默认
数据位置。
真实输入为 D:\AppData\test\农业生态学题库.pdf,本轮明确关闭 OCR。注册与登录成功,
随后经 Gateway 完成文件处理和知识图谱构建:
| 项目 | 观测值 |
|---|---|
| IngestionJob | SUCCEEDED |
ocrRequested / ocrUsed |
false / false |
| parser | files-text-v1 |
| 规范化文本长度 | 26,139 |
| 文本 SHA-256 | 5b233c1cb5ea7e7cd0cc37a4958bd9eb7ee8593ef39603a4cc72adeacc86ae1b |
| source spans / blocks | 20 / 20 |
| 图谱规模 | 7 章、243 个知识点、207 条关系 |
PREREQUISITE 关系 |
207 |
| 图性质 | DAG |
| 初始掌握度 | 全部为 0 |
| Neo4j 对账 | 章节、知识点和关系计数均与 API 一致 |
Assessment 计划包含 9 个节点,其中 3 个为 questionTarget,观测覆盖率为 0.0354;
生成结果包含 11 个场景。任务生成、manifest 和 content 获取均成功,并验证:
- 游戏包中的
reviewPlanId与完整snapshotVersion和输入计划一致; - 每个
QUESTION的知识点绑定与选项处于同一场景; - 题目选项显式包含
answerKind和correct,题目标识均为 UUID v4; - 响应内容字节、manifest checksum 与 ETag 的 SHA-256 均为
770ebcfc90e2fbacdcadeffe24d31e5379fdcb10a34f1c8772a0bca061172913; - 携带对应
If-None-Match再次获取内容返回304; - 原始游戏包校验返回
200;将一个选项的correct故意置为null后,校验返回422。
Learning 计划包含 5 个节点,5 个均为 questionTarget,生成结果包含 7 个场景。任务生成
和内部游戏包校验成功。响应内容、manifest 与 ETag 对应的 SHA-256 为
d7f560452f2573469fd1514b30eff0a8c266314bb70b20ddfd212d9452a8d318,条件请求返回
304。
- OCR 不在本轮测试范围内,不能据此判断 OCR 识别效果。
- RenderService 和实际 GalGame runtime 不在当前仓库及 Compose 栈中。本轮仅使用契约中
可信的
RenderService服务身份调用游戏包校验接口,未执行真实游玩,也未验证最终 掌握度 evidence 回传闭环。 - GalGameService 当前存储标识为
ephemeral-memory;容器重启会丢失生成任务和游戏包, 只适用于本地集成与预发布环境。 - 远程服务器的 SSH 端口可达,但提供的凭据认证失败;为避免触发锁定已停止重试,远程
部署未执行。仓库保留
.env.deploy.example与 Compose 配置,可在凭据可用后快速部署。 本报告不记录服务器地址、账号密码或服务密钥。
本节曾使用错误引入的 C# RenderService 原型得出结论。该原型已移除,本节涉及 ReviewSession、结果提交和 mastery 回写的结论全部作废,不得作为当前实现证据。 当前有效结果以第 12 节为准。
第 10 节中“RenderService 不在仓库及 Compose”的限制已经失效。本轮按
docs/contract.md 的接口完成并测试了 Frontend、Gateway、GalGameService、RenderService
与 KnowledgeService 的最终结果回写:用户可在页面中上传资料、构图、生成复习计划和
GalGame,逐场景作答后由 RenderService 提交证据,KnowledgeService 实际更新掌握度。
最终默认 Compose 同时运行 12 个 healthy 容器:Frontend、Gateway、UserService、
AuthService、FileService、KnowledgeService、GalGameService、RenderService、两套 MySQL、
MongoDB 和 Neo4j。OCRService 仍只属于可选 ocr profile,本轮没有启动或测试 OCR 功能。
Docker Desktop 的数据目录再次核对为 D:\DockerData\DockerDesktopWSL;本轮新增的
MySQL 镜像、应用镜像和数据卷没有改回 C 盘默认位置。
| 项目 | 结果 |
|---|---|
| AuthService Release tests | 8 / 8 |
| UserService Release tests | 32 / 32 |
| KnowledgeService Release tests | 105 / 105 |
| GalGameService Release tests | 285 / 285 |
| RenderService .NET tests | 11 / 11 |
| RenderService JS Adapter tests | 6 / 6 |
| Gateway tests | 176 / 176 |
| Gateway TypeScript build | 通过 |
| Frontend TypeScript + Vite production build | 通过,106 modules |
| Frontend npm audit | 0 vulnerabilities |
| Frontend 代理不可用分支 | 503 SERVICE_UNAVAILABLE,关联 ID 保持一致 |
| FileService Release build | 0 warning / 0 error |
| FileService NuGet vulnerability scan | 0 vulnerable packages |
| Compose config | 通过 |
FileService 的 MongoDB.Driver 从 3.5.0 升级到 3.10.0,消除了原先传递引入的
SharpCompress 和 Snappier 漏洞。Frontend 从存在安全公告的 react-router-dom 7.18.2
迁移到 react-router 8.3.0,容器中的 npm ci 与审计均通过。
Render 契约对照额外覆盖了容易抬高 mastery 的边界:attemptNumber > 1 时
quality <= 3;结果只覆盖实际作答的分支题目,但每条证据仍必须唯一且与题目、知识点、
选项和正确性绑定一致。Adapter 同时校验数量边界、UUID、引用与可达性,并在启动会话时
冻结核对 packageId + reviewPlanId + snapshotVersion。
测试入口使用 Frontend 的同源地址,因此既验证了浏览器部署入口,也验证了 /api/* 到
Gateway 的反向代理。真实输入仍为 D:\AppData\test\农业生态学题库.pdf,明确使用
enableOcr=false。
| 阶段 | 观测结果 |
|---|---|
| 注册、登录、用户资料 | 成功;用户与令牌 owner 一致 |
| 上传与提取 | SUCCEEDED,ocrRequested=false,ocrUsed=false |
| 规范化文本 | 26,139 UTF-16 code units,20 source spans,20 blocks |
| 构图 | 7 章、243 个知识点、207 条关系,Neo4j 对账一致,初始 mastery 全为 0 |
| Assessment Plan | 5 个节点,3 个实际出题点 |
| GalGame | 7 个场景,完整包 checksum 与响应字节一致 |
| Runtime 资源 | manifest、Adapter 和 WASM 均可经 Gateway 读取,WASM SHA-256 一致 |
| Render 会话 | 权威包读取和二次校验成功;事件重投去重、进度重投幂等 |
| 最终结果 | 首次 ACCEPTED,相同载荷重试 DUPLICATE |
| 冲突载荷 | 409 IDEMPOTENCY_CONFLICT |
| 掌握度 | 3 个作答知识点由 0 更新为 35,版本同步递增 |
创建 ReviewSession 的成功同时证明了新增
GET /internal/v1/game-packages/{packageId}?ownerUserId=... 路由可由精确
RenderService 身份调用。测试期间发现 Gateway 最初把该接口注册成无参数静态路径,
真实请求会 404;已改为 :packageId 参数路由并加入路由测试。
使用本机 Edge 进行无头浏览器测试,从 /login 开始实际操作页面:
- 登录并进入主页;
- 在
/materials上传 PDF,等待非 OCR 提取和知识图谱完成; - 创建 Assessment Plan 并进入
/review; - 生成游戏包,加载 manifest、ES module Adapter 和 WASM;
- 连续点击 8 个场景选择,记录 5 条作答证据;
- 保存进度并提交最终结果,显示“复习完成”。
浏览器控制台错误为 0,未捕获页面异常为 0。完成页截图保存在忽略版本控制的
.artifacts/integration/frontend-complete.png。生产容器的 /healthz、根路径与 SPA
fallback /materials 均返回 200。
首次把 AuthService/UserService 从 Mock 切换到服务器模板使用的 MySql 模式时,发现
MySQL 8.4 的 caching_sha2_password 会拒绝原连接串。Compose 已在两条内部连接串中补充
AllowPublicKeyRetrieval=True,MySQL 端口仍不对宿主或公网开放。
修复后完成了管理员登录、创建一次性邀请码、用户注册、用户登录和资料读取。随后重启
AuthService 与 UserService,再次使用同一账号登录并读取相同用户资料成功;两个 /readyz
均明确返回 storage=mysql,证明不是回退到内存模式。
Render 最终边界校验修复并重建镜像后,又在该 MySQL 模式下经当时的 Frontend 主机映射 (现已废止)重跑完整 API 与 Edge 浏览器流程。API 仍得到 7 章、243 个知识点、207 条关系和 3 个由 0 更新为 35 的 mastery;浏览器完成 8 次场景选择并提交 5 条作答证据,控制台错误和页面异常均为 0。
- RenderService 的会话存储仍是
ephemeral-memory,容器重启会丢失进行中的会话; - 当前执行引擎是 JS Adapter。WASM 响应只有 8 字节的最小可加载模块,
/readyz如实返回wasmAbiComplete=false;C++ ABI、内存所有权和真实帧渲染仍未完成; - GalGameService 仍使用进程内游戏包存储;
ReviewCompleted v2消息总线尚未接入,当前闭环使用契约允许的同步 INTERNAL evidence;- 未执行远程部署;
docs/deploy.md和.env.deploy.example已保留直接在 Linux 服务器启动、 更新、回滚与备份的步骤,且不包含真实服务器凭据。
RenderService 当前只是一套供后续负责人继续开发的 C++ / JS 工具链基础壳。错误加入的 ASP.NET Core 项目、C# 领域代码和 .NET 测试已经全部移除;仓库不再把临时 C# 原型当作 RenderService 实现。
当前镜像只完成以下工作:
- 使用
g++编译并执行src/main.cpp基础壳自检; - 由 JS 静态层公开 manifest、最小 WASM 和 ES module Adapter;
/readyz明确返回runtimeMode=SHELL、reviewSessionsAvailable=false和wasmAbiComplete=false;- ReviewSession 路径直接返回
501 RENDER_SESSION_NOT_IMPLEMENTED。
| 项目 | 结果 |
|---|---|
| C++ 壳容器编译 | 通过,g++ -std=c++23 |
| C++ 壳容器内执行 | 退出码 0 |
| JS Adapter tests | 6 / 6 |
| Frontend TypeScript + Vite build | 通过,106 modules |
| Gateway readiness | 200 |
| Runtime manifest | 200,runtimeMode=SHELL |
| WASM checksum | 与 manifest 一致 |
| ReviewSession 未实现分支 | 501,RENDER_SESSION_NOT_IMPLEMENTED |
| 默认 Compose | 12 个容器 healthy |
Edge 无头浏览器重新执行了登录、上传 PDF、非 OCR 构图、Assessment Plan、GalGame 生成、 Adapter/WASM 加载和本地游玩。结果为 7 章、243 个知识点、207 条关系、8 次场景选择和 5 条浏览器本地作答记录;控制台错误与页面异常均为 0。
本轮没有调用 Render ReviewSession、progress、events 或 result,也没有更新 mastery。 完成页明确提示这是基础壳本地体验,结果未提交。该边界符合当前 RenderService 尚待后续 负责人开发的真实状态。
- ReviewSession、进度、事件、结果幂等与 KnowledgeService evidence 回传;
- 完整 C++ WASM ABI、内存所有权、RuntimeState 与真实帧渲染;
ReviewCompleted v2消息总线和生产持久化;- OCR 功能与远程服务器部署。
design/Prototype 只有登录、注册、忘记密码和主页四张 3456×2234 静态浅色 PNG,按
2 倍导出还原后的验收视口为 1728×1117。原稿没有移动端、深色主题、错误态、加载态或
交互态,因此桌面浅色页面按原稿对照,移动端按同一灰阶、圆角、图标和卡片语言补充设计。
桌面稿的 1728×1117 只作为比例和视觉语言基准,不再作为固定画布。认证内容区、字号、
间距和品牌标志使用视口相关尺寸;主页使用 100dvh 和两行弹性网格,让功能卡占满标题区
之外的剩余高度。输入框浅色填充保持 #DBDADA,主页卡片保持 #BDBDBD、外框为
#C6C6C6、工具栏为 #BDBDBD,但不会把原稿坐标和 414 px 卡片高度无条件套到其他屏幕。
以下差异为有意保留,不能为了静态截图改坏已冻结契约或基本可用性:
- 登录使用邮箱而不是原稿中的用户名;
- 注册使用管理员邀请码而不是原稿中的邮箱验证码;
- 注册/忘记密码保留返回登录入口,主页保留退出登录入口;
- 无自定义头像时按 UserService 契约显示用户名首字符;
- 深色主题没有对应 Prototype,只验证功能和可读性,不宣称与浅色稿一致。
移动端以 390×844 为主要检查尺寸:认证页左右留白 20 px、输入框高 58 px、操作按钮高
50 px,并保留右上角紧凑品牌标志;登录主按钮独占一行,次要入口双列排列。主页把“继续”
设为主卡,“知识点/资料上传”为双列快捷卡,“知识图谱”为宽卡;工具栏触控区为 48 px,
用户信息、头像回退和退出操作均可见。768×1024 平板宽度也已检查,主页无横向溢出。
- “知识点”改为独立页面,不再与“知识图谱”两个入口显示同一内容;
- 知识点和关系读取持续消费 6.1 分页响应的
nextCursor,不再静默截断为前 100 条; - 删除主页通往假设置占位页的入口,用户胶囊只展示真实用户资料;
- 主题选择写入浏览器本地状态,刷新后保持,并同步浏览器
theme-color; - 默认头像改为用户名首字符,存在
avatarUrl时仍显示真实头像; - 主页头部在 840 px 以下提前纵向排列,覆盖原先 701--828 px 可能溢出的区间。
docs/contract.md 的前端路由说明已补入 /knowledge,并明确知识点/图谱页面必须继续读取
nextCursor。本轮没有新增或修改任何后端接口。
| 项目 | 结果 |
|---|---|
| Frontend TypeScript + Vite production build | 通过,107 modules |
| Frontend 容器 | healthy |
| 前端冒烟检查 | 36 / 36 |
| 390×844 登录/忘密/主页横向溢出 | 无 |
| 768×1024 主页横向溢出 | 无 |
| 密码显隐、注册/忘密导航 | 通过 |
| 主题即时切换及刷新保持 | 通过 |
| 默认头像首字符 | 通过 |
| 知识点与知识图谱独立路由 | 通过 |
| 知识点/关系两页游标读取 | 通过 |
| 1366×768、1728×1117、2560×1440 桌面满屏与无滚动溢出 | 通过 |
| 三档桌面卡片随视口高度连续放缩 | 通过 |
| 1728×1117 浅色色值断言 | 通过 |
游标测试通过浏览器拦截构造两页合法契约响应,分别确认知识点页面请求两页、图谱页面请求
两页知识点和两页关系;没有重复执行此前已经通过的注册、上传、提取、构图、GalGame 和
Render 基础壳全流程。最终截图保存在忽略版本控制的 .artifacts/visual-audit/。
2026-08-02 复核发现,早先把“服务器防火墙只放行 5000-5300”错误扩展到了容器 target、
数据库监听、SMTP、系统代理和测试端口。该做法改变了协议语义,且把 Compose 宿主映射写死,
在 Windows WinNAT 保留段存在时无法通过 .env 避让。原端口策略结论自本次纠偏起作废;
接口路径、方法、鉴权与数据契约始终未因宿主端口调整而改变。
正确边界只包含 Docker 发布到宿主机的端口:Gateway 5000、KnowledgeService 5104、
Frontend 5120、Neo4j Browser 5254 和 Bolt 5255。五项均为 .env 中可覆盖的
*_HOST_PORT 默认值,覆盖后仍须处于 5000-5300。对应容器 target 分别保持
5000/8080/8080/7474/7687;User/Auth MySQL 使用 3306、MongoDB 使用 27017,均不向
宿主发布。SMTP 默认 465,代理端口按系统配置,测试监听由操作系统分配临时端口。
| 检查项 | 结果或适用范围 |
|---|---|
Test-PortPolicy.ps1 |
通过;现在只检查 Compose/docker publish 的宿主侧和 *_HOST_PORT 默认值,不扫描 target、EXPOSE、URI、连接串、SMTP、代理或测试端口 |
| 宿主端口参数化 | GATEWAY_HOST_PORT、KNOWLEDGE_HOST_PORT、FRONTEND_HOST_PORT、NEO4J_BROWSER_HOST_PORT、NEO4J_BOLT_HOST_PORT 均有范围内默认值 |
| 数据库发布 | User/Auth MySQL 与 MongoDB 均不发布到宿主机;其原生内部端口不属于防火墙策略 |
| 早先 12 容器 healthy、Gateway readiness 与全流程结果 | 仍可证明当时业务链路可运行,但不能证明错误的内部端口迁移合理;纠偏后的容器拓扑必须以本轮后续实际复测为准 |
早先 KnowledgeService 105/105、Gateway 176/176、Render Adapter 6/6、Frontend build/UI smoke |
功能回归结果保留;其中固定 5260-5299 测试端口的做法已废止 |
当时的容器环境使用 D:\AppData\test\农业生态学题库.pdf 执行过一次真实主链路:
- Gateway 注册、登录、令牌内省和用户资料读取成功;
- 上传和提取任务为
SUCCEEDED,ocrRequested=false、ocrUsed=false; - 得到 26,139 个 UTF-16 code unit、20 个 source span 和 20 个结构块,checksum 与偏移校验通过;
- KnowledgeService 构建出 7 章、243 个知识点和 207 条先修关系;
- API 与 Neo4j 数量一致,先修子图无环,初始 mastery 全为 0;
- 幂等重放稳定,冲突请求返回
IDEMPOTENCY_KEY_REUSED。
此前已经通过的 GalGame 生成业务用例没有重复执行;当时通过镜像重建、Gateway readiness
和既有自动化测试确认业务服务可达。该结论不再用于支持错误的内部端口策略。Render 基础壳的 manifest、Adapter 和
WASM 均再次经 Gateway 读取成功;manifest 如实为 runtimeMode=SHELL、
reviewSessionsAvailable=false,WASM 为 8 字节最小模块。OCR 功能仍未测试。
RenderService 已从基础壳升级为真实实现:§8.3 八个 ABI 函数在 C++ 中实现并经
emcc 4.0.13 编译为 standalone WASM(179 KB),§8.1 五个 ReviewSession 接口与
§8.2.1 学习证据回传上线。默认 12 容器环境中,manifest 经 Gateway 首次如实返回
runtimeMode=FULL、reviewSessionsAvailable=true、wasmAbiComplete=true
(三个标志均由服务端对实际 wasm 产物导出自省与回调配置推导,非硬编码)。
| 项目 | 结果 |
|---|---|
| C++ 原生自检(JSON/校验器/状态机/ABI) | 129 / 129 |
| JS Adapter 测试(含 JS↔WASM 校验器奇偶校验) | 7 / 7 |
| ReviewSession 领域 + HTTP wire 测试 | 11 / 11 |
| build_support fixture 回归 | 183 / 183 |
| scripts/Test-PortPolicy.ps1 | 通过 |
| 镜像构建门禁(容器内编译 + 自检 + 服务层测试) | 通过 |
奇偶校验将 backend/GalGameService/mocks/ 全部包同时喂给 JS 与 WASM 两个校验器并
断言 valid 与 (path, code) 集合一致;期间发现并修复了 C++ 侧对"多 QUESTION 绑定
场景同时触发 SCORING_WITHOUT_QUESTION"的分支遗漏。
真实输入为脚本内置的 Markdown 题库(三章、每章 4 条名词解释),enableOcr=false:
| 阶段 | 观测结果 |
|---|---|
| 注册、登录(邀请码) | 成功 |
| 上传与提取 | SUCCEEDED,ocrUsed=false |
| 构图 | 3 章、3 知识点,初始 mastery 全为 0 |
| Assessment Plan | 3 节点、3 个出题点 |
| GalGame 生成 | 5 场景、3 道题,包与计划快照一致 |
| 创建会话 | 权威包 INTERNAL 读取 + 共同校验通过;reviewPlanId+snapshotVersion 冻结核对一致 |
| 事件 | 首投 3 条全收,重投 3 条全去重 |
| 进度 | 版本 0→1;相同载荷重投幂等返回原快照 |
| 结果 | 首次 ACCEPTED;重放 DUPLICATE 且 resultId 不变;篡改载荷 409 IDEMPOTENCY_CONFLICT |
| 掌握度 | 3 个作答知识点 0→35(quality=5 首答的 SM-2 期望值),version 递增至 1 |
| 会话终态 | COMPLETED,completedAt 非空 |
- 会话存储为 ephemeral-memory(/readyz 如实上报),容器重启丢失进行中会话;
ReviewCompleted v2消息总线仍未接入,本轮闭环使用契约允许的同步 INTERNAL evidence;- 浏览器端已完成操作验证:真实登录后经
/review生成 CAMPUS 游戏包、创建服务端 会话(页面显示 cpp-wasm-0.2.0)、UI 点击作答 2 题并提交,页面提示"本次复习结果 已提交,共记录 2 条作答证据",控制台错误为 0,API 复核对应知识点 mastery 0→35 (DIRECT:ASSESSMENT:quality=5,version 1);TS 迁移后复测; - OCR 与远程部署仍未测试。
按“仅约束宿主发布端口”的规则重新构建并启动 compose.integration.yaml,12 个容器全部
进入 healthy。实际发布结果为 Gateway 5000 -> 5000、KnowledgeService
5104 -> 8080、Frontend 5120 -> 8080、Neo4j Browser 5254 -> 7474、Neo4j Bolt
5255 -> 7687;五个宿主默认端口均可通过对应的 *_HOST_PORT 环境变量覆盖。
| 复验项 | 结果 |
|---|---|
docker compose up -d --build --wait |
通过,12 / 12 healthy |
Gateway http://127.0.0.1:5000/readyz |
HTTP 200 |
KnowledgeService http://127.0.0.1:5104/readyz |
HTTP 200 |
Frontend http://127.0.0.1:5120/healthz |
HTTP 200 |
Neo4j Browser http://127.0.0.1:5254/ |
HTTP 200 |
User/Auth MySQL 容器内 3306 |
mysqld is alive |
MongoDB 容器内 27017 |
ping = 1 |
Neo4j 容器内 Bolt 7687 |
RETURN 1 成功 |
scripts/Test-PortPolicy.ps1 |
通过 |
MySQL 3306、MongoDB 27017 及各微服务内部监听端口没有发布到宿主,因此不会扩大
服务器防火墙放行范围。SMTP 和系统代理端口也不再被项目端口策略改写。
运行态检查确认 Frontend、Gateway、FileService 和 MongoDB 均 healthy,Gateway 容器访问
FileService readiness 为 HTTP 200。10 MiB multipart 探针可以完整通过 Frontend 和 Gateway,
仓库内两层代理的请求体限制不是故障点;失败现场也没有 POST /api/v1/materials 到达
FileService,因此界面所见的纯文本/HTML Bad Gateway 来源于仓库外层反向代理。
同时发现浏览器客户端在检查 HTTP 状态前无条件解析 JSON,导致外层代理返回的 HTML/纯文本
413/502/504 被统一改写成客户端 502 UPSTREAM_CONTRACT_INVALID。本轮修复后,客户端保留
真实 HTTP 状态、响应类型和 X-Correlation-Id;上传、文字提取、已有图谱读取和构图错误也
分别标明阶段。Frontend 与 Gateway 代理增加了不记录凭证的结构化错误日志。接口路径、请求、
成功响应和服务错误信封均未改变。
| 复验项 | 结果 |
|---|---|
| Frontend TypeScript + Vite production build | 通过,110 modules |
| Gateway TypeScript build | 通过 |
| Gateway 自动化测试 | 176 / 176 |
Nginx 上传反代示例 nginx -t |
通过 |
| 浏览器代理非 JSON 错误保真 | 413 与 502 均保留真实状态和对应提示 |
| 修复后 Gateway / Frontend 容器 | healthy |
2.38 MiB 真实 PDF 经 Frontend :5120 上传 |
HTTP 201 |
| 1.53 MiB PDF 上传、非 OCR 提取与构图 | 7 章、243 点、207 条关系,DAG 合法 |
| Frontend 入口完整注册至 mastery 闭环 | 通过,3 / 3 掌握度更新 |
部署侧新增 deploy/nginx/galreview.conf.example:外层请求体允许 12 MiB,上传读写超时为
190 秒并关闭请求缓冲。应用仍按契约拒绝超过 10 MiB 的单文件。
本轮没有改变 Gateway 路由、请求体、异步任务状态、GamePackage schema 1.0 或 RenderService
读取方式。原 GameGenerator 继续负责确定性骨架;新增 galgame-narrative-v2 叙事层,只能
重写 scene title、dialogue 与 choice 显示文本。所有 ID、跳转、知识绑定、correct、
scoreDelta、assets、reviewPlanId 和 snapshotVersion 均由代码锁定。
提示词按“整包共同主线”工作,要求知识作为线索、规则、工具、争议或行动依据改变剧情,禁止
退化为“知识点讲解—来看看这道题—本轮复习结束”。内部草稿使用 groundingQuotes 核对事实
出处,使用 knowledgeUse 说明知识怎样改变当前局面;两者在最终 GamePackage 中删除。
| 项目 | 结果 |
|---|---|
| GalGameService 全量测试 | 348 / 348 |
| 新增叙事单元/集成测试 | 15 个,全部通过 |
DeepSeek deepseek-v4-pro 真实 JSON Output |
通过 |
| GalGameService build | 通过,0 warning / 0 error |
docker compose config --quiet |
通过 |
scripts/Test-PortPolicy.ps1 |
通过 |
| GalGameService 镜像重建 | 未执行;本机 Docker Linux engine 未运行,Compose 静态解析已通过 |
真实模型测试使用仓库测试 PlanGraph(CAMPUS / STANDARD),以
GALGAME_RUN_LIVE_LLM_TEST=1 显式启用,API key 只从宿主 DEEPSEEK_API_KEY 读取。最终草稿通过:
- sceneId/choiceId 集合完整且无新增;
- EXPLAIN/QUESTION 的依据可逐字回溯到绑定节点;
- 对白包含对应概念锚点,且没有暴露权重、掌握度或内部 ID;
- 装配前后所有锁定字段保持不变;
- 最终 GamePackage 通过现有跨字段校验器。
第一次校准暴露出“强迫对白逐字复述依据会重新产生教材腔”的问题,因此最终实现把原文引用 保留为隐藏审计字段,只要求对白自然包含概念锚点,并增加“删除知识后剧情是否仍能原样成立” 与“删除选择后事件是否仍以相同方式推进”两项提示词拒收检查。真实模型复测通过。
测试覆盖恶意 summary 提示注入、缺失/伪造 scene 或 choice ID、错误 promptVersion、无依据 题目、知识场景没有 grounding、模板化问答文本、非法草稿整包回退,以及第一次失败后的一次 有界修复。Mock 模式强制关闭外部调用;模型超时、HTTP 错误、空响应、非 JSON、修复后仍非法 时均不保存半成品,继续返回契约有效的确定性包。供应商响应正文、异常详情和 API key 不进入 公开 job error。
本轮没有重新执行文件上传、文本提取、KnowledgeService 构图、浏览器渲染和 mastery 全流程; 这些接口及数据结构未变,沿用本报告前述已通过结果。
干净还原发现 GalGame.GalGameService.csproj 漏写 MongoDB.Driver 依赖,本机旧的
obj/project.assets.json 曾掩盖该问题;同时,合并提交误删了 GameGenerator 已有的资源、
学习模式、讲解深度和语义干扰项实现,却保留了对应测试。本轮恢复显式依赖和被误删的既有实现,
未改变 Gateway 路由、请求响应结构或 GamePackage schema,因此 contract.md 无需调整。
| 复验项 | 结果 |
|---|---|
dotnet restore --force --no-cache |
通过 |
CI 原命令 dotnet test ... --nologo |
348 / 348 通过 |
git diff --check |
通过 |
Gateway workflow 的 cache-dependency-path 正确指向 gateway/package-lock.json,但提交
507b0a2 删除了该文件并将其加入 gateway/.gitignore,导致 actions/setup-node 无法计算
npm 缓存键;即使关闭缓存,后续 npm ci 也会因缺少 lockfile 失败。本轮恢复原 lockfile 并
解除忽略,workflow 与 Gateway 接口均无需修改。
| 复验项 | 结果 |
|---|---|
lockfile v3 与 package.json 根依赖一致 |
通过 |
npm ci --no-audit --no-fund |
通过,干净安装 177 packages |
| TypeScript build | 通过 |
| Gateway 自动化测试 | 178 / 178 通过 |
git diff --check |
通过 |
日志中的 Action Node 20/24 与 punycode 内容是非致命弃用提示,本次失败点是缺失的缓存依赖
路径。项目测试运行时仍由 workflow 固定为 Node 22。
上一轮修复后 vite dev 路径仍有"已接受残余"。本轮用 socket 级仪器化 vite(记录每个客户端 连接的生命周期与 destroy 调用栈)抓到了两个独立机制并全部根治:
- CL 提前完成绕过网关的排空保护。 网关 bodyDrain 冲刷的错误信封带
Content-Length, 中继跳(vite http-proxy)的 HTTP 客户端凑满 CL 字节即判定响应完成并立刻结束对客户端的 响应,不等网关排空后的延迟 FIN;此时客户端请求体未上传完、响应又是connection: close(http-proxy 无 agent 的默认),Node 在响应 finish 时销毁客户端 socket,RST 冲掉在途 上传与客户端未读缓冲。仪器化日志中损坏请求均呈现PROXYRES 429早于req-end、随后res-finish → DESTROY的固定序列;服务端(FileService/网关限流器)对这些请求零痕迹。 - 旧钩子的 unpipe 悬空腿。 此前 vite 代理钩子在 proxyRes 时
req.unpipe()截断 "客户端体 → 网关"转发,网关排空等不到剩余字节,10 秒超时后硬毁该连接;销毁事件在 vite 进程内间歇性殃及无关的在途请求(组合探针中 S3#8 恰在 S2 竞态请求 +10s 时刻死亡,可 复现)。该钩子是网关修复前的遗物,其"本地排空"作用已由机制 1 的正确修复取代。
修复(frontend/vite.config.ts,仅 dev 代理段):移除 unpipe 钩子,改为与
frontend/server.mjs 的 endAfterDrain、网关 bodyDrain 同构的 drain-before-end
包装——响应字节照常立即转发,res.end 推迟到客户端请求体收完;不截断上游腿;排空有
12 MiB 上限与 10 秒超时,越界时在响应完成后只断开本连接。接口、构建产物与生产路径行为
均未改变。
| 复验项 | 结果 |
|---|---|
| vite dev 路径全矩阵 ×12(S1 401 / S2 429 / S3 413 / S4 正常 / S5 恶劣客户端) | 两次独立运行均 PASS,0 mangled |
| vite dev 路径 413 专项 ×8(限流窗清空,直达 FileService) | 8/8 信封完整 |
| 生产代理路径全矩阵 ×12 | PASS,0 mangled(此前"已接受残余" 2 项消失) |
| 生产代理路径 413 专项 ×8 | 8/8 信封完整 |
| keep-alive 头部与 X-Correlation-Id 存活探针(vite + 生产双路径) | 全部回显 |
frontend tsc --noEmit 与 Vite production build |
通过 |
| S5 停滞 12 秒 / 中途撕裂后服务存活 | 双路径均 YES |
对照:修复前基线 vite 路径 14/39 损坏(S3 全灭);仅移除旧钩子后仍余 5/10(机制 1 暴露); drain-before-end 后为 0。所有场景要求响应为完整契约信封且延迟 ≤ 4 秒。dev 路径仍保留一个 文档化边界:单请求体超过网关 12 MiB 排空上限时该连接会被明示断开(生产路径行为一致)。
E2E 闭环回归在"创建游戏生成任务"处失败:网关返回 500 INTERNAL_ERROR,GalGameService
日志为 GuidSerializer cannot serialize a Guid when GuidRepresentation is Unspecified
(GameGenerationJob.GenerationId)。检查发现 qzwl_galgame 库的 game_jobs、
game_manifests 集合均为 0 文档——MongoGameStore 自启用以来从未成功写入:此前全部闭环
验证走的是 InMemoryGameStore,合并启用 Mongo 持久化后首次真实流量即触发。MongoDB.Driver
3.x 起 Guid 序列化必须显式指定表示,类映射对 Guid 成员的 AutoMap 缺少该配置。
修复(backend/GalGameService/MongoGameStore.cs):在 MongoGameStoreMappings. EnsureRegistered 注册 GuidSerializer(GuidRepresentation.Standard),与同文件原生查询
使用的 BsonBinaryData(..., GuidRepresentation.Standard) 保持一致;库中无旧表示数据,
无兼容负担。接口、GamePackage schema 与任务状态机均未改变。
| 复验项 | 结果 |
|---|---|
CI 原命令 dotnet test .../GalGame.GalGameService.Tests.csproj --nologo |
348 / 348 通过 |
| galgame-service 镜像重建与容器重建 | healthy |
| E2E 闭环(注册→提取→构图→计划→游戏包→复习会话→结果→mastery) | PASS,3/3 掌握度 0 → 35 |
| Mongo 持久化实证 | game_jobs / game_packages / game_manifests / game_owners 各 1 文档 |
对全部成员组件(Gateway、Frontend、FileService、AuthService、UserService、 KnowledgeService、GalGameService,以及编排/CI/文档横切面)做了一轮以实证为准的缺陷巡检: 每条发现都要求给出"触发条件 → 错误结果"的完整代码级因果链,严重级发现另经一轮以驳倒为 目标的独立复核,未能复推的一律丢弃。本节记录经复核成立并已修复的问题。所有修复均保持 原有接口、请求响应结构与业务语义不变。
匿名限流可被请求头绕过(Gateway,实测确认)。 app.set('trust proxy', 1) 使
req.ip 无条件采信客户端自带的 X-Forwarded-For,而该头未被 headerSanitizer 剥离,且
Gateway 端口在集成与演示部署中直接对外发布。实测:对 POST /api/v1/auth/sessions 连续
请求时,固定 XFF 的 ratelimit-remaining 正常递减(19→16),每次轮换 XFF 则恒为 19——
即任何人都能靠改一个请求头无限刷新匿名配额,登录/注册/找回密码的爆破节流完全失效。
修复:新增 TRUST_PROXY 配置(gateway/src/config.ts、limits.ts 与 app.ts),
默认不采信 X-Forwarded-For,req.ip 取 socket 对端地址;部署在可信反代之后时显式
声明该代理的地址/网段。frontend/server.mjs 相应地以真实 socket 对端地址覆盖客户端自带
的 X-Forwarded-*,使配置该项后网关拿到的是可信值。已认证路由本就按 userId 计量,不受
影响。
真实 SMTP 凭据入库(AuthService)。 appsettings.Development.json 提交了可用的
126.com 邮箱账号与授权码,且该文件未被 .dockerignore 排除、会随 COPY . . 进入镜像。
修复:文件内改为 CHANGE_ME_* 占位(容器一律经 compose 已支持的 SMTP_* → Email__*
环境变量注入,机制未变),并在 .dockerignore 排除本机开发配置与 launchSettings。
该凭据须视为已泄露:仓库历史仍包含原值,必须在邮箱侧吊销/更换授权码。
| 组件 | 问题 | 修复 |
|---|---|---|
| Frontend | GET /% 等畸形百分号路径使 decodeURIComponent 抛 URIError,异常逸出为未处理拒绝并终止进程(免认证、单请求即可循环打断服务) |
server.mjs 就地捕获并回退原始路径,入口再加 .catch 兜底返回 400 |
| Frontend | 客户端上传中断时不销毁上游腿,网关连接被占用至 180 秒空闲超时 | 监听 request 关闭,未收到响应且请求体未完时销毁上游请求 |
| Frontend | 刷新令牌请求遇到 503/超时也清除本地会话,把仍有效的会话踢下线 | 仅在服务端明确拒绝(401/403 或 AUTH_REQUIRED/TOKEN_EXPIRED)时清除,管理端同理 |
| FileService | 在 Kestrel 监听之前同步 await 全量重解析(含最长 20 分钟/次的 OCR),期间 /healthz、/readyz 不可达,容器被判 unhealthy 并阻塞 gateway、frontend 启动 |
恢复改到 ApplicationStarted 之后的后台任务;索引创建改为可失败可重试,MongoDB 不可用时按契约由 /readyz 返回 503 而不是构造函数抛出 |
| FileService | 超过 multipart 限制的上传返回 400 VALIDATION_ERROR(契约要求 413) | 异常处理器识别 413 状态与 InvalidDataException 链,输出 FILE_TOO_LARGE |
| FileService | 空白文件名触发 ArgumentException → 500 |
入口补 400 校验 |
| FileService | 上传补偿删除复用已取消的请求令牌,客户端断连时 GridFS 内容永久孤儿化 | 补偿改用 CancellationToken.None 并记录清理失败 |
| FileService | 删除与创建解析任务的 check-then-act 竞态(已删除资料复活 / 活动任务下内容被删) | 两处状态迁移改为条件写(Mongo 过滤器带期望状态、内存实现用 CAS),冲突时按既有 404/409 路径处理 |
| FileService | 崩溃窗口留下"任务 QUEUED 但资料未 PROCESSING"或"任务 FAILED 但资料仍 PROCESSING",该资料永远 409、既不能重试也不能删除 | 启动恢复补齐这两类残留态的收敛 |
| AuthService | 注册时凭证与邀请码已提交后,CreateProfileAsync 只捕获 HttpRequestException;超时/客户端断连抛的取消异常绕过补偿,留下孤儿凭证并烧掉 single-use 邀请码 |
该段包入 try/catch 后统一回滚,补偿调用不再使用请求取消令牌 |
| AuthService | Rotate 忽略撤销 UPDATE 的受影响行数,同一刷新令牌可被并发兑换出两个有效会话 |
以"撤销影响 1 行"作为原子抢占,未抢到则返回 null |
| AuthService | 畸形收件地址使 MailboxAddress.Parse 抛出,密码重置返回 500 且残留有效重置令牌 |
改用 TryParse,失败按"发送失败"返回,交由既有补偿删除令牌 |
| UserService | UpdateProfile 忽略受影响行数产生"幻影成功"(资料已被并发删除仍返回 200) |
受影响行数不为 1 时返回 null,上层按契约回 404 |
| UserService | 偏好写入撞外键约束(1452)逸出为 500 | 捕获该错误码返回 null,上层回 404 |
| KnowledgeService | 构建队列是纯内存 Channel 且无启动恢复:重启后持久化为 Running 的任务永远不会被处理,GET 永远返回 RUNNING,幂等键也救不回来 | 新增 ListUnfinishedBuildJobsAsync 与 GraphBuildRecoveryService,启动后把 Queued/Running 任务重新排队(处理器对 Succeeded 短路,重放安全) |
| KnowledgeService | 切分参数越界、DELIMITER 模式缺 delimiter 被 202 受理后异步失败(契约要求同步 400) | CreateGraphBuildCommandHandler 补齐与切分器完全一致的同步校验,后台校验保留为防御层 |
| KnowledgeService | 概念去重合并标签无上限,同概念跨约 18 个以上章节时超出 1-20 校验,整份资料构建确定性失败 | 合并后按同一上限截断 |
| GalGameService | MongoDB 不可达时驱动抛 TimeoutException,catch (MongoException) 接不住,优雅降级失效(每请求阻塞约 30 秒后 500) |
构造函数与 RecoverStaleJobs 同时捕获 TimeoutException |
| GalGameService | 叙事模型响应对 chunked(无 Content-Length)无实际大小上限,可无界缓冲至 OOM | 改为按上限流式读取,超限即中止 |
| GalGameService | 内存模式下 _jobLocks 只增不减 |
淘汰 job 时一并移除锁对象 |
| Gateway | 请求体上限只在有 Content-Length 时生效,chunked 请求可绕过 11 MiB / 10 MB 前置限制 | 阈值抽到 limits.ts 共用,代理阶段对无 Content-Length 的 chunked 请求流式计数,超限返回 413 信封 |
| 复验项 | 结果 |
|---|---|
| Gateway 自动化测试(新增 chunked 上限 2 项、限流分桶 2 项) | 188 / 188 通过 |
| AuthService | 8 / 8 通过 |
| UserService | 32 / 32 通过 |
| KnowledgeService | 105 / 105 通过 |
| GalGameService | 348 / 348 通过 |
FileService dotnet build |
通过,0 warning / 0 error |
Frontend tsc --noEmit 与 Vite production build |
通过 |
| XFF 伪造探针(修复前后对照) | 修复前轮换 XFF 恒得满配额(remaining 恒为 19);修复后按真实对端稳定递减(19→15) |
容器全部重建后在集成栈上的端到端复验:
| 集成复验项(Gateway :5000 / Frontend :5220 / vite dev :5223) | 结果 |
|---|---|
| 全部 12 个容器 | healthy |
畸形百分号路径 /%、/%E0%A4%A、/%zz、/a/% |
均返回 200(SPA 回退),前端进程存活 |
| chunked(无 Content-Length)超限请求 | 413 FILE_TOO_LARGE 信封 |
| 10.5 MB 上传 | 413 FILE_TOO_LARGE(此前多部分限制路径会退化为 400) |
| 空白文件名上传 | 400 VALIDATION_ERROR(此前 500) |
| 正常上传 | 201 |
| E2E 闭环(注册→提取→构图→计划→游戏包→会话→结果→mastery) | PASS,3/3 掌握度 0 → 35 |
| 上传竞态全矩阵 ×12(生产代理 :5220) | PASS,0 mangled |
| 上传竞态全矩阵 ×12(vite dev :5223) | PASS,0 mangled |
| keep-alive 头部与 X-Correlation-Id 存活(双路径) | 全部回显 |
/api/v1/assessment-plans与/api/v1/learning-plans归入generation限流类别,与 contract.md §9 "只有 knowledge-graph-builds 与 game-generations 两个 POST 使用 generation 限流"的枚举不一致。二者确为较昂贵的创建型端点,究竟改代码还是改契约属 PM 决策,此处仅记录。- AuthService 的 time-window 邀请码在未指定 maxUses 时被隐式赋予 10 次上限,契约对该类型 只描述了时间窗。同样属语义待澄清项,未擅自更改既有行为。
本轮把 ReciteHelper 的复习资料库、题库、练习、试卷、项目包与资源中心作为产品主线迁入 四层 PracticeService;GalReview 的知识图谱、SM-2 和故事生成作为 StudyProject 下的复习 能力复用。测试使用本机完整 ReciteHelper 运行时资源,未修改源仓库中的用户工作区改动。
| 验证项 | 结果 |
|---|---|
| PracticeService solution build | PASS,0 warning / 0 error |
| PracticeService 单元测试 | PASS,15 / 15 |
其中旧 .rhproj 五题型、.qzwlp round-trip、ZIP traversal |
PASS |
| 其中资料生成出处、生成幂等冲突、整卷缺答案不猜测 | PASS |
| 其中 MongoDB Guid 表示 | PASS,UUID Standard |
| 其中项目 material 所有权 | PASS,拒绝他人资料并接受本人 READY 文本 |
| KnowledgeService PracticeService allowlist 目标测试 | PASS,12 / 12 |
| Gateway 全量 Vitest | PASS,15 files,199 / 199 |
| Gateway 项目包请求体边界 | PASS,50 MiB 文件 + 1 MiB multipart;资料上传原限制未变 |
| FileService build(含 PPTX/MHTML parser) | PASS,0 warning / 0 error |
| Frontend TypeScript + Vite production build | PASS;仅保留既有 KnowledgeDag >500 kB chunk 警告 |
docker compose -f compose.integration.yaml config --quiet |
PASS |
| PracticeService 容器镜像 build | PASS;14 项资源门禁通过后完成 Linux publish |
模型关键资产校验值:SBERT 994a58868f7abacacbf2192aa0aae8f56da8c4505dbde2740c861b24426ede6b,
XGBoost 53b563e2df2c6026f7a996b4d8f63e83c63bbf64d1dde5e03a3c7f9dbf688ea0,
vocab.txt 45bbac6b341c319adc98a532532882e91a9cefc0329aa57bac9ae761c27b291c。
本节初次记录时尚未运行全栈容器;现已由第 27 节的真实 Gateway 全链冒烟结果取代。普通题库 复习、故事复习、SM-2 证据回写和 MySQL credits 结算均已有持久化集成结果。
本轮取消注册邀请码,新增 CreditService,并把 Practice 题目生成与 GalGame 故事生成接入 “生成前预授权、成功后按实际使用量结算、失败释放”的同一计费链路。旧用户通过幂等惰性 建账获得一次初始额度,不执行 Auth/Credit 跨数据库扫描。面向用户的页面只显示 credits, 没有展示内部 token 换算规则。
| 验证项 | 结果 |
|---|---|
| CreditService 四层 solution build + tests | PASS,6 / 6 |
| 初始额度幂等、旧用户惰性建账、兑换单次使用 | PASS |
| 预授权不足 402、实际结算、失败释放 | PASS |
| AuthService 无邀请码注册与 credits 建账补偿 | PASS,11 / 11 |
| PracticeService 生成计费与现有功能 | PASS,15 / 15 |
| GalGameService usage 累计、计费与现有功能 | PASS,362 / 362 |
| Gateway Credit 路由、配置与全量 Vitest | PASS,15 files,203 / 203 |
| Frontend 注册、设置页、管理员兑换码与不足跳转 production build | PASS;仅既有 KnowledgeDag chunk 警告 |
docker compose -f compose.integration.yaml config --quiet |
PASS |
| CreditService / 全栈容器镜像 build 与 MySQL E2E | PASS;真实预授权、结算、兑换及故事生成扣费见第 27 节 |
安全检查结论:兑换码持久化只保存 SHA-256 摘要,完整明文仅在批量创建响应返回;管理员列表
只返回掩码;余额使用整数内部单位;operationId 负责计费幂等;Gateway 按目标服务密钥转发。
购买页固定为 https://pay.ldxp.cn/shop/7CX09W5E,只有用户在 credits 不足提示中确认后跳转。
第 23.4 节关于 time-window 邀请码的待澄清记录已被本轮决策取代:邀请码接口已撤下且注册不 再读取邀请码。遗留表和 repository 仅作迁移兼容,不是可调用能力。
本轮将 backend/PracticeService/Resources 从源码分发范围中移除。仓库保留单文件下载器及
14 文件的路径、长度与 SHA-256 清单;下载器内置的凭据由所有者确认为只能读取和列举
20277-gal-res,不能访问其他储桶或执行写入。PracticeService Dockerfile 在 publish 前检查
关键模型、tokenizer 与词表是否已经恢复。
| 验证项 | 结果 |
|---|---|
download-practice-resources.ps1 PowerShell AST parser |
PASS,0 syntax error |
| 缺少 AWS CLI 的失败路径 | PASS,联网和文件同步前明确报错 |
Resources Git ignore |
PASS,模型与 vocab.txt 均命中精确规则 |
| 下载脚本可跟踪状态 | PASS,不命中 .gitignore,可随仓库提交 |
| 下载凭据权限 | OWNER-CONFIRMED,仅 20277-gal-res 读取/列举;本轮未独立审计云端 IAM 策略 |
仓库外恢复备份对 resources.manifest.json |
PASS,14 / 14 路径、长度、SHA-256 一致 |
| OSCA S3 真实列举、同步与下载后复验 | NOT RUN:当前环境未安装 AWS CLI v2 |
| PracticeService 容器缺失门禁 | PASS;关键模型/词表存在时镜像构建通过 |
| Git 中断恢复 | PASS,本地 main 纯 fast-forward 到远端 feat: Integrate recite helper,6 个 stash 冲突已逐项解决 |
| Git 暂存与冲突状态 | PASS,unmerged 0、staged 0、冲突标记 0 |
| 仓库工作树资源残留 | PASS;冒烟临时恢复的 14 个文件已再次移到仓库外,源目录不存在 |
未把“脚本语法通过”当作云端下载成功。开发者或部署人员必须安装 AWS CLI v2,运行仓库内
下载脚本并看到 manifest verification: passed 后,才能继续开发、测试或执行
docker compose build。默认储桶根目录与云端对象布局若不一致,应显式使用
-RemotePrefix Resources,不得通过关闭哈希验证掩盖路径错误。
中断恢复时还确认:Git 检出的 vocab.txt 因文本换行转换从清单要求的 109,540 字节变为
130,668 字节,原始 SHA-256 随之变化;仓库外备份仍与清单一致。这进一步证明模型、词表和
分词资源不能通过 Git 分发。当前提交历史仍包含此前误提交的资源 blob;普通删除提交只能从
新版本工作树移除它们,若要从远端历史和仓库存储中彻底清除,必须另行确认历史重写与强制推送。
本轮在 compose.integration.yaml 的真实持久化栈上,以全新普通用户经 Gateway 完成两种复习
方式。题库复习和故事复习共享同一个 material、graph 与 mastery,但各自创建一次性评估计划
快照;完成后的计划按领域规则关闭,不重复接收第二份学习证据。本轮按用户最新要求不修改 UI。
| 验证项 | 结果 |
|---|---|
| Compose 配置与全部应用镜像构建 | PASS |
| 15 个容器(3 MySQL、MongoDB、Neo4j、8 个领域/边界服务、Gateway、Frontend) | healthy |
| 六个 .NET 服务测试 | PASS,Auth 11 + User 32 + Credit 6 + Practice 15 + Knowledge 109 + GalGame 362 = 535 |
| RenderService 镜像内测试 | PASS,22 passed / 1 skipped |
Gateway :5000/readyz 与 Frontend :5120 |
HTTP 200 |
| 注册、登录、用户资料与初始额度 | PASS,新用户精确获得 1 credit |
| TXT 上传、Mongo 持久化、规范化提取 | PASS |
| Neo4j 知识图谱构建与评估计划 | PASS |
| StudyProject + PlanGraph 题目生成 | PASS,生成 5 题并人工状态推进至 READY |
| SMART_REVIEW 作答 | PASS,首次写入、答案重放去重、完成重放去重 |
| 题目帮助 | PASS,解释 grounded 且包含资料出处匹配 |
| Practice credits | PASS,1.00000 -> 0.99790,无 held 余额 |
| 管理员批量制码与普通用户兑换 | PASS,1 个 3-credit 兑换码只走正式 API |
| GalGame credits 不足路径 | PASS,HTTP 402 CREDITS_INSUFFICIENT,含购买 URL;失败任务可从 Mongo 读回 |
| 故事生成及实际结算 | PASS,绑定新的不可变 PlanGraph 快照 |
| Render 事件、进度、结果 | PASS,事件重投去重、进度重放幂等、结果 ACCEPTED -> DUPLICATE |
| Knowledge evidence / SM-2 回写 | PASS,故事中所有已答知识点均生成递增版本的 mastery record |
| 最终 credits | PASS,3.92011,held = 0 |
| 冒烟临时资源清理 | PASS,14 文件 / 109,930,654 bytes 移至 D:\Projects\GalReview-resource-recovery-20260808-1930\post-smoke-Resources |
最终通过样本:user 9636baa5-3fee-4ab2-8d92-36e1c6e04bb1,material
3d37eca2-57ed-4637-9a4c-4313fbff988f,graph 92004435-43a5-4378-9330-96e465f26a95,
Practice session 4dd4a86d-5a4f-499f-bcf9-a24df615d93f,GamePackage
b84a90a5-c96d-4454-a505-5ccb0a7b4fbc,Render session
218d2e94-9ef2-457d-a4e3-2017ac7f9c51。
冒烟首先暴露并随后修复了三个仅在真实持久化链路出现的问题:Practice 聚合无法忽略 MongoDB
自动 _id;MySqlConnector 将 CHAR(36) 读取为 Guid 时 Credit 持久层仍调用 GetString;
GalGame 在 credits 不足任务中把 JsonElement details 直接写入 Mongo。修复后分别重跑目标测试、
目标镜像和完整 Gateway 链路。故障阶段留下的一笔已知预授权
400e04bc-febe-46d6-84a7-40e1c4fd1072 已通过正式 release 接口恢复为 RELEASED。
本轮按 ReciteHelper 原始代码、用户手册和领域模型重新冻结业务边界:StudyProject 是唯一用户可见
的顶层项目,在界面称为“研习册”;藏书阁只负责资料与识网;章节练习、智能复习、模拟试卷和
故事回响都从同一册内发起。故事不再建立独立项目,也不再从全局 /review 恢复旧计划。
| 验证项 | 结果 |
|---|---|
| PracticeService 测试 | PASS,21 / 21 |
KnowledgeService sm2-graph-v2 测试 |
PASS,111 / 111 |
| RenderService TypeScript + Vitest | PASS,23 / 23 |
| Frontend TypeScript + Vite production build | PASS,1401 modules;仅既有 KnowledgeDag chunk 警告 |
| PracticeService / KnowledgeService / Frontend 镜像 | PASS |
| Compose 运行态 | PASS,15 个容器全部 healthy |
Gateway :5000/readyz / Frontend :5120/healthz |
HTTP 200 / 200 |
| Practice 资源清单 | 构建前 14 / 14 哈希通过;构建后仓库内 Resources 再次移至仓库外 |
全新用户经 Gateway 完成 TXT 上传、文字提取、三章/三点知识图谱、研习册、评估计划、自动成题、 人工确认入库、SMART_REVIEW、模拟试卷与 mastery 读取。结果如下:
| 验证项 | 结果 |
|---|---|
| 自动成题 | SUCCEEDED,生成 15 题 |
| 知识点贴签 | 15 / 15 非空;没有按序号轮转绑定 |
| SMART_REVIEW | 3 题对应 3 个不同 PlanGraph 目标,COMPLETED |
| 模拟试卷 | 3 题对应 3 个不同 PlanGraph 目标,COMPLETED |
| mastery | 两类答题都经同一 KnowledgeService evidence 入口更新 |
| Practice credits | 1.00000 -> 0.99600,held 为 0 |
样本:material 7af9920d-6881-4e69-b782-23b0bc2d7f5a,graph
7695e007-3a74-4d01-8c76-100c00a4ce26,StudyProject
f1c93fe2-40d8-4ae1-9760-1873f3ea02f4。
同一用户、同一 graph 的首次故事生成按预估正确返回 402 CREDITS_INSUFFICIENT:余额
0.996、最低预估 1.63827,details 包含固定购买地址。随后只通过管理员正式批量制码 API
生成一枚 3-credit 兑换码,并由普通用户正式兑换;未直接修改数据库余额。
兑换后故事生成 SUCCEEDED,产出 5 个场景;Render 会话提交 3 道题,知识点 3 / 3 唯一,
事件接收 3 条,结果首次 ACCEPTED、同载荷重放 DUPLICATE。三个相同 graph 的 mastery
版本均再次递增,证明普通答题、试卷与故事回响共享掌握记录。最终 credits 3.92482,held=0。
样本 GamePackage b275de54-cc39-4b60-b797-db269848aeab,Render session
d0ba10fe-87c4-4dc8-b1a0-6f6e40790db8。
终检发现旧 sm2-graph-v1 尚有 score*0.65 + observed*0.35,计划侧还额外假定 90% 到期保持率;
另有“答对上层点便给前置点最多增加 5 分”的间接证据。三者都不是原始 SM-2 的一部分,且后者
没有前置点的真实作答证据。本轮升级后:
score=quality/5*100,只解释该知识点最近一次直接观测;- SM-2 的 easiness、repetitions、interval、lapses 与 nextReviewAt 继续作为唯一纵向调度状态;
lastReviewedAt=null、repetitions=0或now>=nextReviewAt才进入到期集合;- 图谱只在该集合上通过最大乘积路径与单调次模覆盖选择题目,不与 SM-2 按百分比相加;
- mastery 只更新题目明确绑定的知识点,不给未作答前置点或后继点推断加减分;
- 没有到期点时只返回一个稳定的探测题。
新 KnowledgeService 镜像部署后,以既有三点 graph 创建计划时确实只得到 1 个探测目标;通过
Practice SMART 会话正确作答后,目标 score 变为 100,reason 为
DIRECT:ASSESSMENT:quality=5。Gateway 与 Frontend 仍为 HTTP 200,仓库内资源目录不存在。
- 侧栏顺序调整为“起点 → 藏书阁 → 研习册 → 识网 → 我的”;首页快捷入口同样先展示藏书阁,
没有旧册时主按钮进入
/materials,不再绕过资料解析与识网直接进入立册页。 /projects的“立册 / 建立经典复习项目”保留在普通文档流中,CSS 明确为.practice-project-create { position: static; };题目编辑区原有 sticky 行为不受影响。- Frontend TypeScript + Vite production build 通过,1401 modules;仅保留既有 KnowledgeDag
大 chunk 警告。Frontend 镜像重新构建并替换后
/healthz为 HTTP 200,Compose 15 个容器 全部 healthy。按实测要求,验证后未停止 Docker。
实测用户项目 4ae99304-7751-497d-8ca1-684070cbf2af 已绑定 graph,但 MongoDB 中题目数为 0,
且没有题目生成任务。旧前端因此用 !ready 同时禁用章节练习、智能复习与模拟试卷;全局
.button:disabled { cursor: wait; } 又把业务前置条件错误表现为进行中状态。
修正后,无 READY 题时页面显示“题库待成”及“前往成题”入口;三个按钮保持可点击,并在任何
PlanGraph 或试卷创建请求发出前由 requireReadyQuestions() 返回成题、补签和核对入库指引。
普通禁用按钮使用 not-allowed,只有显式 aria-busy=true 的真实进行中按钮使用等待光标。
“循网”文案也改为 SM-2 到期集合后再按图谱依赖覆盖,不再描述为两种遗忘风险的混合。
Frontend TypeScript + Vite production build 通过,1401 modules;镜像重新构建并替换后
/healthz 为 HTTP 200,Compose 15 个容器全部 healthy。Docker 保持运行供人工实测。
重新核对 ReciteHelper.Infrastructure/Services/ProjectCreationService.cs 与中文用户手册后,确认经典
创建流程本来就在一次操作中完成读取文本、知识/题目生成、章节整理和知识库构建;没有生成任何
题目时创建会明确失败。首次生成包含单选、填空、名词解释和简答,判断题只来自导入套卷。
该节首次验证时仍采用了已废止的 material-scoped graph 顺序;第 32 节已按最新边界重新完成真实
全链复验。当前产品级编排为:创建 graphId=null 的 StudyProject → 以本册 projectId 建图并绑定
→ 读取本册 graph 全部章节 → 创建 OPEN assessment plan → 自动成题 → 进入本册。当前
recite-question-v1 只有在答案、唯一知识点和精确
SourceReference 同时成立时才创建题目,因此成功结果直接 READY;独立成题区只负责追加或失败重试。
credits 不足或生成失败时保留已创建的册并显示恢复说明,不重复建立第二册。
修复前已经存在的空册不在页面加载时自动扣费;其成题按钮明确显示“恢复自动成题”,由用户确认后
复用同一生成链路补齐 READY 题目。
验证结果:
| 验证项 | 结果 |
|---|---|
| PracticeService tests | PASS,21 / 21 |
| Frontend production build | PASS,1401 modules;仅既有 KnowledgeDag chunk 警告 |
| 真实 Gateway 立册编排 | PASS,project 7a2e78c5-a0ce-45d1-bd27-692234990b38 |
| 自动成题任务 | SUCCEEDED,4 题 |
| READY / 知识点绑定 | 4 / 4;未绑定 0 |
| 首次题型 | 单选、填空、名词解释、简答;判断题 0 |
| 自动成题后 SMART_REVIEW | PASS,session 99808d97-eb23-4a2c-b7cd-935069447115,1 题 ACTIVE |
| Gateway / Frontend | HTTP 200 / 200 |
| Compose | 15 个容器全部 healthy,按人工实测要求保持运行 |
| Practice Resources | 构建时从仓库外临时恢复 14 文件;构建后已移回,仓库内目录不存在 |
本轮纠正聚合边界:藏书阁的 material 只是研习册图谱的来源,KnowledgeGraph、版本序列、知识点
身份与 mastery 均归 StudyProject。新册先以 graphId=null 创建,KnowledgeService 再通过 Gateway
调用 PracticeService 的受信 INTERNAL 查询,核验 studyProjectId + ownerUserId + materialId 后构图;
PracticeService 绑定时反向核验图谱作用域。旧 material-scoped 图只保留按 graphId 的兼容读取,不得挂入新册。
| 验证项 | 结果 |
|---|---|
| KnowledgeService tests | PASS,112 / 112 |
| PracticeService tests | PASS,24 / 24;包含跨册拒绝、项目指纹隔离与首批题型轮转回归 |
| Gateway Vitest / TypeScript | PASS,203 / 203;TypeScript build 通过 |
| Frontend production build | PASS,1401 modules;仅既有 KnowledgeDag 大 chunk 警告 |
knowledge-service / practice-service / gateway / frontend 镜像 |
PASS;按同一发布单元构建并替换 |
| Compose | PASS,15 个容器全部 healthy;Gateway /readyz、Frontend /healthz 均为 HTTP 200 |
| Practice Resources | PASS,镜像构建时临时恢复 14 文件;构建后仓库内 Resources 不存在,109,930,654 bytes 仍在仓库外恢复目录 |
使用全新普通用户上传并解析同一 TXT 后,先后创建甲、乙两本 graphId=null 研习册,再分别以相同
material 构图。两次构图均成功,但 graphId、4 个知识点 ID 和各自初始 mastery 记录完全隔离;
按 studyProjectId 查询图谱时两册各只返回自己的 1 张图。把甲册图谱 PATCH 到乙册得到
409 GRAPH_OUTSIDE_PROJECT_SCOPE,拒绝后乙册版本未被占用,随后可正常绑定乙册自己的图谱。
| 对象 | 研习册 | 图谱 | 知识点 / mastery |
|---|---|---|---|
| 甲册 | be9148e2-1303-4c3d-b0ba-394b90a3d859 |
87667298-4dbc-4d11-b457-4dd2745c8066 |
4 / 4 |
| 乙册 | a5c4e2c7-cdd2-455a-9501-d246db0eea8d |
e717d8af-b3b9-4270-af64-63ccc834c0bd |
4 / 4 |
样本 user ff24f386-08a4-424f-9f74-f172b1d7dfbb,material
fd7dc177-0624-4ad8-aadd-25ad9c08dc62;两图共享知识点 ID 数为 0。
首次部署冒烟同时发现:生成器原先按“题型外层、知识点内层”遍历,四个知识点、四道目标题会在 第一种题型达到数量上限,实际得到 4 道单选。修复后改为题型与知识点同步轮转;在不伪造答案、 干扰项或出处的前提下,题数足够时先覆盖全部请求题型,再重复任一题型。
甲册基于本册 graph 创建 assessment plan 0f04d560-08eb-417e-b1bf-4ff6cebe5f07 后,首次自动成题
得到 4 道 READY 题,精确覆盖单选、填空、名词解释、简答,4 / 4 带本册知识点标签,未绑定为 0;
随后成功创建 SMART_REVIEW session d024a4d0-a808-42d2-8757-0a5f7d3a6f01 并取得 1 道计划内题。
credits 从 1.00000 结算为 0.99776,held 为 0。最终容器保持运行,供人工实测。
本节只记录本轮实际重新执行的证据,不沿用第 31/32 节的 v1 结论。测试数据统一使用
recite-v2-* 前缀;最终完整样本用户为 1704abfc-25f7-4e6d-a9ca-8dfffc9b3276。浏览器实际路径
均经公开 Gateway API 完成注册、multipart 上传、解析、先立册、按 studyProjectId + materialId
构图、绑定、读取全部章节、创建 LEARNING PlanGraph,并在请求中省略 targetCount 执行
recite-question-v2。内部 extracted-text 接口只在前置定位阶段使用,最终结果未绕过公开归属约束。
| 验证项 | 结果 |
|---|---|
| PracticeService | PASS,36 / 36,0 skipped |
| KnowledgeService | PASS,112 / 112,0 skipped |
| Gateway Vitest | PASS,15 files、203 / 203;TypeScript build PASS |
| Frontend | PASS,TypeScript + Vite,1401 modules;保留既有 KnowledgeDag 大 chunk 警告 |
| PracticeService API build | PASS,0 warning / 0 error |
Practice 新增/复验用例覆盖:DeepSeek/OpenAI-compatible JSON 与 usage.total_tokens 累计;非 JSON、
缺 usage、非法 pointId、quote 不在原文、答案不受 quote 支持、单选多答案、第二遍回验拒绝;扁平化
PDF 页内题号;农业三道坏题;微生物“名词解释/大题/重要知识点”;答案内部 1./2./3. 不截断;
以及当前答案必须在更早的章节/题型边界停止,不能吞入后续章节。
| 项目 | 农业生态学.pdf | 微生物学B.pdf |
|---|---|---|
| 规范化文本 | 26,139 UTF-16 chars / 20 source spans | 26,513 UTF-16 chars / 39 source spans |
| 本册图谱章节 / Learning Plan 点 | 7 / 243 | 4 / 15 |
| 自动题数 | 189(不是 30) | 240(不是 30) |
| READY / DRAFT | 172 / 17 | 9 / 231 |
| 已绑定唯一知识点数 | 161 | 7 |
| READY 无 pointId | 0 | 0 |
| SourceReference 校验 | 189 / 189 offset + checksum 通过 | 240 / 240 offset + checksum 通过 |
| 水印/页脚泄漏 | 0 | 0 |
| 跨章节答案泄漏 | 0 | 0 |
农业三道回归题均保留原问题和真实答案:“生态平衡的基本特征有哪些?”保留四个分点;“循环农业
坚持的‘4R原则’是什么?”保留适量化、再循环、再利用、可控化;“解读中国生态农业原理之绿色
发展原理。”的答案与题干不同并保留三个原文分点。修复前,部分带 【参考答案】 的题会跨越
“综合题/下一章”吞入远处内容;结束边界改为“下一题标记与结构边界的较早者”后,答案内部编号
仍完整,真实样本的章节/题型栏泄漏为 0。
微生物资料确认为半结构化讲义而非标准题库。v2 直接忠实提取了“术语:定义”和“问题标题:分点
答案”,没有把它们统一重写成模板。该图谱实际只产生 15 个 Learning Plan 点,故 231 道原文问答
无法唯一贴签并按契约保留 DRAFT,9 道通过唯一绑定后 READY;这不是可宣称“240 道可练习题”的
结果。当前本地 DEEPSEEK_API_KEY 未配置,且该样本的半结构化范围覆盖了可成题区间,因此普通正文的真实
DeepSeek + 第二遍来源约束回验未发生;该分支只由模拟 provider 单元测试验证,不能据此宣称线上模型质量已验收。
最终完整样本 ID:
| 对象 | 农业生态学 | 微生物学B |
|---|---|---|
| material | be87269a-47b0-4849-befd-49f6208b1d82 |
35e774a5-b8eb-4ddb-8973-b5a1c9128da6 |
| ingestion job | 05acf178-d4f8-444c-a7c4-7ee8d6326d86 |
dce867f3-90fc-4a1b-8e9e-41815c93f914 |
| StudyProject | cf80e37a-4249-42aa-8f23-0fa5a959e9dc |
ba3ad4e4-425e-452f-b6c5-2497ae9ee4db |
| graph build / graph | 5f148584-0497-4694-9959-02ed63b5ceba / b9bc3e62-201f-42cb-8b00-4c2c0a07c3a0 |
87463f3b-c674-4e84-ae42-f7020b6ec5c0 / ce6cc834-4d4e-444f-a24f-3f7095b72905 |
| Learning Plan | 234195d1-671f-4500-973a-126fbaa6528a |
e1417683-a1d0-43bf-9035-8690a57994a2 |
| generation job | c5fa536c-cb3b-44cd-909f-9daf76095802 |
f1cfa24f-772a-48b6-9407-5795d24a34ba |
为确定性复现余额不足,另建 recite-v2-credit-1786238134851 测试册并显式请求 1000 题作为
预授权边界探针;首次请求经 Gateway 返回 402 CREDITS_INSUFFICIENT,没有启动生成。随后经公开
管理员登录和 /admin/credit-codes/batches 创建 20 credits 兑换码,再由用户通过
/credits/redemptions 兑换;重试生成 PARTIALLY_SUCCEEDED、创建 240 题,held 最终为 0,余额
20.99997。完整兑换码未写入日志或文档。对应 project 8b67786a-e840-4d3c-a121-94895ae77f38,
graph build e55d0d9d-8916-407b-986e-80e6d4150bea,graph
dc4079f2-80db-4d18-b664-9b1eeb49b186,Learning Plan
f5ca19dd-d11b-48c2-a59f-5546ca5d8ebf,credit code ID
a9ee3158-8fd2-4cdd-82e7-9981d0da4312,generation job
5cf29a00-a416-4385-96b5-eedba2670046。
同一农业研习册随后完成三条公开 HTTP 闭环:
- SMART_REVIEW:plan
9353c225-e24e-4bb8-9110-464c11f73dda,session97ad35b7-e23a-4066-948f-2a195f93a94c;正确作答后目标 mastery score=100,nextReviewAt=2026-08-10T01:18:41.4076626Z。 - 模拟试卷:plan
868ad704-5bd3-4430-8ebe-7a727cb1b456,paper22385c18-7661-4af3-955c-38918202e057,session2d789afc-ac20-43f9-93fb-100a497f8aca,答题与 completion 成功。 - 故事回响:plan
cbb36522-bcaa-4004-9827-f80de5a4e6d2,generationffd90c40-58ab-4619-bd7b-c88f6f6ac04b,package9d8904cf-cf46-42e8-a189-3c9acd4c5997,Render session06dfac8b-b295-4c3e-b409-76a22f6758ab,结果674ae756-f3e9-4b3a-9dec-06c5c444fec2为ACCEPTED;1 条游戏题证据写回同一 mastery。
脚本前两次分别因遗漏图谱构建 Idempotency-Key 和 Gateway 生成限流失败;修复脚本后未关闭
幂等/限流门禁。为便于精确清理,失败样本记录如下:
recite-v2-1786237468840:user838f9f31-50b3-4be9-933a-4bd2f2cc8c0f;materialsd40256b6-bb88-41d2-b834-46f384e70755、5d5f691c-b51a-41e0-b3c6-0958f1ff4bd3;空图 project6ecab00f-7167-44e4-a03c-1f49a3ba4ffc。recite-v2-1786237502214:user2b990d6d-8740-47b6-898b-86d2a5872e8b;materials1b4850fb-5e52-4bf7-b9fc-df58d405399c、011a97d2-53e9-4aa2-b8f4-b2a2b0c3faa0;projects/graphs6c19a884-d99c-43a5-89c3-ba4bb7b59da4/67e95939-3003-4bce-965f-bcc225d3b8dd,11798cdf-9c1c-47fb-a421-0ec9dc3455cc/4dcd532c-64a7-4e3a-a473-46fa38e5d5da。
本机当前没有 docker.exe、Docker 服务或 Docker Desktop 进程,题设中的 D 盘资源恢复目录也不存在,
所以本轮不能重建或声称“15 个容器 healthy”。8 个后端服务、Gateway 与 Frontend 使用本地启动栈,
其 /healthz、/readyz 均为 HTTP 200。Practice 14 个资源文件在本地出现后已按 manifest 对
109,930,654 bytes 完成 14 / 14 长度与 SHA-256 校验;Docker 镜像门禁未运行。OCR、真实 DeepSeek
普通正文质量和容器态回归仍未验证,结论必须与上面的单元测试/本地 HTTP 冒烟分开。
本节是第 33 节之后的增量复核,不改写第 33 节的历史数据。开始时 main 与 origin/main 同步、工作树
无暂存/未暂存/冲突;最新提交主要覆盖生产部署与 Gateway/Render 安全修复,但第 33.2 节已明确记录的
“微生物 240 题仅 9 READY”仍未解决。审查定位到三条独立原因:
- PDFPig 会把“第一章…名词解释1.”压成无空格单行,v2 章节、题型栏和题号规则均要求空格或普通 左边界,导致 26,513 字符退化为 4 个固定窗口和 15 个点;
- 顶层术语
6.2μm质粒的标题以数字开头,旧题号规则把6.当成小数前缀并使后续序列全部丢失; - Practice 将题干命中和答案中顺带出现的同长度术语一起判为歧义,且普通长文的单行 PDF 不会按 1400 字符继续切片,可能把整份资料只规划为一个最多 8 题的模型分片。
修复后版本为 chapter-segmenter-v3 / knowledge-extractor-v3。v3 保留绪论,允许表格末词与下一章
粘连但拒绝“见/参见第 X 章”引用,识别裸题型栏和数字起始术语,合并结构块之外的普通段落并移除已确认
的页眉水印。Practice 的唯一绑定改为题干精确相等优先的离散顺序,未引入相似度或混合权重;无唯一
结果仍保留 DRAFT。普通正文按完整句边界拆成至多 1400 UTF-16 字符的连续证据块。
| 验证项 | 结果 |
|---|---|
| KnowledgeService | PASS,115 / 115,0 skipped(原 112,新增紧凑章节、裸题型栏、数字起始术语) |
| PracticeService | PASS,38 / 38,0 skipped(原 36,新增单行长文多分片与题干精确绑定) |
| Gateway | PASS,15 files、203 / 203;TypeScript build PASS(本轮改动前基线,Gateway 源码未改) |
| Frontend | PASS,TypeScript + Vite,1401 modules;仅保留既有 KnowledgeDag 大 chunk 警告 |
| CreditService | PASS,6 / 6,0 skipped |
| FileService | dotnet test 返回 0;该 solution 当前没有独立测试项目 |
输入为 D:\AppData\微生物学B.pdf(39 页)。临时探针直接复用 FileService 的 PDFPig、
KnowledgeService 的生产切分/抽取器和 PracticeService 的生产 ReciteQuestionGenerator;探针与 PNG
均已删除,未写数据库、未创建可复用 ID,也未把资料内容加入 Git。
| 项目 | v2(第 33.2 节) | v3 本轮 |
|---|---|---|
| 规范化文本 | 26,513 UTF-16 chars | 26,513 UTF-16 chars |
| 章节 | 4 个固定窗口 | 10 个结构章节;原文第五章标为“略”,不伪造空章 |
| 图谱点 | 15 | 210 |
| 自动题数 | 240 | 240 |
| READY / DRAFT | 9 / 231 | 220 / 20 |
| 已绑定唯一知识点数 | 7 | 184 |
图谱标题/摘要残留 2019级植科 水印 |
未专项计数 | 0 |
20 道 DRAFT 中,14 道无同名/包含知识点,6 道仍有多个同长度候选;门禁没有为追求数量而放宽。
本轮没有配置 DEEPSEEK_API_KEY,因此普通正文模型生成仍只验证了分片规划和模拟 provider 测试,不能
宣称真实模型质量已验收。Docker CLI 可见但 Linux engine pipe 不可用,未重建容器,也未重复第 33.3
节的公开 HTTP、credits、SM-2、试卷和故事回响链;部署复验必须新建用户/资料/研习册,不依赖第 33 节
的历史 ID。
本节回答“当前题目生成是否经过研究证明有效”。结论分成两个不能混淆的层级:
- 研究与实现对齐:VERIFIED。 答案原子先行、生成后第二遍来源约束回验、逐字证据、one-best-answer 布尔门禁和主动提取复习均能找到直接的论文或专业指南依据,生产代码确实实现了对应职责;
- 千知万理当前组合系统的题目质量与学习效果:NOT VALIDATED。 现有论文没有测试本项目的中文 农学/社科/人文资料、当前 provider/model/prompt、知识图谱绑定和门禁组合。本轮也没有执行领域专家 盲评或延迟学习对照实验,因而不得写成“已经研究证明有效”。
| 原始来源 | 原来源实际支持的结论 | 生产实现映射 | 不能外推的结论 |
|---|---|---|---|
| Answer-focused and Position-aware Neural Question Generation | 在论文模型和数据集上,显式答案焦点/位置改进 QG | ReciteQuestionGenerator 先识别可独立作答的答案原子,再写题;答案必须受 sourceQuote 支持 |
未复现该模型或其实验,不能据此证明当前中文生成质量 |
| Synthetic QA Corpora Generation with Roundtrip Consistency | 生成与答案抽取回路可筛选合成 QA,并在论文任务上改进结果 | 第一次生成后进行分离的第二次来源约束作答,恢复答案集合必须与标准答案严格相等 | 第二次调用可使用同一 provider/model,不是独立模型或无偏评价者 |
| Putting the Horse before the Cart | 在 SQuAD 条件下 generator-evaluator 框架的自动与人工评价优于对照 | 生成职责与回验职责分离 | 当前实现没有复现论文架构、奖励函数或实验结果 |
| QGEval | 人工评价是自动指标的金标准;七个维度的自动指标与人工判断对齐不足 | 拟用流畅、清晰、简洁、相关、一致、可回答、答案一致作为人工黄金集维度 | 自动门禁通过不能替代人工题目质量验收 |
| Evaluating Rewards for Question Generation Models | 模型会提高被优化的奖励,但奖励可能与人工判断错位并被利用 | 不引入一个合成质量分,不用任意比例混合自动指标 | 不能以内部分数或题量证明质量 |
| NBME Item-Writing Guide | 专业命题指南要求聚焦 lead-in、同质可信选项并清除形式线索 | 单选恰有 A-D、一个最佳答案、来源不支持干扰项;不可靠就不生成 | 指南不是针对本项目或中文通识材料的随机对照试验 |
| Test-enhanced learning;Retrieval practice and concept mapping | 在各自实验条件下,主动提取改善延迟保持或概念学习 | 用户作答、反馈、SM-2 后续调度与掌握度回写 | 只支持复习形态,不证明机器生成题达到人工题质量 |
ChunkCharacters=1400、每片至多 8 题、temperature=0.1、max_tokens=6000 分别控制连续证据边界、
单次输出规模、采样稳定性和响应上限。它们有边界/回归测试和成本约束,但没有上述论文给出的“最优题目
质量”证据;文档已禁止把这些值宣传为科学阈值。知识点绑定采用离散、可解释的优先顺序并在歧义时
进入 DRAFT,没有设置 40%/60% 等混合权重。
生产提示词中“独立核验器”已校正为“与生成调用分离的第二次来源约束回验”。该修改保留重新作答、 逐字 evidence 约束和答案集合严格一致三个行为,仅移除对同一模型第二次调用的过度表述。
| 项目 | 结果 |
|---|---|
| 生产实现—研究职责逐项复核 | PASS |
| PracticeService 回归 | PASS,38 / 38,0 skipped |
git diff --check |
PASS |
| 生产 DeepSeek 普通正文样本 | NOT RUN,本轮没有可用 API key |
| 中文领域专家盲评、评审一致性与人工题/ReciteHelper 对照 | NOT RUN |
| 关键组件消融 | NOT RUN |
| 预注册延迟学习效果对照实验 | NOT RUN |
因此本轮允许的最终表述只有:“题目生成不是随意拼装;关键设计有原始研究/专业指南依据,且生产实现
和确定性技术门禁已经核对与回归。”如果要升级为“当前算法经验证有效”,必须先完成
docs/contract.md §14.3.1 的版本化中文资料集、盲评、对照、消融和(涉及学习效果声明时)延迟测验;
样本量、主要终点与判定界值必须在看结果前由功效分析和人工基线确定。
用户可见的两个症状均已在生产路径定位:
请解释“第二性比”。之类名词解释题虽然已有答案和来源,却可能因图谱标题包含别名/限定词而无法 通过旧的“标题必须被完整包含在题干”规则,题目停在DRAFT + knowledgePointId=null;历史草稿也没有 自动重跑绑定,只能在前端逐题选择。SelectSmartQuestions先截取前questionCount个计划点,只要其中任何一点没有 READY 题就抛出QUESTION_COVERAGE_GAP。因此“计划部分覆盖”被错误实现为“必须全部覆盖”,一个缺口会阻断整次会话。
- Domain 新增同一套离散绑定规则:名词解释焦点精确标题、精确标签、题干最长唯一标题、唯一标题包含 焦点、题目与知识点在同一资料中的唯一来源区间重叠、来源最长唯一标题、来源唯一标签。没有相似度分数 或混合权重;多个来源区间同时重叠仍判为歧义。
- 生成器、现有题目 PATCH、SMART 会话和试卷组卷复用该规则。已有草稿只有在 material 属于本册、 source range/sourceMapVersion/checksum 全部有效、答案受同一原文支持且候选唯一时才自动写为 READY; 显式 pointId 也必须属于本册图谱。
- KnowledgeService 的既有
/internal/v1/knowledge-graphs/{graphId}/scope以向后兼容的附加字段返回points[],没有新增路由。Frontend 只对高置信候选调用既有题目 PATCH;真正的来源和归属决定仍在 PracticeService。 - SMART/EXAM 按 PlanGraph 顺序扫描,跳过缺题目标并继续取后续已有 READY 题的计划点;不选计划外题,
不在一次会话重复同一知识点。仅当计划与 READY 题库零交集时返回
QUESTION_COVERAGE_GAP。
| 验证项 | 结果 |
|---|---|
| PracticeService | PASS,44 / 44,0 skipped;新增名词解释自动补签、唯一/多义来源区间、历史草稿 PATCH、scope 候选解析、部分覆盖继续选择 |
| KnowledgeService | PASS,115 / 115,0 skipped;API 项目随 scope 附加字段成功编译 |
| Gateway | PASS,15 files、203 / 203;TypeScript build PASS。因 pnpm 本机 ignored-build 策略,使用固定 Node 直接执行现有 Vitest/tsc,未修改锁文件或批准策略 |
| Frontend | PASS,TypeScript + Vite,1401 modules;只保留既有 KnowledgeDag 大 chunk 警告 |
本节没有伪造真实数据库 ID,也没有依赖第 33 节的历史项目。Docker CLI 29.4.3 可见,但
dockerDesktopLinuxEngine pipe 不存在,故未声称容器态 HTTP 已复验。engine 恢复后须按
docs/deploy.md 的发布顺序重建 Knowledge/Practice/Frontend,并使用新册或明确准备的 DRAFT 样本执行
§36.2 三条不变量的 HTTP 冒烟。
本轮完整读取 D:\Projects\ReciteHelper 的生产依赖注入、QuizWindow -> QuizService -> SbertModelJudge/SuperMemoService 调用链、训练脚本和历史可恢复样本。结论不是“阈值偏低”,而是旧算法
不能作为可靠基线:
- 主观题把 SBERT cosine 与字符 Jaccard 按答案长度混合,权重在
0.6..0.9,固定阈值0.70;没有 训练、消融、校准或领域金标依据。词表缺失时还会静默退化为 Levenshtein,使干净构建和开发机运行 不同算法。 - q 模型生产特征只有相对速度和相似度。可恢复的明示数据约 40 条、来自单一用户且严重失衡;训练没有
train/test split、交叉验证、校准或正式指标。训练速度为字符/分钟,生产输入为字符/秒,存在 60 倍
量纲差;相似度又在
0..1、0..100和8300三种尺度间混用。 - 旧复习只更新 EF 并按 EF 排序,没有 SM-2 的 repetitions、interval 与 next due date;不能把它称为
完整 SM-2。模拟考试又使用另一套 overlap/Jieba 和
0.8/0.2混合规则。
因此 SBERT 仅保留诊断资产,XGBoost q 与全部混合权重退出生产。此决定也避免拿 ReciteHelper 自身没有 验证过的实现去“证明”迁移算法。
本轮只用原始论文、任务论文、算法作者资料与模型作者模型卡确定方法边界:
| 来源 | 原始结论 | 本项目采用方式 | 明确不推断的内容 |
|---|---|---|---|
| SemEval-2013 Task 7 | 短答案应区分 correct、partial/incomplete、contradictory、irrelevant、non-domain,并评估未见答案/题目/领域 | 冻结五类离线金标,重点包含遗漏、反转和邻近概念 | 不把单一语义相似度当正确率 |
| Nielsen et al. 2008 与 Mohler et al. 2011 | 可把参考答案拆成 facets,判断学生答案覆盖了哪些必要内容 | 标准答案拆为必要事实,逐事实判断 | 不声称当前自动拆分达到人工 rubric 质量 |
| Sentence-BERT | bi-encoder/cosine 面向语义相似检索与 STS | SBERT 仅保留诊断/检索职责 | cosine 不等于方向性正确性、遗漏或矛盾 |
| Semantic Answer Similarity | 成对 cross-encoder 可直接交互比较答案语义 | 使用方向性成对 NLI,而非两个独立句向量相加权 | 该论文不直接证明本项目完整组合 |
| OCNLI | 中文 NLI 有人工数据,强模型与人类仍有明显差距 | NLI 作为候选主判器,同时强制本域拒判与金标验证 | OCNLI/XNLI 成绩不能替代农学/社科本域测试 |
| SelectiveNet | 选择性预测以降低覆盖率换取更低的已决定风险 | 低 top probability 或 margin 自动 ABSTAINED |
拒判阈值不能凭直觉,需要冻结校准集 |
| Guo et al. 2017 | 现代神经网络置信常失准;temperature scaling 是简单有效基线 | 当前只把概率用于拒判并在本域冻结门禁;保留未来独立校准入口 | softmax 数字不展示为“正确率” |
| SuperMemo SM-2 原始说明 | q 是离散回忆质量;EF、间隔和失败重置有固定步骤 | Domain 只给离散 q,KnowledgeService 是完整 SM-2 唯一写入方 | q 不是 cosine、模型信心或打字速度;原作者也说明 EF 公式带启发式来源 |
| BEA 2024 零/少样本 LLM 评分研究 | 零/少样本 LLM 自动评分在不同领域和复杂推理上表现不足 | 不让裸 LLM 直接写 correct/q/SM-2 | 不把“模型会解释”当可靠主判证据 |
| 模型作者模型卡 | 多语种 MiniLM NLI,模型卡给出 XNLI 中文结果与 MIT 许可 | 固定 revision 0a71e92a985b6e1ad1828cf67ce9c459639c1dca、ONNX/Tokenizer 哈希与许可证 |
上游 XNLI 指标不代替本项目金标 |
“必要事实 + 多语种 NLI + 严格同义词复核 + 选择性拒判”以及“图谱只约束 SM-2 到期集合后的选题”是 基于上述研究作出的工程组合,不是任何单篇论文已经证明的整体算法。本节验证的是:组合不是随意加权, 生产实现符合所述离散规则,并在当前农学/社科边界的冻结资料集上达到发布门槛;不把它夸大为所有学科、 所有答案或长期学习效果的普遍证明。
- 单选/判断规范化后精确匹配;填空逐位置精确匹配,不使用编辑距离阈值。
- 名词解释/简答先对标准答案精确一致短路;否则从已核对答案拆出 1-12 个必要事实。
- 固定 NLI 以
premise=用户答案, hypothesis=必要事实逐项输出 entailment/neutral/contradiction。 SentencePiece 原始 ID 按 XLM-R/fairseq 词表约定整体+1;专项测试锁定相同、否定与无关三类。 - top probability
<0.75或前两类 margin<0.20即INDETERMINATE。cn_synonym.txt只读取=严格同义组;原始为OMITTED且替换标准事实后可靠ENTAILED才接受,不覆盖矛盾。 - Domain 状态机固定:全蕴含
q=5;部分蕴含且其余遗漏q=2;可靠矛盾q=1;空白/零覆盖q=0;任一不确定或模型故障q=null。当前不伪造 q=3/4,不使用响应时间。 ABSTAINED的 correct/quality/score 均为空;完成会话自动过滤,不调用 KnowledgeService,用户只看到 中性说明并继续下一题。没有用户自评、人工对照、q 按钮或补标流程。
资料页已经逐页提取并渲染核对;测试运行读取受版本控制的金标 JSON,不在测试中依赖本机 PDF 文件:
D:\AppData\农业生态学\农业生态学.pdf:PDF p4/p5/p13/p16;D:\AppData\微生物学B.pdf:PDF p3/p18/p30;D:\AppData\土壤肥料学\土壤肥料学.pdf:PDF p19/p21/p65(PDF 页码比部分印刷页码大 1)。
共 12 题、每题 5 个答案:原文完整、完整同义改写、部分遗漏、关键矛盾、邻近概念干扰,共 60 条。
冻结文件为 backend/PracticeService/PracticeService.Tests/TestData/answer-judging-real-pdf-v1.json。
农业生态学与微生物学 40 条用于门禁/同义词规则校准;土壤肥料学 20 条在规则冻结后作为未见资料保留集。
| 指标 | 校准资料 40 条 | 未见土壤资料 20 条 | 合计 60 条 |
|---|---|---|---|
| 自动决定数 / 覆盖率 | 27 / 67.5% | 15 / 75.0% | 42 / 70.0% |
| 已决定样本误判 | 0 / 27 | 0 / 15 | 0 / 42 |
| 完整同义改写自动决定 | 5 / 8 | 4 / 4 | 9 / 12 |
| 完整同义改写被判错 | 0 | 0 | 0 |
未决定的 18 条全部自动拒判,不产生错误 q 或 SM-2 证据。未开拒判时首轮 whole/facet NLI 的二元结果为
53/60,包含把部分答案当完整与把完整改写当部分的风险;加入冻结门禁后不再用覆盖率掩盖误判。严格
同义词复核修复了“基础/根基”完整改写,未降低未见土壤集的已决定精度。用户报告的“腐食性食物链”
完整同义改写专项用例得到 DECIDED + quality=5,相似度字段为 null。
| 项目 | 结果 |
|---|---|
| PracticeService | PASS,54 / 54;含领域 q 状态机、NLI pair encoding、同义改写、真实 PDF risk/coverage 门禁 |
| 模型资产 | 固定 NLI revision、ONNX/SentencePiece/Synonym SHA-256;Dockerfile 改为缺失即构建失败 |
| 前端三态 | PASS,TypeScript + Vite production build(1401 modules);隐藏 similarity/quality,拒判可继续且完成页不计错,禁止用户自评 |
| Gateway | PASS,15 files、203 / 203;TypeScript build PASS |
| 主观题 HTTP 全链 | PASS:资料上传/解析、先立册后构图、计划、三道真实语义样本、会话完成;PERFECT q=5 与 WRONG_RELATED q=1 各写一次 mastery,ABSTAINED 的 point version 保持 0 |
| 容器 | Practice/Frontend 当前镜像构建通过;NLI /readyz 为 READY;与 §38 安全批次合并后完整全链再次通过 |
允许的结论是:在当前“中文农学/社科/人文事实与概念题”边界内,判分组合有明确研究依据、没有任意 混合权重,并已在三个真实 PDF 的冻结集上以选择性风险门槛验证;对已决定样本没有观察到误判。它仍需 持续扩充未见资料、对抗否定/关系反转集并监控 risk-coverage,才可降低拒判率。计算题、复杂推导、跨段 开放论述和长期学习效果不在本次证据范围内。
本地 remote reflog 明确记录 origin/main 从已推送的 3fc0d9d 到 7ae0fd7 为
forced-update。本地旧根 3614976 与远端新根 a3f54fc 的 tree 都是 3dc9967;本地
PR 前基线 c4ff9f9 与远端 5c543bc 的 tree 都是 65b0ba3,两组 snapshot 无文件差异。
但远端每个旧 commit 都保留原 author、改为 committer=missile <3397241346@qq.com> 并增加 GPG
签名,从而重建全部 commit object 和 parent 链。原 commit message 中没有新增 Co-authored-by: trailer;
GitHub 的多人归属来自 author/committer 分离。仅凭对象不能断言执行过的具体命令,但可以确认“重写整条
历史并 force-push”的效果,而不是普通 merge。
重写使用的 5c543bc 只等同于 3fc0d9d 的父提交,因此把已经推送的自动绑题修复也从远端主线删除。
修复前先创建 codex/pre-pr18-reconcile-20260810 指向旧 HEAD,并将全部 tracked/untracked WIP 保存到
stash@{0};随后只把 3fc0d9d 的一个 patch 重放到 7ae0fd7,得到 65e1248,再无冲突地恢复 WIP。
最终拓扑为 main...origin/main [ahead 1],没有 merge/rebase/cherry-pick 状态和 unmerged path,也没有用
--allow-unrelated-histories 生成双份历史。
5cb9132 是实际安全代码提交(10 files,159 insertions / 47 deletions);7ae0fd7 是二父 merge
commit,文件树与 5cb9132 相同。保留了固定时序服务密钥比较、管理员哈希、8 位重置码、防邮箱枚举、
OCR 鉴权与 50 页门禁、错误脱敏和 Mongo 包事务等修改,同时修复以下实测缺陷:
- PR 写入的 54-byte 管理员 hash 被 .NET 10
PasswordHasher验证为Failed,Compose 默认 hash 又为空; 现改为实际生成/验证的 Identity V3 hash,生产变量统一为GALREVIEW_ADMIN_PASSWORD_HASH,增加scripts/new-admin-password-hash.ps1与回归测试。 - 重置码原实现对随机 byte 直接
% 30,与“30 个符号等概率”描述不符;现拒绝>=240的 byte 后再 映射,不引入任意权重。 - FastAPI user middleware 直接抛
HTTPException可能绕过异常处理中间件;现直接返回稳定 401 JSON。 - Mongo driver 在 standalone 上实际抛
NotSupportedException: Standalone servers do not support transactions.,原 catch 只接MongoCommandException,导致默认 Compose 的每个故事包都失败。现同时 处理该异常和已知 Mongo transaction 错误码,在 standalone 上使用幂等顺序 upsert,在 replica set 上 继续使用事务。 - credits 原先在音频/包落库前结算,后续持久化失败仍可能扣费;现只有持久化成功后才结算,失败路径释放 reservation。
- OSCA access/secret 从脚本移除,改由
OSCA_ACCESS_KEY_ID/OSCA_SECRET_ACCESS_KEY进程环境或 CI secret store 注入;README、部署文档和 Windows 生产模板已同步。
| 项目 | 结果 |
|---|---|
| AuthService | PASS,18 / 18;开发 hash 由生产同型 PasswordHasher 验证 |
| FileService | PASS,Release build,0 warning / 0 error |
| GalGameService | PASS,362 / 362 |
| PracticeService | PASS,54 / 54 |
| 管理员与密码恢复 | 管理员正确密码 201、错误密码 401;未注册有效邮箱的 reset request 202 |
| OCR | /healthz 无密钥 200;受保护路径无密钥 401 JSON;正确 X-Gateway-Key 200;容器内 app.py SHA-256 与工作树相同 |
| NLI HTTP 冒烟 | PASS;两条确定性答案更新 mastery,一条自动弃判不更新且不要求用户自评 |
| 完整产品链 | PASS:注册、1 credit、上传/解析、先立册后构图、PlanGraph、3 道来源题、SMART/SM-2、credits 实扣、批量制码/兑换、故事包、Render 事件/进度/结果幂等、第二次 mastery 回写 |
| 完整链余额 | 1.0 -> 0.99999 -> 3.99999,最终 held 为 0 |
| Compose | 主栈 15 个容器 healthy;额外启用 ocr profile 后 OCR 也 healthy,均按要求保持运行 |
| 静态检查 | Compose config、PowerShell AST、Python AST、git diff --check PASS |
Docker Hub token endpoint 起初两次连接超时,后续已成功拉取官方 python:3.12-bookworm;canonical
OCR rebuild 在依赖安装阶段按用户“本轮 OCR 不用测”的指示终止,因此不列为本轮接受门槛。此前为诊断
鉴权行为而使用本机既有、requirements 未变化的 OCR 镜像作为基础,仅替换当前 app.py;文件 hash 与
401/200 行为已经核对,但不能冒充一次完整的干净 OCR image build。其余受 PR 影响的 Auth、File、
GalGame 镜像均由当前 Dockerfile 完整重建。
本轮不测试 OCR。专项测试直接使用当前工作树的 AutomaticAnswerScorer、本地固定版本 NLI 资产与正在
运行的 Gateway/Practice/Knowledge 主链,分别提交确定正确、确定错误和不确定样本;不以字符串相似度
作为正确率,也不要求用户自评。
| 类别 | 样本 | 预期 | 实际 |
|---|---|---|---|
| 完整同义正确 | 腐食性食物链:从尸体或排泄物开始,经细菌和原生动物分解,最终还原成无机物并获取能量 | 判对 | DECIDED / PERFECT / correct=true / quality=5 |
| 关键关系反转 | 土壤缓冲性能:加入少量酸或碱后,土壤 pH 会立刻剧烈变化 | 判错 | DECIDED / WRONG_RELATED / correct=false / quality=1 |
| 置信不足边界 | 第二性比的完整同义改写 | 不得误判 | ABSTAINED,correct/quality/similarity=null |
HTTP 会话正常完成。正确与错误两条确定性答案各使对应知识点 mastery version 从 0 变为 1;弃判样本 保持 version 0,证明其未污染掌握度、quality 或 SM-2 证据。专项策略/模型/真实 PDF 测试 10/10 通过, 随后完整 PracticeService 回归 54/54 通过。真实 PDF 门禁仍为 60 条中自动决定 42 条、已决定误判 0; 其中未见土壤资料 20 条自动决定 15 条、已决定误判 0。该结论只说明当前冻结样本和已声明业务边界内 未观察到误判,不等同于所有未来资料上的绝对正确率。
填空不再只做规范化字符串逐字相等,也不引入模糊相似度。新增
deterministic-fill-equivalence-v1,按位置对数值、纯数值元组格式及封闭专业别名做确定性规范化。
首轮专项 27 条中,g⁺菌 / Gram-positive bacteria 暴露英文带连字符复数别名遗漏;补齐该明确别名后
27/27 通过。正例覆盖 两个/二/2/2.0、全角数字、(1, 3)/( 1,3 )、中文数字元组、
G+/革兰氏阳性 和 G-/革兰阴性菌;反例锁定 2 != 3、元组换序、圆/方括号、G+ != G-、
二氧化碳 != 2氧化碳 及普通近义词不得自动相等。应用层多空测试同时确认全部等价为
PERFECT quality=5,仅部分位置等价为 PARTIAL quality=2,且填空路径不会调用 NLI。
新增 NLI 模型此前已进入 resources.manifest.json 与统一下载器。本轮进一步将补充下载白名单从泛化的
huggingface.co 主机收紧为 MoritzLaurer/multilingual-MiniLMv2-L6-mnli-xnli 固定 revision
0a71e92a985b6e1ad1828cf67ce9c459639c1dca 路径;模型、SentencePiece 与三个配置文件仍逐项执行
长度和 SHA-256 校验。随后 §41 将这些资产整体迁入独立 ModelService;本节结论保留为迁移前的
填空规则与下载来源验证记录。OCR 不在测试范围。
新增 backend/ModelService,按 API、Application、Domain、Persistence 四层拆分;Application
使用 MediatR 的 AdjudicateFacetsCommand 与 readiness query。Persistence 只拥有 ONNX Runtime、
SentencePiece、严格同义词词典与资产 SHA-256 校验,不使用数据库。唯一生产推理入口为
POST /internal/v1/model-inference/facet-adjudications,只允许精确 PracticeService 身份经 Gateway
调用。ModelService 只产生逐事实 verdict 与概率诊断;correct/quality/score/SM-2 仍由
PracticeService.Domain 的离散状态机决定。
Resources、19 文件 manifest、模型许可证和下载脚本已迁至 ModelService。manifest schema 升级为
qzwl-model-resources-1;默认路径为 backend/ModelService/Resources,下载器名为
scripts/download-model-resources.ps1。旧 backend/PracticeService/Resources 不存在,Practice
Persistence 已移除 ONNX Runtime、Tokenizers、模型目录和本地资产 readiness;其 /readyz 只报告
storage=mongodb 与 modelInference=gateway:model-service。
| 范围 | 结果 |
|---|---|
| ModelService tests | PASS,8/8;含输入边界、真实 NLI pair、完整释义和 3 份真实 PDF 的 60 条冻结金标基准 |
| PracticeService tests | PASS,68/68;模型测试移出后保留复习聚合、拒判状态机、填空确定性等价与模型超时自动拒判回归 |
| Gateway tests / TypeScript build | PASS,204/204;INTERNAL 模型路由、默认 URL/key/readiness 和所有测试配置已同步 |
| PowerShell AST | PASS;download-model-resources.ps1、deploy-windows.ps1、start_dev.ps1 均为 0 syntax error |
| Model resource manifest | PASS,19/19 文件长度与 SHA-256 一致;固定 NLI revision/source path 门禁仍生效 |
| Compose config | PASS;默认 16 个服务,新增 model-service:5109,Gateway readiness 共检查 9 个应用服务 |
| Linux image build | PASS;model-service、practice-service、gateway 均从当前工作树成功构建 |
| 容器健康 | PASS;16 个默认容器 healthy;此前已启动的可选 OCR 容器也保持 healthy,但未调用 OCR 接口 |
ModelService /readyz 返回 ready,三个必需资产 NLI ONNX、SentencePiece、同义词词典均为
READY 且实际哈希等于预期;可选 SBERT/vocab 同样为 READY。Practice /readyz 不再泄漏或重复
扫描模型状态。Gateway /readyz 为 200 ready。
维护者在本轮末确认 OSCA 已创建 multilingual-minilm-nli 文件夹并按 Resources 同构路径上传相关
模型和配置。考虑 OSCA 稳定性,下载器不再把成功列举作为单点前提,恢复顺序改为已校验本地缓存、OSCA、
受信同构离线副本、manifest 固定远端版本。灾备覆盖全部 19 个文件:NLI 固定 Hugging Face revision,旧
sbert.onnx、旧 q 模型、tokenizer 与词典固定 ReciteHelper 审计提交,vocab.txt 固定历史提交;所有
URL 均经过主机和精确路径白名单,最终仍强制长度与 SHA-256。
新增容灾探针结果:空临时目录、无 OSCA 凭据时,从同构离线副本恢复并复验 19/19,随后完成临时目录
清理;另从固定远端实际下载 NLI onnx/config.json 与 ReciteHelper prob_trans.json,两者均通过
manifest 哈希,其中旧文本资源执行显式 LF→CRLF 规范化。两份大模型固定 URL 的 HEAD 均为 200,
sbert.onnx 报告 90,445,823 bytes,NLI model.onnx 报告 428,127,016 bytes。首次探针还发现根目录
config.json 与 ONNX 导出配置不是同一文件,现已把 NLI 配置、SentencePiece 和 tokenizer 配置统一锁定
到 revision 内的 onnx/ 目录;没有为通过测试而放宽校验。本会话未把 OSCA 凭据写入命令或仓库,也未
重新执行远端列举,故 OSCA 上传状态仅记为维护者确认;本地哈希、固定源探针、镜像构建和容器 readiness
分别独立验证。
主观题脚本从公开 Gateway 完成无邀请码注册、非 OCR 文本上传、解析、立册、本册构图、评估计划、 READY 题录、SMART_REVIEW、逐题提交、会话完成和 mastery 查询。三条结果为:
- 明显矛盾:
DECIDED / WRONG_RELATED / correct=false / quality=1,mastery version 1; - 完整正确释义:
DECIDED / PERFECT / correct=true / quality=5,mastery version 1; - 低置信同义改写:自动
ABSTAINED,correct/quality/score=null,mastery version 0。
三条 similarity 均为 null;拒判自动继续且不要求用户对照、标注或选择 q。该链路只能在
Practice → Gateway → ModelService 成功时取得 NLI verdict,证明容器测试没有绕过新服务边界。
填空 HTTP 专项通过 6/6:2/两个、(1,3)/( 1, 3 )、G+/革兰氏阳性 均为
PERFECT quality=5;G+/G-、元组换序和圆/方括号变化均为 NO_RECALL quality=0。所有响应版本为
deterministic-fill-equivalence-v1+facet-sm2-observation-v1,similarity=null 且 facets=[],
确认填空没有误调 NLI 或模糊相似度。
INTERNAL 身份探针结果为:无服务身份 403、正确密钥但调用方为 KnowledgeService 403、
精确 PracticeService 身份 200;成功响应保持统一 data/meta/traceId 信封。OCR 识别准确率、模型加载
与接口均未测试,本节不作任何 OCR 声明。Docker 按用户要求保持运行。
新增 wiki/Home.md 及 14 个分页面,覆盖快速开始、账户、藏书阁、研习册、答题与智能复习、识网、
故事回响、项目包、credits、个人设置、管理员、服务架构、部署和故障排查。采集的当前本地产品截图全部
位于 wiki/res;截图使用真实注册、非 OCR 文本上传和轻量项目外壳,不用示意图冒充产品页面。真实自动
成题截图任务超过总时限后被停止,没有再次触发生成,也没有把加载页写入 Wiki。Credits 服务在截图时返回
不可用,因此没有使用失败状态截图;对应说明按当前前端实现和合同编写。
Wiki 样式使用灰白纸面、固定目录、直角搜索框、表格和正文层级,没有 emoji、发光渐变、悬浮胶囊卡片或
拟人化 AI 文案。以 1440×1000 浏览器实拍复核 藏书阁与资料解析.html,页面目录、中文标题、截图和正文
均无明显遮挡。
| 检查 | 结果 |
|---|---|
| Frontend TypeScript + Vite | PASS;1401 modules transformed |
| Wiki generator | PASS;15 Markdown pages generated |
| Frontend Docker build | PASS;根上下文经 .dockerignore 仅发送 frontend/wiki,context 21.24 MB |
| Markdown 内链与资源引用 | PASS;29 个仓库内引用,缺失 0 |
| Wiki 视觉规则静态审计 | PASS;无 emoji、渐变、阴影、发光或胶囊式圆角规则 |
| Frontend 完整依赖审计 | PASS;nanoid 构建期传递依赖由 3.3.16 锁定到 3.3.17 后,npm audit 为 0 |
/wiki/ |
HTTP 200,包含“千知万理使用 Wiki” |
/wiki/快速开始.html |
HTTP 200 |
/wiki/res/藏书阁资料就绪.png |
HTTP 200,image/png |
| 产品首页入口 | 新 bundle 已部署;顶部导航、首屏和页脚均含 /wiki/ 链接 |
15 个同名 HTML、/wiki/ 索引及 wiki/res 的 16 个资源逐一请求均为 HTTP 200;主页 bundle 中确认
存在 3 个 /wiki/ 入口。Frontend 容器使用新镜像重建,当前 16 个默认容器均为 healthy 并保持运行。
Wiki 是 Frontend 的公开静态内容,不依赖用户会话;本轮没有调用 OCR,也没有关闭 Docker。