diff --git a/plugins/ai_auto_response/context_builder.py b/plugins/ai_auto_response/context_builder.py index 88b0145..b119bca 100644 --- a/plugins/ai_auto_response/context_builder.py +++ b/plugins/ai_auto_response/context_builder.py @@ -189,9 +189,13 @@ class ContextBuilder: meta = member_context.get("meta", {}) or {} topics = member_context.get("topics_of_interest", []) or [] recent_focus = member_context.get("recent_focus", []) or [] + common_scenarios = ContextBuilder._stringify_items(meta.get("common_scenarios", []), 4) skills = ContextBuilder._stringify_items(meta.get("skill_profile", []), 5) + problem_solving = ContextBuilder._stringify_items(meta.get("problem_solving_profile", []), 4) stable_traits = ContextBuilder._stringify_items(meta.get("stable_traits", []), 4) habits = ContextBuilder._stringify_items(meta.get("habit_patterns", []), 4) + expression_profile = ContextBuilder._stringify_items(meta.get("expression_profile", []), 4) + reply_entry = ContextBuilder._stringify_items(meta.get("reply_entry_profile", []), 4) reply_prefs = ContextBuilder._stringify_items(meta.get("long_term_reply_preferences", []), 4) recent_state = ContextBuilder._stringify_items(meta.get("recent_state", []), 4) reply_taboos = ContextBuilder._stringify_items(meta.get("reply_taboos", []), 3) @@ -201,9 +205,13 @@ class ContextBuilder: f"回复偏好:{member_context.get('response_style_hint', '')}".strip(), f"长期主题:{', '.join(topics[:5])}" if topics else "", f"近期关注:{', '.join(recent_focus[:4])}" if recent_focus else "", + f"常见发言场景:{common_scenarios}" if common_scenarios else "", f"技能侧重点:{skills}" if skills else "", + f"处理问题方式:{problem_solving}" if problem_solving else "", f"稳定特征:{stable_traits}" if stable_traits else "", f"习惯模式:{habits}" if habits else "", + f"表达标记:{expression_profile}" if expression_profile else "", + f"有效接话点:{reply_entry}" if reply_entry else "", f"长期回复偏好:{reply_prefs}" if reply_prefs else "", f"近期状态:{recent_state}" if recent_state else "", f"气质倾向:{meta.get('temperament_tendency', '')}".strip(), diff --git a/plugins/member_context/DIFY_WORKFLOW.md b/plugins/member_context/DIFY_WORKFLOW.md index 3e6deb8..af80793 100644 --- a/plugins/member_context/DIFY_WORKFLOW.md +++ b/plugins/member_context/DIFY_WORKFLOW.md @@ -67,10 +67,14 @@ 3. 请先按 wxid 汇总该成员全天发言,再提取结果。 4. 即使成员发言以短句为主,只要样本量足够,也必须尽量提炼: - topics + - discussion_scenarios - skill_signals + - problem_solving_signals - value_preferences - habit_signals + - expression_markers - engagement_traits + - reply_entry_points - social_role - temperament_signal - summary_text @@ -88,8 +92,10 @@ "wxid": "成员wxid", "display_name": "显示名", "topics": ["主题1"], + "discussion_scenarios": ["场景1"], "identity_clues": ["身份线索1"], "skill_signals": ["技能信号1"], + "problem_solving_signals": ["处理方式1"], "family_signals": ["家庭线索1"], "life_stage_signals": ["阶段线索1"], "value_preferences": ["价值偏好1"], @@ -97,7 +103,9 @@ "message_pattern": "一句中文", "response_style_hint": "一句中文", "habit_signals": ["习惯1"], + "expression_markers": ["表达标记1"], "engagement_traits": ["参与特征1"], + "reply_entry_points": ["接话点1"], "decision_style": "一句中文", "social_role": "一句中文", "reply_taboos": ["避坑1"], diff --git a/plugins/member_context/digest_service.py b/plugins/member_context/digest_service.py index d90ed18..b9d436b 100644 --- a/plugins/member_context/digest_service.py +++ b/plugins/member_context/digest_service.py @@ -149,10 +149,12 @@ class MemberDigestService: normalized = {} list_keys = { "topics", "identity_clues", "skill_signals", "family_signals", "life_stage_signals", - "value_preferences", "habit_signals", "engagement_traits", "reply_taboos", + "discussion_scenarios", "problem_solving_signals", "value_preferences", "habit_signals", + "expression_markers", "engagement_traits", "reply_entry_points", "reply_taboos", "representative_messages", "stable_topics", "identity_traits", "skill_profile", - "family_profile", "life_stage_profile", "value_profile", "stable_traits", - "habit_patterns", "reply_preferences", "long_term_topics", "long_term_reply_preferences", + "problem_solving_profile", "family_profile", "life_stage_profile", "value_profile", + "stable_traits", "habit_patterns", "expression_profile", "reply_entry_profile", + "reply_preferences", "long_term_topics", "long_term_reply_preferences", "common_scenarios", "phase_state", "recent_state" } for key, value in item.items(): diff --git a/plugins/member_context/member_context.yml b/plugins/member_context/member_context.yml new file mode 100644 index 0000000..0cc64fb --- /dev/null +++ b/plugins/member_context/member_context.yml @@ -0,0 +1,351 @@ +app: + description: 按群和日期提取群成员日画像,输出严格 JSON,供 member_context 插件直接消费 + icon: 🤖 + icon_background: '#E0F2FE' + mode: workflow + name: member_context + use_icon_as_answer_icon: false +dependencies: +- current_identifier: null + type: marketplace + value: + marketplace_plugin_unique_identifier: langgenius/openai_api_compatible:0.0.27@f9ce3ff5e28f09931a3a7fca59add2d09590408f7e9a3d701b10c77a60249719 + version: null +kind: app +version: 0.5.0 +workflow: + conversation_variables: [] + environment_variables: [] + features: + file_upload: + allowed_file_extensions: + - .TXT + allowed_file_types: + - document + allowed_file_upload_methods: + - local_file + - remote_url + enabled: false + fileUploadConfig: + audio_file_size_limit: 50 + batch_count_limit: 5 + file_size_limit: 15 + image_file_batch_limit: 10 + image_file_size_limit: 10 + single_chunk_attachment_limit: 10 + video_file_size_limit: 100 + workflow_file_upload_limit: 10 + image: + enabled: false + number_limits: 3 + transfer_methods: + - local_file + - remote_url + number_limits: 3 + opening_statement: '' + retriever_resource: + enabled: false + sensitive_word_avoidance: + enabled: false + speech_to_text: + enabled: false + suggested_questions: [] + suggested_questions_after_answer: + enabled: false + text_to_speech: + enabled: false + language: '' + voice: '' + graph: + edges: + - data: + isInIteration: false + isInLoop: false + sourceType: start + targetType: llm + id: start-source-llm-target + selected: false + source: start_node + sourceHandle: source + target: llm_node + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: false + isInLoop: false + sourceType: llm + targetType: end + id: llm-source-end-target + selected: false + source: llm_node + sourceHandle: source + target: end_node + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: false + isInLoop: false + sourceType: llm + targetType: llm + id: llm_node-fail-branch-1775115372864-target + source: llm_node + sourceHandle: fail-branch + target: '1775115372864' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInLoop: false + sourceType: llm + targetType: end + id: 1775115372864-source-end_node-target + source: '1775115372864' + sourceHandle: source + target: end_node + targetHandle: target + type: custom + zIndex: 0 + nodes: + - data: + desc: '' + selected: false + title: 开始 + type: start + variables: + - label: query + max_length: 120000 + options: [] + required: false + type: paragraph + variable: query + - label: chatroom_id + max_length: 128 + options: [] + required: true + type: text-input + variable: chatroom_id + - label: digest_date + max_length: 32 + options: [] + required: true + type: text-input + variable: digest_date + - label: member_labels + max_length: 50000 + options: [] + required: true + type: paragraph + variable: member_labels + - label: compressed_chat + max_length: 200000 + options: [] + required: true + type: paragraph + variable: compressed_chat + height: 213 + id: start_node + position: + x: 0 + y: 0 + positionAbsolute: + x: 0 + y: 0 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + context: + enabled: false + variable_selector: [] + desc: '' + error_strategy: fail-branch + model: + completion_params: + temperature: 0.2 + mode: chat + name: gpt-5.4-mini + provider: langgenius/openai_api_compatible/openai_api_compatible + prompt_template: + - id: system_prompt_member_context + role: system + text: "你是微信群后台的成员日行为证据提取器。\n\n任务:\n根据给定的一天群聊记录,只按 wxid 识别成员,输出每个成员当天的结构化行为观察。\n\ + \n关键规则:\n1. wxid 是唯一标识。display_name 仅用于展示,不用于身份判定。\n2. 每个 wxid 最终只能输出一条记录,严禁重复输出同一个\ + \ wxid。\n3. 请先按 wxid 汇总该成员全天发言,再提取结果。\n4. 即使成员发言以短句为主,只要样本量足够,也必须尽量提炼:\n\ + \ - topics\n - discussion_scenarios\n - skill_signals\n - problem_solving_signals\n\ + \ - value_preferences\n - habit_signals\n - expression_markers\n\ + \ - engagement_traits\n - reply_entry_points\n - social_role\n\ + \ - temperament_signal\n - summary_text\n5. identity_clues、family_signals、life_stage_signals\ + \ 没有明确公开证据时允许为空。\n6. 不允许因为“短句较多”就统一输出空数组和通用摘要。\n7. 不做心理诊断、不做隐私猜测、不把玩笑当事实。\n\ + 8. 只能输出候选成员列表中的 wxid。\n9. topics 更偏向反复出现的关注方向;discussion_scenarios 更偏向什么情境下会发言;skill_signals\ + \ 更偏向能力表现;problem_solving_signals 更偏向怎么处理问题;value_preferences 更偏向判断偏好;social_role 更偏向当天在群里的实际作用。\n\ + 10. expression_markers 记录可观察的表达标记,如常用句式、口头禅、是否爱列步骤/贴日志;reply_entry_points 记录什么样的接话方式最容易接住他。\n\ + 11. 输出前自行去重,同一个 wxid 只保留一条最完整结果。\n\n输出要求:\n- 只输出严格 JSON,不要 markdown,不要解释,不要前后缀。\n\ + - 输出格式:\n{\n \"members\": [\n {\n \"wxid\": \"成员wxid\",\n \ + \ \"display_name\": \"显示名\",\n \"topics\": [\"主题1\"],\n \"discussion_scenarios\"\ + : [\"场景1\"],\n \"identity_clues\": [\"身份线索1\"],\n \"skill_signals\"\ + : [\"技能信号1\"],\n \"problem_solving_signals\": [\"处理方式1\"],\n \ + \ \"family_signals\": [\"家庭线索1\"],\n \"life_stage_signals\": [\"\ + 阶段线索1\"],\n \"value_preferences\": [\"价值偏好1\"],\n \"interaction_style\"\ + : \"一句中文\",\n \"message_pattern\": \"一句中文\",\n \"response_style_hint\"\ + : \"一句中文\",\n \"habit_signals\": [\"习惯1\"],\n \"expression_markers\"\ + : [\"表达标记1\"],\n \"engagement_traits\": [\"参与特征1\"],\n \"reply_entry_points\"\ + : [\"接话点1\"],\n \"decision_style\": \"一句中文\",\n \"social_role\"\ + : \"一句中文\",\n \"reply_taboos\": [\"避坑1\"],\n \"temperament_signal\"\ + : \"一句中文\",\n \"summary_text\": \"不超过100字\",\n \"representative_messages\"\ + : [\"原话1\", \"原话2\"],\n \"confidence\": 0.95\n }\n ]\n}\n\n字段约束:\n\ + - topics、discussion_scenarios、skill_signals、problem_solving_signals、value_preferences、habit_signals、expression_markers、engagement_traits、reply_entry_points\ + \ 最多 4 个\n- identity_clues、family_signals、life_stage_signals 最多 3 个\n\ + - reply_taboos 最多 3 个\n- representative_messages 最多 3 条\n- 如果某成员样本明显不足,可以不输出该成员\n" + - id: user_prompt_member_context + role: user + text: '群ID: {{#start_node.chatroom_id#}} + + 日期: {{#start_node.digest_date#}} + + + 候选成员: + + {{#start_node.member_labels#}} + + + 压缩后的群聊记录: + + {{#start_node.compressed_chat#}} + + ' + retry_config: + max_retries: 3 + retry_enabled: true + retry_interval: 1000 + selected: false + title: 成员画像提取 + type: llm + variables: [] + vision: + enabled: false + height: 154 + id: llm_node + position: + x: 342 + y: 30 + positionAbsolute: + x: 342 + y: 30 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + desc: '' + outputs: + - value_selector: + - llm_node + - text + variable: text + selected: false + title: 结束 + type: end + height: 88 + id: end_node + position: + x: 1066 + y: 52 + positionAbsolute: + x: 1066 + y: 52 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + context: + enabled: false + variable_selector: [] + default_value: + - key: text + type: string + value: '{"members": []}' + error_strategy: default-value + model: + completion_params: + temperature: 0.7 + mode: chat + name: grok-4-fast + provider: langgenius/openai_api_compatible/openai_api_compatible + prompt_template: + - id: c5ee983e-d6e0-4790-ac3b-f4e097013b70 + role: system + text: "你是微信群后台的成员日行为证据提取器。\n\n任务:\n根据给定的一天群聊记录,只按 wxid 识别成员,输出每个成员当天的结构化行为观察。\n\ + \n关键规则:\n1. wxid 是唯一标识。display_name 仅用于展示,不用于身份判定。\n2. 每个 wxid 最终只能输出一条记录,严禁重复输出同一个\ + \ wxid。\n3. 请先按 wxid 汇总该成员全天发言,再提取结果。\n4. 即使成员发言以短句为主,只要样本量足够,也必须尽量提炼:\n\ + \ - topics\n - discussion_scenarios\n - skill_signals\n - problem_solving_signals\n\ + \ - value_preferences\n - habit_signals\n - expression_markers\n\ + \ - engagement_traits\n - reply_entry_points\n - social_role\n\ + \ - temperament_signal\n - summary_text\n5. identity_clues、family_signals、life_stage_signals\ + \ 没有明确公开证据时允许为空。\n6. 不允许因为“短句较多”就统一输出空数组和通用摘要。\n7. 不做心理诊断、不做隐私猜测、不把玩笑当事实。\n\ + 8. 只能输出候选成员列表中的 wxid。\n9. topics 更偏向反复出现的关注方向;discussion_scenarios 更偏向什么情境下会发言;skill_signals\ + \ 更偏向能力表现;problem_solving_signals 更偏向怎么处理问题;value_preferences 更偏向判断偏好;social_role 更偏向当天在群里的实际作用。\n\ + 10. expression_markers 记录可观察的表达标记,如常用句式、口头禅、是否爱列步骤/贴日志;reply_entry_points 记录什么样的接话方式最容易接住他。\n\ + 11. 输出前自行去重,同一个 wxid 只保留一条最完整结果。\n\n输出要求:\n- 只输出严格 JSON,不要 markdown,不要解释,不要前后缀。\n\ + - 输出格式:\n{\n \"members\": [\n {\n \"wxid\": \"成员wxid\",\n \ + \ \"display_name\": \"显示名\",\n \"topics\": [\"主题1\"],\n \"discussion_scenarios\"\ + : [\"场景1\"],\n \"identity_clues\": [\"身份线索1\"],\n \"skill_signals\"\ + : [\"技能信号1\"],\n \"problem_solving_signals\": [\"处理方式1\"],\n \ + \ \"family_signals\": [\"家庭线索1\"],\n \"life_stage_signals\": [\"\ + 阶段线索1\"],\n \"value_preferences\": [\"价值偏好1\"],\n \"interaction_style\"\ + : \"一句中文\",\n \"message_pattern\": \"一句中文\",\n \"response_style_hint\"\ + : \"一句中文\",\n \"habit_signals\": [\"习惯1\"],\n \"expression_markers\"\ + : [\"表达标记1\"],\n \"engagement_traits\": [\"参与特征1\"],\n \"reply_entry_points\"\ + : [\"接话点1\"],\n \"decision_style\": \"一句中文\",\n \"social_role\"\ + : \"一句中文\",\n \"reply_taboos\": [\"避坑1\"],\n \"temperament_signal\"\ + : \"一句中文\",\n \"summary_text\": \"不超过100字\",\n \"representative_messages\"\ + : [\"原话1\", \"原话2\"],\n \"confidence\": 0.95\n }\n ]\n}\n\n字段约束:\n\ + - topics、discussion_scenarios、skill_signals、problem_solving_signals、value_preferences、habit_signals、expression_markers、engagement_traits、reply_entry_points\ + \ 最多 4 个\n- identity_clues、family_signals、life_stage_signals 最多 3 个\n\ + - reply_taboos 最多 3 个\n- representative_messages 最多 3 条\n- 如果某成员样本明显不足,可以不输出该成员\n" + - id: 2df6d4ce-4e40-42a9-85f3-184075da24c7 + role: user + text: '群ID: {{#start_node.chatroom_id#}} + + 日期: {{#start_node.digest_date#}} + + + 候选成员: + + {{#start_node.member_labels#}} + + + 压缩后的群聊记录: + + {{#start_node.compressed_chat#}} + + ' + selected: true + title: 异常分支 + type: llm + vision: + enabled: false + height: 124 + id: '1775115372864' + position: + x: 704 + y: 132 + positionAbsolute: + x: 704 + y: 132 + selected: true + sourcePosition: right + targetPosition: left + type: custom + width: 242 + viewport: + x: 210 + y: 340 + zoom: 0.7 + rag_pipeline_variables: [] diff --git a/plugins/member_context/member_context_workflow.yml b/plugins/member_context/member_context_workflow.yml index 89ff0bf..d704512 100644 --- a/plugins/member_context/member_context_workflow.yml +++ b/plugins/member_context/member_context_workflow.yml @@ -162,10 +162,14 @@ workflow: 3. 请先按 wxid 汇总该成员全天发言,再提取结果。 4. 即使成员发言以短句为主,只要样本量足够,也必须尽量提炼: - topics + - discussion_scenarios - skill_signals + - problem_solving_signals - value_preferences - habit_signals + - expression_markers - engagement_traits + - reply_entry_points - social_role - temperament_signal - summary_text @@ -173,8 +177,9 @@ workflow: 6. 不允许因为“短句较多”就统一输出空数组和通用摘要。 7. 不做心理诊断、不做隐私猜测、不把玩笑当事实。 8. 只能输出候选成员列表中的 wxid。 - 9. topics 更偏向反复出现的关注方向;skill_signals 更偏向能力表现;value_preferences 更偏向判断偏好;social_role 更偏向当天在群里的实际作用。 - 10. 输出前自行去重,同一个 wxid 只保留一条最完整结果。 + 9. topics 更偏向反复出现的关注方向;discussion_scenarios 更偏向什么情境下会发言;skill_signals 更偏向能力表现;problem_solving_signals 更偏向怎么处理问题;value_preferences 更偏向判断偏好;social_role 更偏向当天在群里的实际作用。 + 10. expression_markers 记录可观察的表达标记,如常用句式、口头禅、是否爱列步骤/贴日志;reply_entry_points 记录什么样的接话方式最容易接住他。 + 11. 输出前自行去重,同一个 wxid 只保留一条最完整结果。 输出要求: - 只输出严格 JSON,不要 markdown,不要解释,不要前后缀。 @@ -185,8 +190,10 @@ workflow: "wxid": "成员wxid", "display_name": "显示名", "topics": ["主题1"], + "discussion_scenarios": ["场景1"], "identity_clues": ["身份线索1"], "skill_signals": ["技能信号1"], + "problem_solving_signals": ["处理方式1"], "family_signals": ["家庭线索1"], "life_stage_signals": ["阶段线索1"], "value_preferences": ["价值偏好1"], @@ -194,7 +201,9 @@ workflow: "message_pattern": "一句中文", "response_style_hint": "一句中文", "habit_signals": ["习惯1"], + "expression_markers": ["表达标记1"], "engagement_traits": ["参与特征1"], + "reply_entry_points": ["接话点1"], "decision_style": "一句中文", "social_role": "一句中文", "reply_taboos": ["避坑1"], @@ -207,7 +216,7 @@ workflow: } 字段约束: - - topics、skill_signals、value_preferences、habit_signals、engagement_traits 最多 4 个 + - topics、discussion_scenarios、skill_signals、problem_solving_signals、value_preferences、habit_signals、expression_markers、engagement_traits、reply_entry_points 最多 4 个 - identity_clues、family_signals、life_stage_signals 最多 3 个 - reply_taboos 最多 3 个 - representative_messages 最多 3 条 diff --git a/plugins/member_context/prompt_builder.py b/plugins/member_context/prompt_builder.py index 2eb505b..1348c7b 100644 --- a/plugins/member_context/prompt_builder.py +++ b/plugins/member_context/prompt_builder.py @@ -21,8 +21,10 @@ class MemberContextPromptBuilder: "\"wxid\":\"成员wxid\"," "\"display_name\":\"成员显示名\"," "\"topics\":[\"主题1\"]," + "\"discussion_scenarios\":[\"场景1\"]," "\"identity_clues\":[\"身份线索1\"]," "\"skill_signals\":[\"技能信号1\"]," + "\"problem_solving_signals\":[\"处理方式1\"]," "\"family_signals\":[\"家庭线索1\"]," "\"life_stage_signals\":[\"阶段线索1\"]," "\"value_preferences\":[\"价值偏好1\"]," @@ -30,7 +32,9 @@ class MemberContextPromptBuilder: "\"message_pattern\":\"一句中文\"," "\"response_style_hint\":\"一句中文\"," "\"habit_signals\":[\"信号1\"]," + "\"expression_markers\":[\"表达标记1\"]," "\"engagement_traits\":[\"特征1\"]," + "\"reply_entry_points\":[\"接话点1\"]," "\"decision_style\":\"一句中文\"," "\"social_role\":\"一句中文,描述当天在群中的角色表现\"," "\"reply_taboos\":[\"避坑1\"]," @@ -43,15 +47,17 @@ class MemberContextPromptBuilder: "}\n" "要求:\n" "1. 只输出当天真正参与发言且能看出明确行为信号的成员;发言极少的人可以不输出。\n" - "2. 每个成员的 topics、identity_clues、skill_signals、family_signals、life_stage_signals、value_preferences、habit_signals、engagement_traits 最多4个,reply_taboos 最多3个。\n" + "2. 每个成员的 topics、discussion_scenarios、identity_clues、skill_signals、problem_solving_signals、family_signals、life_stage_signals、value_preferences、habit_signals、expression_markers、engagement_traits、reply_entry_points 最多4个,reply_taboos 最多3个。\n" "3. representative_messages 只保留最能代表当天表达方式的短句,最多3条。\n" "4. 必须严格使用候选成员列表中的 wxid 和显示名。\n" "5. identity_clues、family_signals、life_stage_signals 只能写公开聊天中出现的线索,不可把弱线索写成确定事实。\n" "6. skill_signals 重点提炼成员解决问题、提供信息、组织表达、专业能力等信号。\n" "7. social_role 只描述当天在群里的角色表现,例如:问题提出者、信息补充者、气氛调节者、组织推进者。\n" - "8. topics 更偏向持续关注的话题方向;habit_signals 更偏向重复表达或互动习惯;engagement_traits 更偏向参与方式。\n" - "9. value_preferences 只记录公开表达出的偏好,如效率优先、成本敏感、谨慎验证、乐于助人,不要写抽象大词。\n" - "10. summary_text 应是后台观察摘要,不要写成对用户说的话。\n" + "8. topics 更偏向持续关注的话题方向;discussion_scenarios 更偏向他通常在什么情境下发言,如求助排错、补充经验、分享资源、接梗互动。\n" + "9. problem_solving_signals 要写得具体,比如先确认现象再给步骤、倾向贴链接/文档、会直接给结论和执行项,不要只写'善于解决问题'。\n" + "10. expression_markers 记录可观察的表达标记,如常用句式、口头禅、是否爱列步骤/贴截图/贴日志;reply_entry_points 记录什么样的接话方式最容易接住他。\n" + "11. value_preferences 只记录公开表达出的偏好,如效率优先、成本敏感、谨慎验证、乐于助人,不要写抽象大词。\n" + "12. summary_text 应是后台观察摘要,不要写成对用户说的话。\n" f"群ID: {chatroom_id}\n" f"日期: {digest_date}\n" "候选成员:\n" + "\n".join(member_labels[:80]) + "\n" @@ -68,13 +74,17 @@ class MemberContextPromptBuilder: "period_key": item.get("period_key"), "summary_text": item.get("summary_text", ""), "topics": structured.get("topics") or structured.get("stable_topics") or structured.get("long_term_topics") or [], + "discussion_scenarios": structured.get("discussion_scenarios") or structured.get("common_scenarios") or [], "identity_clues": structured.get("identity_clues") or structured.get("identity_traits") or [], "skill_signals": structured.get("skill_signals") or structured.get("skill_profile") or [], + "problem_solving_signals": structured.get("problem_solving_signals") or structured.get("problem_solving_profile") or [], "family_signals": structured.get("family_signals") or structured.get("family_profile") or [], "life_stage_signals": structured.get("life_stage_signals") or structured.get("life_stage_profile") or [], "value_preferences": structured.get("value_preferences") or structured.get("value_profile") or [], "habit_signals": structured.get("habit_signals") or structured.get("habit_patterns") or [], + "expression_markers": structured.get("expression_markers") or structured.get("expression_profile") or [], "engagement_traits": structured.get("engagement_traits") or structured.get("stable_traits") or [], + "reply_entry_points": structured.get("reply_entry_points") or structured.get("reply_entry_profile") or [], "reply_preferences": structured.get("reply_preferences") or structured.get("long_term_reply_preferences") or [], "social_role": structured.get("social_role") or structured.get("group_role") or "", "decision_style": structured.get("decision_style") or structured.get("decision_profile") or "", @@ -87,13 +97,17 @@ class MemberContextPromptBuilder: schema = ( "{" "\"stable_topics\":[\"主题1\"]," + "\"common_scenarios\":[\"场景1\"]," "\"identity_traits\":[\"身份特征1\"]," "\"skill_profile\":[\"技能画像1\"]," + "\"problem_solving_profile\":[\"处理方式1\"]," "\"family_profile\":[\"家庭线索1\"]," "\"life_stage_profile\":[\"阶段线索1\"]," "\"value_profile\":[\"价值偏好1\"]," "\"stable_traits\":[\"特征1\"]," "\"habit_patterns\":[\"习惯1\"]," + "\"expression_profile\":[\"表达标记1\"]," + "\"reply_entry_profile\":[\"接话点1\"]," "\"reply_preferences\":[\"偏好1\"]," "\"group_role\":\"一句中文\"," "\"decision_profile\":\"一句中文\"," @@ -108,13 +122,17 @@ class MemberContextPromptBuilder: schema = ( "{" "\"long_term_topics\":[\"主题1\"]," + "\"common_scenarios\":[\"场景1\"]," "\"identity_traits\":[\"身份特征1\"]," "\"skill_profile\":[\"技能画像1\"]," + "\"problem_solving_profile\":[\"处理方式1\"]," "\"family_profile\":[\"家庭线索1\"]," "\"life_stage_profile\":[\"阶段线索1\"]," "\"value_profile\":[\"价值偏好1\"]," "\"stable_traits\":[\"特征1\"]," "\"habit_patterns\":[\"习惯1\"]," + "\"expression_profile\":[\"表达标记1\"]," + "\"reply_entry_profile\":[\"接话点1\"]," "\"long_term_reply_preferences\":[\"偏好1\"]," "\"group_role\":\"一句中文\"," "\"decision_profile\":\"一句中文\"," @@ -136,9 +154,11 @@ class MemberContextPromptBuilder: "2. 只有多个下级摘要反复出现的特征,才允许写进 stable_traits / habit_patterns / long_term_reply_preferences。\n" "3. recent_state / phase_state 只描述当前阶段状态,不要冒充长期人格。\n" "4. identity_traits、family_profile、life_stage_profile 只能保留反复出现的公开线索,不可编造事实。\n" - "5. skill_profile 要优先提炼稳定出现的能力、专业方向、擅长处理的问题类型。\n" - "6. group_role 描述其在群中的长期角色位置,decision_profile 描述其决策与判断风格。\n" - "7. value_profile 需要优先保留真正反复出现的判断偏好,如效率优先、成本敏感、风险谨慎、愿意分享。\n" + "5. common_scenarios 要总结这个人通常会在什么场景下参与,problem_solving_profile 要总结其典型处理问题方式,尽量具体到行为动作。\n" + "6. skill_profile 要优先提炼稳定出现的能力、专业方向、擅长处理的问题类型。\n" + "7. expression_profile 要保留反复出现的表达标记,如先说结论、爱列步骤、常贴原话/日志;reply_entry_profile 要总结适合接话的切入点。\n" + "8. group_role 描述其在群中的长期角色位置,decision_profile 描述其决策与判断风格。\n" + "9. value_profile 需要优先保留真正反复出现的判断偏好,如效率优先、成本敏感、风险谨慎、愿意分享。\n" f"成员: {display_name} ({wxid})\n" f"群ID: {chatroom_id}\n" f"周期: {period_key}\n" @@ -164,13 +184,17 @@ class MemberContextPromptBuilder: "\"response_style_hint\":\"一句中文\"," "\"topics_of_interest\":[\"主题1\"]," "\"recent_focus\":[\"近期主题1\"]," + "\"common_scenarios\":[\"场景1\"]," "\"identity_traits\":[\"身份线索1\"]," "\"skill_profile\":[\"技能画像1\"]," + "\"problem_solving_profile\":[\"处理方式1\"]," "\"family_profile\":[\"家庭线索1\"]," "\"life_stage_profile\":[\"阶段线索1\"]," "\"value_profile\":[\"价值偏好1\"]," "\"stable_traits\":[\"长期特征1\"]," "\"habit_patterns\":[\"习惯1\"]," + "\"expression_profile\":[\"表达标记1\"]," + "\"reply_entry_profile\":[\"接话点1\"]," "\"long_term_reply_preferences\":[\"偏好1\"]," "\"group_role\":\"一句中文\"," "\"decision_profile\":\"一句中文\"," @@ -186,9 +210,10 @@ class MemberContextPromptBuilder: "2. recent_focus、recent_state 更依赖最近周级和日级。\n" "3. summary_text 要像后台备注,不要明显暴露在给用户做画像。\n" "4. identity_traits、family_profile、life_stage_profile 必须写成公开线索或长期观察,不得伪造事实。\n" - "5. skill_profile 要尽量覆盖专业能力、问题解决能力、表达组织能力、资源协调能力等维度。\n" - "6. group_role 要描述其在群中的角色定位,decision_profile 要描述其决策/判断方式。\n" - "7. 如果月级与周级证据不足,宁可少写,也不要把短期状态写成长期人格。\n" + "5. common_scenarios、problem_solving_profile、expression_profile、reply_entry_profile 要尽量写具体行为,不要只写抽象人格词。\n" + "6. skill_profile 要尽量覆盖专业能力、问题解决能力、表达组织能力、资源协调能力等维度。\n" + "7. group_role 要描述其在群中的角色定位,decision_profile 要描述其决策/判断方式。\n" + "8. 如果月级与周级证据不足,宁可少写,也不要把短期状态写成长期人格。\n" f"成员: {display_name} ({wxid})\n" f"群ID: {chatroom_id}\n" "月级摘要:\n" + ("\n".join(monthly_lines) or "暂无") diff --git a/plugins/member_context/service.py b/plugins/member_context/service.py index 73f8050..5a35531 100644 --- a/plugins/member_context/service.py +++ b/plugins/member_context/service.py @@ -130,10 +130,18 @@ class MemberContextService: monthly_structured + weekly_structured + daily_structured, ["identity_traits", "identity_clues"], limit=5 ), + "common_scenarios": self._extract_scored_items( + monthly_structured + weekly_structured + daily_structured, + ["common_scenarios", "discussion_scenarios"], limit=5 + ), "skill_profile": self._extract_scored_items( monthly_structured + weekly_structured + daily_structured, ["skill_profile", "skill_signals"], limit=6 ), + "problem_solving_profile": self._extract_scored_items( + monthly_structured + weekly_structured + daily_structured, + ["problem_solving_profile", "problem_solving_signals"], limit=5 + ), "family_profile": self._extract_scored_items( monthly_structured + weekly_structured + daily_structured, ["family_profile", "family_signals"], limit=4 @@ -153,6 +161,14 @@ class MemberContextService: monthly_structured + weekly_structured + daily_structured, ["habit_patterns", "habit_signals"], limit=self.stable_max_items ), + "expression_profile": self._extract_scored_items( + monthly_structured + weekly_structured + daily_structured, + ["expression_profile", "expression_markers"], limit=5 + ), + "reply_entry_profile": self._extract_scored_items( + monthly_structured + weekly_structured + daily_structured, + ["reply_entry_profile", "reply_entry_points"], limit=4 + ), "long_term_reply_preferences": self._extract_scored_items( monthly_structured + weekly_structured, ["long_term_reply_preferences", "reply_preferences"], limit=4 ), @@ -538,12 +554,16 @@ class MemberContextService: "confidence": max(0.0, min(1.0, confidence)), "meta": { "identity_traits": norm_list(data.get("identity_traits"), 5), + "common_scenarios": norm_list(data.get("common_scenarios"), 5), "skill_profile": norm_list(data.get("skill_profile"), 6), + "problem_solving_profile": norm_list(data.get("problem_solving_profile"), 5), "family_profile": norm_list(data.get("family_profile"), 4), "life_stage_profile": norm_list(data.get("life_stage_profile"), 4), "value_profile": norm_list(data.get("value_profile"), 5), "stable_traits": norm_list(data.get("stable_traits"), self.stable_max_items), "habit_patterns": norm_list(data.get("habit_patterns"), self.stable_max_items), + "expression_profile": norm_list(data.get("expression_profile"), 5), + "reply_entry_profile": norm_list(data.get("reply_entry_profile"), 4), "long_term_reply_preferences": norm_list(data.get("long_term_reply_preferences"), 4), "group_role": str(data.get("group_role", "")).strip(), "decision_profile": str(data.get("decision_profile", "")).strip(), @@ -586,11 +606,21 @@ class MemberContextService: meta.get("identity_traits", []), current_context.get("confidence", 0) * 0.75, ) + merged_common_scenario_scores = self._merge_scored_items( + existing_meta.get("common_scenario_scores", {}), + meta.get("common_scenarios", []), + current_context.get("confidence", 0) * 0.85, + ) merged_skill_scores = self._merge_scored_items( existing_meta.get("skill_profile_scores", {}), meta.get("skill_profile", []), current_context.get("confidence", 0) * 0.85, ) + merged_problem_solving_scores = self._merge_scored_items( + existing_meta.get("problem_solving_profile_scores", {}), + meta.get("problem_solving_profile", []), + current_context.get("confidence", 0) * 0.9, + ) merged_family_scores = self._merge_scored_items( existing_meta.get("family_profile_scores", {}), meta.get("family_profile", []), @@ -606,6 +636,16 @@ class MemberContextService: meta.get("value_profile", []), current_context.get("confidence", 0) * 0.75, ) + merged_expression_scores = self._merge_scored_items( + existing_meta.get("expression_profile_scores", {}), + meta.get("expression_profile", []), + current_context.get("confidence", 0) * 0.95, + ) + merged_reply_entry_scores = self._merge_scored_items( + existing_meta.get("reply_entry_profile_scores", {}), + meta.get("reply_entry_profile", []), + current_context.get("confidence", 0), + ) merged_reply_pref_scores = self._merge_scored_items( existing_meta.get("long_term_reply_preference_scores", {}), meta.get("long_term_reply_preferences", []), @@ -621,32 +661,48 @@ class MemberContextService: meta["stable_trait_scores"] = merged_trait_scores meta["habit_pattern_scores"] = merged_habit_scores meta["identity_trait_scores"] = merged_identity_scores + meta["common_scenario_scores"] = merged_common_scenario_scores meta["skill_profile_scores"] = merged_skill_scores + meta["problem_solving_profile_scores"] = merged_problem_solving_scores meta["family_profile_scores"] = merged_family_scores meta["life_stage_profile_scores"] = merged_life_stage_scores meta["value_profile_scores"] = merged_value_scores + meta["expression_profile_scores"] = merged_expression_scores + meta["reply_entry_profile_scores"] = merged_reply_entry_scores meta["long_term_reply_preference_scores"] = merged_reply_pref_scores meta["temperament_tendency_scores"] = merged_temperament_scores meta["identity_traits"] = self._top_scored_items(merged_identity_scores, limit=5) + meta["common_scenarios"] = self._top_scored_items(merged_common_scenario_scores, limit=5) meta["skill_profile"] = self._top_scored_items(merged_skill_scores, limit=6) + meta["problem_solving_profile"] = self._top_scored_items(merged_problem_solving_scores, limit=5) meta["family_profile"] = self._top_scored_items(merged_family_scores, limit=4) meta["life_stage_profile"] = self._top_scored_items(merged_life_stage_scores, limit=4) meta["value_profile"] = self._top_scored_items(merged_value_scores, limit=5) meta["stable_traits"] = self._top_scored_items(merged_trait_scores, limit=self.stable_max_items) meta["habit_patterns"] = self._top_scored_items(merged_habit_scores, limit=self.stable_max_items) + meta["expression_profile"] = self._top_scored_items(merged_expression_scores, limit=5) + meta["reply_entry_profile"] = self._top_scored_items(merged_reply_entry_scores, limit=4) meta["long_term_reply_preferences"] = self._top_scored_items(merged_reply_pref_scores, limit=4) temperament = self._top_scored_items(merged_temperament_scores, limit=1) meta["temperament_tendency"] = temperament[0] if temperament else meta.get("temperament_tendency", "") if not meta["identity_traits"]: meta["identity_traits"] = (existing_meta.get("identity_traits") or [])[:5] + if not meta["common_scenarios"]: + meta["common_scenarios"] = (existing_meta.get("common_scenarios") or [])[:5] if not meta["skill_profile"]: meta["skill_profile"] = (existing_meta.get("skill_profile") or [])[:6] + if not meta["problem_solving_profile"]: + meta["problem_solving_profile"] = (existing_meta.get("problem_solving_profile") or [])[:5] if not meta["family_profile"]: meta["family_profile"] = (existing_meta.get("family_profile") or [])[:4] if not meta["life_stage_profile"]: meta["life_stage_profile"] = (existing_meta.get("life_stage_profile") or [])[:4] if not meta["value_profile"]: meta["value_profile"] = (existing_meta.get("value_profile") or [])[:5] + if not meta["expression_profile"]: + meta["expression_profile"] = (existing_meta.get("expression_profile") or [])[:5] + if not meta["reply_entry_profile"]: + meta["reply_entry_profile"] = (existing_meta.get("reply_entry_profile") or [])[:4] meta["group_role"] = meta.get("group_role") or existing_meta.get("group_role") or "" meta["decision_profile"] = meta.get("decision_profile") or existing_meta.get("decision_profile") or "" meta["engagement_traits"] = (meta.get("engagement_traits") or existing_meta.get("engagement_traits") or [])[:4] @@ -706,6 +762,13 @@ class MemberContextService: ) if preferences: return "更适合:" + "、".join(preferences[:3]) + entry_points = self._extract_scored_items( + monthly_structured + weekly_structured + daily_structured, + ["reply_entry_profile", "reply_entry_points"], + limit=3, + ) + if entry_points: + return "优先从这些点接话:" + "、".join(entry_points[:3]) return "保持自然口语化,结论和解释尽量平衡" def _calc_digest_confidence(self, monthly_digests: List[Dict], weekly_digests: List[Dict], @@ -745,12 +808,18 @@ class MemberContextService: if meta.get("temperament_tendency"): label = "长期沟通倾向" if meta.get("stable_ready") else "阶段性沟通倾向" parts.append(f"{label}:{meta.get('temperament_tendency')}") + if meta.get("common_scenarios"): + parts.append(f"常见场景:{'、'.join(meta.get('common_scenarios')[:3])}") + if meta.get("problem_solving_profile"): + parts.append(f"处理方式:{'、'.join(meta.get('problem_solving_profile')[:3])}") if meta.get("stable_traits"): parts.append(f"长期特征:{'、'.join(meta.get('stable_traits')[:3])}") if meta.get("identity_traits"): parts.append(f"身份线索:{'、'.join(meta.get('identity_traits')[:2])}") if meta.get("skill_profile"): parts.append(f"技能画像:{'、'.join(meta.get('skill_profile')[:3])}") + if meta.get("expression_profile"): + parts.append(f"表达标记:{'、'.join(meta.get('expression_profile')[:3])}") if meta.get("value_profile"): parts.append(f"判断偏好:{'、'.join(meta.get('value_profile')[:2])}") if meta.get("habit_patterns"):