6.2 KiB
6.2 KiB
任务 11 检查记录:AI 讲解与申论占位
日期:2026-09-01
交付内容
后端
server/src/ai.ts(新增):readAiConfig():从环境变量读取AI_BASE_URL/AI_API_KEY/AI_MODEL(默认 gpt-4o-mini)/AI_TIMEOUT_MS(默认 20000),未配置返回 null。buildMessages():组装模块/题干/选项/标准答案/题库解析/标签/用户作答,要求模型输出严格 JSON(summary / steps / knowledgePoints / commonMistakes / answer)。parseExplainJson():兼容 markdown 代码块等杂文的稳健 JSON 提取与字段校验。explainWithAi():OpenAI 兼容 Chat Completions 调用(${baseUrl}/chat/completions+ Bearer),AbortController 超时;未配置/超时/网络/HTTP/解析失败分别抛可识别错误;任何错误信息不含 API Key。
server/src/handlers/ai.ts(重写):配置有效 → 调用 AI 并返回source:'ai';未配置 →source:'fallback'(「AI 尚未配置」);超时 →source:'fallback'(「AI 请求超时」);其余失败 →source:'fallback'(「AI 讲解暂时不可用」)。回退内容为题库解析 + 标签 + 答案,前端可识别。server/src/schemas/api.ts:AiExplainBodySchema.userAnswer改为可选(从错题详情进入且无作答历史时省略)。server/.env.example(新增):记录 PORT / DATA_DIR / AI 配置项说明。server/src/env.ts+server.ts(补充):引入 dotenv,启动时最先加载server/.env(进程内环境变量优先),确保PORT/DATA_DIR/AI_*在模块读取前就位;实测server/.env中配置 AI 变量后aiConfigured=true且讲解走 AI,删除.env后自动回退。
前端
client/src/views/practice/AiExplainView.vue(新增):- 路由
/practice/explain?questionId=&userAnswer=&requestType=,加载/错误/重试三态,缺少题目参数给出明确提示。 - 展示:来源徽章(AI 生成 / 题库解析回退)、讲解结论、分步解析(编号步骤)、考点(chip)、常见错误、正确答案(绿色高亮);回退时显示说明提示条。
requestType=deep时标题为「AI 深度讲解」。
- 路由
client/src/views/practice/AnswerView.vue:- 单题结果「没看懂?让 AI 讲一讲」接入跳转(携带题目 ID + 用户答案)。
- 整卷结果页错题回顾每项新增「AI 讲解」入口。
client/src/views/practice/WrongDetailView.vue:AI 讲解按钮由占位禁用改为可用,携带题目 ID 与最近一次作答(无历史则省略)。client/src/views/practice/EssayView.vue(新增):申论占位页——功能说明(五大题型)、后续能力(在线写作/AI 批改/范文库)、不保存写作内容的提示。client/src/views/practice/PracticeView.vue:申论 tab 由通用 ComingSoon 替换为EssayView。client/src/router/index.ts:新增/practice/explain;client/src/api/index.ts导出AiExplain类型。
接口实测(本地 OpenAI 兼容 mock + curl)
| 场景 | 结果 |
|---|---|
| 未配置 AI | source:fallback,「AI 尚未配置,已回退到题库标准解析(用户答案:A)」,steps=题库解析、answer=C |
| 配置有效 Token(mock) | source:ai,summary/steps/knowledgePoints/commonMistakes/answer 结构化解析正确 |
| deep 模式且无 userAnswer | 正常返回 source:ai(prompt 中用户作答为「未作答」) |
| 超时(AI_TIMEOUT_MS=1200 + 8s 慢响应) | 1.2s 内返回 source:fallback,「AI 请求超时」 |
| AI 返回不可解析内容 | source:fallback,「AI 讲解暂时不可用」 |
| 非法输入 | 缺 questionId → VALIDATION_ERROR;不存在题目 → NOT_FOUND;非法 requestType → VALIDATION_ERROR |
| Token 不泄漏 | 后端日志仅含请求方法/URL/状态码,无 API Key;OpenAPI 无 AI Token(仅有的 apiKey 字段是要闻 API 导入占位请求结构,任务06 既有);前端源码无密钥 |
浏览器检查(Chrome headless + CDP)
| 场景 | 结果 |
|---|---|
| AI 讲解页(配置) | AI 生成徽章、结论、分步解析、考点、常见错误、答案 C 全部渲染 |
| deep 模式 | 标题「AI 深度讲解」 |
| 错题详情 AI 按钮 | 可用;点击跳转 /practice/explain?questionId=demo-q-004&userAnswer=A |
| AI 未配置回退 UI | 「题库解析回退」徽章 + 回退说明 + 题库解析步骤 + 正确答案 |
| 申论占位页 | 功能说明 + 五大题型 + 后续能力 + 不保存写作内容提示 |
| 移动 390 | AI 讲解页与申论页均无横向溢出(390/390) |
| 类型检查与构建 | vue-tsc / tsc 通过;生产构建 139 模块通过;api:generate 与 OpenAPI 一致 |
截图:deliverables/checks/task11-ai-desktop.png、task11-ai-mobile.png、task11-ai-fallback-desktop.png、task11-essay-desktop.png、task11-essay-mobile.png。
完成标准核对(开发计划任务11)
- ✅ OpenAI 兼容客户端(Chat Completions + Bearer Token,环境变量配置)。
- ✅ Token 配置读取(
AI_BASE_URL/AI_API_KEY/AI_MODEL/AI_TIMEOUT_MS,server/.env.example示例)。 - ✅ 结构化讲解(summary / steps / knowledgePoints / commonMistakes / answer)。
- ✅ 超时处理(AbortController +
AI_TIMEOUT_MS,超时回退题库解析)。 - ✅ 题库解析回退(未配置/超时/解析失败均回退,
source:fallback可识别,不阻塞刷题)。 - ✅ 前端结果展示(AI 讲解页 + 刷题结果/错题详情入口 + 未配置与失败提示)。
- ✅ 申论占位页面(功能说明与后续入口,不保存写作内容)。
- ✅ Token 不出现在前端、OpenAPI 与日志。
边界说明
- 测试使用本地 OpenAI 兼容 mock 服务(127.0.0.1:3199/3198/3197)验证「配置有效」「超时」「不可解析」三条链路,未使用真实外部 Token。
- 未配置 AI 时不影响刷题、错题复习、模考等任何流程;设置页
aiConfigured同步反映配置状态。
当前结论
任务 11 完成:AI 讲解接口与前端结果页、未配置/超时/失败回退、申论占位页全部实测通过;桌面与移动双端可操作;Token 仅存于后端环境变量且未进入日志/文档/前端。