diff --git a/AGENTS.md b/AGENTS.md index 854646e8..b594c364 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -55,7 +55,7 @@ - 资料处理:`frontend/src/views/knowledge/processing.vue`,路由 `/knowledge/processing`。 - 模型配置:`frontend/src/views/system/model/index.vue`,路由 `/system/model`。 - MVP 阶段侧栏只保留“首页 / AI面试 / 三角色对练 / 案例沉淀 / SOP知识库 / 资料处理 / 系统设置-模型配置”。过滤逻辑在 `frontend/src/layout/components/Sidebar/index.vue`,不要在未明确要求时恢复若依默认全量菜单。 -- 移动端三端首页:`mobile/src/App.vue`,路由 `/h5/user`、`/h5/candidate`、`/h5/supervisor`,分别对应用户/员工端、候选人端、主管端;兼容 `/h5/employee`、`/h5/admin`,但移动端不要把主管端等同后台系统管理员。员工端首页是「学/复盘/用/报/成长」五个情境区;底部 tab 是整页互斥切换(isHomeTab + activeUserPanel/activeSupervisorPanel 条件),**不要把面板改回插入首页长页面的叠加渲染**;未实现入口必须走 `tap()` 的「建设中」toast,不留无反应按钮。查 SOP 全屏层(sopPanel)调 `/api/knowledge/search`(category='sop' 全库),关闭后回到进入前 tab。 +- 移动端三端首页:`mobile/src/App.vue`,路由 `/h5/user`、`/h5/candidate`、`/h5/supervisor`,分别对应用户/员工端、候选人端、主管端;兼容 `/h5/employee`、`/h5/admin`,但移动端不要把主管端等同后台系统管理员。员工端首页是「师傅区→今日安排→我的地图→我的成长摘要」(视觉基准 `docs/feedback/2026070423/design/` + REVIEW.md 修正项,修正项优先于图面);师傅四状态(idle/listening/thinking/speaking)只从 `sopRecording/sopLoading/sopSpeaking` 推导,**不要新造状态源**;员工端底部 tab 钉死「今日/练/问/我」(visibleTabs 强制,不跟随后端 home 旧文案),整页互斥切换(isHomeTab + activeUserPanel/activeSupervisorPanel 条件),**不要把面板改回插入首页长页面的叠加渲染**;未实现入口进「建设中」整页(openBuildingPage),不留无反应按钮。查 SOP 全屏层(sopPanel)调 `/api/knowledge/search`(category='sop' 全库),关闭后回到进入前 tab;「生成总结卡」按需调 `/api/knowledge/summary-card`,失败明确报错可重试,不兜假数据。 - 移动端首页接口:`GET /api/aihr/mobile/home/{role}`,当前 `@SaIgnore` 公开只读 seed,用于 H5 首屏 API 优先 + 本地 fallback;后续确定小程序登录后再接移动端 token,不复用管理后台登录态。 - 移动端手机号登录:验证码发送走 `GET /resource/sms/code?phonenumber=...`,登录走 `POST /auth/mobile/sms-login`;短信发送复用 `sms.blends.config1` 阿里云配置。手机号不存在时自动注册 `app_user`,备注为“移动端短信自动注册”。模板 ID 用 `AIHR_SMS_LOGIN_TEMPLATE_ID`,AccessKey/Secret/签名用 `ALIYUN_SMS_ACCESS_KEY_ID`、`ALIYUN_SMS_ACCESS_KEY_SECRET`、`ALIYUN_SMS_SIGN_NAME`,本地放 `.env.local`。dev 兜底:`aihr.sms.dev-fixed-code` 非空则验证码固定(dev 默认 `123456`)、不真发短信;prod profile 代码级强制失效,不要移除该闸门。 - 移动端员工训练闭环:员工端首页“开始训练”复用 `POST /api/train/practice/start`、`/turn`、`/finish`,请求必须带移动端登录返回的 `Authorization` 与 `clientid`;`mode=mobile` 完成后写入 `aihr_practice_session`,并更新主管端首页完训率、“待复盘对练”、员工训练历史和能力画像。主管端复盘详情查 `GET /api/aihr/mobile/practice/reviews/{id}`,标记复盘用 `POST /api/aihr/mobile/practice/reviews/{id}/reviewed`。 diff --git a/README.md b/README.md index 45f3fadc..ac22ace5 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ - 品牌:前端标题为“物业AI人力资源系统”,侧栏 logo 使用银城物业图标。 - 已验证:租户列表、验证码、`000000 / admin / admin123` 加密登录链路。 - 管理端 MVP:首页与五个关键页面已落地,路由为 `/index`、`/recruit/interview`、`/train/practice`、`/knowledge/cases`、`/knowledge/sop`、`/knowledge/processing`。 -- 移动端 MVP:三端首页已拆到独立工程 `mobile/`;路由为 `/h5/user`、`/h5/candidate`、`/h5/supervisor`,并兼容 `/h5/employee`、`/h5/admin`;已接手机号短信登录、`GET /api/aihr/mobile/home/{role}` 首页 API。员工端首页按「今天要学/练完要复盘/工作中要用/碰到问题要报/我的成长」五个情境区组织,底部 tab 整页切换;“开始训练”可完成两轮三角色对练,记录落 `aihr_practice_session` 并刷新主管端待复盘列表、复盘详情、标记已复盘状态和员工能力画像;「查 SOP」支持文本/语音提问并返回带引用片段的答案;候选人端“开始面试/面试练习”复用 AI 面试 API 拉题、作答、评分,“补充资料”走 OSS + `aihr_candidate_material` 入库,HR 可在 AI 面试页审核通过/驳回;前端保留本地 fallback。 +- 移动端 MVP:三端首页已拆到独立工程 `mobile/`;路由为 `/h5/user`、`/h5/candidate`、`/h5/supervisor`,并兼容 `/h5/employee`、`/h5/admin`;已接手机号短信登录、`GET /api/aihr/mobile/home/{role}` 首页 API。员工端首页为「数字师傅」形态(师傅头像四状态+按住说话/打字问+快捷chips→今日安排→我的地图四色块→成长摘要,视觉基准 `docs/feedback/2026070423/design/`),底部 tab 今日/练/问/我整页切换;“开始训练”可完成两轮三角色对练,记录落 `aihr_practice_session` 并刷新主管端待复盘列表、复盘详情、标记已复盘状态和员工能力画像;「查 SOP」文本/语音提问返回结构化概要+引用片段,可按需生成信息图总结卡(听一遍/开始跟读);候选人端“开始面试/面试练习”复用 AI 面试 API 拉题、作答、评分,“补充资料”走 OSS + `aihr_candidate_material` 入库,HR 可在 AI 面试页审核通过/驳回;前端保留本地 fallback。 - 英雄路径:AI 面试(真模型出题/评分)、三角色对练(真 LLM 客户回复+评分,asr/tts 配置后支持语音)、案例沉淀(真实语音上传+ASR 转写+模型整理)均为真模型优先、未配置或失败回退本地兜底;SOP 知识库已接中文关键词 + MySQL Fulltext + Qdrant 混合召回 + rerank 重排,支持 txt/md/PDF/Word/Excel/PPT/图片/视频上传解析入库(图片走 vision OCR,视频走 ffmpeg 抽音轨转写+关键帧描述);模型配置页可查看向量库维度/点数并一键重建。 - 资料处理:已新增解析任务状态页,按 `aihr_knowledge_attach` 和 `aihr_knowledge_fragment` 展示等待解析、解析中、已完成、失败、片段数和向量化状态;页面支持多文件/浏览器目录选择,也支持从后端 `AIHR_IMPORT_ROOT` 下启动后台目录导入任务、轮询进度并取消运行中任务。 - 侧栏:MVP 阶段只展示“首页 / AI面试 / 三角色对练 / 案例沉淀 / SOP知识库 / 资料处理 / 系统设置-模型配置”,其余若依默认菜单暂时隐藏。 diff --git a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java index 261bce86..bfc96ba8 100644 --- a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java +++ b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrSopController.java @@ -7,6 +7,8 @@ import org.dromara.aihr.domain.AihrSopDto.LocalImportTaskResponse; import org.dromara.aihr.domain.AihrSopDto.ProcessingOverviewResponse; import org.dromara.aihr.domain.AihrSopDto.SearchRequest; import org.dromara.aihr.domain.AihrSopDto.SearchResponse; +import org.dromara.aihr.domain.AihrSopDto.SummaryCardRequest; +import org.dromara.aihr.domain.AihrSopDto.SummaryCardResponse; import org.dromara.aihr.domain.AihrSopDto.UploadEnqueueResponse; import org.dromara.aihr.domain.AihrSopDto.UploadItemResponse; import org.dromara.aihr.domain.AihrSopDto.UploadResponse; @@ -61,6 +63,11 @@ public class AihrSopController { return R.ok(sopSeedService.search(request)); } + @PostMapping("/summary-card") + public R summaryCard(@RequestBody SummaryCardRequest request) { + return R.ok(sopSeedService.summaryCard(request)); + } + @PostMapping("/doc/upload") public R uploadDoc(@RequestPart("file") MultipartFile file, @RequestParam("category") String category) { return R.ok(sopSeedService.uploadDoc(file, category)); diff --git a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/domain/AihrSopDto.java b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/domain/AihrSopDto.java index a40f67c1..f50ae605 100644 --- a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/domain/AihrSopDto.java +++ b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/domain/AihrSopDto.java @@ -10,6 +10,18 @@ public final class AihrSopDto { public record SearchRequest(String queryText, String category, Integer limit) { } + public record SummaryCardRequest(String queryText, String category) { + } + + public record SummaryCardResponse(String title, List steps, List objections, List scripts, List reminders) { + } + + public record CardStep(String label, String desc) { + } + + public record CardObjection(String concern, String response) { + } + public record UploadResponse(String docId, Long ossId, String fileName, String category, Integer fragments, String summary, List tags, List snippets) { } @@ -65,7 +77,9 @@ public final class AihrSopDto { List docs, List snippets, List training, - List records + List records, + List keyPoints, + List cautions ) { } diff --git a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java index 60d5424f..c0ac6dba 100644 --- a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java +++ b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java @@ -6,6 +6,8 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.dromara.aihr.domain.AihrSopDto.CategoryStatResponse; +import org.dromara.aihr.domain.AihrSopDto.CardObjection; +import org.dromara.aihr.domain.AihrSopDto.CardStep; import org.dromara.aihr.domain.AihrSopDto.DocResponse; import org.dromara.aihr.domain.AihrSopDto.LocalImportItemResponse; import org.dromara.aihr.domain.AihrSopDto.LocalImportRequest; @@ -21,6 +23,8 @@ import org.dromara.aihr.domain.AihrSopDto.RecordResponse; import org.dromara.aihr.domain.AihrSopDto.SearchRequest; import org.dromara.aihr.domain.AihrSopDto.SearchResponse; import org.dromara.aihr.domain.AihrSopDto.SnippetResponse; +import org.dromara.aihr.domain.AihrSopDto.SummaryCardRequest; +import org.dromara.aihr.domain.AihrSopDto.SummaryCardResponse; import org.dromara.aihr.domain.AihrSopDto.UploadResponse; import org.dromara.aihr.domain.AihrSopDto.VectorIndexStatusResponse; import org.dromara.aihr.domain.AihrSopDto.VectorizeResponse; @@ -123,10 +127,61 @@ public class AihrSopSeedService { seed.docs(), seed.snippets(), seed.training(), - records() + records(), + List.of(), + List.of() ); } + public SummaryCardResponse summaryCard(SummaryCardRequest request) { + String queryText = request == null ? "" : Optional.ofNullable(request.queryText()).orElse("").trim(); + if (isBlank(queryText)) { + throw new ServiceException("请先输入问题"); + } + String category = request == null ? "sop" : firstNonBlank(request.category(), "sop"); + List hits; + try { + hits = dbHits(dbCategory(category), queryText, 5); + } catch (DataAccessException e) { + hits = List.of(); + } + if (hits.isEmpty()) { + throw new ServiceException("知识库中未检索到相关内容"); + } + Optional runtime = chatRuntime(); + if (runtime.isEmpty()) { + throw new ServiceException("未配置对话模型,无法生成总结卡"); + } + + StringBuilder context = new StringBuilder(); + int index = 1; + for (KnowledgeHit hit : hits.stream().limit(5).toList()) { + context.append("片段").append(index++).append("(来源:").append(hit.title()).append("):\n") + .append(truncate(hit.content(), 1500)).append("\n\n"); + } + try { + String content = callSummaryCardModel(runtime.get(), queryText, context.toString()); + JsonNode root = objectMapper.readTree(jsonObjectText(content)); + String title = truncate(root.path("title").asText("").strip(), 14); + List steps = jsonCardSteps(root.path("steps")); + if (isBlank(title) || steps.isEmpty()) { + throw new IllegalStateException("summary card missing title or steps"); + } + return new SummaryCardResponse( + title, + steps, + jsonCardObjections(root.path("objections")), + jsonStrings(root.path("scripts"), 3, 90), + jsonStrings(root.path("reminders"), 5, 20) + ); + } catch (ServiceException e) { + throw e; + } catch (Exception e) { + log.warn("summary card failed: {}", e.getMessage()); + throw new ServiceException("总结卡生成失败,请稍后重试"); + } + } + public UploadResponse uploadDoc(MultipartFile file, String category) { if (file == null || file.isEmpty()) { throw new ServiceException("上传文件不能为空"); @@ -1565,10 +1620,7 @@ public class AihrSopSeedService { private SearchResponse dbSearch(String category, String queryText, Integer limit) { try { - int limitValue = limitValue(limit); - List keywordHits = likeHits(category, queryText, limitValue); - List textHits = rrf(keywordHits, fulltextHits(category, queryText, limitValue), limitValue); - List hits = rerankHits(queryText, rrf(textHits, vectorHits(category, queryText, limitValue), limitValue)); + List hits = dbHits(category, queryText, limit); if (hits.isEmpty()) { return null; } @@ -1578,6 +1630,13 @@ public class AihrSopSeedService { } } + private List dbHits(String category, String queryText, Integer limit) { + int limitValue = limitValue(limit); + List keywordHits = likeHits(category, queryText, limitValue); + List textHits = rrf(keywordHits, fulltextHits(category, queryText, limitValue), limitValue); + return rerankHits(queryText, rrf(textHits, vectorHits(category, queryText, limitValue), limitValue)); + } + private List vectorHits(String category, String queryText, int limit) { for (EmbeddingRuntime runtime : embeddingRuntimes()) { try { @@ -1726,7 +1785,7 @@ public class AihrSopSeedService { } } - private static SearchResponse responseFromHits(String category, String queryText, List hits) { + private SearchResponse responseFromHits(String category, String queryText, List hits) { KnowledgeHit first = hits.get(0); String responseCategory = isBlank(category) ? first.category() : category; List snippets = hits.stream() @@ -1736,18 +1795,203 @@ public class AihrSopSeedService { for (KnowledgeHit hit : hits) { docs.putIfAbsent(hit.title(), new DocResponse(hit.title(), hit.description(), "命中 " + scoreText(hit.score()), "pill-success")); } + SearchDigest digest = searchDigest(queryText, hits); return new SearchResponse( queryText, responseCategory, - "根据已入库 SOP:" + first.content(), + digest != null ? digest.answer() : "根据已入库 SOP:" + first.content(), "引用 " + docs.size() + " 份文档 / " + snippets.size() + " 个片段", List.copyOf(docs.values()), snippets, List.of("题 1:请按命中 SOP 复述处理步骤。", "题 2:哪些信息必须留痕并可追溯?"), - records() + records(), + digest != null ? digest.keyPoints() : List.of(), + digest != null ? digest.cautions() : List.of() ); } + /** + * RAG 生成环节:把命中片段交给 chat 模型压成「直接回答+要点+注意事项」结构化概要。 + * 模型未配置、超时或解析失败返回 null,由调用方回退片段原文拼接——检索永不因概要失败而报错。 + */ + private SearchDigest searchDigest(String queryText, List hits) { + Optional runtime = chatRuntime(); + if (runtime.isEmpty()) { + return null; + } + StringBuilder context = new StringBuilder(); + int index = 1; + for (KnowledgeHit hit : hits.stream().limit(5).toList()) { + context.append("片段").append(index++).append("(来源:").append(hit.title()).append("):\n") + .append(truncate(hit.content(), 1500)).append("\n\n"); + } + try { + String content = callDigestModel(runtime.get(), queryText, context.toString()); + JsonNode root = objectMapper.readTree(jsonObjectText(content)); + String answer = root.path("answer").asText(""); + if (isBlank(answer)) { + return null; + } + return new SearchDigest( + truncate(answer, 200), + jsonStrings(root.path("keyPoints"), 6, 120), + jsonStrings(root.path("cautions"), 3, 120) + ); + } catch (Exception e) { + log.warn("search digest failed, keep raw answer: {}", e.getMessage()); + return null; + } + } + + private String callDigestModel(ChatRuntime runtime, String queryText, String context) throws Exception { + ObjectNode body = objectMapper.createObjectNode(); + body.put("model", runtime.modelName()); + body.put("temperature", 0); + body.put("stream", false); + + ArrayNode messages = body.putArray("messages"); + ObjectNode system = messages.addObject(); + system.put("role", "system"); + system.put("content", "你是物业一线员工的SOP助手。只返回 JSON,不要 markdown。字段固定为 answer, keyPoints, cautions。" + + "answer 是对问题的直接回答(不超过60字,含关键数字/时限);keyPoints 是3到6条可执行要点(每条一句话,含具体数字);" + + "cautions 是0到3条注意事项或红线。硬约束:只能基于给定片段内容作答,片段中没有的信息不得编造;" + + "若片段无法回答问题,answer 固定为「未在已入库 SOP 中找到明确依据」且 keyPoints 为空数组。"); + + ObjectNode user = messages.addObject(); + user.put("role", "user"); + user.put("content", """ + 员工的问题:%s + + 检索命中的 SOP 片段: + %s + """.formatted(queryText, context)); + + HttpRequest.Builder builder = HttpRequest.newBuilder() + .uri(URI.create(normalizeBaseUrl(runtime.baseUrl()) + "/chat/completions")) + .timeout(Duration.ofSeconds(15)) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(objectMapper.writeValueAsString(body))); + if (!isBlank(runtime.apiKey())) { + builder.header("Authorization", "Bearer " + runtime.apiKey()); + } + HttpResponse response = HttpClient.newBuilder() + .connectTimeout(Duration.ofSeconds(10)) + .build() + .send(builder.build(), HttpResponse.BodyHandlers.ofString()); + if (!ok(response.statusCode())) { + throw new IllegalStateException("digest HTTP " + response.statusCode()); + } + JsonNode choices = objectMapper.readTree(response.body()).path("choices"); + if (!choices.isArray() || choices.isEmpty()) { + throw new IllegalStateException("digest response missing choices"); + } + return choices.get(0).path("message").path("content").asText(); + } + + private String callSummaryCardModel(ChatRuntime runtime, String queryText, String context) throws Exception { + ObjectNode body = objectMapper.createObjectNode(); + body.put("model", runtime.modelName()); + body.put("temperature", 0); + body.put("stream", false); + + ArrayNode messages = body.putArray("messages"); + ObjectNode system = messages.addObject(); + system.put("role", "system"); + system.put("content", """ + 你是物业一线员工的SOP信息图总结助手。只返回 JSON,不要 markdown、代码块或解释。 + 输出格式固定为: + {"title":"XX总结卡","steps":[{"label":"步骤名","desc":"一句话"}],"objections":[{"concern":"异议","response":"应对"}],"scripts":["可直接开口的完整话术"],"reminders":["注意事项"]} + 字段约束:title ≤14字;steps 3-5条,label ≤6字,desc ≤20字;objections 2-4条,concern ≤8字,response ≤20字;scripts 2-3条;reminders 3-5条,每条≤20字。 + 硬约束:只能基于给定片段内容归纳,不得编造片段中没有的流程、时限、承诺、金额、政策或话术。 + """); + + ObjectNode user = messages.addObject(); + user.put("role", "user"); + user.put("content", """ + 员工的问题:%s + + 检索命中的知识片段: + %s + """.formatted(queryText, context)); + + HttpRequest.Builder builder = HttpRequest.newBuilder() + .uri(URI.create(normalizeBaseUrl(runtime.baseUrl()) + "/chat/completions")) + .timeout(Duration.ofSeconds(30)) + .header("Content-Type", "application/json") + .POST(HttpRequest.BodyPublishers.ofString(objectMapper.writeValueAsString(body))); + if (!isBlank(runtime.apiKey())) { + builder.header("Authorization", "Bearer " + runtime.apiKey()); + } + HttpResponse response = HttpClient.newBuilder() + .connectTimeout(Duration.ofSeconds(10)) + .build() + .send(builder.build(), HttpResponse.BodyHandlers.ofString()); + if (!ok(response.statusCode())) { + throw new IllegalStateException("summary card HTTP " + response.statusCode()); + } + JsonNode choices = objectMapper.readTree(response.body()).path("choices"); + if (!choices.isArray() || choices.isEmpty()) { + throw new IllegalStateException("summary card response missing choices"); + } + return choices.get(0).path("message").path("content").asText(); + } + + private static List jsonStrings(JsonNode node, int maxItems, int maxChars) { + if (!node.isArray()) { + return List.of(); + } + List values = new ArrayList<>(); + for (JsonNode item : node) { + String text = item.asText("").strip(); + if (!text.isEmpty()) { + values.add(truncate(text, maxChars)); + } + if (values.size() >= maxItems) { + break; + } + } + return List.copyOf(values); + } + + private static List jsonCardSteps(JsonNode node) { + if (!node.isArray()) { + return List.of(); + } + List values = new ArrayList<>(); + for (JsonNode item : node) { + String label = item.path("label").asText("").strip(); + String desc = item.path("desc").asText("").strip(); + if (!label.isEmpty() && !desc.isEmpty()) { + values.add(new CardStep(truncate(label, 6), truncate(desc, 20))); + } + if (values.size() >= 5) { + break; + } + } + return List.copyOf(values); + } + + private static List jsonCardObjections(JsonNode node) { + if (!node.isArray()) { + return List.of(); + } + List values = new ArrayList<>(); + for (JsonNode item : node) { + String concern = item.path("concern").asText("").strip(); + String response = item.path("response").asText("").strip(); + if (!concern.isEmpty() && !response.isEmpty()) { + values.add(new CardObjection(truncate(concern, 8), truncate(response, 20))); + } + if (values.size() >= 4) { + break; + } + } + return List.copyOf(values); + } + + private record SearchDigest(String answer, List keyPoints, List cautions) { + } + private static String dbCategory(String category) { return "sop".equalsIgnoreCase(Optional.ofNullable(category).orElse("").trim()) ? "" : category; } diff --git a/docs/AI陪练二期开发推进计划.md b/docs/AI陪练二期开发推进计划.md new file mode 100644 index 00000000..223f4f84 --- /dev/null +++ b/docs/AI陪练二期开发推进计划.md @@ -0,0 +1,392 @@ +# AI 陪练二期开发推进计划 + +> 版本:v0.2 | 日期:2026-07-06 +> 输入源:《AI陪练功能优化与缺口分析(修订版 · 2026-07-06)》+《物业行业 AI 人力资源系统 · BRD v1.1》。 +> v0.2 修订:与两份输入源逐项复核后补齐 6 处缺漏——#6/#9/#16/师徒演练的明确处置(§2)、#13 定向再练的里程碑落点(M4)、画像雷达图与 BRD L1「留」段验收承载(M4)、TechSpec P1 表映射与岗位身份前置(M1)、场景管理权限口径(M1/§7)、G6 成本上限量化(§7)。 +> 结论:二期不做“24 个缺口全量铺开”,先把 AI 陪练从 MVP 演示闭环推进到**生活顾问岗位试点可用**。 + +## 1. 二期目标 + +二期的交付主题是:围绕“生活顾问”岗位,把 AI 陪练做成可训练、可复盘、可派发、可衡量的“数字师傅”训练闭环。 + +业务上承接 BRD 的“育 / 用 / 留”三段: + +| BRD 主线 | 二期落点 | +|---|---| +| 用 | 岗位 SOP 与训练场景绑定,训练前给预习卡和红线话术 | +| 育 | 语音对手戏、多场景训练、求助、打断纠偏、训练记录 | +| 留 | 画像扩展、复盘标注、主管预警、定向再练 | + +二期不追求招聘、薪酬、绩效、知识图谱、原生 APP 的全量扩张。AI 分数仍定位为“辅助参考,人可否决”。 + +## 2. 范围切割 + +### 必做 + +| 工作包 | 覆盖缺口 | 价值 | +|---|---|---| +| 场景地基 | #1 #2 #3 #13 #17 #19 | 把代码 seed 迁到表,让岗位、场景、分发可运营 | +| 对练体验增强 | #4 #5 #7 #8 #10 | 训练前有准备、训练中有情绪变化和纠偏、语音体验更像真实业主 | +| 复盘与记录 | #11 #12 #14 #20 | 训练后能回放、标注、人工校准,形成可复盘资产 | +| 画像与主管侧 | #15 #17 #19 | 员工看成长,主管看风险并派专项 | +| 数据治理底线 | #22 #23 | Rubric 有版本,文本入库前脱敏,为试点和合规留余地 | + +### 暂缓 + +| 暂缓项 | 原因 | +|---|---| +| 实时主管旁听(#18) | WebSocket + 低延迟音频工程量大;先做事后回放 | +| 录像存储 | 涉员工隐私和 G3/G4,二期只存录音 | +| 完整离线对练(#24) | 无 LLM 不可离线;只做 SOP/预习/题目缓存 | +| 纯视觉向量检索 | 依赖素材规模和供应商合规;现有关键帧 caption 已可覆盖大部分检索 | +| 原生 APP、SSO、知识图谱、绩效挂钩 | BRD 已明确为二期后或生产闸门项 | +| 图片/视频情境输入(#9) | BRD 4.3.3 明列(拍照报修),vision 模型已就绪;但场景素材是瓶颈,待 12 条文本场景跑稳后独立评估,可与 B3 文生图联动 | +| 环境噪音模拟(#6) | 前端混音成本不高但非试点闭环必需;噪音音源授权未解决 | +| 自动案例沉淀(#16) | 挂 BRD 4.6/4.7 共创与 review_task 审核链;二期只保证"贡献度"有人工计分入口(见 M4),自动沉淀待案例量起来再评估 | +| 师徒演练(BRD 4.4) | 依赖 mentor_assignment(P1)与真人师父运营;二期由"主管复盘 + 派发"承载师徒最小语义 | +| 动态难度调级(#1 后半 / BRD 4.5) | 场景表保留 difficulty 字段即可;自动升降级依赖画像数据稳定,分发规则 v1 不做 | + +## 3. 技术路线 + +### 3.1 数据地基先行 + +二期第一优先级是把训练运营对象落库。当前现状是 2 个 seed 场景 + `aihr_practice_session` 汇总表,足够演示,不够试点。 + +建议新增或补齐以下表,统一使用 `aihr_*` 前缀: + +| 表 | 用途 | +|---|---| +| `aihr_practice_scenario` | 训练场景、岗位、项目类型、难度、人设、SOP 绑定 | +| `aihr_practice_rubric` / `aihr_practice_rubric_dimension` | Rubric 版本、维度、权重、启停 | +| `aihr_practice_assignment` | 主管/规则引擎派发每日小训、专项训练 | +| `aihr_practice_annotation` | finish 后逐句标注、好/差示例、证据 turn | +| `aihr_practice_calibration` | 人工修正评分,沉淀一致率校准集 | +| `aihr_practice_audio` 或 `audio_oss_id` 字段 | 对练录音 MinIO/sys_oss 回放 | + +注意:旧 TechSpec 中出现过 `practice_scenario` 等无前缀名称,二期实现时应同步修正文档或在 DDL 中明确采用 `aihr_practice_*`,避免和若依/未来模块冲突。 + +与 TechSpec P1 表的映射(不另建平行体系): + +- `aihr_practice_assignment` 吸收 `daily_drill`(每日一练)与 `training_camp`(专项训练营)的语义,用 `source`(daily/camp/retry/manual)+ `reason` 字段区分来源,不再单独建这两张表。 +- `mistake_book`(错题本)语义由“低分维度 → 定向再练派发”承载(见 M4),`assignment.source='retry'` 即错题重练记录。 +- 跨期五维画像仍走 TechSpec M6 的 `competency_assessment`,本计划只做单场四维分与四项画像聚合,勿与跨期评估混成一个数(BACKLOG B5 口径)。 + +### 3.2 复用现有能力 + +- 语音识别继续复用 `POST /api/ai/asr`,不新建移动端专用 ASR。 +- TTS 继续复用 `POST /api/ai/tts`,二期把 `voice` 扩成 `voice_profile`。 +- 文件与录音存储复用 `sys_oss` + MinIO。 +- SOP 求助复用 `/api/knowledge/search` 和 `/api/knowledge/summary-card`。 +- 模型配置继续走 `aihr_model_config` 的 `chat/vector/rerank/asr/tts/vision` 分类。 +- 员工端训练仍复用 `/api/train/practice/start|turn|finish`,不要另起一套移动端训练后端。 + +## 4. 里程碑计划 + +### M0. 二期基线冻结(2 天) + +目标:确认二期不再追 MVP 演示壳,而是追试点可用闭环。 + +- 锁定首个岗位:生活顾问。 +- 锁定首批场景:投诉处理、催费/停车费、报修跟进、突发求助。 +- 每类至少 3 条场景,二期首批共 12 条。 +- 每条场景必须有:人设、开场、成功条件、雷点/爽点、SOP 引用、Rubric。 +- 明确试点指标:20 名员工、每人 10 次对练、完训率 80%、满意度 4/5。 + +验收: + +- `docs/BACKLOG.md` 和本计划无冲突。 +- 内容负责人能提供或确认 12 条场景素材。 +- 开发不再把“实时旁听、录像、知识图谱、原生 APP”拉进二期。 + +### M1. 场景与 Rubric 地基(第 1-2 周) + +目标:让“场景管理”和“评分标准版本”从代码 seed 变成可运营数据。 + +后端: + +- `backend/script/sql/aihr_practice_mysql8.sql`:补二期表或新增同目录二期 DDL。 +- `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/domain/AihrPracticeDto.java`:补 scenario/rubric/assignment DTO。 +- 新增 `AihrPracticeScenarioService` 或扩展 `AihrPracticeSeedService`:优先读表,失败回退 seed。 +- `AihrPracticeController`:新增场景列表、详情、保存、启停、Rubric 查询接口。 +- `scripts/reset-dev-db.sh`:导入新 DDL 和 12 条生活顾问 seed。 + +管理端: + +- `frontend/src/api/aihr/practice.ts`:补场景/Rubric API。 +- `frontend/src/views/train/practice.vue`:先加轻量场景选择和 Rubric 预览,不做复杂 CRUD。 + +移动端: + +- `mobile/src/App.vue`:“开始训练”根据岗位/今日安排传 `scenarioId`,没有数据时回退当前 seed。 + +验收: + +- 管理端能选择不同生活顾问场景并开始对练。 +- API 可返回场景绑定的 SOP、Rubric、persona。 +- 模型失败或表为空时,原 MVP 对练仍可跑。 +- 场景 CRUD 接口挂 `hr_operator` 角色权限(BACKLOG B1“出题”职责),普通员工端只读。 +- 员工进入训练时能按登录身份取到岗位(`position`),场景列表按岗位过滤——这是 M4 派发与分发规则的身份前置。 + +### M2. 训练前与训练中增强(第 3-4 周) + +目标:把体验从“回合制答题”推进到“有准备、有情绪、有求助”的语音对手戏。 + +功能: + +- 训练前预习卡:从场景 SOP 生成“要点 / 红线 / 可说话术”。 +- 每轮 emotion 由 LLM 输出并落入 turn/session,不再只用 seed 锚点。 +- 求助按钮:训练中弹出该场景 SOP 提示,记录一次求助事件。 +- 主动打断:COACH_CHECK 命中红线时中断本轮,提示纠正后重来。 +- 方言/多语气:先支持 `voice_profile.speed/emotion/dialect`,不做语音克隆。 + +文件落点: + +- `AihrPracticeLlmService.java`:扩展 turn 输出 JSON,包含 `emotion/trustDelta/redFlag/coachHint`。 +- `AihrPracticeSeedService.java`:seed fallback 也返回同样字段。 +- `AihrSpeechDto.java` / `AihrSpeechService.java`:扩展 TTS voice 参数,保留旧字符串兼容。 +- `frontend/src/views/train/practice.vue`:加预习卡、情绪条、求助按钮、红线提示。 +- `mobile/src/App.vue`:复用训练前卡和求助逻辑,语音失败仍退文本。 + +验收: + +- 员工开始训练前能看到该场景预习卡。 +- 至少 3 条场景能看到情绪随话术变化。 +- 求助按钮命中 SOP 并显示引用。 +- 红线话术触发纠偏,但不在训练中做逐句评分。 + +### M3. 录音回放与复盘标注(第 5 周) + +目标:训练完成后能复盘,但不做实时旁听。 + +功能: + +- 训练中员工音频和 TTS 音频按需存 MinIO,记录 `oss_id`。 +- 主管复盘详情展示音频回放。 +- finish 后对整场 dialogue 做一次性逐句标注,输出“问题句 / 推荐改写 / 命中或漏掉的 SOP 点”。 +- 标杆对话先用导师改写生成“更优示例”,不等案例库自然积累。 + +文件落点: + +- `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java`:保存 session 时写 audio/annotation 信息。 +- 新增 `AihrPracticeReviewService.java`:生成和查询逐句标注。 +- `AihrMobileController.java`:复盘详情返回 audio 和 annotations。 +- `mobile/src/App.vue`:主管复盘详情增加回放和逐句标注区。 + +验收: + +- 完成一场训练后,主管端能打开回放。 +- 逐句标注只在 finish 后生成一次,刷新不重复算分。 +- 没有录音权限时仍保存文本 transcript。 + +### M4. 画像、校准与主管派发(第 6-7 周) + +目标:把训练结果变成员工成长与主管管理动作。 + +功能: + +- 员工画像扩展为四项:训练时长、贡献度、测评分、AI 等级(BACKLOG B5;贡献度二期用人工计分入口记 `incentive_point`,不做自动案例沉淀)。 +- 员工端画像以雷达图 + 趋势呈现单场四维分聚合(BRD 4.5 可视化口径,数据留跨期 `competency_assessment` 的入口)。 +- 主管端预警:连续低分、超期未训、分数下滑。 +- 批量训练任务派发:主管按岗位/短板派发专项。 +- 定向再练(#13):低分维度自动生成 `source='retry'` 的待训任务,员工“今日安排”可见——错题本语义的最小实现。 +- 人工评分校准:HR/主管可修正 AI 评分,形成校准集。 +- 训练数据脱敏:入库前处理姓名、手机号、房号等明显 PII。 + +文件落点: + +- `AihrMobileSeedService.java`:扩展 `profile()` 聚合。 +- `AihrMobileDto.java`:补画像、预警、派发 DTO。 +- `AihrMobileController.java`:新增/扩展主管预警、派发接口。 +- `frontend/src/views/train/practice.vue` 或新增管理端页:提供人工校准入口。 +- `mobile/src/App.vue`:员工画像和主管待办展示新增字段。 + +验收: + +- 员工端能看到四项画像,不做同侪排名。 +- 主管端能看到待处理预警并派发训练。 +- 低分场景完成后,员工待训列表出现对应的再练任务。 +- 人工修正评分后,保留原 AI 分和修正分。 +- 入库文本中明显手机号/房号被脱敏。 + +### M5. 试点收口(第 8 周) + +目标:二期可进入 1-2 个住宅项目小范围试点。 + +验收清单: + +- 生活顾问 12 条场景可用。 +- 员工从“今日安排”进入训练,完成后画像和主管端同步。 +- 主管能复盘、听录音、看逐句标注、派发专项。 +- AI 与人工评分分档一致率用不少于 20 条样本评估,目标 ≥70%。 +- 住宅类 SOP 问答人工评审可用率目标 ≥80%。 +- 语音单轮响应目标 ≤5 秒;超过时有文本降级。 +- 试点数据可导出:训练次数、完训率、平均分、预警数、主管复盘率。 + +## 5. 二期 API 建议 + +| 能力 | 接口 | +|---|---| +| 场景列表 | `GET /api/train/practice/scenarios?position=&scenarioType=` | +| 场景详情 | `GET /api/train/practice/scenarios/{id}` | +| 保存场景 | `POST /api/train/practice/scenarios` | +| Rubric 版本 | `GET /api/train/practice/rubrics?scenarioId=` | +| 训练预习卡 | `GET /api/train/practice/scenarios/{id}/prep-card` | +| 逐句标注 | `POST /api/train/practice/sessions/{sessionId}/annotations` | +| 评分校准 | `POST /api/train/practice/sessions/{sessionId}/calibration` | +| 主管预警 | `GET /api/aihr/mobile/practice/alerts` | +| 派发训练 | `POST /api/aihr/mobile/practice/assignments` | +| 员工待训 | `GET /api/aihr/mobile/practice/assignments` | + +这些接口都应保留 seed fallback 或空数据兜底,但真实验收必须以数据库链路为准。 + +## 6. 内容准备清单 + +二期能不能推进,最大风险不是代码,是场景内容。每个场景至少准备: + +| 字段 | 示例 | +|---|---| +| 岗位 | 生活顾问 | +| 场景类型 | 投诉 / 催费 / 报修 / 突发求助 | +| 业主人设 | 年龄、身份、说话风格、情绪、偏见 | +| 开场白 | “你们物业到底管不管楼上噪音?” | +| 显性诉求 | 投诉噪音 | +| 隐性诉求 | 想确认物业是否愿意持续跟进 | +| 雷点 | 被敷衍、先讲规定、推给业主自己协调 | +| 爽点 | 先共情、给首次反馈时间、明确责任人 | +| SOP 引用 | 对应知识库片段 ID | +| Rubric | 合规、沟通、情绪、营销四维权重 | +| 标杆话术 | 1 条优秀版本、1 条反例 | + +## 7. 决策待确认 + +| 决策 | 推荐口径 | +|---|---| +| 二期时长 | 8 周,每两周一个可验收增量 | +| 首个岗位 | 生活顾问,四保一服后置 | +| 录音合规 | 二期只录音不录像;进入训练前给明确提示 | +| 方言/TTS | 先扩 `voice_profile`,不做克隆;供应商稳定后再评估克隆 | +| AI 排序 | 分发引擎 v1 用规则表,不上 AI 排序 | +| 排名 | 先做自我对比和主管预警,不做同侪排名 | +| 场景管理权限 | 场景/Rubric CRUD 归 `hr_operator` 与管理员;主管只派发不编场景 | +| G6 成本上限 | 试点期设 LLM/ASR/TTS 月度调用金额上限与告警阈值(具体数额由 HR 负责人与供应商账单确认);超限降级为文本对练 + 关闭逐句标注 | + +## 8. 主要风险 + +| 风险 | 控制 | +|---|---| +| 内容不足 | M0 先锁 12 条生活顾问场景,内容没到位不进 M2 | +| 移动端过大 | M1 同步拆 `mobile/src/App.vue` 的训练/画像/主管面板,不继续堆单文件 | +| 成本失控 | 逐句标注只在 finish 后一次性做;总结卡/视频按需触发 | +| 评分争议 | 原 AI 分和人工修正分并存;AI 分不挂绩效 | +| 隐私问题 | 只做录音,不做录像;文本脱敏;音频存储权限明确 | +| 范围膨胀 | 实时旁听、原生 APP、知识图谱、纯视觉检索全部不进二期主线 | + +## 9. 交付物 + +| 类型 | 交付 | +|---|---| +| 产品 | 员工端训练前卡、语音对手戏、求助、回放、画像;主管端复盘、预警、派发 | +| 后端 | 场景/Rubric/Assignment/Annotation/Calibration 数据与 API | +| 管理端 | 场景选择/轻管理、Rubric 预览、评分校准 | +| 内容 | 12 条生活顾问场景、SOP 引用、Rubric、标杆话术 | +| 验收 | 20 条评分校准样本、试点数据导出、演示脚本更新 | + +## 10. 第一批实施任务 + +### Task 1: 二期 DDL 与 seed + +**Files** + +- Modify: `backend/script/sql/aihr_practice_mysql8.sql` +- Modify: `scripts/reset-dev-db.sh` +- Verify: `./scripts/dev.sh --reset` + +**Steps** + +- [ ] 增加 `aihr_practice_scenario`、`aihr_practice_rubric`、`aihr_practice_rubric_dimension`。 +- [ ] 写入 12 条生活顾问场景 seed。 +- [ ] reset 后确认场景表和 rubric 表有数据。 + +### Task 2: 场景读取替换 seed + +**Files** + +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/domain/AihrPracticeDto.java` +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java` +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrPracticeController.java` + +**Steps** + +- [ ] `start` 优先按 `scenarioId` 读数据库场景。 +- [ ] 数据库无场景时回退现有 seed。 +- [ ] `turn/finish` 保持当前契约不破坏前端。 + +### Task 3: 训练前卡和求助 + +**Files** + +- Modify: `frontend/src/api/aihr/practice.ts` +- Modify: `frontend/src/views/train/practice.vue` +- Modify: `mobile/src/App.vue` +- Reuse: `POST /api/knowledge/search` + +**Steps** + +- [ ] 开始训练前展示场景预习卡。 +- [ ] 训练中加“求助”入口,用场景 category/SOP 引用查知识库。 +- [ ] 求助失败显示明确错误,不兜假答案。 + +### Task 4: 动态情绪和红线纠偏 + +**Files** + +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeLlmService.java` +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/domain/AihrPracticeDto.java` +- Modify: `frontend/src/views/train/practice.vue` +- Modify: `mobile/src/App.vue` + +**Steps** + +- [ ] LLM turn 输出增加 `emotion/trust/redFlag/coachHint`。 +- [ ] 前端展示情绪条和红线纠偏。 +- [ ] 解析失败时回退现有 coachHint,不阻断训练。 + +### Task 5: 回放与逐句标注 + +**Files** + +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSpeechService.java` +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrPracticeSeedService.java` +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrMobileController.java` +- Modify: `mobile/src/App.vue` + +**Steps** + +- [ ] 对练音频写 `sys_oss`/MinIO 并关联 session。 +- [ ] 主管复盘详情显示音频回放。 +- [ ] finish 后生成逐句标注,刷新不重复生成。 + +### Task 6: 画像与主管派发 + +**Files** + +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrMobileSeedService.java` +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/domain/vo/AihrMobileDto.java` +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/controller/AihrMobileController.java` +- Modify: `mobile/src/App.vue` + +**Steps** + +- [ ] 画像补训练时长、贡献度、测评分、AI 等级。 +- [ ] 主管端补预警列表。 +- [ ] 主管可派发专项训练,员工端今日安排可见。 + +## 11. 二期完成定义 + +二期完成不是“所有缺口清零”,而是满足以下条件: + +- 生活顾问岗位训练从 seed 演示变成数据库驱动。 +- 员工能完成“预习 → 语音/文本训练 → 求助 → 评分 → 复盘”的闭环。 +- 主管能完成“看风险 → 听回放 → 看标注 → 派专项”的闭环。 +- 画像能支撑“下一步练什么”的规则分发。 +- 试点所需的合规、成本、人工校准底线已经留好。 diff --git a/docs/AI陪练功能优化与缺口分析 补充功能.docx b/docs/AI陪练功能优化与缺口分析 补充功能.docx new file mode 100644 index 00000000..93e827d6 Binary files /dev/null and b/docs/AI陪练功能优化与缺口分析 补充功能.docx differ diff --git a/docs/AI陪练功能优化与缺口分析(修订).md b/docs/AI陪练功能优化与缺口分析(修订).md new file mode 100644 index 00000000..b559d908 --- /dev/null +++ b/docs/AI陪练功能优化与缺口分析(修订).md @@ -0,0 +1,90 @@ +# AI 陪练功能优化与缺口分析(修订版 · 2026-07-06) + +> 基于《AI陪练功能优化与缺口分析 补充功能.docx》逐条对照代码库核实修订。 +> 修订内容:更正 4 处现状盘点错误、划掉/降级 3 条已实现或半实现的"缺口"、 +> 标注 2 处与既定架构决策的冲突、每条缺口标注对应 BACKLOG/规格锚点。 +> 原 docx 保留归档;后续排期以本文为准。 + +## 一、现有功能盘点(已按代码实况更正) + +| 能力 | 状态 | 更正说明 | +|---|---|---| +| 员工端对练 /start /turn /finish 闭环 | ✅ | 移动端带 Authorization+clientid,mode=mobile 落 `aihr_practice_session` | +| AI 扮演业主(情绪/身份/诉求人设) | ✅ | 真 LLM 生成客户回复(seed 剧本作剧情锚点);**情绪值仍走 seed 锚点,未纳入 LLM 输出** | +| 语音输入 ASR → LLM → TTS 播报 | ✅(部分) | **原文"多语气播报"系夸大**:TTS 单一默认音色,多语气/方言/语速=BACKLOG B2 待办 | +| ASR 转写可编辑后提交 | ✅ | **原文漏计且缺口表误列为缺失**:转写先填入输入框,员工可修改再提交 | +| 每轮 AI 教练提示(coachHint) | ✅ | **原文漏计**:每轮 turn 返回教练提示(卡壳/流程偏差提醒) | +| 训练完成多维评分 | ✅ | 4 维:合规/沟通/情绪/营销 + 导师改写 + 点评;LLM temperature=0 结构化输出,评分仅 /finish 一次 | +| 训练历史与画像聚合 | ✅(部分) | **原文"时长"系错误**:画像只聚合分数均值,无 duration 字段与时长统计(=BACKLOG B5 已记缺口) | +| 主管端复盘列表/详情/导师改写/标记复盘 | ✅ | `GET/POST /api/aihr/mobile/practice/reviews*` | +| 场景管理(后台配置) | ❌ | **原文错误**:场景为代码写死的 2 个 seed(投诉/催费),无管理端 CRUD,`practice_scenario` 表未建(TechSpec P0 表设计在,未实施) | + +## 二、缺口分析(修订) + +图例:🔴 确认缺口 | 🟡 半有(降级表述) | ⛔ 已实现(划掉) | ⚠️ 与既定决策冲突(按决策口径执行) + +### (一)训练前侧 + +| # | 项 | 状态 | 修订说明与锚点 | +|---|---|---|---| +| 1 | 岗位能力模型配置(按岗位设维度权重、按职级设难度) | 🔴 | ≈ BACKLOG B1(数据权限/角色)+ B8(岗位×阶段);`practice_scenario.position` 字段 TechSpec 已设计。前置依赖:岗位身份(H5_REDESIGN §5) | +| 2 | 智能训练计划引擎(每日微训练推送) | 🔴 | = B8 个性化分发引擎;蓝本已定 v1 用规则表不上 AI 排序。"推送"受限 H5 无推送通道,一期做首页「今日安排」拉取式 | +| 3 | 场景分类筛选与搜索 | 🔴 | 前置:先把场景从代码 seed 迁到 `practice_scenario` 表并做管理端 CRUD(即补上"现状盘点"第 9 行缺口),再谈筛选 | +| 4 | 训练前预习卡片 | 🟡 | **素材链路已通**:查SOP 结构化概要+总结卡即现成的"SOP要点+红线话术"卡;缺的只是挂到对练 /start 前的流程串接,前端活 | + +### (二)训练中侧 + +| # | 项 | 状态 | 修订说明与锚点 | +|---|---|---|---| +| 5 | 业主情绪动态系统 + 情绪仪表盘 | 🟡 | 客户回复已随员工话术真实变化;缺口收窄为:①emotion 纳入 LLM 每轮输出(小改)②前端情绪仪表盘 UI(真增量)。勿按"从零建系统"排期 | +| 6 | 环境噪音模拟 | 🔴 | 新增量。实现建议:TTS 播放时前端混音本地噪音音轨(Web Audio),不动后端;音源需授权素材 | +| 7 | 训练中求助按钮 | 🔴 | 新增量。可复用查SOP检索,弹出该场景 SOP 提示;扣分策略进 rubric 配置 | +| 8 | AI 教练主动打断/引导 | 🟡 | **原文定性"无"不实**:每轮已有 coachHint 轻提示;缺的是"红线触发→中断本轮→纠正后重来"机制(COACH_CHECK 规则判定已在 TechSpec 6 章设计内) | +| 9 | 图片/视频情境输入 | 🔴 | 新增量。vision 模型已配(Qwen3-VL),技术就绪;场景素材是瓶颈,可与 B3 文生图联动 | +| 10 | 方言切换 | 🔴 | = BACKLOG B2 既有项(方言 TTS/克隆);ASR 方言识别 SenseVoice 原生支持,评分侧不区分方言 | + +### (三)训练后侧 + +| # | 项 | 状态 | 修订说明与锚点 | +|---|---|---|---| +| 11 | 逐句评分与标注 | ⚠️🔴 | **与 TechSpec §6 既定决策冲突**("每轮不评分,评分仅 /finish 一次",防漂移+G6 成本)。修订口径:**finish 后的复盘阶段对整场 dialogue 一次性逐句标注**(单次 LLM 调用),不做训练中逐句打分 | +| 12 | 标杆对话对比(好/差示例并排) | 🔴 | 依赖案例库积累;"优秀示例"可先用导师改写充当,MVP 成本低 | +| 13 | 薄弱点定向再练(变体场景) | 🔴 | ≈ B8 分发引擎的"错题重练";变体生成可用 chat 模型改写 persona(如愤怒→委屈) | +| 14 | 训练全程录音存储与回放 | 🔴 | **文档独有亮点**。对练音频现用完即弃;存 MinIO 复用 sys_oss 链路即可。⚠️ 录像(摄像头)涉员工隐私,须过 G3/G4,建议一期只做录音 | +| 15 | 画像扩展(成长曲线/雷达/排名/错题集) | 🔴 | = BACKLOG B5 + TechSpec M6(competency_assessment/mistake_book P1 表);排名功能注意 G4 接受度,建议先做"自我对比"不做"同侪排名" | +| 16 | 自动案例沉淀(优秀/典型错误→教学案例) | 🔴 | 与 BRD 4.7 共创机制契合;复用案例审核链(review_task),脱敏走 G3 | + +### (四)主管/管理侧 + +| # | 项 | 状态 | 修订说明与锚点 | +|---|---|---|---| +| 17 | 训练异常预警(连续不合格/超期未训/分数下滑) | 🔴 | ≈ B8 主管监控面板;H5 无推送,一期做主管端首页预警列表 | +| 18 | 主管旁听/陪练模式 | 🔴 | **文档独有亮点**,契合"数字师傅+真师傅"带教叙事;实时旁听需 WebSocket,工程量大,建议先做"事后回放"(依赖 #14)再评估实时 | +| 19 | 批量训练任务派发 | 🔴 | = B8 主管面板"指派专项"(设计基准 key-interactions 图已画);依赖任务表(TechSpec M5 daily_drill/training_camp P1) | +| 20 | 评分校准工具(人工修正回流) | 🔴 | **文档独有亮点**;与 TechSpec"一致率≥70% 校准集+人工评审"(P1)同源,修正数据即校准集来源。挂钩 G1(AI 分仅辅助参考) | + +### (五)技术/数据层 + +| # | 项 | 状态 | 修订说明与锚点 | +|---|---|---|---| +| 21 | 语音识别结果实时纠错 | ⛔ | **已实现**:对练页与查SOP 的 ASR 转写均先入文本框、可编辑后提交。整条划掉 | +| 22 | 评分标准版本控制 | 🔴 | = TechSpec §6 既有设计(固定 prompt_version+评分缓存已实现前半);缺 rubric 后台配置+版本历史(M6 P1) | +| 23 | 训练数据脱敏 | 🔴 | = 生产前闸门 G3 的落地项;对练文本涉业主房号/姓名时自动脱敏再入库 | +| 24 | 离线训练包 | 🔴 | **文档独有亮点**,契合弱网铁律(H5_REDESIGN §1-6);但"离线对练"无 LLM 不可行,修订口径:**离线仅覆盖预习/SOP缓存/题目浏览**,对练必须在线,弱网降级文本 | + +## 三、排期建议(按依赖与性价比) + +1. **随手做**(依赖已就绪,单点小改):#4 预习卡串接、#5 emotion 入 LLM 输出、#7 求助按钮、#21(无需做) +2. **P2 一并做**(挂 B2/B5 既有排期):#10 方言、#14 录音存储回放、#15 画像扩展、#12 标杆对比 +3. **P3 挂 B8**:#1/#2/#3/#13/#17/#19 —— 全部依赖"场景表落库+岗位身份+分发规则表"三块地基 +4. **独立评估**:#11 逐句标注(按修订口径)、#20 评分校准、#16 自动沉淀、#6 噪音、#9 图片情境 +5. **暂缓**:#18 实时旁听(先回放后实时)、#24 离线包(按修订口径收窄)、录像存储(G3/G4 未过) + +## 四、原 docx 的错误清单(留档防再引用) + +1. "TTS 多语气播报"——未实现,B2 待办 +2. "场景管理(后台配置训练场景)"——不存在,场景是代码 seed +3. "画像聚合含时长"——无时长字段与聚合,B5 已记 +4. 漏计:每轮教练提示、ASR 可编辑提交(后者还被误列为缺口 #21) +5. "逐句评分"未注意与 TechSpec §6"评分仅 finish 一次"决策的冲突 +6. "业主情绪动态"未反映真 LLM 已上线后的现状 diff --git a/docs/API_INTEGRATION.md b/docs/API_INTEGRATION.md index 2c2bd57e..9bf6acc5 100644 --- a/docs/API_INTEGRATION.md +++ b/docs/API_INTEGRATION.md @@ -60,7 +60,8 @@ SOP 文档上传第三片已经落最小后端边界: | 缺失向量补跑 | `POST /api/knowledge/doc/vectorize-missing` | 扫描缺少 `embedding_json` 的片段,复用同一向量化与 Qdrant 入库链路,适合模型配置变更后或历史资料补齐向量 | | 向量库状态与重建 | `GET /api/knowledge/doc/vector-index-status`、`POST /api/knowledge/doc/rebuild-vector-index` | 模型配置页展示当前 vector 模型维度、Qdrant collection 维度、点数和片段向量数;维度不一致时可一键清空旧 embedding、删除 collection 并按当前模型重建 | | Qdrant 向量索引 | 同一上传接口内机会性执行 | embedding 写入 MySQL 后尽力 upsert 到 Qdrant;同名文档替换会尽力删除旧 points;Qdrant 不可用不影响上传和 MySQL 检索;外部 embedding 成功但 collection 维度不一致时不静默降级为 `local-hash-v1`,通过状态接口和重建入口处理 | -| 混合检索 | `POST /api/knowledge/search` | 先跑中文关键词 `LIKE` 打分和 MySQL Fulltext,再生成 query embedding 走 Qdrant,RRF 融合后交给 `category='rerank'` 模型(如硅基流动 bge-reranker-v2-m3)按语义相关性重排;rerank 未配置或失败保持 RRF 顺序,Qdrant 或外部向量接口不可用时保留关键词/全文检索 | +| 混合检索 | `POST /api/knowledge/search` | 先跑中文关键词 `LIKE` 打分和 MySQL Fulltext,再生成 query embedding 走 Qdrant,RRF 融合后交给 `category='rerank'` 模型(如硅基流动 bge-reranker-v2-m3)按语义相关性重排;rerank 未配置或失败保持 RRF 顺序,Qdrant 或外部向量接口不可用时保留关键词/全文检索。命中后若 chat 模型可用,自动生成结构化轻概要写入响应 `answer/keyPoints/cautions`(≤60字直答+3-6要点+0-3注意;temperature=0,15s 超时,硬约束不得编造、答不了明示无依据);概要失败回退片段原文拼接,检索不报错 | +| 检索总结卡 | `POST /api/knowledge/summary-card`(JSON `{queryText, category}`) | 按需生成信息图式总结卡 `{title, steps[], objections[], scripts[], reminders[]}`:复用检索命中片段(最多5条)调 chat 模型,temperature=0、30s 超时;无命中/无 chat 模型/生成失败均抛明确错误供前端重试,不兜假数据。移动端查SOP结果页「生成总结卡」按钮触发,底部弹层展示并可 TTS 朗读 | | 解析状态聚合 | `GET /api/knowledge/processing/overview` | 聚合 `aihr_knowledge_attach.status`、fragment 数、embedding 数、`sys_oss.ext1.fileSize`,生成资料处理页指标、分类、任务、链路和事件列表 | | 服务端目录导入 | `POST /api/knowledge/doc/import-local` | JSON `{ directory, category, limit }`;`directory` 只能是 `AIHR_IMPORT_ROOT` / `aihr.import.root` 下的相对目录,默认根目录为 `./.data/import`;逐文件复用上传解析链路,同步执行,保留给小批量/调试 | | 服务端导入任务 | `POST /api/knowledge/doc/import-local-task`、`GET /api/knowledge/doc/import-tasks`、`POST /api/knowledge/doc/import-tasks/{id}/cancel` | 启动后台目录导入并返回任务;任务写入 `aihr_knowledge_import_task`,页面轮询查看总数、成功数、失败数、当前文件和进度;运行中任务可取消;重试当前按同目录重新启动一轮 | diff --git a/docs/BACKLOG.md b/docs/BACKLOG.md index 06e1429c..a42afa99 100644 --- a/docs/BACKLOG.md +++ b/docs/BACKLOG.md @@ -28,6 +28,9 @@ - 方言 → 粤语/川话预置音色,或语音克隆接口用 ~15s 真实录音复刻(方言 TTS 本来就在一期集成清单第 4 项) - 吵架 → `emotion: angry` + `speed: 1.2` - 求救听不清 → TTS 出 `emotion: sad/fearful` 底音 + ffmpeg 后期加噪/压音量/断续("信道劣化"无现成 API,后期处理最可控) +- **2026-07-04 反馈增补**(截图见 `feedback/2026070423/`,解读见其 ANALYSIS.md): + - **连贯语音对手戏**:对练从"回合制答题"升级为实时情景对话——ASR→LLM(情绪人设+对话记忆)→TTS 低延迟循环,"很凶的老太太吵架"是验收场景; + - **场景库分岗位**:**生活顾问为英雄岗位优先做深**(日常小区服务/物业费停车费催缴/投诉处理全面覆盖,"老带新"式陪练;现有投诉/催费 seed 场景即属该岗位),四保一服(保安/保洁/保绿/保修/客服)其次做广;启用 `practice_scenario.position/project_type` 字段,内容不混岗。 ### B3. AI 一键生成场景培训图片(优先级 3) @@ -48,6 +51,7 @@ 2. **数字人口播**(案例讲解视频主力):腾讯智影 / 百度曦灵 / 硅基智能,按分钟计费,比文生视频便宜一个量级 3. **文生视频**(片头/场景氛围镜头):可灵 Kling / 火山即梦 Seedance / MiniMax 海螺 / 通义万相 - 选型倾向:二期先只接 **MiniMax 一家**打通(LLM + TTS + 文生图 + 视频一个 key 一套鉴权,对 2 人团队集成量最小,且同时覆盖 B2 语气声);视频质量不满意再单换可灵/即梦——全部封装在自有 provider 抽象后(参照 siliconflow embedding provider 先例)。 +- **2026-07-04 反馈:优先级上调**(解读见 `feedback/2026070423/ANALYSIS.md`)——视频不是"锦上添花的生成玩具",而是**表演型/动作类隐性知识的主传递媒介**(巡逻、查卫生、礼仪、上门催费:"几个人去、带什么、开口怎么说,经验上的东西没办法说,好的领导现场表演一遍,现在都可以用 AI 做出来")。切入点:催费上门等 1-2 条示范视频先行,验证价值再铺量。 ### B5. 员工四维画像:训练时长 / 贡献度 / 测评分 / AI 等级 @@ -79,6 +83,23 @@ - 前置:火山引擎开户 + G3/G6 供应商合规评估;图片/视频资料成规模、以图搜图成为真实高频需求。 - 已做的替代缓解(2026-07-04):视频关键帧 caption(vision prompt 带画面描述)+ rerank(bge-reranker-v2-m3)已上线,覆盖大部分检索需求;B7 只补最后一段纯视觉语义。 +### B8. 数字师傅:学习旅程模块化 + 个性化分发引擎(2026-07-04 反馈,方向级) + +来源:2026-07-04 用户反馈(截图与完整解读见 `feedback/2026070423/ANALYSIS.md`)。核心洞察:系统的对标物是"会带人的老师傅"——把言传(话术语音)、身教(动作视频)、判断(该学什么)用 AI 规模化;用户原话背书:"感觉像老带新的感觉"(截图 10)。 + +三个子块,可独立排期: + +1. **员工端五段学习旅程模块化**:入职前面试考核 → 岗前培训 → **每日小训(5-10 分钟,独立模块)** → 棘手问题即时求助("发一个语音,给我两三个 SOP 或话术"——检索输出加话术层)→ 场景模拟。角色主页做成"职责地图"(板块化菜单,不是卡片流)。 +2. **主管端学习监控面板**:员工×活动(看了哪些视频/做了哪些互动)×时长×效果,衔接 B5 四维画像与视频观看时长统计(心跳+区间合并方案,见 2026-07-04 会话讨论)。 +3. **个性化分发引擎**(系统的大脑):岗位(生活顾问为先,四保一服其后)× 阶段(入职前/岗前/在岗)× 画像短板 → 决定"今天该看什么/练什么";内容多了以后分发比生产更难("用 AI 分析该让他们看到哪些东西")。闭环:AI 生产内容 → AI 匹配媒介 → AI 决定给谁看 → 数据回流画像。 + +配套知识模型扩容:知识类型维度(SOP=标准 / 案例=参照 / 话术=直接能说 / 经验策略=火候),检索按类型分层供给;案例沉淀链路是隐性知识的采集入口,战略地位上调。 + +### 零散待办 + +- **总结卡「保存图片」按钮**(设计基准 summary-modal 图内有):需 html2canvas 类依赖做 DOM 转图,2026-07-05 P0-2 实现时按"不加依赖"红线砍掉;引依赖或后端出图方案定型后补。 +- **员工端「学」板块**(我的地图橙色块):当前进「建设中」整页,承接 B8 五段旅程的岗前课程/每日小训模块。 + ## Future Enhancements ### Model Classification Version Governance diff --git a/docs/DEMO_ACCEPTANCE.md b/docs/DEMO_ACCEPTANCE.md index 48e68acc..51244f33 100644 --- a/docs/DEMO_ACCEPTANCE.md +++ b/docs/DEMO_ACCEPTANCE.md @@ -15,7 +15,7 @@ | 顺序 | 页面 | 点击路径 | 必须看到 | |---|---|---|---| | 1 | 首页 `/index` | 打开首页 | 只展示“首页 / AI面试 / 三角色对练 / 案例沉淀 / SOP知识库 / 资料处理 / 系统设置-模型配置” | -| 2 | 移动端三端 `http://127.0.0.1:5174/h5/user`、`/h5/candidate`、`/h5/supervisor` | 打开后先手机号登录;已配置 `AIHR_SMS_LOGIN_TEMPLATE_ID` 时可真实收短信,新手机号会自动注册;员工端首页为「今天要学/练完要复盘/工作中要用/碰到问题要报/我的成长」五个情境区,点“开始训练”完成两轮提交,再打开主管端;点「遇到问题,问一句」可文本/语音查 SOP 并看到引用片段;候选人端点“开始面试/面试练习”和“补充资料”;管理端 `/recruit/interview` 审核候选资料 | 员工端显示评分、“训练完成,主管端待复盘已更新”、训练历史和能力画像;底部 tab 为整页切换(首页/训练/知识/我的互斥渲染、切换回顶);未实现入口点击弹「本期演示未开放」toast,无死按钮;主管端完训人数、“待复盘对练”计数和待复盘列表增加;训练页语音输入在浏览器录音失败时应切到“选择语音”而不是死按钮;点进待复盘记录可看评分、话术、导师改写,并可标记已复盘;候选人端可拉题/答题/评分,资料上传后显示“已提交资料”、`OSS ...`、`待审核`;HR 审核后候选人端显示 `已通过/已驳回`;接口不可用时走本地 fallback | +| 2 | 移动端三端 `http://127.0.0.1:5174/h5/user`、`/h5/candidate`、`/h5/supervisor` | 打开后先手机号登录;已配置 `AIHR_SMS_LOGIN_TEMPLATE_ID` 时可真实收短信,新手机号会自动注册;员工端首页为「师傅区(头像+气泡+按住说话/打字问+快捷chips)→今日安排→我的地图(问/练/学/报四色块)→我的成长摘要」,点“开始训练”完成两轮提交,再打开主管端;点快捷 chip 或「问」可文本/语音查 SOP,结果页点「生成总结卡·约10秒」弹出信息图卡(四步法/异议应对/开口话术/关键提醒+听一遍/开始跟读);候选人端点“开始面试/面试练习”和“补充资料”;管理端 `/recruit/interview` 审核候选资料 | 员工端显示评分、“训练完成,主管端待复盘已更新”、训练历史和能力画像;底部 tab 整页切换(员工端为 今日/练/问/我,切换回顶),滚动后师傅头像收缩为右下悬浮球;未实现入口进「建设中」整页(返回首页按钮),无死按钮;主管端完训人数、“待复盘对练”计数和待复盘列表增加;训练页语音输入在浏览器录音失败时应切到“选择语音”而不是死按钮;点进待复盘记录可看评分、话术、导师改写,并可标记已复盘;候选人端可拉题/答题/评分,资料上传后显示“已提交资料”、`OSS ...`、`待审核`;HR 审核后候选人端显示 `已通过/已驳回`;接口不可用时走本地 fallback | | 3 | AI面试 `/recruit/interview` | 生成题目 → 填满参考回答或输入真实回答 → 完成评分 | 配置 chat 模型时真实生成题目和评分;未配置时用本地 Rubric 估分,不返回固定候选人分数;应看到新增面试记录 | | 4 | 三角色对练 `/train/practice` | 开始对练 → 填入参考回复或输入真实回复 → 继续一轮 → 结束并评分 | 已完成闭环、导师改写、新增对练记录;数据库已启用 chat 模型时,客户回复与评分为真实 LLM 生成(回复不再是固定台词,分数随话术变化),未配置时使用本地剧本兜底 | | 5 | 案例沉淀 `/knowledge/cases` | 上传真实语音 → ASR 转写 → AI 整理 → 送审 → 入库 → 查看样片 | 已完成闭环,案例列表新增真实转写生成的记录;视频仍用样片兜底 | diff --git a/docs/DEV_SETUP.md b/docs/DEV_SETUP.md index 25339597..8ab204b4 100644 --- a/docs/DEV_SETUP.md +++ b/docs/DEV_SETUP.md @@ -129,7 +129,7 @@ curl -k -s https://peilian.njzhmj.top/h5/supervisor | sed -n '1,20p' - 案例沉淀:进入 `/knowledge/cases`,上传真实语音 → “AI 整理” → “送审” → “入库”,应看到“已完成闭环”和新增案例记录;上传必须先完成 ASR 转写。 - SOP知识库:进入 `/knowledge/sop`,可上传 txt/md/PDF/Word/Excel/PPT 文档入库;点击“检索” → “生成训练题”,应看到“已完成闭环”、命中数据库 SOP 原文片段和训练题;数据库不可用时页面回退 seed。 - 资料处理:进入 `/knowledge/processing`,应看到资料总量、解析任务表、处理链路、规则与风险;可用少量文件验证“批量导入/选择目录/服务端导入”。**批量导入走异步队列**:提交即返回,页面出现“本次批量上传”进度面板(排队/加工中/完成/失败 + 单条重试),后台 worker 并发 2 逐条解析入库;暂存目录默认 `./.data/staging`(`aihr.upload.staging` 覆盖)。服务端导入读取 `./.data/import` 下的相对目录,启动后台任务并在页面显示进度,运行中任务可点“取消”;目录导入的重试粒度是同目录重新导入,批量上传的重试粒度是单文件。 -- 移动端员工训练:进入 `http://127.0.0.1:5174/h5/user`,手机号登录(dev 验证码固定 `123456`)。员工端首页为「今天要学/练完要复盘/工作中要用/碰到问题要报/我的成长」五个情境区,底部 tab(首页/训练/知识/我的)为整页切换;未实现入口点击弹「建设中」toast。“开始训练” → 两次“填入建议回复 / 提交本轮”,应看到评分、导师改写、训练历史和能力画像;切到主管端后完训人数与“待复盘对练”计数增加,并展示待复盘列表;点进单条可看评分、话术、导师改写,并可标记已复盘。训练页有“语音输入”和“播报”按钮,需启用 asr/tts 模型后生效;iOS/WebView 若无法直接访问麦克风,会切到“选择语音”并用系统录音/音频文件继续走 ASR,否则降级文本。「遇到问题,问一句」打开查 SOP 面板:文本/语音提问 → 返回回答+依据+引用片段(需后端在跑;语音需 asr 模型)。 +- 移动端员工训练:进入 `http://127.0.0.1:5174/h5/user`,手机号登录(dev 验证码固定 `123456`)。员工端首页为「师傅区(mentor.png 头像+气泡+按住说话/打字问+快捷chips)→今日安排→我的地图四色块→我的成长摘要」(视觉基准 `feedback/2026070423/design/`),底部 tab(今日/练/问/我)整页切换,滚动后师傅头像收缩为右下悬浮球;未实现入口进「建设中」整页。“开始训练” → 两次“填入建议回复 / 提交本轮”,应看到评分、导师改写、训练历史和能力画像;切到主管端后完训人数与“待复盘对练”计数增加,并展示待复盘列表;点进单条可看评分、话术、导师改写,并可标记已复盘。训练页有“语音输入”和“播报”按钮,需启用 asr/tts 模型后生效;iOS/WebView 若无法直接访问麦克风,会切到“选择语音”并用系统录音/音频文件继续走 ASR,否则降级文本。快捷 chip 或「问」打开查 SOP 面板:文本/语音提问 → 轻概要(回答+要点+注意)+依据折叠;点「生成总结卡·约10秒」弹出信息图卡(四步法/异议/话术/提醒+听一遍/开始跟读),需 chat 模型;语音需 asr 模型。 - 移动端候选人:进入 `http://127.0.0.1:5174/h5/candidate`,手机号登录(dev 验证码固定 `123456`)→ “开始面试/面试练习”完成答题评分;“补充资料”选择 PDF/Word/图片后上传,应看到“已提交资料”、`OSS ...`、`待审核`。再进入管理端 `/recruit/interview` 底部“候选资料审核”点“通过/驳回”,候选人端刷新后应显示新状态。Codex 内置浏览器不支持本地文件选择时,用真实 HTTP multipart smoke 代替浏览器文件选择。 - 真 LLM 激活:在 `/system/model` 给供应商填 api_host/api_key 并启用 `category=chat` 模型后,三角色对练的客户回复与评分即为真实 LLM 生成;再启用 `category=asr/tts`(如硅基流动 SenseVoice/CosyVoice2)语音路径生效。未配置时全链路自动回退 seed。 diff --git a/docs/H5_REDESIGN.md b/docs/H5_REDESIGN.md new file mode 100644 index 00000000..cd120fee --- /dev/null +++ b/docs/H5_REDESIGN.md @@ -0,0 +1,179 @@ +# H5 前端重构设计蓝本(v1.1 · 2026-07-05) + +> 依据:BRD 选用育留主线 + `feedback/2026070423/ANALYSIS.md`(用户 13 条反馈解读)+ 2026-07-03/04 已落地的体验修复。 +> 定位一句话:**每个一线员工口袋里的老师傅**(用户原话背书:"感觉像老带新的感觉",feedback 截图 10)。 +> 本文是 P1 开工蓝本,每节可拆成独立执行规格;工程现状与红线见 `AGENTS.md` 移动端条目。 +> v1.1 变更:新增 §2.0 数字师傅形象层(甲方构想:数字形象 + 今日安排/我的地图两面板 + 语音/文字/按钮三交互),员工端首页布局随之修订,路线表 P1/P2 更新。 +> 视觉基准:`feedback/2026070423/design/` 5 张效果图 + 其 `REVIEW.md` 修正项(修正项优先于图面)。 + +## 0. 从"工具箱"到"随身师傅" + +现状前端是**功能陈列**:知识库、对练、面试是并列工具,用户要自己想"该用哪个"。 +"数字师傅"认知下,产品应像**一个人**:你问他答(言传)、他做给你看(身教)、 +他安排你今天练什么(判断)、他记得你的成长。所有设计从这个心智推导。 + +主角色:**生活顾问**(英雄岗位,沟通密集型:日常小区服务/物业费停车费催缴/投诉处理)。 +demo 数据、今日安排样例、问师傅 placeholder、对练入口场景,全部用生活顾问真实工作填充 +(seed 人设"王敏·客服管家"即该岗位,内容口径钉死,不向泛化漂移)。 +四保一服(保安/保洁/保绿/保修/客服)后置,以视频教学为主、对练为辅。 + +## 1. 六条设计铁律 + +全部从用户反馈和一线处境反推,不是审美偏好: + +| # | 铁律 | 依据 | +|---|---|---| +| 1 | **媒介即答案**:规则数字→信息图卡;话术→语音+大字;动作流程→视频。文字是给办公室的,用户戴着手套站在地库里 | ANALYSIS 跃迁一 | +| 2 | **结论先行**:任何回答 3 秒内给可执行结论,深度内容层层下钻,永不倾泻原文 | 反馈 #1/#2 | +| 3 | **一屏一事**:导航一律整页切换;弹出/页内展开/底部追加不用 | 反馈 #3"页面一定要切换" | +| 4 | **可点即明显**:按钮长得像按钮,入口行行有色差;像介绍的就不该能点 | 反馈 #4 | +| 5 | **贵操作按需触发**:AI 生成类(总结图/视频)显式按钮+预期等待动画,点了才花钱 | 反馈 #2、G6 成本闸门 | +| 6 | **弱网可用**:高频 SOP 本地缓存;请求失败有可读兜底,绝不白屏 | 一线场景(地库/电梯间) | + +## 2. 信息架构:三端三种形态 + +三端不是三份同构 tab 页,按各自使用心智取形态。 + +### 2.0 数字师傅形象层(v1.1 新增,甲方构想) + +**构想**:前端做一个数字形象,配合「今日安排」「我的地图」两个面板,语音、文字、按钮三种交互。 + +**为什么对**:形象的真正价值不是好看,是**给所有系统行为一个"发出者"**—— +"系统推送了 2 个任务"是监工,"师傅今天给你排了两件事"是带教。"老带新的感觉"靠信息 +架构只能到 60 分,剩下 40 分在这个"人"身上:任务是师傅安排的、点评是师傅说的、话术 +是师傅教的。同时直接化解 G4 员工接受度风险("被 AI 监考"→"师傅带我")。 + +**必须做轻(红线)**:BRD 一期明确不做数字人视频生成(慢且贵,G6)。本层 = **2D 插画 +形象 + 四状态状态机**,与数字人视频成本差两个数量级: + +| 要素 | 方案 | +|---|---| +| 形象 | 一张"老师傅"人设插画:可亲、专业、40+ 岁感(用户是中年一线员工,不要二次元吉祥物);用 B3 文生图快速出候选给甲方选 | +| 状态机 | **待机**(呼吸/眨眼,CSS 动画)/ **聆听**(录音中,波纹)/ **思考**(检索中)/ **说话**(TTS 播放中,音波);驱动状态的代码已存在——`sopRecording`/`sopLoading`/`sopSpeaking` 即状态源,只差绑一个可视化形象。不做嘴型同步、不做视频、不加依赖 | +| 三交互 | **按钮**(最高频):快捷问题 chips 按岗位配置(生活顾问:催费话术/投诉应对/收费标准)+ 今日安排 CTA + 地图入口,零打字路径;**语音**(现场急用):长按师傅说话→ASR→答案卡+师傅读话术,链路全部已通;**文字**(安静环境/精确提问):查SOP面板已有 | + +**两条克制原则**: +1. **形象是氛围不是路障**:默认占屏 ≤1/5,滚动收缩为右下悬浮小头像(即原"问师傅" + 悬浮球的升维合体);老用户要效率;形象加载失败时静态头像兜底(铁律 6)。 +2. **人格一致**:气泡文案、答案卡口吻("你可以这么说")、对练点评、TTS 音色统一为 + 同一个"师傅"——人格不一致比没有人格更出戏。**师傅人设卡**(名字/语气/口头禅) + 邀甲方参与定稿。 + +**分阶段**:P1 静态形象+四状态+快捷 chips;P2 配合语音对手戏,师傅"演业主"时明示 +人设切换(避免角色混乱);数字人视频远期视试点反馈再议(维持 BRD 决策)。 + +### 2.1 员工端(核心,几千日活)——师傅形象 + 双引擎首页 + +``` +┌──────────────────────────┐ +│ ┌────┐ 「王敏,今天给你 │ ← 师傅形象+气泡(≤1/5屏) +│ │师傅 │ 排了两件事」 │ 滚动收缩为右下悬浮头像 +│ └────┘ 🎤长按说话 ⌨️打字 │ 状态机:待机/聆听/思考/说话 +│ [催费话术][投诉应对][...] │ ← 快捷 chips,按岗位配置 +│ │ +│ ▍今日安排(师傅排的) │ ← 引擎一:回答"我该干什么" +│ ● 每日小训:催费沟通 8min │ 叙述者=师傅 +│ ● 待复盘:昨天的投诉对练 │ +│ │ +│ ▍我的地图(全量菜单) │ ← 引擎二:回答"我能干什么" +│ [问] 查SOP 求助话术 │ 生活顾问视图:[问][练]为大区 +│ [练] 场景模拟 错题重练 │ 每区专属色,按钮化(铁律4) +│ [学] 岗前课程 每日小训 │ +│ [报] 上报案例 拍照记录 │ +└──────────────────────────┘ + tab: 今日 | 练 | 问 | 我 +``` + +**为什么双引擎分开**:用户既要"AI 分析该让他们看什么"(安排),又要"很明确看到我可以 +对哪几个方面操作"(地图,反馈 #5)——两个不同心理诉求,混在一起谁都不满足。 +师傅形象层(§2.0)统摄两个面板:今日安排以师傅口吻叙述,问答从师傅气泡发起。 + +**职责地图按岗位配置渲染**(不写死布局):岗位 → 模块权重/排序/入口集,查配置表。 +生活顾问 [问][练] 大区;保安/保洁将来 [学](视频)为大区。 +这张配置表即分发引擎规则版 v1 的第一块积木(见 §4)。 + +**「问师傅」= 产品灵魂交互**(入口即 §2.0 形象层的语音/文字/chips)。回答统一用**答案卡系统**: + +``` +[话术卡] 你可以这么说 → 2-3 条直接能开口的话(大字,语音可播) +[要点卡] 记住这 3 点 → 信息图式排版(徽章数字/色块分区) +[依据] 折叠,点开看原文片段(现有分层已实现) +[视频] 有相关演示则给时间戳跳转 +操作:听一遍 | 跟读练一遍 | 生成总结图(按需,反馈 #2 小窗) +``` + +**"跟读练一遍"把问和练焊接**:刚问完催费话术,一键进催费对练场景——师傅的教法, +说完就让你练。这也是"问→练"数据闭环的入口(§4)。 + +### 2.2 候选人端——线性漏斗,不用 tab + +低频、目标单一(通过面试入职):进度条驱动的流程页 +**面试 → 补资料 → 岗前学习 → 等通知**,每步只有一个主按钮。不加社交/个人中心。 + +### 2.3 主管端——监工驾驶舱 + +主管不是来"使用功能"的,是来**看人和派活**的(反馈 #7): +- 今日团队动态流:谁练了什么、效果如何(时间线形态); +- 待办:复盘 / 指派; +- 团队短板热力图 → 点短板一键给相关员工指派专项。 +衔接 BACKLOG B5 四维画像、视频观看时长统计(心跳+区间合并)。 + +## 3. 答案卡的知识类型分层 + +知识模型加类型维度(ANALYSIS 跃迁二),检索输出按类型供给: + +| 类型 | 定位 | 呈现 | +|---|---|---| +| 话术 | 直接能说的话 | 话术卡(大字+语音),生活顾问最高频 | +| SOP | 标准 | 要点卡(信息图式) | +| 案例 | 参照 | 折叠区"看看别人怎么处理" | +| 经验策略 | 火候 | 融入话术卡备注("如果业主还是不满意,再…") | + +digest prompt 增加 `scripts`(话术 2-3 条)字段;案例沉淀链路是隐性知识采集入口, +供给侧战略地位上调。 + +## 4. 贯穿闭环:问 → 学 → 练 → 评 → 再分发 + +员工一周内问了 3 次催费相关问题 → 分发引擎把"催费沟通专项"排进他明天的今日安排 +→ 练完评分入画像 → 主管端看到短板收敛。 +**问答行为是最真实的短板信号**——这条数据回流是产品从"工具"变"师傅"的分水岭。 + +**克制**:分发引擎 v1 用**规则表**(岗位 × 阶段 × 近期问题类目查表),不上真 AI 排序。 +规则版一周能落地、可解释、可演示;数据够了再换模型。 + +## 5. 前置依赖:岗位身份 + +整个岗位化设计(内容口径/模块权重/场景过滤)依赖岗位字段,而 H5 登录只有手机号。 +**轻方案(必须进 P1)**:首次登录后选一次岗位(生活顾问/保安/保洁/保修/客服), +存用户 profile;组织快照(`aihr_org_snapshot`)同步后自动带出,选择页仅对无快照记录者出现。 + +## 6. 视觉方向:蓝领工具感 + +不是企业 SaaS 后台感。参照美团骑手端/顺丰小哥端(已验证一线作业者可用性范式): +- 正文 ≥16px,触点 ≥44px,高对比度(阳光下可读); +- 实心按钮+箭头,可点元素与介绍文字视觉分离(铁律 4); +- 每情境区专属强调色; +- 「建设中」提示从 toast 升级为整页/居中大提示(反馈 #3)。 + +## 7. 工程还债(与 P1 并行,先行) + +`mobile/src/App.vue` 已 2500+ 行,是所有改动越来越贵的根因: +- 重构为 vue-router 多路由 + 组件化(按端拆目录)+ design tokens; +- 高频 SOP 进 localStorage(铁律 6); +- 为小程序迁移(uni-app/Taro,AGENTS.md 既定方向)留接缝; +- 迁移期间 demo-check.sh 标记与三端演示流不可回归。 + +## 8. 路线与度量(尊重 2 人团队) + +| 阶段 | 交付 | 验证信号 | +|---|---|---| +| P0(立即) | 发布现有版本上线 | 用户基于新版的下一轮反馈 | +| P1(约一周) | 员工端新 IA(师傅形象层:静态形象+四状态+快捷 chips;今日安排+职责地图+问师傅答案卡)、岗位选择、工程拆分 | 问答采纳率(点"听/练"的比例) | +| P2 | 语音对手戏(师傅"演业主"人设切换)+ **生活顾问场景库做深**(日常服务/催费/投诉三大类) | 人均对练次数(验收 D2≥10 次) | +| P3 | 主管驾驶舱 + 规则版分发引擎 | 每日小训完成率(验收 D3 完训 80%) | +| P3 后 | 四保一服场景做广(视频为主) | — | + +**刻意砍掉的**(每加一个模块都在稀释"师傅"心智): +- 案例学习不做独立模块(折进"练"与"问"的供给侧); +- 候选人端不加社交/个人中心; +- 积分商城不碰(B5 画像先行,激励规则后置)。 diff --git a/docs/README.md b/docs/README.md index dc2f9db0..770e9256 100644 --- a/docs/README.md +++ b/docs/README.md @@ -13,7 +13,10 @@ | [AI人力资源系统一期MVP版作战清单.md](AI人力资源系统一期MVP版作战清单.md) | MVP 作战图、切割线、排期、验收承诺 | | [物业AI人力资源系统业务需求文档BRD.md](物业AI人力资源系统业务需求文档BRD.md) | 业务需求、范围边界、角色、风险与验收 | | [物业AI人力资源系统开发规格TechSpec.md](物业AI人力资源系统开发规格TechSpec.md) | 工程结构、数据模型、页面路由、API 和核心实现规格 | -| [BACKLOG.md](BACKLOG.md) | 需求池与延后事项(含 2026-07-03 甲方反馈 B1–B5:数据权限/人设语音/生成图片/生成视频/员工四维画像) | +| [BACKLOG.md](BACKLOG.md) | 需求池与延后事项(B1–B8:数据权限/人设语音/生成图片/生成视频/员工画像/视觉检索/数字师傅) | +| [H5_REDESIGN.md](H5_REDESIGN.md) | H5 前端重构设计蓝本(随身师傅定位、双引擎首页、答案卡、岗位化、路线图) | +| [AI陪练功能优化与缺口分析(修订).md](AI陪练功能优化与缺口分析(修订).md) | 陪练功能缺口 24 项核实修订与排期建议(原 docx 的代码实况校对版,排期以此为准) | +| [feedback/](feedback/) | 用户反馈原始截图与解读(按日期归档,含 ANALYSIS.md) | | [CODEBASE_ORIGINS.md](CODEBASE_ORIGINS.md) | 代码库来源与上游仓库说明 | | [prototypes/ai-hr-dashboard.png](prototypes/ai-hr-dashboard.png) | 后台首页高保真目标图 | | [prototypes/ai-hr-p0-pages-composite.png](prototypes/ai-hr-p0-pages-composite.png) | P0 四个页面组合高保真原型图 | diff --git a/docs/feedback/2026070423/.gitignore b/docs/feedback/2026070423/.gitignore new file mode 100644 index 00000000..6bd8c334 --- /dev/null +++ b/docs/feedback/2026070423/.gitignore @@ -0,0 +1 @@ +design/.DS_Store diff --git a/docs/feedback/2026070423/1.png b/docs/feedback/2026070423/1.png new file mode 100644 index 00000000..0d471105 Binary files /dev/null and b/docs/feedback/2026070423/1.png differ diff --git a/docs/feedback/2026070423/10.png b/docs/feedback/2026070423/10.png new file mode 100644 index 00000000..38800141 Binary files /dev/null and b/docs/feedback/2026070423/10.png differ diff --git a/docs/feedback/2026070423/2.png b/docs/feedback/2026070423/2.png new file mode 100644 index 00000000..a9d6d7b6 Binary files /dev/null and b/docs/feedback/2026070423/2.png differ diff --git a/docs/feedback/2026070423/3.png b/docs/feedback/2026070423/3.png new file mode 100644 index 00000000..881953f8 Binary files /dev/null and b/docs/feedback/2026070423/3.png differ diff --git a/docs/feedback/2026070423/4.png b/docs/feedback/2026070423/4.png new file mode 100644 index 00000000..0d0eca4e Binary files /dev/null and b/docs/feedback/2026070423/4.png differ diff --git a/docs/feedback/2026070423/5.png b/docs/feedback/2026070423/5.png new file mode 100644 index 00000000..93844fa7 Binary files /dev/null and b/docs/feedback/2026070423/5.png differ diff --git a/docs/feedback/2026070423/6.png b/docs/feedback/2026070423/6.png new file mode 100644 index 00000000..e152b17b Binary files /dev/null and b/docs/feedback/2026070423/6.png differ diff --git a/docs/feedback/2026070423/7.png b/docs/feedback/2026070423/7.png new file mode 100644 index 00000000..c1b7cf32 Binary files /dev/null and b/docs/feedback/2026070423/7.png differ diff --git a/docs/feedback/2026070423/8.png b/docs/feedback/2026070423/8.png new file mode 100644 index 00000000..7329ff89 Binary files /dev/null and b/docs/feedback/2026070423/8.png differ diff --git a/docs/feedback/2026070423/9.png b/docs/feedback/2026070423/9.png new file mode 100644 index 00000000..ab04b58f Binary files /dev/null and b/docs/feedback/2026070423/9.png differ diff --git a/docs/feedback/2026070423/ANALYSIS.md b/docs/feedback/2026070423/ANALYSIS.md new file mode 100644 index 00000000..4c0e5af0 --- /dev/null +++ b/docs/feedback/2026070423/ANALYSIS.md @@ -0,0 +1,131 @@ +# 2026-07-04 23:21 用户反馈解读(张立·微信语音转文字,截图 1-9) + +> 原始截图见本目录 1-9.png。反馈时用户使用的是线上 `peilian.njzhmj.top` **旧版**—— +> 2026-07-03/04 本地已完成的死按钮 toast、整页 tab 切换、情境化首页、结构化概要均未发布。 +> 采纳反馈前先发布新版,避免对旧版问题重复投入。 + +## 一、逐条解码:他说的 → 他真正要的 + +### 1. 喜报图示范(截图 1-2)——要"设计过的排版",不是"生成图片" + +用户拿南师附中录取喜报做示范:同样的信息,两段长文字"又多又长又臭",做成图后 +"条理清晰、布局有逻辑、一眼看清重点"。并自己点破实现方式:**"整理成 HTML 版本的, +是不是就不用花钱?"**(他对生图 API 成本敏感,自己在充值)。 + +→ 要的是**信息图式 HTML 卡片**:大标题、徽章式重点数字、分栏列表、色块分区。 +当前概要卡是"素文字列表",差的是视觉设计,不是数据结构。 + +### 2. 按需生成 + 小窗口(截图 3-4)——交互设计他已给全 + +原话拆解出三个明确决策: +- **总结入口是显式按钮**("旁边有个按钮,想看总结时再点一下"),不是默认自动; +- **呈现载体是弹出的小窗/卡片**(和"查到的原文"分开,有仪式感); +- **按需调用**("不用提前搞,提前搞成本高、风险大"),点了才花钱, + loading 有预期所以可接受("他会有一个预期的时间,不会太耗时")。 + +→ 轻概要可保留自动;**重排版总结卡改为按钮触发 + 小窗呈现**,还省 token。 + +### 3. "页面一定要切换"(截图 7)——弹出式反馈不算反馈 + +"按钮点完一定要跳转页面,要不然很怪。底下可能弹出来了,但我会很诧异为什么点了 +没反应,因为弹出的部分不明显。" + +→ 底部 toast 可见度不够;页内展开/底部追加模式都要改成**明确的页面切换**。 +整页 tab 已符合;「建设中」toast 需升级为整页或居中大提示。 + +### 4. 可点的要长得像可点的(截图 7) + +"每一行可以用不同的颜色,有差异性,好看也好找;同一个颜色看起来像介绍, +不像按钮不像超链接,要更像我能点的东西。" + +→ 入口行做视觉分化:每区不同强调色、按钮化(边框/箭头/底色)。 + +### 5. 角色主页 = 职责地图(截图 5) + +"不论什么身份进来,希望很明确看到我可以对哪几个方面进行安排/学习/操作, +界面分布不清晰"+"目前的感受是过于简单了,拿咱们方案再细化"。 + +→ **板块化、菜单化的角色主页**,每个角色的"我能干什么"直接摆出来,不是滚动卡片流。 + +### 6. 员工端五段旅程(截图 6)——他给出的产品主线 + +① 入职前:面试/考核(视频或语音文字) +② 岗前培训 +③ **每日小训(5-10 分钟)**——要独立成模块,当前归在"今天要学"粒度不够 +④ 碰到棘手问题:**"发一个语音,给我两三个 SOP 或话术"**——期待输出是拿来就能说的话术,不是知识片段 +⑤ 场景模拟 + +### 7. 主管端定位 = 学习监工(截图 5) + +"检查我管理的员工最近看了哪些视频、做了哪些互动学习、做了多长时间、反馈内容的 +有效性。"→ 主管端首页应是**员工学习活动监控面板**(人×活动×时长×效果)。 +与 BACKLOG B5(四维画像)、视频观看时长统计(心跳+区间合并方案)直接衔接。 + +### 8. 场景模拟分行业、分"四保一服"(截图 7) + +保安/保洁/保绿/保修/客服按岗位出对应场景,"内容要分得清,不要乱"。 +→ 对练场景库加**岗位维度**;TechSpec `practice_scenario` 本有 `position/project_type` +字段设计,应启用。 + +### 9. 多维训练 + 知识类型扩容(截图 8) + +"不光是 SOP,还有案例的应对、经验策略、方案步骤"——知识模型需要**类型维度**: +SOP(标准)/ 案例(参照)/ 话术(直接能说)/ 经验策略(火候)。检索输出按类型分层供给。 + +### 10. 连贯语音对手戏(截图 8) + +"模拟很凶的老头老太太过来吵架,说一堆话,你怎么回复?回复完他那边还要有一个回复, +最好是有连贯性的语音交互。"→ 对练不是"出题-作答-评分"的考试,是**实时情景对手戏**: +ASR→LLM(情绪人设+对话记忆)→TTS 低延迟循环 + 情绪化音色(BACKLOG B2)。 + +### 11. 动作类知识 = 视频教学(截图 8-9) + +巡逻、查卫生、收文档、礼仪、上门催费——"几个人去、带什么、开口怎么说,很多经验上 +的东西没办法说,好的领导可以给你现场表演一遍,**现在都可以用 AI 把它做出来**。" + +### 12. 内容分发要 AI 决策(截图 9) + +"是不是还得用 AI 再分析分析,到底应该让他们看到哪些东西。"——内容多了以后 +**分发比生产更难**,需要个性化学习路径引擎。 + +### 13. 生活顾问 = 英雄岗位;"老带新"验证产品北极星(截图 10,2026-07-05 00:02 补充) + +"跟业主打交道的生活顾问,大量的工作是解决日常各类小区服务工作,以及物业费、 +停车费催缴工作,客户投诉工作,还有其他的,针对这个岗位也要考虑**全面的 AI 陪练**, +**感觉像老带新的感觉**。" + +两个关键信号: +- **"老带新"是用户原话**——第二节拔高的"数字师傅"定位被用户本人验证,可作为产品北极星的依据引用; +- **生活顾问不是"第 6 个岗位"而是英雄岗位**:项目最初名称即《物业管家与生活顾问 AI 陪练系统》。四保一服偏动作型知识(视频为主),生活顾问是**沟通密集型**(催费/投诉/日常服务),正是三角色对练价值最大处——现有 seed 场景(投诉接待、催费沟通)本就属于该岗位。 +- → 场景库岗位策略:**生活顾问优先做深(日常服务/催费/投诉全面覆盖),四保一服其次做广**。 + +## 二、拔高:三个产品定位跃迁 + +**系统的真正对标物不是"知识库+练习工具",而是"老师傅"。** +物业知识传承的现实是师徒制;张立要的是把公司最稀缺的资源——会带人的老师傅—— +用 AI 克隆并规模化("数字师傅")。这正是 BRD"经验资产化中央厨房"的具象化。 +**用户原话背书(截图 10):"感觉像老带新的感觉"。** + +| 跃迁 | 从 | 到 | 依据 | +|---|---|---|---| +| 媒介 | 文字为主的呈现 | **知识类型决定媒介**:规则数字→信息图;话术→语音示范;动作流程→视频表演 | 用户是几千一线员工,文字对系统最便宜、对他们最贵 | +| 知识模型 | SOP 库 | **经验资产库**:显性(SOP)+隐性(案例/话术/策略);案例沉淀链路是隐性知识采集入口 | 截图 8"不光是 SOP" | +| 对练形态 | 回合制答题 | **有情绪、有连贯性的语音对手戏** | 截图 8"吵架的老太太" | + +**收束点**:最后"用 AI 分析该让他们看什么"= 系统的大脑—— +AI 生产内容 → AI 匹配媒介 → AI 决定给谁看 → 数据回流画像,闭环。 + +## 三、行动清单(2026-07-05 起) + +| 优先级 | 事项 | 状态/去向 | +|---|---|---| +| P0 | 发布本地版本到 peilian.njzhmj.top,让用户基于新版反馈 | 待执行 | +| P0 | 检索总结卡:按钮触发 + 小窗 + 信息图式 HTML 排版 | 待执行 | +| P1 | 可点元素视觉分化;「建设中」toast 升级整页/居中提示 | 待执行 | +| P1 | 问 SOP 输出加"推荐话术 2-3 条"(digest prompt 加 scripts 字段) | 待执行 | +| P2 | 员工端五段旅程模块化、每日小训独立、主管学习监控面板 | BACKLOG B8 | +| P2 | 个性化学习分发引擎(岗位×阶段×画像短板→内容推荐) | BACKLOG B8 | +| P2 | 连贯语音对手戏 + 情绪人设 | BACKLOG B2 增补 | +| P2 | 场景库分四保一服岗位维度 | BACKLOG B2 增补 | +| P2 | 表演型知识视频(催费上门等 1-2 条示范先行) | BACKLOG B4 优先级上调 | +| P2 | 知识类型维度(SOP/案例/话术/策略)与分层检索供给 | BACKLOG B8 | diff --git a/docs/feedback/2026070423/design/REVIEW.md b/docs/feedback/2026070423/design/REVIEW.md new file mode 100644 index 00000000..3b5890ed --- /dev/null +++ b/docs/feedback/2026070423/design/REVIEW.md @@ -0,0 +1,25 @@ +# 设计图评审结论(2026-07-05) + +> 5 张图由 gpt-image-2 基于 `docs/H5_REDESIGN.md` v1.1 + `ANALYSIS.md` 生成。 +> **结论:采纳为 P1 视觉基准**,以下修正项优先于图面。 + +## 采纳基准 + +| 图 | 用途 | +|---|---| +| h5-redesign-employee-home.png | 员工端首页布局基准(师傅区/语音条/chips/今日安排/我的地图四色/tab) | +| h5-feedback-summary-modal.png | 查SOP结果页 + 总结卡(按钮触发+底部弹层+信息图排版+保存/听/跟读) | +| h5-feedback-voice-roleplay.png | P2 语音对手戏基准(业主/师傅双角色、对话记忆、师傅提醒) | +| h5-feedback-key-interactions-board.png | 建设中整页、问师傅答案卡(话术+SOP+策略分层)、主管驾驶舱 | +| h5-redesign-effect-board.png | **师傅人设基准(老年男性、蓝色工装、插画风)**、答案卡、候选人漏斗、驾驶舱 | + +## 修正项(实现时以此为准,不照抄图面) + +1. **师傅形象全线统一**为效果总板的老师傅(男性 40-55、蓝工装、插画风)。 + 员工首页图中的中年女性写实形象**不采用**——她像用户本人,气泡却对"王敏"说话, + "发出者"逻辑(§2.0)破裂;写实照片风也有恐怖谷+加载重问题。 +2. **首页形象占屏压回 ≤1/5 并保留滚动收缩**为右下悬浮头像(图中约 1/3 屏仅取首屏氛围意象)。 +3. **对手戏图的"本轮评分参考:沟通态度80/解决效率70"不做每轮真评分**—— + TechSpec §6 既定:评分只在 /finish 一次;该位置只能放教练轻提示(高/中/低档),防设计倒逼引擎。 +4. **"全部/SOP/案例/话术"四类 tab 依赖知识类型维度字段(B8 配套)**,后端未落前 + 该 tab 组先隐藏或用现有分类近似,不做假 tab。 diff --git a/docs/feedback/2026070423/design/h5-feedback-key-interactions-board.png b/docs/feedback/2026070423/design/h5-feedback-key-interactions-board.png new file mode 100644 index 00000000..ec57e7d7 Binary files /dev/null and b/docs/feedback/2026070423/design/h5-feedback-key-interactions-board.png differ diff --git a/docs/feedback/2026070423/design/h5-feedback-summary-modal.png b/docs/feedback/2026070423/design/h5-feedback-summary-modal.png new file mode 100644 index 00000000..c56c03f8 Binary files /dev/null and b/docs/feedback/2026070423/design/h5-feedback-summary-modal.png differ diff --git a/docs/feedback/2026070423/design/h5-feedback-voice-roleplay.png b/docs/feedback/2026070423/design/h5-feedback-voice-roleplay.png new file mode 100644 index 00000000..67d0c186 Binary files /dev/null and b/docs/feedback/2026070423/design/h5-feedback-voice-roleplay.png differ diff --git a/docs/feedback/2026070423/design/h5-redesign-effect-board.png b/docs/feedback/2026070423/design/h5-redesign-effect-board.png new file mode 100644 index 00000000..46d68c52 Binary files /dev/null and b/docs/feedback/2026070423/design/h5-redesign-effect-board.png differ diff --git a/docs/feedback/2026070423/design/h5-redesign-employee-home.png b/docs/feedback/2026070423/design/h5-redesign-employee-home.png new file mode 100644 index 00000000..81c80472 Binary files /dev/null and b/docs/feedback/2026070423/design/h5-redesign-employee-home.png differ diff --git a/mobile/public/candidate-avatar.png b/mobile/public/candidate-avatar.png new file mode 100644 index 00000000..8b65016b Binary files /dev/null and b/mobile/public/candidate-avatar.png differ diff --git a/mobile/public/map-icons/ask.png b/mobile/public/map-icons/ask.png new file mode 100644 index 00000000..6bb6afcf Binary files /dev/null and b/mobile/public/map-icons/ask.png differ diff --git a/mobile/public/map-icons/learn.png b/mobile/public/map-icons/learn.png new file mode 100644 index 00000000..560fa89b Binary files /dev/null and b/mobile/public/map-icons/learn.png differ diff --git a/mobile/public/map-icons/practice.png b/mobile/public/map-icons/practice.png new file mode 100644 index 00000000..9064298c Binary files /dev/null and b/mobile/public/map-icons/practice.png differ diff --git a/mobile/public/map-icons/report.png b/mobile/public/map-icons/report.png new file mode 100644 index 00000000..5edc59c9 Binary files /dev/null and b/mobile/public/map-icons/report.png differ diff --git a/mobile/public/mentor.png b/mobile/public/mentor.png new file mode 100644 index 00000000..2dd16b73 Binary files /dev/null and b/mobile/public/mentor.png differ diff --git a/mobile/public/practice/coach-avatar.svg b/mobile/public/practice/coach-avatar.svg new file mode 100644 index 00000000..4fdfbb76 --- /dev/null +++ b/mobile/public/practice/coach-avatar.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/mobile/public/practice/customer-avatar.svg b/mobile/public/practice/customer-avatar.svg new file mode 100644 index 00000000..bbe012d7 --- /dev/null +++ b/mobile/public/practice/customer-avatar.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/public/practice/trainee-avatar.svg b/mobile/public/practice/trainee-avatar.svg new file mode 100644 index 00000000..32ebe07b --- /dev/null +++ b/mobile/public/practice/trainee-avatar.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/mobile/public/supervisor-avatar.png b/mobile/public/supervisor-avatar.png new file mode 100644 index 00000000..c2ab98c4 Binary files /dev/null and b/mobile/public/supervisor-avatar.png differ diff --git a/mobile/src/App.vue b/mobile/src/App.vue index 84c89245..1cdbdd14 100644 --- a/mobile/src/App.vue +++ b/mobile/src/App.vue @@ -32,15 +32,25 @@ -
-
+
+

AI人力助手

- @@ -52,124 +62,70 @@

- -
- -
-
- @@ -314,192 +284,232 @@