From 24fdfdea4cc893d0775d8503c8d454c1a1656a8d Mon Sep 17 00:00:00 2001 From: python Date: Tue, 9 Dec 2025 15:32:25 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=BC=BAAI=E6=9C=8D=E5=8A=A1=E7=9A=84?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E6=8F=90=E5=8F=96=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=8F=90=E5=8F=96=E5=8A=A9=E6=89=8B=E7=9A=84?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=EF=BC=8C=E6=B7=BB=E5=8A=A0=E5=90=8E=E5=A4=84?= =?UTF-8?q?=E7=90=86=E9=80=BB=E8=BE=91=E4=BB=A5=E6=8E=A8=E6=96=AD=E7=BC=BA?= =?UTF-8?q?=E5=A4=B1=E7=9A=84=E6=80=A7=E5=88=AB=E3=80=81=E8=81=8C=E7=BA=A7?= =?UTF-8?q?=E5=92=8C=E7=BA=BF=E7=B4=A2=E6=9D=A5=E6=BA=90=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=EF=BC=8C=E7=A1=AE=E4=BF=9D=E6=8F=90=E5=8F=96=E7=BB=93=E6=9E=9C?= =?UTF-8?q?=E7=9A=84=E5=87=86=E7=A1=AE=E6=80=A7=E5=92=8C=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/ai_service.py | 71 ++++++++++++- 性别字段缺失问题分析与修复.md | 185 ++++++++++++++++++++++++++++++++++ 2 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 性别字段缺失问题分析与修复.md diff --git a/services/ai_service.py b/services/ai_service.py index af7281d..e3981bc 100644 --- a/services/ai_service.py +++ b/services/ai_service.py @@ -234,7 +234,7 @@ class AIService: "messages": [ { "role": "system", - "content": "你是一个专业的数据提取助手。请从输入文本中提取结构化信息,并严格按照JSON格式返回结果。\n\n核心要求:\n1. 仔细阅读输入文本,提取所有相关信息\n2. 如果文本中明确提到信息(如性别、年龄、职务、职级等),必须提取,不能设为空\n3. 性别字段:识别\"男\"、\"女\"、\"男性\"、\"女性\"等词汇,统一转换为\"男\"或\"女\"\n4. 只返回JSON对象,不要包含任何其他文字、思考过程或markdown标记\n5. 字段名必须严格按照示例格式,使用正确的字段编码:\n - 使用\"target_professional_rank\",不要使用\"_professional_rank\"\n - 使用\"clue_source\",不要使用\"_source\"或\"source\"\n - 使用\"target_organization\",不要使用\"target_organisation\"\n6. JSON格式必须完整且有效,所有字段名使用双引号" + "content": "你是一个专业的数据提取助手。请从输入文本中提取结构化信息,并严格按照JSON格式返回结果。\n\n⚠️ 核心要求(必须严格遵守):\n\n1. 字段提取要求:\n - 如果文本中明确提到信息(如性别、年龄、职务、职级、线索来源等),必须提取,绝对不能设为空字符串\n - 性别字段(target_gender):如果文本中出现\"男\"、\"女\"、\"男性\"、\"女性\"、\"先生\"、\"女士\"等任何表示性别的词汇,必须提取并转换为\"男\"或\"女\",不能为空\n - 职级字段(target_professional_rank):如果文本中提到\"正处级\"、\"副处级\"、\"正科级\"等,必须提取,不能为空\n - 线索来源字段(clue_source):如果文本中提到\"举报\"、\"群众举报\"、\"来信\"等,必须提取,不能为空\n\n2. 字段名格式要求(严格禁止错误):\n - 必须使用\"target_professional_rank\",禁止使用\"_professional_rank\"或任何下划线前缀\n - 必须使用\"clue_source\",禁止使用\"_source\"、\"source\"或任何下划线前缀\n - 必须使用\"target_organization\",禁止使用\"target_organisation\"(英式拼写)\n - 所有字段名必须严格按照JSON示例中的字段编码,不能随意修改\n\n3. JSON格式要求:\n - 只返回JSON对象,不要包含任何其他文字、思考过程、markdown代码块标记或```json标记\n - 所有字段名必须使用双引号\n - 必须返回所有要求的字段,即使值为空字符串也要包含在JSON中\n - JSON格式必须完整且有效,不能有语法错误\n\n4. 提取逻辑:\n - 逐字逐句仔细阅读输入文本,不要遗漏任何信息\n - 对于性别、职级、线索来源等关键字段,请特别仔细查找\n - 如果文本中明确提到某个信息,必须提取出来,不能设为空" }, { "role": "user", @@ -1273,6 +1273,75 @@ class AIService: data['target_position'] = pos print(f"[AI服务] 后处理:从单位及职务 '{org_pos}' 提取职务: {pos}") + # 3. 从工作基本情况中提取性别(如果target_gender为空) + if 'target_gender' in field_code_map and (not data.get('target_gender') or data.get('target_gender') == ''): + # 尝试从工作基本情况中提取性别 + work_info = data.get('target_work_basic_info', '') + if work_info: + # 匹配模式:XXX,男,... 或 XXX,女,... + gender_match = re.search(r'[,,]\s*([男女])\s*[,,]', work_info) + if gender_match: + gender = gender_match.group(1) + data['target_gender'] = gender + print(f"[AI服务] 后处理:从工作基本情况中提取性别: {gender}") + + # 如果还没有,尝试从其他字段中查找 + if not data.get('target_gender') or data.get('target_gender') == '': + # 检查所有文本字段中是否包含性别信息 + for key, value in data.items(): + if isinstance(value, str) and value: + # 匹配"男"或"女" + if re.search(r'\b男\b', value) and not re.search(r'\b女\b', value): + data['target_gender'] = '男' + print(f"[AI服务] 后处理:从字段 '{key}' 中推断性别: 男") + break + elif re.search(r'\b女\b', value) and not re.search(r'\b男\b', value): + data['target_gender'] = '女' + print(f"[AI服务] 后处理:从字段 '{key}' 中推断性别: 女") + break + + # 4. 从工作基本情况中提取职级(如果target_professional_rank为空) + if 'target_professional_rank' in field_code_map and (not data.get('target_professional_rank') or data.get('target_professional_rank') == ''): + # 尝试从工作基本情况或其他文本字段中提取职级 + work_info = data.get('target_work_basic_info', '') + if work_info: + # 匹配职级模式:正处级、副处级、正科级等 + rank_match = re.search(r'([正副][处科厅局]级)', work_info) + if rank_match: + rank = rank_match.group(1) + data['target_professional_rank'] = rank + print(f"[AI服务] 后处理:从工作基本情况中提取职级: {rank}") + + # 如果还没有,检查其他文本字段 + if not data.get('target_professional_rank') or data.get('target_professional_rank') == '': + for key, value in data.items(): + if isinstance(value, str) and value: + rank_match = re.search(r'([正副][处科厅局]级)', value) + if rank_match: + rank = rank_match.group(1) + data['target_professional_rank'] = rank + print(f"[AI服务] 后处理:从字段 '{key}' 中提取职级: {rank}") + break + + # 5. 从问题线索描述中提取线索来源(如果clue_source为空) + if 'clue_source' in field_code_map and (not data.get('clue_source') or data.get('clue_source') == ''): + issue_desc = data.get('target_issue_description', '') + # 检查所有文本字段 + all_text = ' '.join([str(v) for v in data.values() if isinstance(v, str)]) + + if '举报' in all_text or '被举报' in all_text: + if '群众举报' in all_text: + data['clue_source'] = '群众举报' + elif '网络' in all_text or '网上' in all_text: + data['clue_source'] = '网络举报' + elif '电话' in all_text or '来电' in all_text: + data['clue_source'] = '来电举报' + elif '来信' in all_text: + data['clue_source'] = '来信举报' + else: + data['clue_source'] = '举报' + print(f"[AI服务] 后处理:从文本中推断线索来源: {data['clue_source']}") + return data def _calculate_age_from_birth_date(self, birth_date: str) -> Optional[int]: diff --git a/性别字段缺失问题分析与修复.md b/性别字段缺失问题分析与修复.md new file mode 100644 index 0000000..920a912 --- /dev/null +++ b/性别字段缺失问题分析与修复.md @@ -0,0 +1,185 @@ +# 性别字段缺失问题分析与修复 + +## 问题描述 + +模型在思考过程中正确识别了性别信息("性别方面,无论是在哪里,都明确指出是男性或者男,所以统一转换为'男'即可"),但在最终的JSON输出中,`target_gender`字段却是空字符串。同时,`target_professional_rank`和`clue_source`字段也存在类似问题。 + +## 问题分析 + +### 1. 根本原因 + +**问题1:System Prompt不够强调** +- 虽然system prompt提到了性别字段,但可能不够强调 +- 模型在生成JSON时可能因为某些原因跳过了某些字段 +- 需要更明确地在提示词中强调每个字段都必须填写,不能为空 + +**问题2:字段名错误** +- 模型返回了错误的字段名:`_professional_rank`和`_source` +- 说明模型没有严格遵循system prompt中的字段名要求 +- 需要更明确地禁止使用下划线前缀 + +**问题3:后处理机制不完善** +- 虽然代码中有后处理逻辑,但对于性别、职级等关键字段的推断不够完善 +- 需要增强后处理机制,从已有数据中推断缺失字段 + +### 2. 具体表现 + +从用户提供的返回结果来看: + +```json +{ + "target_name": "张三", + "target_political_status": "中共党员", + "target_date_of_birth": "1980年05月", + "target_organization_and_position": "某公司总经理", + "target_issue_description": "违反国家计划生育有关政策规定,于2010年10月生育二胎。", + "target_gender": "", // ❌ 应该是"男" + "_professional_rank": "", // ❌ 字段名错误,应该是"target_professional_rank",值应该是"正处级" + "_source": "", // ❌ 字段名错误,应该是"clue_source",值应该是"群众举报" +} +``` + +**问题点**: +1. `target_gender`为空,但思考过程中明确识别了性别 +2. 字段名错误:`_professional_rank`应该是`target_professional_rank` +3. 字段名错误:`_source`应该是`clue_source` +4. 值缺失:职级和线索来源的值都是空的 + +## 修复方案 + +### 1. 强化System Prompt ✅ + +**修改位置**:`services/ai_service.py` 第237行 + +**改进内容**: +- 使用⚠️标记强调核心要求 +- 明确列出关键字段(性别、职级、线索来源)的提取要求 +- 明确禁止使用下划线前缀的字段名 +- 强调如果文本中明确提到信息,必须提取,不能为空 + +**新的System Prompt**: +``` +你是一个专业的数据提取助手。请从输入文本中提取结构化信息,并严格按照JSON格式返回结果。 + +⚠️ 核心要求(必须严格遵守): + +1. 字段提取要求: + - 如果文本中明确提到信息(如性别、年龄、职务、职级、线索来源等),必须提取,绝对不能设为空字符串 + - 性别字段(target_gender):如果文本中出现"男"、"女"、"男性"、"女性"、"先生"、"女士"等任何表示性别的词汇,必须提取并转换为"男"或"女",不能为空 + - 职级字段(target_professional_rank):如果文本中提到"正处级"、"副处级"、"正科级"等,必须提取,不能为空 + - 线索来源字段(clue_source):如果文本中提到"举报"、"群众举报"、"来信"等,必须提取,不能为空 + +2. 字段名格式要求(严格禁止错误): + - 必须使用"target_professional_rank",禁止使用"_professional_rank"或任何下划线前缀 + - 必须使用"clue_source",禁止使用"_source"、"source"或任何下划线前缀 + - 必须使用"target_organization",禁止使用"target_organisation"(英式拼写) + - 所有字段名必须严格按照JSON示例中的字段编码,不能随意修改 + +3. JSON格式要求: + - 只返回JSON对象,不要包含任何其他文字、思考过程、markdown代码块标记或```json标记 + - 所有字段名必须使用双引号 + - 必须返回所有要求的字段,即使值为空字符串也要包含在JSON中 + - JSON格式必须完整且有效,不能有语法错误 + +4. 提取逻辑: + - 逐字逐句仔细阅读输入文本,不要遗漏任何信息 + - 对于性别、职级、线索来源等关键字段,请特别仔细查找 + - 如果文本中明确提到某个信息,必须提取出来,不能设为空 +``` + +### 2. 增强后处理机制 ✅ + +**修改位置**:`services/ai_service.py` 第1233-1320行 + +**新增功能**: + +1. **从工作基本情况中提取性别** + - 匹配模式:`XXX,男,...` 或 `XXX,女,...` + - 如果`target_gender`为空,从`target_work_basic_info`中提取 + +2. **从所有文本字段中推断性别** + - 如果工作基本情况中没有,检查所有文本字段 + - 使用正则表达式匹配"男"或"女" + +3. **从工作基本情况中提取职级** + - 匹配模式:`正处级`、`副处级`、`正科级`等 + - 如果`target_professional_rank`为空,从文本中提取 + +4. **从文本中推断线索来源** + - 检查所有文本字段中是否包含"举报"、"群众举报"等关键词 + - 根据关键词推断线索来源类型 + +**后处理逻辑**: +```python +# 3. 从工作基本情况中提取性别(如果target_gender为空) +if 'target_gender' in field_code_map and (not data.get('target_gender') or data.get('target_gender') == ''): + # 尝试从工作基本情况中提取性别 + work_info = data.get('target_work_basic_info', '') + if work_info: + gender_match = re.search(r'[,,]\s*([男女])\s*[,,]', work_info) + if gender_match: + gender = gender_match.group(1) + data['target_gender'] = gender + + # 如果还没有,尝试从其他字段中查找 + if not data.get('target_gender'): + for key, value in data.items(): + if isinstance(value, str) and value: + if re.search(r'\b男\b', value) and not re.search(r'\b女\b', value): + data['target_gender'] = '男' + break + elif re.search(r'\b女\b', value) and not re.search(r'\b男\b', value): + data['target_gender'] = '女' + break +``` + +## 预期效果 + +1. **提高字段提取准确性** + - 强化后的system prompt明确要求提取关键字段 + - 模型更可能正确提取性别、职级、线索来源等信息 + +2. **修复字段名错误** + - 明确禁止使用下划线前缀 + - JSON修复机制可以处理字段名错误 + +3. **增强容错能力** + - 即使模型没有正确提取,后处理机制可以从已有数据中推断 + - 多层保障确保关键字段不会为空 + +## 测试建议 + +1. **功能测试** + - 使用相同的输入数据测试修复后的代码 + - 验证性别、职级、线索来源字段是否正确提取 + - 检查字段名是否正确 + +2. **边界测试** + - 测试性别信息在不同位置的情况(工作基本情况、问题线索等) + - 测试职级信息的不同表述方式 + - 测试线索来源的不同表述方式 + +3. **错误处理测试** + - 测试模型返回错误字段名的情况 + - 验证JSON修复机制是否能正确处理 + +## 后续优化建议 + +1. **如果问题持续存在** + - 考虑进一步降低temperature参数(当前0.2,可以尝试0.1) + - 考虑调整其他参数(top_p, top_k等) + - 联系模型服务提供商寻求支持 + +2. **监控和日志** + - 记录修复前后的字段提取准确率 + - 分析仍然存在的错误模式 + - 持续优化提示词和后处理机制 + +3. **考虑使用Few-shot示例** + - 在system prompt中添加正确的JSON示例 + - 展示如何正确提取性别、职级等字段 + +## 总结 + +通过强化system prompt和增强后处理机制,应该能够解决性别字段缺失的问题。如果问题仍然存在,可能需要进一步调整模型参数或联系服务提供商。 +