# 任务 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 配置项说明。 ### 前端 - `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 仅存于后端环境变量且未进入日志/文档/前端。