diff --git a/client-api/src/main/java/org/jiayunet/service/JobService.java b/client-api/src/main/java/org/jiayunet/service/JobService.java index 4f97abf..0fe167d 100644 --- a/client-api/src/main/java/org/jiayunet/service/JobService.java +++ b/client-api/src/main/java/org/jiayunet/service/JobService.java @@ -2,6 +2,7 @@ package org.jiayunet.service; import org.jiayunet.ai.AiChatAbility; import org.jiayunet.ai.AiResponseCleanTool; +import org.springframework.util.StopWatch; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.extern.slf4j.Slf4j; @@ -591,13 +592,17 @@ public class JobService { *

1. 查求职意向构造筛选条件(无意向则不设条件) 2. 排除已推荐的+已投递的 3. 取前35条候选 4. AI精筛返回8-10个

*/ public JobAgentRecommendDto recommendJobs(JobAgentRecommendParam param, Long userId) { + StopWatch sw = new StopWatch("recommendJobs"); + // 1. 查求职意向 + sw.start("查求职意向"); UserJobIntention intention = userJobIntentionMapper.selectOne(new LambdaQueryWrapper().eq(UserJobIntention::getUserId, userId)); + sw.stop(); // 2. 构造查询参数 JobQueryParam queryParam = new JobQueryParam(); queryParam.setPageNum(1); - queryParam.setPageSize(35); + queryParam.setPageSize(60); if (intention != null) { queryParam.setCategoryIds(intention.getCategoryIds()); queryParam.setRegionCodes(intention.getRegionCodes()); @@ -610,12 +615,16 @@ public class JobService { if (param.getExcludeJobIds() != null) { excludeIds.addAll(param.getExcludeJobIds()); } + sw.start("查投递记录"); List applications = userJobApplicationMapper.selectList(new LambdaQueryWrapper().eq(UserJobApplication::getUserId, userId)); + sw.stop(); applications.forEach(a -> excludeIds.add(a.getJobId())); queryParam.setExcludeJobIds(excludeIds); // 4. 查询候选岗位(完整列表数据) + sw.start("查候选岗位"); PageResult candidates = listJobs(queryParam, userId); + sw.stop(); if (candidates.getList().isEmpty()) { JobAgentRecommendDto dto = new JobAgentRecommendDto(); dto.setSummary("暂无合适的岗位推荐"); @@ -624,18 +633,28 @@ public class JobService { } // 5. 批量查岗位详情(title + description + requirement) + sw.start("查岗位详情"); List candidateJobIds = candidates.getList().stream().map(JobDto::getId).collect(Collectors.toList()); List jobDetails = jobMapper.selectList(new LambdaQueryWrapper().in(Job::getId, candidateJobIds).select(Job::getId, Job::getTitle, Job::getDescription, Job::getRequirement)); Map jobDetailMap = jobDetails.stream().collect(Collectors.toMap(Job::getId, j -> j)); + sw.stop(); - // 6. 构造候选岗位映射(供AI返回后过滤使用) - Map candidateMap = candidates.getList().stream().collect(Collectors.toMap(JobDto::getId, d -> d)); + // 6. 构造别名映射(短序号 → 真实ID)和候选岗位映射 + Map aliasToRealId = new HashMap<>(); + Map candidateMap = new HashMap<>(); + int seq = 1; + for (JobDto dto : candidates.getList()) { + aliasToRealId.put(seq, dto.getId()); + candidateMap.put(dto.getId(), dto); + seq++; + } - // 7. 构造AI输入 + // 7. 构造AI输入(用短序号替代19位雪花ID,减少token消耗) StringBuilder jobInfo = new StringBuilder(); + seq = 1; for (JobDto dto : candidates.getList()) { Job detail = jobDetailMap.get(dto.getId()); - jobInfo.append("ID:").append(dto.getId()) + jobInfo.append("ID:").append(seq++) .append("\n标题:").append(dto.getTitle()) .append("\n公司:").append(dto.getCompanyName()) .append("\n岗位职责:").append(detail != null ? detail.getDescription() : "") @@ -646,25 +665,29 @@ public class JobService { String preferenceInfo = param.getPreference() != null ? param.getPreference() : "无特殊偏好"; String systemPrompt = "你是一个求职推荐助手。根据用户的偏好,从候选岗位中选出8-10个最合适的岗位。" + - "返回JSON格式:{\"summary\":\"推荐说明(20字以内)\",\"jobIds\":[岗位ID列表]}。" + + "返回JSON格式:{\"summary\":\"推荐说明(14字以内)\",\"jobIds\":[岗位ID列表]}。" + "只返回JSON,不要其他内容。"; String userMessage = "【用户偏好】\n" + preferenceInfo + "\n\n【候选岗位】\n" + jobInfo; // 8. 调用AI + sw.start("AI调用"); String aiResponse = aiChatAbility.chat("job-recommend", systemPrompt, userMessage); String json = AiResponseCleanTool.clean(aiResponse); + sw.stop(); - // 9. 解析AI返回,过滤出选中的岗位 + log.info("recommendJobs耗时统计:\n{}", sw.prettyPrint()); + + // 9. 解析AI返回,别名映射回真实ID,过滤出选中的岗位 try { JsonNode root = HttpTool.objectMapper.readTree(json); String summary = root.path("summary").asText("为你精选了合适的岗位"); - List selectedIds = new ArrayList<>(); - root.path("jobIds").forEach(node -> selectedIds.add(node.asLong())); + List selectedRealIds = new ArrayList<>(); + root.path("jobIds").forEach(node -> { + Long realId = aliasToRealId.get(node.asInt()); + if (realId != null) selectedRealIds.add(realId); + }); - List selectedJobs = selectedIds.stream() - .map(candidateMap::get) - .filter(Objects::nonNull) - .collect(Collectors.toList()); + List selectedJobs = selectedRealIds.stream().map(candidateMap::get).filter(Objects::nonNull).collect(Collectors.toList()); JobAgentRecommendDto result = new JobAgentRecommendDto(); result.setSummary(summary); diff --git a/client-api/src/main/resources/application-local.yml b/client-api/src/main/resources/application-local.yml index 04bab45..7c6d172 100644 --- a/client-api/src/main/resources/application-local.yml +++ b/client-api/src/main/resources/application-local.yml @@ -91,7 +91,7 @@ app: job-recommend: base-url: ${AI_BASE_URL:https://ark.cn-beijing.volces.com/api/v3} api-key: ${AI_API_KEY:fd065993-bee2-4f31-8bf2-56d5d3012c02} - model: ${AI_MODEL:doubao-1-5-pro-32k-250115} + model: ${AI_MODEL:doubao-1-5-lite-32k-250115} # 岗位清洗配置