feat(aihr): persist sop prompt version

This commit is contained in:
2026-07-14 04:32:21 +08:00
parent cd2c7fdd45
commit c5a6e677dd
8 changed files with 30 additions and 9 deletions
@@ -35,7 +35,7 @@ public final class AihrSopDto {
public record SopReviewRequest(Boolean usable, String reviewer, String note) { public record SopReviewRequest(Boolean usable, String reviewer, String note) {
} }
public record SopReviewResponse(Long id, Boolean usable, String status, String reviewedAt) { public record SopReviewResponse(Long id, Boolean usable, String status, String reviewedAt, String promptVersion) {
} }
public record AnswerFeedbackRequest(String queryText, Long fragmentId, List<Long> fragmentIds, String verdict, String category, String position, String source, Long reviewId) { public record AnswerFeedbackRequest(String queryText, Long fragmentId, List<Long> fragmentIds, String verdict, String category, String position, String source, Long reviewId) {
@@ -117,6 +117,7 @@ public final class AihrSopDto {
List<RecordResponse> records, List<RecordResponse> records,
List<String> keyPoints, List<String> keyPoints,
List<String> cautions, List<String> cautions,
String promptVersion,
Long reviewId Long reviewId
) { ) {
} }
@@ -1839,6 +1839,7 @@ public class AihrSopSeedService {
response.records(), response.records(),
response.keyPoints(), response.keyPoints(),
response.cautions(), response.cautions(),
response.promptVersion(),
reviewId reviewId
); );
} }
@@ -1854,8 +1855,8 @@ public class AihrSopSeedService {
jdbcTemplate.update(connection -> { jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement(""" PreparedStatement ps = connection.prepareStatement("""
INSERT INTO aihr_sop_answer_review INSERT INTO aihr_sop_answer_review
(tenant_id, query_text, category, answer_text, reference_text, snippet_count, source, status, create_time, update_time) (tenant_id, query_text, category, answer_text, reference_text, snippet_count, source, prompt_version, status, create_time, update_time)
VALUES (?, ?, ?, ?, ?, ?, ?, '待评审', ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, '待评审', ?, ?)
""", Statement.RETURN_GENERATED_KEYS); """, Statement.RETURN_GENERATED_KEYS);
ps.setString(1, TENANT_ID); ps.setString(1, TENANT_ID);
ps.setString(2, truncate(response.queryText(), 1000)); ps.setString(2, truncate(response.queryText(), 1000));
@@ -1864,9 +1865,10 @@ public class AihrSopSeedService {
ps.setString(5, truncate(response.reference(), 1000)); ps.setString(5, truncate(response.reference(), 1000));
ps.setInt(6, response.snippets() == null ? 0 : response.snippets().size()); ps.setInt(6, response.snippets() == null ? 0 : response.snippets().size());
ps.setString(7, normalizeSearchSource(source)); ps.setString(7, normalizeSearchSource(source));
ps.setString(8, firstNonBlank(response.promptVersion(), "unknown"));
Timestamp now = Timestamp.valueOf(java.time.LocalDateTime.now()); Timestamp now = Timestamp.valueOf(java.time.LocalDateTime.now());
ps.setTimestamp(8, now);
ps.setTimestamp(9, now); ps.setTimestamp(9, now);
ps.setTimestamp(10, now);
return ps; return ps;
}, keyHolder); }, keyHolder);
Number key = keyHolder.getKey(); Number key = keyHolder.getKey();
@@ -1878,7 +1880,7 @@ public class AihrSopSeedService {
private SopReviewResponse sopReview(Long id) { private SopReviewResponse sopReview(Long id) {
List<SopReviewResponse> rows = jdbcTemplate.query(""" List<SopReviewResponse> rows = jdbcTemplate.query("""
SELECT id, usable, status, reviewed_time SELECT id, usable, status, reviewed_time, prompt_version
FROM aihr_sop_answer_review FROM aihr_sop_answer_review
WHERE tenant_id = ? AND id = ? WHERE tenant_id = ? AND id = ?
LIMIT 1 LIMIT 1
@@ -1888,7 +1890,8 @@ public class AihrSopSeedService {
rs.getLong("id"), rs.getLong("id"),
rs.getObject("usable") == null ? null : rs.getBoolean("usable"), rs.getObject("usable") == null ? null : rs.getBoolean("usable"),
rs.getString("status"), rs.getString("status"),
reviewedTime == null ? "" : reviewedTime.toLocalDateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) reviewedTime == null ? "" : reviewedTime.toLocalDateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")),
firstNonBlank(rs.getString("prompt_version"), "unknown")
); );
}, TENANT_ID, id); }, TENANT_ID, id);
return rows.isEmpty() ? null : rows.get(0); return rows.isEmpty() ? null : rows.get(0);
@@ -1936,6 +1939,7 @@ public class AihrSopSeedService {
`reference_text` varchar(1000) DEFAULT NULL COMMENT '引用', `reference_text` varchar(1000) DEFAULT NULL COMMENT '引用',
`snippet_count` int DEFAULT 0 COMMENT '命中片段数', `snippet_count` int DEFAULT 0 COMMENT '命中片段数',
`source` varchar(50) DEFAULT 'knowledge_search' COMMENT '来源', `source` varchar(50) DEFAULT 'knowledge_search' COMMENT '来源',
`prompt_version` varchar(80) DEFAULT NULL COMMENT '答案生成提示词版本',
`usable` tinyint DEFAULT NULL COMMENT '人工评审是否可用', `usable` tinyint DEFAULT NULL COMMENT '人工评审是否可用',
`reviewer` varchar(100) DEFAULT NULL COMMENT '评审人', `reviewer` varchar(100) DEFAULT NULL COMMENT '评审人',
`note` varchar(500) DEFAULT NULL COMMENT '评审备注', `note` varchar(500) DEFAULT NULL COMMENT '评审备注',
@@ -1948,6 +1952,7 @@ public class AihrSopSeedService {
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='AI HR SOP问答人工评审'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='AI HR SOP问答人工评审';
"""); """);
ensureColumn("aihr_sop_answer_review", "source", "ALTER TABLE aihr_sop_answer_review ADD COLUMN `source` varchar(50) DEFAULT 'knowledge_search' COMMENT '来源' AFTER `snippet_count`"); ensureColumn("aihr_sop_answer_review", "source", "ALTER TABLE aihr_sop_answer_review ADD COLUMN `source` varchar(50) DEFAULT 'knowledge_search' COMMENT '来源' AFTER `snippet_count`");
ensureColumn("aihr_sop_answer_review", "prompt_version", "ALTER TABLE aihr_sop_answer_review ADD COLUMN `prompt_version` varchar(80) DEFAULT NULL COMMENT '答案生成提示词版本' AFTER `source`");
sopReviewTableReady = true; sopReviewTableReady = true;
} }
} }
@@ -2293,6 +2298,7 @@ public class AihrSopSeedService {
records(), records(),
digest.keyPoints(), digest.keyPoints(),
appendCautions(digest.cautions(), extraCautions), appendCautions(digest.cautions(), extraCautions),
digest.promptVersion(),
null null
); );
} }
@@ -2356,6 +2362,7 @@ public class AihrSopSeedService {
records(), records(),
List.of(), List.of(),
appendCautions(List.of("未生成结构化概要,已返回命中原文片段。"), extraCautions), appendCautions(List.of("未生成结构化概要,已返回命中原文片段。"), extraCautions),
null,
null null
); );
} }
@@ -2660,6 +2667,7 @@ public class AihrSopSeedService {
records(), records(),
List.of(), List.of(),
cautions, cautions,
null,
null null
); );
} }
@@ -210,6 +210,7 @@ CREATE TABLE IF NOT EXISTS `aihr_sop_answer_review` (
`reference_text` varchar(1000) DEFAULT NULL COMMENT '引用', `reference_text` varchar(1000) DEFAULT NULL COMMENT '引用',
`snippet_count` int DEFAULT 0 COMMENT '命中片段数', `snippet_count` int DEFAULT 0 COMMENT '命中片段数',
`source` varchar(50) DEFAULT 'knowledge_search' COMMENT '来源', `source` varchar(50) DEFAULT 'knowledge_search' COMMENT '来源',
`prompt_version` varchar(80) DEFAULT NULL COMMENT '答案生成提示词版本',
`usable` tinyint DEFAULT NULL COMMENT '人工评审是否可用', `usable` tinyint DEFAULT NULL COMMENT '人工评审是否可用',
`reviewer` varchar(100) DEFAULT NULL COMMENT '评审人', `reviewer` varchar(100) DEFAULT NULL COMMENT '评审人',
`note` varchar(500) DEFAULT NULL COMMENT '评审备注', `note` varchar(500) DEFAULT NULL COMMENT '评审备注',
+1 -1
View File
@@ -11,7 +11,7 @@
| 正式试点数据导出 | `GET /api/train/practice/export?startDate=YYYY-MM-DD&endDate=YYYY-MM-DD` | 起止日期必填且包含结束日;只统计窗口内能通过唯一手机号或外部 ID 映射到在职组织快照的正式会话,排除重复手机号和身份碰撞。完训定义为每人至少 10 次,校准必须关联同一窗口内正式会话;CSV 同时给出校准命中数、SOP 可用数、满意度响应数/平均分,以及明细级 AI 分、人工校准分、校准人、校准时间、最终采用分、满意度分和意见,避免用四舍五入后的比率反推门禁状态;汇总和明细均携带正式人员及项目口径,不混入历史 seed/开发身份 | | 正式试点数据导出 | `GET /api/train/practice/export?startDate=YYYY-MM-DD&endDate=YYYY-MM-DD` | 起止日期必填且包含结束日;只统计窗口内能通过唯一手机号或外部 ID 映射到在职组织快照的正式会话,排除重复手机号和身份碰撞。完训定义为每人至少 10 次,校准必须关联同一窗口内正式会话;CSV 同时给出校准命中数、SOP 可用数、满意度响应数/平均分,以及明细级 AI 分、人工校准分、校准人、校准时间、最终采用分、满意度分和意见,避免用四舍五入后的比率反推门禁状态;汇总和明细均携带正式人员及项目口径,不混入历史 seed/开发身份 |
| 对练语音 | `POST /api/ai/asr`(multipart 字段 `file`,≤5MB)、`POST /api/ai/tts`(JSON `{text≤300字, voice}`,返回 `{audioUrl}` base64 dataURL) | 走 OpenAI-compatible audio 接口(如硅基流动 SenseVoice/CosyVoice2);模型管理需启用 `category=asr/tts` 配置;移动端优先用浏览器录音,`getUserMedia/MediaRecorder` 不可用或麦克风权限失败时,用 `audio/*` file input 选择/录制音频后继续调同一 ASR 接口;ASR/TTS 未配置或失败返回 fail,前端降级文本 | | 对练语音 | `POST /api/ai/asr`(multipart 字段 `file`,≤5MB)、`POST /api/ai/tts`(JSON `{text≤300字, voice}`,返回 `{audioUrl}` base64 dataURL) | 走 OpenAI-compatible audio 接口(如硅基流动 SenseVoice/CosyVoice2);模型管理需启用 `category=asr/tts` 配置;移动端优先用浏览器录音,`getUserMedia/MediaRecorder` 不可用或麦克风权限失败时,用 `audio/*` file input 选择/录制音频后继续调同一 ASR 接口;ASR/TTS 未配置或失败返回 fail,前端降级文本 |
| 案例沉淀 `/knowledge/cases` | `POST /api/knowledge/case/upload`、`/organize`、`/curate` | `/upload` 改为 multipart 真实语音上传并走 ASR;`/organize` 用真实转写调 chat 模型整理案例,未配置模型时按真实 transcript 本地结构化;不再用固定样例转写冒充成功 | | 案例沉淀 `/knowledge/cases` | `POST /api/knowledge/case/upload`、`/organize`、`/curate` | `/upload` 改为 multipart 真实语音上传并走 ASR;`/organize` 用真实转写调 chat 模型整理案例,未配置模型时按真实 transcript 本地结构化;不再用固定样例转写冒充成功 |
| SOP知识库 `/knowledge/sop` | `POST /api/knowledge/search`、`POST /api/knowledge/answer-feedback`、`GET /api/knowledge/position-sop`、`POST /api/knowledge/doc/upload` | 已接入 MySQL Fulltext + Qdrant 混合召回、岗位学习适配摘要、OSS-first 文档上传、txt/md/PDF/Word/Excel/PPT 解析和 embedding 写入,失败回退 seed;搜索返回 `reviewId`,员工反馈回传并保存该评审批次,管理端可继续复核;`position-sop` 目前只返回一期生活顾问学习导航,不代表正式上岗资格 | | SOP知识库 `/knowledge/sop` | `POST /api/knowledge/search`、`POST /api/knowledge/answer-feedback`、`GET /api/knowledge/position-sop`、`POST /api/knowledge/doc/upload` | 已接入 MySQL Fulltext + Qdrant 混合召回、岗位学习适配摘要、OSS-first 文档上传、txt/md/PDF/Word/Excel/PPT 解析和 embedding 写入,失败回退 seed;搜索返回 `reviewId` 与 `promptVersion`,员工反馈回传并保存该评审批次,SOP 人工评审记录同时保留答案生成提示词版本,管理端可继续复核;`position-sop` 目前只返回一期生活顾问学习导航,不代表正式上岗资格 |
| 资料处理 `/knowledge/processing` | `GET /api/knowledge/processing/overview`、`POST /api/knowledge/doc/upload-async`、`GET /api/knowledge/doc/upload-items`、`POST /api/knowledge/doc/upload-items/{id}/retry`、`POST /api/knowledge/doc/import-local-task`、`GET /api/knowledge/doc/import-tasks`、`POST /api/knowledge/doc/import-tasks/{id}/cancel` | 已接入解析任务状态聚合;**批量上传走异步队列**:接口只暂存+入队即秒回,后台 worker(并发 2)逐条解析/归类/向量化;ZIP 在 worker 内安全解压后把支持的子文件继续入同一批次队列,页面按批次轮询进度、失败可单文件重试;服务端目录导入、进度轮询和任务取消保留,失败回退 seed | | 资料处理 `/knowledge/processing` | `GET /api/knowledge/processing/overview`、`POST /api/knowledge/doc/upload-async`、`GET /api/knowledge/doc/upload-items`、`POST /api/knowledge/doc/upload-items/{id}/retry`、`POST /api/knowledge/doc/import-local-task`、`GET /api/knowledge/doc/import-tasks`、`POST /api/knowledge/doc/import-tasks/{id}/cancel` | 已接入解析任务状态聚合;**批量上传走异步队列**:接口只暂存+入队即秒回,后台 worker(并发 2)逐条解析/归类/向量化;ZIP 在 worker 内安全解压后把支持的子文件继续入同一批次队列,页面按批次轮询进度、失败可单文件重试;服务端目录导入、进度轮询和任务取消保留,失败回退 seed |
| 组织人员同步 | `POST /api/aihr/org/sync` | 从开放组织同步系统的 `/api/open/v1/sync/snapshot` 拉取 `company/department/employee` 快照,分页参数使用 `limit`;员工手机号只落 `person_phone` 用于移动端身份映射,不在组织列表响应暴露;岗位识别 `position/job_title/post/job_name/role/title` 等字段。默认 `replaceExisting=true`,写入前必须先用 `{"dryRun":true}`;dry-run 不访问本地快照表、不执行 DDL/写库,返回 `phoneLinked/maskedPhone/suspectText/warnings` 且 `syncedCount=0`。2026-07-10 源接口实测 3474 人仅 1 个可用手机号、3473 个脱敏手机号、8 条疑似乱码,因此未执行覆盖同步,正式试点需上游先开放至少 20 名试点人员手机号 | | 组织人员同步 | `POST /api/aihr/org/sync` | 从开放组织同步系统的 `/api/open/v1/sync/snapshot` 拉取 `company/department/employee` 快照,分页参数使用 `limit`;员工手机号只落 `person_phone` 用于移动端身份映射,不在组织列表响应暴露;岗位识别 `position/job_title/post/job_name/role/title` 等字段。默认 `replaceExisting=true`,写入前必须先用 `{"dryRun":true}`;dry-run 不访问本地快照表、不执行 DDL/写库,返回 `phoneLinked/maskedPhone/suspectText/warnings` 且 `syncedCount=0`。2026-07-10 源接口实测 3474 人仅 1 个可用手机号、3473 个脱敏手机号、8 条疑似乱码,因此未执行覆盖同步,正式试点需上游先开放至少 20 名试点人员手机号 |
| 移动端手机号登录 | `GET /resource/sms/code`、`POST /auth/mobile/sms-login` | 已复用 sms4j 阿里云配置 `config1` 和 RuoYi `sms` 授权策略;短信发送成功后才写 Redis 验证码;手机号不存在时自动注册 `app_user`;`aihr.sms.dev-fixed-code` 非空时不真发短信、验证码固定(dev 默认 `123456`,prod profile 代码级强制失效) | | 移动端手机号登录 | `GET /resource/sms/code`、`POST /auth/mobile/sms-login` | 已复用 sms4j 阿里云配置 `config1` 和 RuoYi `sms` 授权策略;短信发送成功后才写 Redis 验证码;手机号不存在时自动注册 `app_user`;`aihr.sms.dev-fixed-code` 非空时不真发短信、验证码固定(dev 默认 `123456`,prod profile 代码级强制失效) |
+1 -1
View File
@@ -39,7 +39,7 @@
| 智能错题本 | 3.1、4.4、TechSpec P1 | 已补员工本人错题本和主管 `/api/aihr/mobile/practice/team` 内的 `mistakes` 聚合:按当前项目权限统计场景、归因、次数、影响人数、平均分和最近发生时间,员工端仍可跳已有 retry assignment;不新增独立错题事实表 | 继续用正式组织快照和试点窗口验证主管只看到本项目数据;后续可在真实错题量上来后补趋势/导出,不把本地演示样本当正式指标 | | 智能错题本 | 3.1、4.4、TechSpec P1 | 已补员工本人错题本和主管 `/api/aihr/mobile/practice/team` 内的 `mistakes` 聚合:按当前项目权限统计场景、归因、次数、影响人数、平均分和最近发生时间,员工端仍可跳已有 retry assignment;不新增独立错题事实表 | 继续用正式组织快照和试点窗口验证主管只看到本项目数据;后续可在真实错题量上来后补趋势/导出,不把本地演示样本当正式指标 |
| 案例学习/视频详情 | 3.1、4.6、矩阵第35行 | `AihrCaseService` 当前用内存 `CaseState` + `aihr_case_record` 保存转写/摘要,没有稳定 media URL、学习点、主管点评和案例详情 API;仓库也没有可用于一期验收的预渲染样片资产 | 先由业务提供样片或正式媒体 URL,并确定案例详情、学习点、主管点评字段,再扩持久化契约和权限;没有真实媒体 URL 时不造播放按钮 | | 案例学习/视频详情 | 3.1、4.6、矩阵第35行 | `AihrCaseService` 当前用内存 `CaseState` + `aihr_case_record` 保存转写/摘要,没有稳定 media URL、学习点、主管点评和案例详情 API;仓库也没有可用于一期验收的预渲染样片资产 | 先由业务提供样片或正式媒体 URL,并确定案例详情、学习点、主管点评字段,再扩持久化契约和权限;没有真实媒体 URL 时不造播放按钮 |
| 案例库质量与数量 | L1-B、4.6/4.7 | 上传/整理/筛选路径已有;“至少20条脱敏案例”和每周每项目一条需要正式数据证明,当前也没有可用于一期样片验收的真实视频资产 | 先按项目窗口采集并脱敏检查,再补案例统计/学习详情导出;不使用空壳视频或演示 seed 冒充 | | 案例库质量与数量 | L1-B、4.6/4.7 | 上传/整理/筛选路径已有;“至少20条脱敏案例”和每周每项目一条需要正式数据证明,当前也没有可用于一期样片验收的真实视频资产 | 先按项目窗口采集并脱敏检查,再补案例统计/学习详情导出;不使用空壳视频或演示 seed 冒充 |
| RAG 可用率与知识治理 | 4.7、L1-C、G3 | 搜索、引用、反馈、知识缺口和人工评审入口已有;本轮已让员工反馈保存同一次搜索的 `reviewId`,但尚未保存模型/提示词答案版本;住宅核心流程覆盖率与可用率仍需正式评审数据,不能宣称完成逐问答全链路追溯 | 下一步由业务确认答案版本口径,再把 `prompt_version`/检索批次纳入评审批次;先用当前 `reviewId + query + fragment` 做可追溯统计,不伪造模型版本 | | RAG 可用率与知识治理 | 4.7、L1-C、G3 | 搜索、引用、反馈、知识缺口和人工评审入口已有;员工反馈保存同一次搜索的 `reviewId`,SOP 评审记录和搜索响应现在保留答案生成 `promptVersion`;住宅核心流程覆盖率与可用率仍需正式评审数据,不能宣称完成逐问答全链路追溯 | 后续由业务确认模型/检索批次的正式版本口径,再补检索批次与模型版本;当前先用 `reviewId + promptVersion + query + fragment` 做可追溯统计,不伪造正式可用率 |
| 方言语音与多模态训练验收 | 4.3.2、4.3.3、L1-C | ASR/TTS、音频 OSS 留痕和文本降级链路已有;但当前没有四川话/粤语等方言供应商配置、样本集和准确率/响应时延证据,也没有把图像/视频训练输入纳入一期验收口径 | 先确定一期方言厂商和可接受指标,用真实短音频做至少一条方言路径回归;图像/视频输入另按场景与合规边界评审,不用通用文本链路冒充完成 | | 方言语音与多模态训练验收 | 4.3.2、4.3.3、L1-C | ASR/TTS、音频 OSS 留痕和文本降级链路已有;但当前没有四川话/粤语等方言供应商配置、样本集和准确率/响应时延证据,也没有把图像/视频训练输入纳入一期验收口径 | 先确定一期方言厂商和可接受指标,用真实短音频做至少一条方言路径回归;图像/视频输入另按场景与合规边界评审,不用通用文本链路冒充完成 |
### P2/P3:明确暂缓,不应混入当前修复 ### P2/P3:明确暂缓,不应混入当前修复
+9 -1
View File
@@ -38,6 +38,7 @@ export type SopSearchResponse = {
records: SopRecord[]; records: SopRecord[];
keyPoints?: string[]; keyPoints?: string[];
cautions?: string[]; cautions?: string[];
promptVersion?: string | null;
reviewId?: number; reviewId?: number;
}; };
@@ -46,6 +47,7 @@ export type SopReviewResponse = {
usable: boolean | null; usable: boolean | null;
status: string; status: string;
reviewedAt: string; reviewedAt: string;
promptVersion?: string | null;
}; };
export type AnswerFeedbackItem = { export type AnswerFeedbackItem = {
@@ -79,7 +81,13 @@ export type SopUploadResponse = {
snippets: SopSnippet[]; snippets: SopSnippet[];
}; };
export function searchKnowledgeSop(data: { queryText: string; category: string; position?: string; source?: string; limit: number }): Promise<ApiResult<SopSearchResponse>> { export function searchKnowledgeSop(data: {
queryText: string;
category: string;
position?: string;
source?: string;
limit: number;
}): Promise<ApiResult<SopSearchResponse>> {
return request({ return request({
url: '/api/knowledge/search', url: '/api/knowledge/search',
method: 'post', method: 'post',
+1
View File
@@ -101,6 +101,7 @@ export interface KnowledgeSearchResponse {
records: unknown[]; records: unknown[];
keyPoints?: string[]; keyPoints?: string[];
cautions?: string[]; cautions?: string[];
promptVersion?: string | null;
reviewId?: number | null; reviewId?: number | null;
} }
+2
View File
@@ -398,6 +398,8 @@ contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/control
contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "仅支持 txt/md/markdown/pdf/doc/docx/xls/xlsx/ppt/pptx/图片文件" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "仅支持 txt/md/markdown/pdf/doc/docx/xls/xlsx/ppt/pptx/图片文件"
contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "/embeddings" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "/embeddings"
contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "aihr_knowledge_gap" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "aihr_knowledge_gap"
contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "prompt_version"
contains backend/script/sql/aihr_practice_mysql8.sql "prompt_version"
contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "review_id" contains backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java "review_id"
contains mobile-uni/src/services/knowledge.ts "reviewId: params.reviewId" contains mobile-uni/src/services/knowledge.ts "reviewId: params.reviewId"
contains backend/script/sql/aihr_knowledge_mysql8.sql "embedding_json" contains backend/script/sql/aihr_knowledge_mysql8.sql "embedding_json"