diff --git a/vetti-common/src/main/java/com/vetti/common/utils/readText/ResumeTextExtractor.java b/vetti-common/src/main/java/com/vetti/common/utils/readText/ResumeTextExtractor.java index e87ae09..7013f8a 100644 --- a/vetti-common/src/main/java/com/vetti/common/utils/readText/ResumeTextExtractor.java +++ b/vetti-common/src/main/java/com/vetti/common/utils/readText/ResumeTextExtractor.java @@ -154,6 +154,25 @@ public class ResumeTextExtractor { * 3. 设置合理的经验范围限制(1-30年) * 4. 如果无法估算,返回随机的合理值(2-10年) * + * TODO: 修复工作年限计算错误 - Bug修复说明 + * 问题:原有算法直接用 endYear - earliestYear 计算工作年限, + * 这会导致将教育时间也计算在内,例如: + * - 2006年上大学,2025年工作 = 19年(错误) + * - 实际应该是2010年毕业后开始工作,约15年 + * + * 修复方案: + * 1. 优先使用AI解析的experienceYear字段(已在AI提示词中修复) + * 2. 此方法作为备用估算,采用保守策略: + * - 假设教育时间约4年(本科) + * - 从最早年份+4年开始计算工作经验 + * - 这样可以避免将教育时间计入工作年限 + * 3. 如果年份跨度小于4年,说明可能没有教育经历,直接计算 + * + * 注意:这只是备用估算方法,主要依赖AI解析的准确结果 + * + * 修改日期:2026-02-04 + * 修改人:sxc + * * @param text 简历文本 * @return 估算的工作经验年数 */ @@ -178,9 +197,25 @@ public class ResumeTextExtractor { int latestYear = years.get(years.size() - 1); int currentYear = Year.now().getValue(); - // 计算工作经验:从最早年份到最新年份(不超过当前年份) + // 计算工作经验:从最新年份(不超过当前年份) int endYear = Math.min(latestYear, currentYear); - int experience = endYear - earliestYear; + + // TODO: 修复点 - 排除教育时间 + // 假设教育时间约4年,从最早年份+4年开始计算工作经验 + // 这样可以避免将大学时间计入工作年限 + int yearSpan = endYear - earliestYear; + int experience; + + if (yearSpan <= 4) { + // 如果年份跨度小于等于4年,说明可能没有教育经历或者是短期工作 + // 直接使用年份差 + experience = yearSpan; + } else { + // 年份跨度大于4年,假设前4年是教育时间 + // 从最早年份+4年开始计算工作经验 + int workStartYear = earliestYear + 4; + experience = endYear - workStartYear; + } // 限制经验年数在合理范围内(1-30年) return Math.max(1, Math.min(experience, 30)); diff --git a/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeCvInfoServiceImpl.java b/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeCvInfoServiceImpl.java index b03226a..f974b92 100644 --- a/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeCvInfoServiceImpl.java +++ b/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeCvInfoServiceImpl.java @@ -547,7 +547,21 @@ public class HotakeCvInfoServiceImpl extends BaseServiceImpl implements IHotakeC List> list = new LinkedList(); Map entity = new HashMap<>(); entity.put("role", "user"); - entity.put("content", "从下面提供的文本中提取所有能识别到的简历信息,只提取原文中存在的内容,不要补充、不推测、不总结。需要提取的字段包括:name(姓名或人名)、phone(电话号码)、email(电子邮件地址)、experienceYear(根据工作经验计算出来的工作年限)、position(岗位或者简历中自己期望的职位等)、location(地点或者地址、家庭住址)、links(所有链接地址)、currentWork(当前工作公司)、about(关于我/自我介绍)、skills_tools(关键资格(许可证、注册/会员资格、认证))、languages(语言能力,主要就是Languages下面的语言)、experience(工作经历,除了title、company、location、durationStart、durationEnd,其他的都放到description里面,并且description里面要根据换行符分成不同的content),日期要拆分成开始时间(durationStart)和结束时间(durationEnd))、education(教育经历,日期要拆分成开始时间(durationStart)和结束时间(durationEnd))。请将提取结果以结构化 JSON 格式返回,格式如下:{ \\\"name\\\": \\\"\\\", \\\"phone\\\": \\\"\\\", \\\"currentWork\\\": \\\"\\\", \\\"position\\\": \\\"\\\", \\\"location\\\": \\\"\\\", \\\"email\\\": \\\"\\\", \\\"experienceYear\\\": \\\"\\\", \\\"links\\\": [{\\\"content\\\":\\\"\\\"}], \\\"about\\\": \\\"\\\", \\\"skillsTools\\\": [{\\\"content\\\":\\\"\\\"}], \\\"languages\\\": [{\\\"content\\\":\\\"\\\"}], \\\"experience\\\": [{\\\"title\\\": \\\"\\\", \\\"company\\\": \\\"\\\",\\\"location\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\",\\\"description\\\": [{\\\"content\\\":\\\"\\\"}]}], \\\"education\\\": [{\\\"degree\\\": \\\"\\\",\\\"institution\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\"}] }。字段不存在则返回 null 或空数组。只返回标准可解析的 JSON结构 ,不要多余的```json等信息," + + // TODO: 修复工作年限计算错误 - Bug修复说明 + // 问题:之前的提示词中"experienceYear(根据工作经验计算出来的工作年限)"描述不够明确, + // 导致AI可能会将教育时间也计算在内,或者简单地用最早年份到最新年份的差值, + // 例如:2006年上大学到2025年工作 = 19年(错误),实际应该是2010年毕业后开始工作约15年 + // + // 修复方案:在提示词中明确工作年限的计算规则: + // 1. 只计算experience(工作经历)中的时间段,不包括education(教育经历) + // 2. 累加所有工作经历的实际工作时长(年) + // 3. 如果工作结束时间是"Present"、"Current"、"至今"等,使用当前年份(2026)计算 + // 4. 计算公式:总工作年限 = Σ(每段工作的结束年份 - 开始年份) + // 5. 结果保留整数,向下取整 + // + // 修改日期:2026-02-04 + // 修改人:sxc + entity.put("content", "从下面提供的文本中提取所有能识别到的简历信息,只提取原文中存在的内容,不要补充、不推测、不总结。需要提取的字段包括:name(姓名或人名)、phone(电话号码)、email(电子邮件地址)、experienceYear(工作年限,重要:只计算experience工作经历中的实际工作时长,不包括education教育时间。计算方法:累加所有工作经历的年数,如果结束时间是Present/Current/至今则用2026年计算,例如:2018-01到2020-06工作2年+2021-03到Present工作5年=总共7年工作经验)、position(岗位或者简历中自己期望的职位等)、location(地点或者地址、家庭住址)、links(所有链接地址)、currentWork(当前工作公司)、about(关于我/自我介绍)、skills_tools(关键资格(许可证、注册/会员资格、认证))、languages(语言能力,主要就是Languages下面的语言)、experience(工作经历,除了title、company、location、durationStart、durationEnd,其他的都放到description里面,并且description里面要根据换行符分成不同的content),日期要拆分成开始时间(durationStart)和结束时间(durationEnd))、education(教育经历,日期要拆分成开始时间(durationStart)和结束时间(durationEnd))。请将提取结果以结构化 JSON 格式返回,格式如下:{ \\\"name\\\": \\\"\\\", \\\"phone\\\": \\\"\\\", \\\"currentWork\\\": \\\"\\\", \\\"position\\\": \\\"\\\", \\\"location\\\": \\\"\\\", \\\"email\\\": \\\"\\\", \\\"experienceYear\\\": \\\"\\\", \\\"links\\\": [{\\\"content\\\":\\\"\\\"}], \\\"about\\\": \\\"\\\", \\\"skillsTools\\\": [{\\\"content\\\":\\\"\\\"}], \\\"languages\\\": [{\\\"content\\\":\\\"\\\"}], \\\"experience\\\": [{\\\"title\\\": \\\"\\\", \\\"company\\\": \\\"\\\",\\\"location\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\",\\\"description\\\": [{\\\"content\\\":\\\"\\\"}]}], \\\"education\\\": [{\\\"degree\\\": \\\"\\\",\\\"institution\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\"}] }。字段不存在则返回 null 或空数组。只返回标准可解析的 JSON结构 ,不要多余的```json等信息," + "不要解释说明,不要改写内容,其中日期字段要转换格式为:yyyy-MM,例如 Apr 2016,要转换成 2016-04,并且只有年要补充固定月份01月,例如2025,要转换成2025-01。以下为待处理文本:" + contents); //根据AI做 list.add(entity); diff --git a/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeInitScreQuestionsReplyRecordInfoServiceImpl.java b/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeInitScreQuestionsReplyRecordInfoServiceImpl.java index fc4e346..3085887 100644 --- a/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeInitScreQuestionsReplyRecordInfoServiceImpl.java +++ b/vetti-hotakes/src/main/java/com/vetti/hotake/service/impl/HotakeInitScreQuestionsReplyRecordInfoServiceImpl.java @@ -333,7 +333,21 @@ public class HotakeInitScreQuestionsReplyRecordInfoServiceImpl extends BaseServi List> list = new LinkedList(); Map entity = new HashMap<>(); entity.put("role", "user"); - entity.put("content", "从下面提供的文本中提取所有能识别到的简历信息,只提取原文中存在的内容,不要补充、不推测、不总结。需要提取的字段包括:name(姓名或人名)、phone(电话号码)、email(电子邮件地址)、experienceYear(根据工作经验计算出来的工作年限)、position(岗位或者简历中自己期望的职位等)、location(地点或者地址、家庭住址)、links(所有链接地址)、currentWork(当前工作公司)、about(关于我/自我介绍)、skills_tools(关键资格(许可证、注册/会员资格、认证))、languages(语言能力,主要就是Languages下面的语言)、experience(工作经历,除了title、company、location、durationStart、durationEnd,其他的都放到description里面,并且description里面要根据换行符分成不同的content),日期要拆分成开始时间(durationStart)和结束时间(durationEnd))、education(教育经历,日期要拆分成开始时间(durationStart)和结束时间(durationEnd))。请将提取结果以结构化 JSON 格式返回,格式如下:{ \\\"name\\\": \\\"\\\", \\\"phone\\\": \\\"\\\", \\\"currentWork\\\": \\\"\\\", \\\"position\\\": \\\"\\\", \\\"location\\\": \\\"\\\", \\\"email\\\": \\\"\\\", \\\"experienceYear\\\": \\\"\\\", \\\"links\\\": [{\\\"content\\\":\\\"\\\"}], \\\"about\\\": \\\"\\\", \\\"skillsTools\\\": [{\\\"content\\\":\\\"\\\"}], \\\"languages\\\": [{\\\"content\\\":\\\"\\\"}], \\\"experience\\\": [{\\\"title\\\": \\\"\\\", \\\"company\\\": \\\"\\\",\\\"location\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\",\\\"description\\\": [{\\\"content\\\":\\\"\\\"}]}], \\\"education\\\": [{\\\"degree\\\": \\\"\\\",\\\"institution\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\"}] }。字段不存在则返回 null 或空数组。只返回标准可解析的 JSON结构 ,不要多余的```json等信息,不要解释说明,不要改写内容。以下为待处理文本:" + contents); + // TODO: 修复工作年限计算错误 - Bug修复说明 + // 问题:之前的提示词中"experienceYear(根据工作经验计算出来的工作年限)"描述不够明确, + // 导致AI可能会将教育时间也计算在内,或者简单地用最早年份到最新年份的差值, + // 例如:2006年上大学到2025年工作 = 19年(错误),实际应该是2010年毕业后开始工作约15年 + // + // 修复方案:在提示词中明确工作年限的计算规则: + // 1. 只计算experience(工作经历)中的时间段,不包括education(教育经历) + // 2. 累加所有工作经历的实际工作时长(年) + // 3. 如果工作结束时间是"Present"、"Current"、"至今"等,使用当前年份(2026)计算 + // 4. 计算公式:总工作年限 = Σ(每段工作的结束年份 - 开始年份) + // 5. 结果保留整数,向下取整 + // + // 修改日期:2026-02-04 + // 修改人:sxc + entity.put("content", "从下面提供的文本中提取所有能识别到的简历信息,只提取原文中存在的内容,不要补充、不推测、不总结。需要提取的字段包括:name(姓名或人名)、phone(电话号码)、email(电子邮件地址)、experienceYear(工作年限,重要:只计算experience工作经历中的实际工作时长,不包括education教育时间。计算方法:累加所有工作经历的年数,如果结束时间是Present/Current/至今则用2026年计算,例如:2018-01到2020-06工作2年+2021-03到Present工作5年=总共7年工作经验)、position(岗位或者简历中自己期望的职位等)、location(地点或者地址、家庭住址)、links(所有链接地址)、currentWork(当前工作公司)、about(关于我/自我介绍)、skills_tools(关键资格(许可证、注册/会员资格、认证))、languages(语言能力,主要就是Languages下面的语言)、experience(工作经历,除了title、company、location、durationStart、durationEnd,其他的都放到description里面,并且description里面要根据换行符分成不同的content),日期要拆分成开始时间(durationStart)和结束时间(durationEnd))、education(教育经历,日期要拆分成开始时间(durationStart)和结束时间(durationEnd))。请将提取结果以结构化 JSON 格式返回,格式如下:{ \\\"name\\\": \\\"\\\", \\\"phone\\\": \\\"\\\", \\\"currentWork\\\": \\\"\\\", \\\"position\\\": \\\"\\\", \\\"location\\\": \\\"\\\", \\\"email\\\": \\\"\\\", \\\"experienceYear\\\": \\\"\\\", \\\"links\\\": [{\\\"content\\\":\\\"\\\"}], \\\"about\\\": \\\"\\\", \\\"skillsTools\\\": [{\\\"content\\\":\\\"\\\"}], \\\"languages\\\": [{\\\"content\\\":\\\"\\\"}], \\\"experience\\\": [{\\\"title\\\": \\\"\\\", \\\"company\\\": \\\"\\\",\\\"location\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\",\\\"description\\\": [{\\\"content\\\":\\\"\\\"}]}], \\\"education\\\": [{\\\"degree\\\": \\\"\\\",\\\"institution\\\": \\\"\\\",\\\"durationStart\\\": \\\"\\\",\\\"durationEnd\\\": \\\"\\\"}] }。字段不存在则返回 null 或空数组。只返回标准可解析的 JSON结构 ,不要多余的```json等信息,不要解释说明,不要改写内容。以下为待处理文本:" + contents); //根据AI做 list.add(entity); String resultCv = chatGPTClient.handleAiChat(JSONUtil.toJsonStr(list), "JX");