From 71ff2ee5f698cbb188a035346ae31b2b397a4087 Mon Sep 17 00:00:00 2001 From: YangGuo <3420309028@qq.com> Date: Thu, 2 Apr 2026 10:50:57 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E8=87=B3?= =?UTF-8?q?=E3=80=8Cdata=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/feature_extraction.py | 417 ++++++++++++++++++++++++++++++++ data/feature_extraction_2.py | 454 +++++++++++++++++++++++++++++++++++ data/object_convert.py | 60 +++++ 3 files changed, 931 insertions(+) create mode 100644 data/feature_extraction.py create mode 100644 data/feature_extraction_2.py create mode 100644 data/object_convert.py diff --git a/data/feature_extraction.py b/data/feature_extraction.py new file mode 100644 index 0000000..b171962 --- /dev/null +++ b/data/feature_extraction.py @@ -0,0 +1,417 @@ +''' +批量提取特征方法 +''' + + +from openai import OpenAI +import json +import os +from dotenv import load_dotenv +import re + +# 加载环境变量 +env_path = "C:/Users/TaoJing/Desktop/dialogue/.env" +load_dotenv(dotenv_path=env_path) + +# 读取环境变量 +API_KEY = os.getenv("QWEN_API_KEY") +MODEL = os.getenv("MODEL_NAME", "deepseek-v3.1") +TEMPERATURE = float(os.getenv("TEMPERATURE", 0.1)) # 降低随机性,提升格式稳定性 +OUTPUT_DIR = os.getenv("OUTPUT_DIR", "time_12_1/data_ch_1") + + +client = OpenAI( + api_key=API_KEY, + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1" +) + + +def build_extraction_prompt(dialogue_text): + prompt_template = """ +# Role +你是一名拥有15年经验的“家庭教育销售通话审计专家”。你的核心能力是透过家长杂乱的表述,精准捕捉深层心理动机、家庭权力结构、隐形财富信号以及高危销售线索。 + +# Core Protocols (核心审计协议 - 最高优先级) + +## 1. 宁缺毋滥原则 (The Principle of Precision) +* **存在即输出,无证即沉默**:忽略任何关于“字段数量”的限制。如果原文中有20个维度的有效证据,就输出20个;如果只有3个,就输出3个。 +* **严禁凑数**:如果原文未提及某维度,或者证据模糊两可,**绝对不要**输出该 Key。不要为了追求“信息丰富”而强行填空。 +* **严禁不存在特征填'未提及'**:文中没有证据不准构造,不能出现未提及、文中未出现等多余提示。 + +## 2. 证据阵列法则 (The Law of Evidence Arrays) +* **数据结构变更**:`evidence` 字段必须是 **字符串数组 (List)**,严禁使用单一字符串。 +* **颗粒度控制 (Granularity Control)**: + * 数组中的元素必须是 **具有独立语义的完整原句** 或 **包含主谓宾的完整意群**。 + * **禁止碎片**:严禁提取如 "不合适"、"太贵"、"焦虑" 这样缺乏上下文的短语。 + * **主体过滤**:**仅提取家长(客户)表达的原话**,严禁提取销售人员的引导语、复述语或共情语。 +* **纯净引用**:每一个元素必须是原文的 100% 完美复制。 + * **严禁拼接**:严禁使用“+”、“和”、“以及”将两句不连贯的话拼在同一个字符串里。 + * **严禁篡改**:禁止总结、禁止润色、禁止“原文+分析”。你的分析只能体现在 `value` 字段中。 + +## 3. 结论极简法则 (The Law of Concise Conclusion) +* **强制必输字段**:`Follow_up_Priority` 是 **核心必选字段**,无论任何情况都必须输出,不允许缺失。 +* **Follow_up_Priority 兜底规则**: + - 若文本完全无痛点/无财力/无意识,`value` 填“C级 (无痛点/无意识)”,`evidence` 填 ["文本未提及任何痛点、财力或意向相关内容"] + - 若仅部分信息缺失,按规则评级并在 `evidence` 中列出已有有效原句。 +* **Value 约束**:`value` 字段必须是 **客观、简练的定性结论**(必须限制在 **20个汉字以内**)。 + * *正确示例*: "A级 (高痛点+强财力)" + * *错误示例*: "家长表现出对价格的犹豫,虽然她很有钱,但是因为..." (禁止小作文) + +## 4. 身份与财富的高敏嗅觉 +* 对于**高价值信号**(职业/多孩/私立学校/房产)和**生命红线**(自杀/不想活了/抑郁症确诊)保持极度敏感,一旦出现必须提取。 + +# Task +阅读提供的销售通话录音文本,从以下 23 个预设维度中筛选出**有效信息**,生成一份高精度的客户画像 JSON。 + +# Field Definitions (字段定义与提取逻辑) +### [第一组:心理动力与危机] +1. **Core_Fear_Source** (深层恐惧) + * *逻辑*: 驱动家长寻求帮助的终极噩梦。是怕孩子死(生命安全)?怕孩子阶级跌落?还是怕自己面子挂不住? + * *注意*: 必须提取具体的后果描述。 +2. **Pain_Threshold** (痛苦阈值) + * *逻辑*: 家长当前的情绪状态。是“崩溃急救”(无法忍受,必须马上解决),还是“隐隐作痛”(还能凑合)? +3. **Time_Window_Pressure** (时间压力) + * *逻辑*: 客观的截止日期。如:距离中高考仅剩X月、休学复课最后期限、学校劝退通牒。 +4. **Helplessness_Index** (无助指数) + * *逻辑*: 家长是否已经尝试过多种方法均失败(习得性无助),还是盲目自信觉得还能管。 +5. **Social_Shame** (社交耻感) + * *逻辑*: 孩子问题是否影响了家长的社会形象(怕老师找、怕亲戚问、不敢出门)。 +6. **Ultimatum_Event** (爆发事件) + * *逻辑*: 迫使家长此时此刻咨询的导火索。如:昨日发生的激烈争吵、离家出走、打架、学校停课通知。 +7. **Emotional_Trigger** (情绪扳机) + * *逻辑*: 沟通中家长情绪最激动的点(哭泣、愤怒、颤抖)。 + +### [第二组:阻力与障碍] +8. **Secret_Resistance** (隐性抗拒) + * *逻辑*: **阻碍成交**的心理障碍。特指:怕被家人知道买课、怕孩子知道家长在咨询、觉得课程是骗局。 + * *排除*: 孩子的生活秘密(如抽烟/早恋)不属于此字段。 +9. **Trust_Deficit** (信任赤字) + * *逻辑*: 对机构/销售/网课模式的直接质疑。如:“你们正规吗?”“之前被骗过”。 +10. **Family_Sabotage** (家庭阻力) + * *逻辑*: 家庭中明确的反对者或捣乱者(拆台的配偶、干涉的长辈、发病的家属)。 + * *排除*: 客观的不幸(如家人生病/车祸)不属于此字段,除非该事件直接阻碍了家长听课。 +11. **Low_Self_Efficacy** (效能感低) + * *逻辑*: 家长担心**自己**学不会、坚持不下来、没时间听课。 +12. **Attribution_Barrier** (归因偏差) + * *逻辑*: 家长认为错在谁?(全是学校的错 / 全是手机的错 / 全是遗传的错 / 承认自己有错)。 + +### [第三组:资源与决策] +13. **Payer_Decision_Maker** (决策权) + * *逻辑*: 谁掌握财权?谁有一票否决权?是“妈妈独裁”还是“需商量”? +14. **Hidden_Wealth_Proof** (隐形财力) + * *逻辑*: 寻找高消费证据。如:私立学校、出国计划、高昂学费、住别墅、高知职业(教授/医生)。 +15. **Price_Sensitivity** (价格敏感度) + * *逻辑*: 对价格的反应。是“只看效果不差钱”,还是“犹豫比价”、“哭穷”。 +16. **Sunk_Cost** (沉没成本) + * *逻辑*: 过往已投入的无效成本。如:之前报过xx辅导班、做过xx次心理咨询、花了xx万没效果。 +17. **Compensatory_Spending** (补偿心理) + * *逻辑*: 是否因亏欠感而通过花钱(买东西/报课)来弥补孩子。 + +### [第四组:销售价值判断] +18. **Expectation_Bonus** (期望范围) + * *逻辑*: 家长的底线(只要活着/不退学)与理想(考大学/变优秀)。 +19. **Competitor_Mindset** (竞品思维) + * *逻辑*: 家长是否在对比其他**解决方案**。如:特训学校、心理医生(针对孩子)、线下辅导班。 + * *排除*: 家属的就医经历不属于此字段。 +20. **Cognitive_Stage** (认知阶段) + * *逻辑*: 愚昧期(修孩子) -> 觉醒期(修自己/找方法)。 +21. **Referral_Potential** (转介绍潜力) + * *逻辑*: 基于身份判断。重点捕捉:多孩家庭、教师/医生/教授/公务员身份、家长委员会成员。 +22. **Last_Interaction** (互动状态) + * *逻辑*: 通话结束时的温度,互动内容多表现积极。积极互动/索要案例;接受通话/同意配合。 + * *注意*: 原文末尾必须有应答对话。 +23. **Follow_up_Priority** (跟进优先级) - [重点监控字段] + * *逻辑*: 综合评级(S/A/B/C)。 + * **Extraction Rule (必须使用数组逻辑)**: + * 如果评级为 **S/A**(通常需要痛点+财力/意向双重支撑),必须在 `evidence` 数组中分别列出这两方面(甚至三方面)的原话。 + * **S级**: 涉及生命安全 OR (极高痛点 + 强支付能力 + 强意向)。 + * **A级**: 有痛点 + 有支付能力。 + * **B级**: 有痛点 + 无支付能力/犹豫。 + * **C级**: 无痛点/无意识。 + +# Output Format (输出格式指令) +**强制要求1**:JSON 中必须包含 `Follow_up_Priority` 字段,否则视为无效输出。 +**强制要求2**:JSON 格式必须严格合法(逗号分隔、引号成对、括号匹配),可直接被JSON解析工具识别。 +**强制要求3**:严禁23个预设维度中出现'未提及'。 +**强制要求4**:证据必须在原文中,且充足。 +**强制要求5**:不准将示例模板内容作为证据输出,必须从原文中找证据。 +**强制要求6**:严禁凑数,原文中20个维度的有效证据,就输出20个,只有3个,就输出3个,没有就不要构造。 +**强制要求7**:严禁使用“+”、“和”、“以及”将两句不连贯的话拼在同一个字符串里。 +**强制要求7**:"evidence"中必须有原文内容,原文中找不到证据该维度禁止输出。 +**强制要求8**:字段"value"严禁出现"未提及价格反应"、"未明确表态"字样,文中没有证据严禁输出字段。 + +JSON 结构要求: +1. **Key**: 仅使用上述定义中出现的英文 Key。 +2. **Value**: 必须是 **<20字** 的短语结论。 +3. **Evidence**: 必须是 **List** (字符串数组)。 +4. **Strict Validation (自我审查)**: + * 检查 `evidence` 是否包含“家长说”、“意思就是”? -> 若有,**改为纯引用**。 + * 检查 `evidence` 是否为空,为空则删除字段。 + * 检查 JSON 语法是否正确? -> 确保逗号不遗漏、括号成对。 + * 不准将示例模板内容作为证据输出,必须从原文中找证据。 + +**Example Output:** +{ + "Follow_up_Priority": { + "value": "A级 (痛点强+财力足)", + "evidence": [ + "我是今年才确诊,他是焦虑的", // 证据1:完整原句支撑痛点 + "孩子现在在西工大附中上学", // 证据2:完整原句支撑隐形财力 + "留学基金我们已经准备好了" // 证据3:完整原句支撑支付能力 + ] + }, + "Pain_Threshold": { + "value": "崩溃急救状态", + "evidence": [ + "我不知道怎么来处理", + "一看见难了就崩溃啊就崩溃" + ] + }, + "Last_Interaction": { + "value": "积极配合修改信件", + "evidence": [ + "好,那你拿一个哪几点?那个你就给我框一下,然后截一个图,然后下午晚一点有空,我再来稍微修改一下", + "行,那我稍后给您打过去,给您接问一下啊" + ] + } +}""" + full_prompt = f"{prompt_template}\n\n### 原始通话文本\n{dialogue_text}\n\n### 请严格按照上述要求输出JSON(仅JSON,无其他内容)" + return full_prompt + + +def clean_and_fix_json(json_str): + """清洗JSON格式""" + try: + # 移除转义符、控制字符和多余空格 + json_str = json_str.replace('\\"', '"').replace("\\'", "'") + json_str = re.sub(r'[\n\r\t\f\v]', '', json_str) + json_str = re.sub(r'\s+', ' ', json_str).strip() + # 修复末尾多余逗号 + json_str = re.sub(r",\s*}", "}", json_str) + json_str = re.sub(r",\s*]", "]", json_str) + return json_str + except Exception as e: + raise RuntimeError(f"JSON清洗失败: {str(e)}") from e + + +def extract_features_with_qwen(dialogue_text, file_name, output_dir="qwen_new_123"): + """ + 调用API提取特征并保存为JSON文件 + :param dialogue_text: 预处理后的对话文本(字符串) + :param file_name: 原文件名称(用于生成输出文件名) + :param output_dir: 结果保存目录 + :return: 提取的特征字典(失败则返回None) + """ + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + prompt = build_extraction_prompt(dialogue_text) + + try: + response = client.chat.completions.create( + model=MODEL, + messages=[{"role": "user", "content": prompt}], + temperature=TEMPERATURE, + max_tokens=8000 + ) + + feature_json_str = response.choices[0].message.content.strip() + # 提取JSON片段 + json_match = re.search(r"\{[\s\S]*\}", feature_json_str) + if json_match: + feature_json_str = json_match.group() + else: + raise ValueError("返回内容中未找到有效JSON数据") + + # 移除代码块标记 + if feature_json_str.startswith("```json"): + feature_json_str = feature_json_str[7:-3].strip() + elif feature_json_str.startswith("```"): + feature_json_str = feature_json_str[3:-3].strip() + + feature_dict = json.loads(feature_json_str) + + # 验证核心字段 + if "Follow_up_Priority" not in feature_dict: + raise ValueError("返回结果缺失核心必选字段:Follow_up_Priority") + + # 生成输出文件名 + file_base = os.path.splitext(file_name)[0] + json_filename = f"{file_base}.json" + output_path = os.path.join(output_dir, json_filename) + + # 保存文件 + with open(output_path, "w", encoding="utf-8") as f: + json.dump(feature_dict, f, ensure_ascii=False, indent=2) + + print(f"处理完成:{file_name} -> {json_filename}") + return feature_dict + + except json.JSONDecodeError as e: + print(f"JSON解析失败 {file_name}:{str(e)} | 原始内容:{feature_json_str[:200]}...") + return None + except ValueError as e: + print(f"数据验证失败 {file_name}:{str(e)}") + return None + except Exception as e: + print(f"特征提取失败 {file_name}:{str(e)}") + return None + +# 批量处理函数 +def batch_process_first_200_txt(folder_path, output_dir): + """ + 仅处理指定文件夹下的前200个txt文件 + :param folder_path: 待处理文件夹路径 + :param output_dir: 结果输出目录 + """ + # 检查文件夹是否存在 + if not os.path.isdir(folder_path): + print(f"文件夹不存在:{folder_path}") + return + + # 筛选出文件夹中的txt文件并按名称排序(保证处理顺序稳定) + txt_file_list = [ + f for f in os.listdir(folder_path) + if os.path.isfile(os.path.join(folder_path, f)) and f.lower().endswith(".txt") + ] + # 按文件名排序(可选,保证每次处理顺序一致) + txt_file_list.sort() + + # 取前200个txt文件 + target_files = txt_file_list[:200] + + if not target_files: + print(f"文件夹 {folder_path} 中无txt文件可处理") + return + + print(f"始处理前 {len(target_files)} 个txt文件") + + processed_count = 0 + failed_count = 0 + + for file_name in target_files: + file_path = os.path.join(folder_path, file_name) + + # 读取文件内容 + try: + with open(file_path, "r", encoding="utf-8") as f: + dialogue_content = f.read().strip() + if not dialogue_content: + print(f"文件内容为空,跳过:{file_name}") + failed_count += 1 + continue + except Exception as e: + print(f"读取文件失败 {file_name}:{str(e)}") + failed_count += 1 + continue + + # 调用特征提取函数 + result = extract_features_with_qwen(dialogue_content, file_name, output_dir) + if result: + processed_count += 1 + else: + failed_count += 1 + + # 输出批量处理统计结果 + print("\n批量处理完成") + print(f"成功处理:{processed_count} 个文件") + print(f"处理失败:{failed_count} 个文件") + print(f"结果保存至:{os.path.abspath(output_dir)}") + + +def process_single_txt(file_path, output_dir=OUTPUT_DIR): + """ + 处理单个TXT文件,提取特征并保存JSON + :param file_path: 单个TXT文件的完整路径 + :param output_dir: JSON结果保存目录 + """ + # 1. 验证文件是否存在且是TXT文件 + if not os.path.exists(file_path): + raise FileNotFoundError(f"文件不存在:{file_path}") + if not file_path.lower().endswith(".txt"): + raise ValueError(f"不是TXT文件:{file_path}") + if not os.path.isfile(file_path): + raise IsADirectoryError(f"这是文件夹,不是文件:{file_path}") + + # 2. 读取TXT文件内容 + print(f"正在读取文件:{file_path}") + try: + with open(file_path, "r", encoding="utf-8") as f: + dialogue_content = f.read().strip() + if not dialogue_content: + raise ValueError("文件内容为空") + print(f"成功读取文件(字符数:{len(dialogue_content)})") + except Exception as e: + raise RuntimeError(f"读取文件失败:{str(e)}") from e + + # 3. 构建提示词并调用API + prompt = build_extraction_prompt(dialogue_content) + try: + print("正在调用API提取特征...") + response = client.chat.completions.create( + model=MODEL, + messages=[{"role": "user", "content": prompt}], + temperature=TEMPERATURE, + max_tokens=8000, + timeout=30 + ) + except Exception as e: + raise RuntimeError(f"API调用失败:{str(e)}") from e + + # 4. 提取并清洗JSON + feature_json_str = response.choices[0].message.content.strip() + json_match = re.search(r"\{[\s\S]*\}", feature_json_str) + if not json_match: + raise RuntimeError(f"API返回无有效JSON:{feature_json_str[:200]}...") + cleaned_json = clean_and_fix_json(json_match.group()) + + # 5. 解析并验证JSON + try: + parsed_dict = json.loads(cleaned_json) + except json.JSONDecodeError as e: + raise RuntimeError(f"JSON解析失败:{str(e)} | 清洗后内容:{cleaned_json[:500]}") from e + + # 验证核心字段 + if "Follow_up_Priority" not in parsed_dict: + raise RuntimeError("核心字段Follow_up_Priority缺失") + fu_prio = parsed_dict["Follow_up_Priority"] + if not isinstance(fu_prio, dict) or "value" not in fu_prio or "evidence" not in fu_prio: + raise RuntimeError("Follow_up_Priority格式错误(需包含value和evidence)") + if not isinstance(fu_prio["evidence"], list): + raise RuntimeError("evidence必须是数组类型") + if len(str(fu_prio["value"])) >= 20: + raise RuntimeError(f"value超20字限制:{fu_prio['value']}") + + # 6. 保存JSON结果 + os.makedirs(output_dir, exist_ok=True) + file_name = os.path.basename(file_path) + json_file_name = f"{os.path.splitext(file_name)[0]}" + json_save_path = os.path.join(output_dir, json_file_name) + + try: + with open(json_save_path, "w", encoding="utf-8") as f: + json.dump(parsed_dict, f, ensure_ascii=False, indent=2) + print(f"处理完成!JSON保存至:{json_save_path}") + return parsed_dict + except Exception as e: + raise RuntimeError(f"保存JSON失败:{str(e)}") from e + + + +if __name__ == "__main__": + # 要处理的源文件夹路径 + target_folder = "qwen\清洗后\未成交" + + # 执行批量处理:仅处理前200个txt文件,输出到 + batch_process_first_200_txt( + folder_path=target_folder, + output_dir=OUTPUT_DIR + ) + # SINGLE_TXT_PATH = "./qwen/cdb7d561-975a-431e-86d4-9b3ddc714f73.txt" + + # try: + # # 执行单个文件处理 + # process_single_txt(file_path=SINGLE_TXT_PATH) + # except Exception as e: + # print(f"\n处理失败:{str(e)}") + diff --git a/data/feature_extraction_2.py b/data/feature_extraction_2.py new file mode 100644 index 0000000..f7f722b --- /dev/null +++ b/data/feature_extraction_2.py @@ -0,0 +1,454 @@ +from openai import OpenAI +import json +import os +from dotenv import load_dotenv +import re + +# 加载环境变量 +env_path = "C:/Users/TaoJing/Desktop/dialogue/.env" +load_dotenv(dotenv_path=env_path) + +# 读取环境变量 +API_KEY = os.getenv("QWEN_API_KEY") +MODEL = os.getenv("MODEL_NAME") +TEMPERATURE = float(os.getenv("TEMPERATURE", 0.1)) # 降低随机性,提升格式稳定性 +OUTPUT_DIR_2 = os.getenv("OUTPUT_DIR_2") + + +client = OpenAI( + api_key=API_KEY, + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1" +) + + + +# 提示词第二版本 +def build_extraction_prompt(dialogue_text): + prompt_template = """ +[角色定义] +你是一名拥有15年经验的“家庭教育销售通话审计专家”。你的核心能力是透过家长杂乱的表述,精准捕捉深层心理动机、家庭权力结构、隐形财富信号以及高危销售线索。 + +[任务] +阅读对话文本,从以下23个预设维度中筛选数“有效信息”,生成一份高精度的客户画像JSON。 + +[字段定义与提取逻辑] +第一组:心理动力与危机 +1.Core_Fear_Source(深层恐惧) + 核心逻辑:驱动家长寻求帮助的终极噩梦。是怕孩子死(生命安全)?怕孩子阶级跌落?还是怕自己面子挂不住? + 固定标签池:孩子发展受限、阶段跌落/升学失败、社交耻感/面子受损、生命安全/身心健康 + 判定规则: + 1. 孩子发展受限:原文提及“未来没平台/没出路/职业受限/躺平摆烂不努力”; + 2. 阶段跌落/升学失败:原文提及“考不上高中/大学/没学历就没前途/不如别人”; + 3. 社交耻感/面子受损:原文提及“怕老师问/亲戚说/别人看不起/孩子问题没脸说”; + 4. 生命安全/身心健康:原文提及“孩子抑郁/自残/沉迷/霸凌/离家出走/安全问题” + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +2.Pain_Threshold(痛苦阈值) + 核心逻辑:家长当前的情绪状态。 + 固定标签池:崩溃急救、隐隐作痛 + 判定规则: + 1. 崩溃急救:孩子有极端行为(自残/)、重度抑郁、过量服药等风险;或家长情绪崩溃、心疼、过渡操劳 + 2. 隐隐作痛:家长仅着急/焦虑,无生理不适;或孩子自暴自弃/厌学/宅/逃避考试,无极端风险 + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +3.Time_Window_Oressure(时间压力) + 核心逻辑:客观的截止日期。 + 固定标签池:存在时间压力、无时间压力 + 判定规则: + 1. 凡有具体时限 / 截止日期,无论表述如何,统一归为「存在时间压力」:如 “中考倒计时 5 个月”“休学复课最后期限”“一周内大考” ,仅看是否有客观时间约束,不细分场景 / 年级; + 2. 凡明确无截止 / 无时限 / 单纯状态,统一归为「无时间压力」:如 “无明确截止日期”“长期休学无明确期限”“高二关键期(无截止日)” 等,剔除所有 “关键期” 的模糊修饰,仅看是否有时间约束; + 3. 彻底剔除主观表述:如 “时间紧迫”“紧迫的时间压力” 等无具体时限的主观描述,若有配套具体截止日期则归「存在时间压力」; + 4. 不细分年级 / 场景:无论中考 / 高考 / 复学 / 考试,只要有客观时间约束,均归「存在时间压力」。 + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +4.Helplessness_Index(无助指数) + 核心逻辑:家长是否尝试过多种方法均失败,还是盲目自信觉得还能管 + 固定标签池:习得性无助、轻度无助 + 判定规则: + 1. 习得性无助:家长提及“报了很多班/找过老师/做过心理咨询,都没用”“试了各种方法,孩子还是这样”“花了很多钱,一点效果没有”; + 2. 轻度无助:家长提及“不知道怎么办/没人能帮我”“说不动孩子,我没辙”“找不到好方法,很迷茫”(未提过往尝试)。 + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +5.Social_Shame(社交耻感) + 核心逻辑:孩子问题是否影响了家长的社会形象。 + 固定标签池:显性社交耻感、隐性社交耻感、轻度社交耻感、强烈社交耻感、社交回避 + 判定规则: + 1. 显性社交耻感:家长明确表达耻感情绪+具体场景,如“孩子这样我没面子、抬不起头”“怕亲戚议论孩子不上学/休学”“怕老师找我、问责我,觉得丢人”“被老师公开批评很丢脸”; + 2. 隐性社交耻感:家长无明确耻感情绪,但行为/表述隐含耻感,如“不想让亲戚/老师知道孩子问题”“回避家长会、学校沟通”“偷偷咨询,怕被孩子/外人发现”(未说“没面子”,但有回避行为); + 3. 轻度社交耻感:家长仅轻微提及顾虑,无强烈情绪、无回避行为,如“有点怕老师找,但影响不大”“偶尔担心亲戚问起,无所谓”“有点在意他人看法,但不影响生活”; + 4. 强烈社交耻感:家长有极端耻感情绪+明显行为,如“因孩子问题不敢出门、回避所有社交”“怕被贴失败家长/问题学生标签,压力极大”“被老师/亲戚议论后情绪崩溃”“觉得家庭声誉彻底受损”; + 5. 社交回避(伴耻感):家长明确表示“回避社交场合、回避和亲戚/老师沟通”,且核心原因是“怕被议论孩子问题、怕丢面子”(回避行为明确,且与耻感直接相关)。 + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +6.Ultimatum_Event(爆发事件) + 核心逻辑:迫使家长咨询的导火索。 + 固定标签池:拒学/逃学类、离校/休学类、极端行为类、亲子/师生冲突类、学业下滑类、其他突发类 + 判定规则:(优先级从高到低) + 1. 极端行为类:家长提及“孩子自残、吞药、跳楼、以死相逼”“离家出走、失联”“确诊抑郁症、心理问题爆发”等(核心是伤害自身/极端逃避); + 2. 离校/休学类:家长提及“学校通知停课、劝退、让回家反省”“孩子休学、复课失败、长期失学”“频繁请假后休学”等(核心是被动/主动离校停学); + 3. 拒学/逃学类:家长提及“孩子拒绝上学、拒返校、逃学、缺课、拒考、拒上某节课”“连续多日不上学、今日未上学”等(核心是主动拒绝到校); + 4. 亲子/师生冲突类:家长提及“催学引发冲突、夺手机爆发矛盾”“与老师吵架、被老师体罚/批评后拒学”“亲子激烈争吵、孩子怒怼家长”等(核心是近期突发冲突); + 5. 学业下滑类:家长提及“成绩断崖式下滑、断崖式下跌、持续下滑”“考试失利、交白卷、弃考”等(核心是学业突发恶化); + 6. 其他突发类:家长提及“孩子早恋、被霸凌、发现藏手机/电子烟”“家庭变故(离婚、亲人去世)、母亲失联”等(核心是其他近期具体突发事)。 + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出;文中出现多个事件的,按照标签优先级输出,如:拒学/逃学类+其他突发类(早恋) +7.Emotional_Trigger(情绪扳机) + 核心逻辑:沟通中家长情绪最激动的点。 + 固定标签值:情绪平稳、情绪低落、焦虑、情绪激动、情绪崩溃、无力感爆发、学习相关情绪触发、孩子危机相关情绪触发、沟通相关情绪触发 + 判定规则: + 1. 情绪平稳(无波动):家长/孩子情绪无起伏,提及孩子问题时无焦虑、激动等表现(如“情绪平静无波动”“未显性激动”“无情绪波动”“无强烈情绪波动”); + 2. 情绪低落(含敏感/委屈):存在温和负面情绪,无激动/崩溃表现(如“孩子情绪低落”“情绪低落”“提及反感沟通时情绪低落”“被儿子否定时低落”“提及孤立时情绪低落”); + 3. 焦虑(含失眠/担忧):对孩子现状、未来、学业等存在担忧情绪,可伴随躯体表现(如“焦虑”“考试焦虑”“焦虑失眠”“对孩子未来担忧”“对学习动力不足焦虑”“对失控感焦虑”); + 4. 情绪激动(含暴怒/失控):情绪明显起伏,有易怒、激动表现,未达到崩溃程度(如“情绪激动”“因拖拉暴怒”“提及泄密时激动”“提及霸凌时激动”“谈及偷手机时激动”); + 5. 情绪崩溃(含哽咽/哭泣/绝望):情绪达到极端状态,伴随哭泣、哽咽、绝望感(如“情绪崩溃”“家长情绪崩溃”“提及自残时哽咽”“多次哭泣与绝望”“回忆过往时情绪崩溃”); + 6. 无力感爆发(含自责/迷茫):因教育无效、孩子问题无解产生的自责、迷茫、无力(如“无力感爆发”“家长自责与无力感爆发”“对孩子迷茫状态焦虑”“对教育投入无力感”); + 7. 学习相关情绪触发:提及学习、成绩、学业等相关内容即出现情绪波动(如“谈学习即情绪崩溃”“提及学习即情绪爆发”“提及数学成绩崩溃”“成绩断崖下跌引发焦虑”); + 8. 孩子危机相关情绪触发:提及孩子自残、自杀、离家出走、霸凌、心理疾病等危机事件即出现情绪波动(如“提及自残时情绪波动”“提及自杀时情绪失控”“孩子离家出走时恐惧与失控”“提及抑郁诊断时情绪波动”); + 9. 沟通相关情绪触发:因沟通失败、孩子拒绝沟通等沟通问题引发情绪波动(如“沟通困难”“沟通失败情绪激动”“孩子拒绝对话与情绪爆发”“被拒绝沟通时爆发”)。 + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +第二组:阻力与障碍 +8.Secret_Resistance(隐性抗拒) + 核心逻辑:“阻碍成交”的心理障碍。特指:怕被家人知道买课、怕孩子知道家长在咨询、觉得课程是骗局 + 固定标签池:怕孩子知晓咨询/学习相关行为、怕家人知晓咨询/购课相关行为、怀疑课程/机构 + 判定规则: + 1. 怕孩子知晓咨询/学习相关行为:家长提及“怕孩子知道家长在咨询/听课/学习”“担心孩子察觉咨询意图/发现家长求助”“需回避孩子听课/隐瞒孩子咨询行为”“怕孩子知道课程内容/家长报课”“怕孩子觉得自己有病(因咨询产生病耻感)”; + 2. 怕家人知晓咨询/购课相关行为:家长提及“怕被家人(丈夫/妻子/长辈)知道咨询/报课/购课”“担心家人质疑决策/反对报课”“隐瞒配偶购买课程/不愿让家人知悉购课”“怕被家人知道求助”; + 3. 怀疑课程/机构(含效果/正规性/套路):家长提及“担心课程无效/不落地/不适合孩子/浪费钱”“质疑机构正规性/怕课程是骗局”“担心后续销售套路/被推销”“曾被竞品/网课欺骗,对课程存疑”“质疑课程针对性/师资经验”。 + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +9.Trust_Deficit(信任赤字) + 核心逻辑:对机构/销售/网课模式的直接质疑。 + 固定标签池:存在信任赤字、无信任赤字 + 判定规则: + 1. 存在信任赤字:家长对机构、销售、网课 / 课程模式有明确的直接质疑 / 不信任,含资质、正规性、效果、公信力、履约能力等核心维度的质疑,或有被骗经历引发的对同类机构的不信任。 + 2. 无信任赤字:家长无任何对机构 / 销售 / 网课模式的质疑,明确表达信任、建立基础信任,或仅对外部主体 / 非核心内容提问,无信任层面的顾虑。 + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +10.Family_Sabotage(家庭阻力) + 核心逻辑:家庭中明确的反对者或者捣乱者。 + 固定标签池:配偶反对/无共情、长辈干涉/拆台、家属问题阻碍 + 判定规则: + 1. 配偶反对/无共情:家长提及“老公/老婆不支持我找方法”“他不管孩子,也不理解我”“配偶不会共情孩子,总骂孩子”; + 2. 长辈干涉/拆台:家长提及“老人惯着孩子,反对报课”“我爸妈总插手教育,跟我唱反调”“长辈不让我管孩子,说我太严格”; + 3. 家属问题阻碍:家长提及“家人生病/出事/离异/留守,我没时间听课/管孩子”“家属身体不好,我精力都在那边,顾不上孩子”。 + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +11.Low_Self_Efficacy(效能感) + 核心逻辑:家长担心自己学不会、坚持不下来、没时间听课 + 固定标签池:怕自己学不会/坚持不了、怕没时间听课/执行 + 判定规则: + 1. 怕自己学不会/坚持不了:家长提及“我文化低,学不懂方法”“我没毅力,坚持不下来课程”“我怕我学不会,帮不了孩子”; + 2. 怕没时间听课/执行:家长提及“我工作忙,没时间听课”“我要带孩子,没精力做方法”“平时事情多,抽不出时间学习”。 + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +12.Attribution_Barrier(归因偏差) + 核心逻辑:家长认为错在谁? + 固定标签池:归因为孩子自身、归因为家庭/父母、归因为学校/环境、归因为外部因素 + 判定规则: + 1. 归因为孩子自身:家长提及“孩子懒/笨/没毅力”“孩子性格差,不听话”“都是孩子自己不争气”; + 2. 归因为家庭/父母:家长提及“我没教好”“家庭氛围不好,影响孩子”“都是我的错,没管好孩子”“父母缺位”; + 3. 归因为学校/环境:家长提及“老师教的不好/不负责”“学校氛围差,孩子被影响”“同学都不好好学习,带坏我家孩子”; + 4. 归因为外部因素(手机/网络):家长提及“手机/游戏害了孩子”“网络上的东西不好,影响孩子”“孩子沉迷手机,才不想学习”。 + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +第三组:资源与决策 +13.Payer_Decision_Maker(决策权) + 核心逻辑:谁掌握财权?谁有一票否决权? + 固定标签池:母亲独裁、父亲独裁、共同决定、其它家属 + 判定规则: + 1. 母亲/父亲独裁:家长提及“我一人定/不用跟他商量”“我说了算,不用问别人”“家里教育/花钱,我做主”(结合沟通者身份判定母亲/父亲); + 2. 共同决策:家长提及“要跟老公/老婆商量”“家里大事要一起定”“我做不了主,得跟家人商量”; + 3. 其他家属决策:家长提及“要听老人/爸妈的”“长辈说了算,我做不了主”“得问我爸妈的意见”“孩子爷爷/奶奶管得多”“上班没空,孩子交给家里老人带”。 + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +14.Hidden_Wealth_Proof(隐形财力) + 核心逻辑:寻找高消费证据 + 固定标签池:高财力、中等财力 + 判定规则: + 1. 高财力:家长提及孩子就读私立/国际/贵族学校、重点私立名校、民办高价院校及相关重点班;有出国计划、留学规划、留学基金储备;报高价补习班、一对一高价补习、高频高价补课、艺术/编程等高价素质培训;家庭有高知职业(医生、教授、企业主、高管、公务员、事业单位骨干、上市公司相关从业者);有高消费行为(多套房产、学区房、异地购房/租房陪读、高端电子产品、私立医院/特需门诊/跨省就医、保姆、高频旅游);有明确高额支付能力(如五万五寻子、愿投十万教育支出、民办高中花费十余万)。 + 2. 中等财力:家长提及孩子就读重点公立学校、优质公立实验班/快班、普通民办院校;有稳定职业(双职工、教师、普通个体经营、自由职业有储蓄);能承担普通补课、网课、兴趣班、常规医疗支出;有稳定消费能力(电动车、私家车、常规租房、可负担长期课程/职高费用);重视教育投入但无高额消费(持续报普通补习班、为教育转学/择校但无高价支出)。 + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +15.Price_Sensitivity(价格敏感度) + 核心逻辑:对价格的反应。 + 固定标签池:不敏感、价格敏感 + 判定规则: + 1. 不敏感(只看效果不差钱):家长提及“只要有用,钱不是问题”“不在乎价格,效果好就行”“多少钱都可以,只要能帮孩子”; + 2. 价格敏感(哭穷/觉得贵/承担不起):有明确的 “价格顾虑、对价格有负面反应” 证据,核心是 “价格影响决策,存在各类价格相关迟疑 / 抗拒” + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +16.Sunk_Cost(沉没成本) + 核心逻辑:过往已投入无效成本。 + 固定标签池:小额沉没成本、高额沉没成本、多次无效沉没成本 + 判定规则: + 1. 小额沉没成本:家长提及“投入少量金钱/资源且无效果”,如“报过1-2个普通辅导班/网课无效”“做过1-2次心理咨询/医疗检查无效”“花几千元报班/就医无效”“投入少量时间/精力干预无效”“购买低价课程(如9.9元)未使用/无效”。 + 2. 高额沉没成本:家长提及“投入大量金钱/资源且无效果”,如“花几万/十几万报班、就医、转学”“长期(3个月及以上)补习/心理咨询无效”“购买高价课程、私立学校费用、封闭学校费用无效”“投入万元及以上无效支出”“多院就医、多次高端诊疗无效”。 + 3. 多次无效沉没成本:家长提及“多次、多类型投入且均无效果”,如“多次报班(3次及以上)无效”“多重干预(补课+咨询+医疗)均无效”“多年(1年及以上)持续投入无效”“多学科网课+线下私教+心理评估均无效”。 + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +17.Compensatory_Spending(补偿心理) + 核心逻辑:是否因亏欠感而通过花钱来弥补孩子。 + 固定标签池:有补偿心理、无补偿心理 + 判定规则: + 1. 有补偿心理:家长提及“亏欠孩子/没时间陪孩子,想通过花钱/报课弥补”“对不起孩子,想给他最好的,多花钱也愿意”“平时陪不了孩子,就想报个好班补偿他”。 + 2. 无补偿心理:家长无任何亏欠感 / 愧疚感表述,或未通过花钱消费的方式弥补孩子,明确无补偿相关的心理和行为。 + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +第四组:销售价值判断 +18.Expectation_Bonus(期望范围) + 核心逻辑:家长的底线与理想。 + 固定标签池:底线导向、短期改善、中期提升、长期目标、健康优先 + 判定规则: + 1. 底线导向(仅保基础):家长核心诉求为“保住最低标准”,无更高理想,如“不退学/不休学/不辍学”“能返校/正常上学/有学上”“完成基础教育/拿毕业证/混毕业证”“活着/健康平安即可”“不崩溃/不自残/不极端”“保住学籍/不被开除”“返校即可/维持上学”;仅关注基础生存、基础就学,无提升类诉求。 + 2. 短期改善(基础提升):家长诉求为“短期内实现基础状态改善”,无长期升学/成才目标,如“1-3个月内返校”“基础功能恢复/基础行为改善”“缓解厌学/抑郁情绪”“停止自毁/戒手机”“恢复基本沟通/基本生活功能”“下学期正常返校”“先返校再提其他”;核心是“快速缓解问题、达到基础标准”。 + 3. 中期提升(核心改善):家长在守住底线的基础上,追求核心能力提升,如“恢复学习动力/主动学习”“改善亲子沟通/人际关系”“戒掉手机沉迷”“调整情绪/心理状态”“重建基本动力/自控力”“基础成绩提升”;无明确升学、成才等长期目标,聚焦“核心问题解决”。 + 4. 长期目标(升学/成才):家长有明确的长期规划和更高理想,在守住底线的基础上追求进阶目标,如“考高中/重点高中/大学/本科/985/名校”“高考成功/中考达标/艺考通过”“变优秀/重建内驱/恢复优秀状态”“考上单招/警校/留学”“参军/有好前途”“年级排名提升/恢复前三”;核心是“升学、成才、长期能力进阶”。 + 5. 健康优先(身心健康>学业/其他):家长明确表述“身心健康优先于学业/成绩”,如“心理健康优先”“健康活着即可”“身心健康>成绩”“只求身心健康”“先恢复心理健康再谈其他”“预防心理问题/缓解抑郁”“不自杀/保安全”;核心诉求为身心健康,学业、升学等为次要诉求。 + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +19.Competitor_Mindset(竞品思维) + 核心逻辑:家长是否在对比其它“解决方案” + 固定标签池:考虑/对比竞品方案、尝试过竞品方案 + 判定规则: + 1. 考虑/对比竞品方案:家长明确提及“在对比/正在考虑/想选”某类孩子问题解决方案,无“已尝试/正在使用”的实际行为表述;核心为未落地的对比/考量行为。 + 2. 尝试过竞品方案:家长提及“已尝试/正在使用/曾选过”某类孩子问题解决方案,无论是否有效果;核心为已落地的实际尝试行为。 + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +20.Cognitive_Stage(认知阶段) + 核心逻辑:愚昧期(修孩子) -> 觉醒期(修自己/找方法)。 + 固定标签池:愚昧期、觉醒期 + 判定规则: + 1. 愚昧期:仅聚焦“修孩子及自身以外的”,将孩子问题全部归因于孩子自身,不反思家长/家庭责任,不主动寻求自身改变或科学方法。 + 2. 觉醒期:主动反思自身/家庭责任(修自己)、主动寻求解决方法(找方法、求帮助、学技巧),不再单纯归因孩子、试图改变孩子。 + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +21.Referral_Potential(转介绍潜力) + 核心逻辑:判断家庭背景和家长身份 + 固定标签池:高转介绍潜力、低转介绍潜力 + 判定规则: + 1. 高转介绍潜力: + - 家庭特征:明确提及 “多孩家庭、双孩 / 三孩 / 四孩家庭、双胞胎家庭、有多孩亲友圈” 等多孩相关表述; + - 职业身份:明确提及 “教师 / 医生 / 教授 / 公务员、教师亲属 / 家属、医护圈层、体制内亲属、退休局长” 等核心职业 / 关联身份表述; + 2. 低转介绍潜力: + - 家庭特征:明确提及 “单孩家庭、独生子女家庭、单亲单孩家庭” 等无多孩的表述; + - 身份特征:明确提及 “普通家庭、个体经营者 / 企业主 / 商人 / 销售 / 财务 / 技术从业者、农村家庭、乡镇家庭” 等非核心职业,且无多孩家庭特征。 + 注意:原文中未找到相关内容则不输出该字段;该字段特征只能按照“固定标签池”中的内容输出 +22.Last_Interaction(互动状态) + 核心逻辑:通话结束时的温度,互动内容多表现积极。 + 固定标签池:互动积极性高、互动积极性中、中性互动 + 双维度判定核心: + 1. 沟通意愿:家长对通话 / 视频沟通、进一步了解机构 / 课程的主动 / 接受程度(核心是建立信任); + 2. 邀约配合度:家长对课程试听 / 试看、老师指导、行动督促、进群 / 听课安排的接受 / 配合程度(核心是课程转化、后续服务)。 + 判定规则: + 1. 互动积极性高:通话末尾家长主动发起沟通行为+主动配合 / 索要课程 / 指导邀约,双向互动性极强,有明确的后续转化意向,是最高级别的积极状态,直接指向信任建立和课程转化。 + 2. 互动积极性中:通话末尾家长对沟通 / 邀约至少一项明确接受 / 配合,无主动发起行为但无拒绝,是中等积极的配合状态,为信任建立和课程转化奠定基础,也是本字段的核心积极标签。 + 3. 中性互动:通话末尾家长对沟通 / 邀约均无明确接受 / 配合表态,态度中立、犹豫观望,无明确的后续行动意向,互动温度无起伏,仅保持基础沟通,未建立有效信任,也无转化倾向。 + 注意:原文中未找到相关内容则不输出该字段;该字段特征可以在固定标签池的基础上添加你对原文的理解,如:互动积极性高(索要课程)、互动积极性中(同意听课配合) +23.Follow_up_Priority(跟进优先级) - [重点监控字段] + 核心逻辑:综合评级 + 判定规则:按照"痛点、财力、意识"三个指标进行等级分类 + Extraction Rule (必须使用数组逻辑) + 如果评级为 “S/A”(通常需要痛点+财力/意向双重支撑),必须在 `evidence` 数组中分别列出这两方面(甚至三方面)的原话。 + S级: 涉及生命安全 OR (极高痛点 + 强支付能力 + 强意向)。 + A级: 有痛点 + 有支付能力。 + B级: 有痛点 + 支付能力低/犹豫。 + C级: 无痛点/无意识。 + +[JSON 结构要求] +1.Key: 必须是上述23个预设维度。 +2.Value: 必须是23个预设维度中的固定标签池中的标签,按判定规则进行匹配 +3.Evidence: 必须是 **List** (字符串数组),必须能在原文中找到。 + +[JSON示例] +{ + "Follow_up_Priority": { + "value": "A级 (痛点强+财力足)", + "evidence": [ + "我是今年才确诊,他是焦虑的", // 证据1:完整原句支撑痛点 + "孩子现在在西工大附中上学", // 证据2:完整原句支撑隐形财力 + "留学基金我们已经准备好了" // 证据3:完整原句支撑支付能力 + ] + }, + "Pain_Threshold": { + "value": "崩溃急救状态", + "evidence": [ + "我不知道怎么来处理", + "一看见难了就崩溃啊就崩溃" + ] + }, + "Last_Interaction": { + "value": "积极配合修改信件", + "evidence": [ + "好,那你拿一个哪几点?那个你就给我框一下,然后截一个图,然后下午晚一点有空,我再来稍微修改一下", + "行,那我稍后给您打过去,给您接问一下啊" + ] + } +} + + +[核心协议] +1.宁缺毋滥原则 + 存在即输出,无证即沉默:忽略任何关于“字段数量”的限制。如果原文中有20个维度的有效证据,就输出20个;如果只有3个,就输出3个。 + 严禁凑数:如果原文中未提及某维度,或者证据模糊,“绝对不要”输出该字段。 +2.证据阵列法则 + 数据结构变更:`evidence` 字段必须是 **字符串数组 (List)**,严禁使用单一字符串。 + 颗粒度控制: + 数组中的元素必须是 “具有独立语义的完整原句” 或 “包含主谓宾的完整意群”。 + 禁止碎片:严禁提取如 "不合适"、"太贵"、"焦虑" 这样缺乏上下文的短语。 + 主体过滤:“仅提取家长(客户)表达的原话”,严禁提取销售人员的引导语、复述语或共情语。 + 纯净引用:每一个元素必须是原文的 100% 完美复制。 + 严禁拼接:严禁使用“+”、“和”、“以及”将两句不连贯的话拼在同一个字符串里。 + 严禁篡改:禁止总结、禁止润色、禁止“原文+分析”。你的分析只能体现在 `value` 字段中。 +3.结论极简法则 + 强制必输字段:`Follow_up_Priority` 是 **核心必选字段**,无论任何情况都必须输出,不允许缺失。 + Follow_up_Priority 兜底规则: + - 若文本完全无痛点/无财力/无意识,`value` 填“C级 (无痛点/无意识)”,`evidence` 填 ["文本未提及任何痛点、财力或意向相关内容"] + - 若仅部分信息缺失,按规则评级并在 `evidence` 中列出已有有效原句。 + Value 约束:必须围绕 “痛点、财力、意识” 三个指标。 + S级: 涉及生命安全 OR (极高痛点 + 强支付能力 + 强意向)。 —三个指标都为高 + A级: 有痛点 + 有支付能力。 ——三个指标中有两个为高 + B级: 有痛点 + 支付能力低/犹豫。 ——三个指标中只有一个为高 + C级: 无痛点/无意识。 +4.身份与财富的高敏嗅觉 + 对于 “高价值信号” (职业/多孩/私立学校/房产)和 “生命红线”(自杀/不想活/抑郁症确证)保持极高敏感,一旦出现必须提取。 + +[输出格式指令] +强制要求1:JSON 中必须包含 `Follow_up_Priority` 字段,否则视为无效输出。 +强制要求2:JSON 格式必须严格合法(逗号分隔、引号成对、括号匹配),可直接被JSON解析工具识别。 +强制要求3:证据必须在原文中,且充足。 +强制要求4:不准将示例模板内容作为证据输出,必须从原文中找证据。 +""" + full_prompt = f"{prompt_template}\n\n### 原始通话文本\n{dialogue_text}\n\n### 请严格按照上述要求输出JSON(仅JSON,无其他内容)" + return full_prompt + + +def clean_and_fix_json(json_str): + """清洗JSON格式""" + try: + # 移除转义符、控制字符和多余空格 + json_str = json_str.replace('\\"', '"').replace("\\'", "'") + json_str = re.sub(r'[\n\r\t\f\v]', '', json_str) + json_str = re.sub(r'\s+', ' ', json_str).strip() + # 修复末尾多余逗号 + json_str = re.sub(r",\s*}", "}", json_str) + json_str = re.sub(r",\s*]", "]", json_str) + return json_str + except Exception as e: + raise RuntimeError(f"JSON清洗失败: {str(e)}") from e + + +def extract_features_with_qwen(dialogue_text, file_name, output_dir): + """ + 调用API提取特征并保存为JSON文件 + :param dialogue_text: 预处理后的对话文本(字符串) + :param file_name: 原文件名称(用于生成输出文件名) + :param output_dir: 结果保存目录 + :return: 提取的特征字典(失败则返回None) + """ + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + prompt = build_extraction_prompt(dialogue_text) + + try: + response = client.chat.completions.create( + model=MODEL, + messages=[{"role": "user", "content": prompt}], + temperature=TEMPERATURE, + max_tokens=8000 + ) + + feature_json_str = response.choices[0].message.content.strip() + # 提取JSON片段 + json_match = re.search(r"\{[\s\S]*\}", feature_json_str) + if json_match: + feature_json_str = json_match.group() + else: + raise ValueError("返回内容中未找到有效JSON数据") + + # 移除代码块标记 + if feature_json_str.startswith("```json"): + feature_json_str = feature_json_str[7:-3].strip() + elif feature_json_str.startswith("```"): + feature_json_str = feature_json_str[3:-3].strip() + + feature_dict = json.loads(feature_json_str) + + # 验证核心字段 + if "Follow_up_Priority" not in feature_dict: + raise ValueError("返回结果缺失核心必选字段:Follow_up_Priority") + + # 生成输出文件名 + file_base = os.path.splitext(file_name)[0] + json_filename = f"{file_base}.json" + output_path = os.path.join(output_dir, json_filename) + + # 保存文件 + with open(output_path, "w", encoding="utf-8") as f: + json.dump(feature_dict, f, ensure_ascii=False, indent=2) + + print(f"处理完成:{file_name} -> {json_filename}") + return feature_dict + + except json.JSONDecodeError as e: + print(f"JSON解析失败 {file_name}:{str(e)} | 原始内容:{feature_json_str[:200]}...") + return None + except ValueError as e: + print(f"数据验证失败 {file_name}:{str(e)}") + return None + except Exception as e: + print(f"特征提取失败 {file_name}:{str(e)}") + return None + +# 批量处理函数 +def batch_process_first_200_txt(folder_path, output_dir): + """ + 仅处理指定文件夹下的前200个txt文件 + :param folder_path: 待处理文件夹路径 + :param output_dir: 结果输出目录 + """ + # 检查文件夹是否存在 + if not os.path.isdir(folder_path): + print(f"文件夹不存在:{folder_path}") + return + + # 筛选出文件夹中的txt文件并按名称排序(保证处理顺序稳定) + txt_file_list = [ + f for f in os.listdir(folder_path) + if os.path.isfile(os.path.join(folder_path, f)) and f.lower().endswith(".txt") + ] + # 按文件名排序(可选,保证每次处理顺序一致) + txt_file_list.sort() + + # 取前200个txt文件 + target_files = txt_file_list[:200] + + if not target_files: + print(f"文件夹 {folder_path} 中无txt文件可处理") + return + + print(f"始处理前 {len(target_files)} 个txt文件") + + processed_count = 0 + failed_count = 0 + + for file_name in target_files: + file_path = os.path.join(folder_path, file_name) + + # 读取文件内容 + try: + with open(file_path, "r", encoding="utf-8") as f: + dialogue_content = f.read().strip() + if not dialogue_content: + print(f"文件内容为空,跳过:{file_name}") + failed_count += 1 + continue + except Exception as e: + print(f"读取文件失败 {file_name}:{str(e)}") + failed_count += 1 + continue + + # 调用特征提取函数 + result = extract_features_with_qwen(dialogue_content, file_name, output_dir) + if result: + processed_count += 1 + else: + failed_count += 1 + + # 输出批量处理统计结果 + print("\n批量处理完成") + print(f"成功处理:{processed_count} 个文件") + print(f"处理失败:{failed_count} 个文件") + print(f"结果保存至:{os.path.abspath(output_dir)}") + + +if __name__ == "__main__": + # 要处理的源文件夹路径 + target_folder = "./time_12_1/data_200" + + # 执行批量处理:仅处理前200个txt文件,输出到 + batch_process_first_200_txt( + folder_path=target_folder, + output_dir=OUTPUT_DIR_2 + ) \ No newline at end of file diff --git a/data/object_convert.py b/data/object_convert.py new file mode 100644 index 0000000..2714217 --- /dev/null +++ b/data/object_convert.py @@ -0,0 +1,60 @@ +# 将0-家长,1-销售转换 +import os +import re + +# 原始TXT文件所在目录(你的data文件夹路径) +INPUT_DIR = "./data_new/processed_txt_files" +# 处理后文件保存目录(自动创建,与原文件同名) +OUTPUT_DIR = "./data_role_replaced" +# 正则表达式:匹配行首的“0:”或“1:”(确保只改角色标识,不改文本内容) +ROLE_PATTERN = re.compile(r'^([01]):', re.MULTILINE) # re.MULTILINE让^匹配每行开头 + + +def replace_role_in_txt(txt_path, output_path): + """ + 处理单个TXT文件:将行首的0:→家长:,1:→销售: + :param txt_path: 原始TXT路径 + :param output_path: 处理后TXT保存路径 + """ + # 1. 读取原始文件内容 + with open(txt_path, 'r', encoding='utf-8') as f: + content = f.read() + + # 2. 替换角色标识:0→家长,1→销售 + def replace_match(match): + role_code = match.group(1) # 获取匹配到的“0”或“1” + return "家长:" if role_code == "0" else "销售:" + + # 用自定义函数替换所有匹配项 + replaced_content = ROLE_PATTERN.sub(replace_match, content) + + # 3. 保存处理后的文件 + with open(output_path, 'w', encoding='utf-8') as f: + f.write(replaced_content) + + print(f"处理完成:{os.path.basename(txt_path)}") + + +def batch_replace_all_txt(): + """批量处理INPUT_DIR下所有TXT文件""" + # 1. 创建输出目录(不存在则自动创建) + os.makedirs(OUTPUT_DIR, exist_ok=True) + + # 2. 筛选目录下所有TXT文件 + txt_files = [f for f in os.listdir(INPUT_DIR) if f.endswith('.txt')] + if not txt_files: + print(f"未在 {INPUT_DIR} 目录找到TXT文件,请检查路径!") + return + + # 3. 逐个处理TXT文件 + print(f"共发现 {len(txt_files)} 个TXT文件,开始批量替换角色...") + for txt_filename in txt_files: + input_path = os.path.join(INPUT_DIR, txt_filename) + output_path = os.path.join(OUTPUT_DIR, txt_filename) + replace_role_in_txt(input_path, output_path) + + print(f"\n全部处理完成!文件已保存至:{os.path.abspath(OUTPUT_DIR)}") + + +if __name__ == "__main__": + batch_replace_all_txt() \ No newline at end of file