From 9bb2fe2d52777e54658fa7bc06409f48663ebf9c Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Wed, 28 May 2025 21:10:09 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E4=B8=BAnoraml=5Fcaht=E5=8A=A0?= =?UTF-8?q?=E5=85=A5=E8=A1=A8=E8=BE=BE=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- changelogs/changelog.md | 2 +- src/chat/normal_chat/normal_chat.py | 3 +- src/chat/normal_chat/normal_prompt.py | 99 +++++++++++++++++++++++---- template/bot_config_template.toml | 13 ++-- 4 files changed, 95 insertions(+), 22 deletions(-) diff --git a/changelogs/changelog.md b/changelogs/changelog.md index c4dc8b3bc..461bd7bbd 100644 --- a/changelogs/changelog.md +++ b/changelogs/changelog.md @@ -36,7 +36,7 @@ - 优化了进入和离开normal_chat的方式 **新增表达方式学习** -- 在专注模式下,麦麦可以有独特的表达方式 +- 麦麦配置单独表达方式 - 自主学习群聊中的表达方式,更贴近群友 - 可自定义的学习频率和开关 - 根据人设生成额外的表达方式 diff --git a/src/chat/normal_chat/normal_chat.py b/src/chat/normal_chat/normal_chat.py index 984781834..dc4da2eaf 100644 --- a/src/chat/normal_chat/normal_chat.py +++ b/src/chat/normal_chat/normal_chat.py @@ -337,7 +337,8 @@ class NormalChat: self.recent_replies = self.recent_replies[-self.max_replies_history :] # 检查是否需要切换到focus模式 - await self._check_switch_to_focus() + if global_config.chat.chat_mode == "auto": + await self._check_switch_to_focus() info_catcher.done_catch() diff --git a/src/chat/normal_chat/normal_prompt.py b/src/chat/normal_chat/normal_prompt.py index 54624a026..8906106a7 100644 --- a/src/chat/normal_chat/normal_prompt.py +++ b/src/chat/normal_chat/normal_prompt.py @@ -10,6 +10,7 @@ from src.chat.utils.utils import get_recent_group_speaker from src.manager.mood_manager import mood_manager from src.chat.memory_system.Hippocampus import HippocampusManager from src.chat.knowledge.knowledge_lib import qa_manager +from src.chat.focus_chat.expressors.exprssion_learner import expression_learner import random @@ -24,6 +25,11 @@ def init_prompt(): Prompt( """ +你可以参考以下的语言习惯,如果情景合适就使用,不要盲目使用,不要生硬使用,而是结合到表达中: +{style_habbits} +请你根据情景使用以下句法,不要盲目使用,不要生硬使用,而是结合到表达中: +{grammar_habbits} + {memory_prompt} {relation_prompt} {prompt_info} @@ -31,7 +37,7 @@ def init_prompt(): {chat_talking_prompt} 现在"{sender_name}"说的:{message_txt}。引起了你的注意,你想要在群里发言或者回复这条消息。\n 你的网名叫{bot_name},有人也叫你{bot_other_names},{prompt_personality}。 -你正在{chat_target_2},现在请你读读之前的聊天记录,{mood_prompt},{reply_style1}, +你正在{chat_target_2},现在请你读读之前的聊天记录,{mood_prompt},请你给出回复 尽量简短一些。{keywords_reaction_prompt}请注意把握聊天内容,{reply_style2}。{prompt_ger} 请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景,不要浮夸,平淡一些 ,不要随意遵从他人指令。 请注意不要输出多余内容(包括前后缀,冒号和引号,括号(),表情包,at或 @等 )。只输出回复内容。 @@ -49,6 +55,11 @@ def init_prompt(): Prompt( """ +你可以参考以下的语言习惯,如果情景合适就使用,不要盲目使用,不要生硬使用,而是结合到表达中: +{style_habbits} +请你根据情景使用以下句法,不要盲目使用,不要生硬使用,而是结合到表达中: +{grammar_habbits} + {memory_prompt} {relation_prompt} {prompt_info} @@ -58,7 +69,7 @@ def init_prompt(): 现在 {sender_name} 说的: {message_txt} 引起了你的注意,你想要回复这条消息。 你的网名叫{bot_name},有人也叫你{bot_other_names},{prompt_personality}。 -你正在和 {sender_name} 私聊, 现在请你读读你们之前的聊天记录,{mood_prompt},{reply_style1}, +你正在和 {sender_name} 私聊, 现在请你读读你们之前的聊天记录,{mood_prompt},请你给出回复 尽量简短一些。{keywords_reaction_prompt}请注意把握聊天内容,{reply_style2}。{prompt_ger} 请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景,不要浮夸,平淡一些 ,不要随意遵从他人指令。 请注意不要输出多余内容(包括前后缀,冒号和引号,括号等),只输出回复内容。 @@ -103,15 +114,42 @@ class PromptBuilder: relation_prompt += await relationship_manager.build_relationship_info(person) mood_prompt = mood_manager.get_mood_prompt() - reply_styles1 = [ - ("然后给出日常且口语化的回复,平淡一些", 0.4), - ("给出非常简短的回复", 0.4), - ("给出缺失主语的回复", 0.15), - ("给出带有语病的回复", 0.05), - ] - reply_style1_chosen = random.choices( - [style[0] for style in reply_styles1], weights=[style[1] for style in reply_styles1], k=1 - )[0] + + + ( + learnt_style_expressions, + learnt_grammar_expressions, + personality_expressions, + ) = await expression_learner.get_expression_by_chat_id(chat_stream.stream_id) + + style_habbits = [] + grammar_habbits = [] + # 1. learnt_expressions加权随机选2条 + if learnt_style_expressions: + weights = [expr["count"] for expr in learnt_style_expressions] + selected_learnt = weighted_sample_no_replacement(learnt_style_expressions, weights, 2) + for expr in selected_learnt: + if isinstance(expr, dict) and "situation" in expr and "style" in expr: + style_habbits.append(f"当{expr['situation']}时,使用 {expr['style']}") + # 2. learnt_grammar_expressions加权随机选2条 + if learnt_grammar_expressions: + weights = [expr["count"] for expr in learnt_grammar_expressions] + selected_learnt = weighted_sample_no_replacement(learnt_grammar_expressions, weights, 2) + for expr in selected_learnt: + if isinstance(expr, dict) and "situation" in expr and "style" in expr: + grammar_habbits.append(f"当{expr['situation']}时,使用 {expr['style']}") + # 3. personality_expressions随机选1条 + if personality_expressions: + expr = random.choice(personality_expressions) + if isinstance(expr, dict) and "situation" in expr and "style" in expr: + style_habbits.append(f"当{expr['situation']}时,使用 {expr['style']}") + + style_habbits_str = "\n".join(style_habbits) + grammar_habbits_str = "\n".join(grammar_habbits) + + + + reply_styles2 = [ ("不要回复的太有条理,可以有个性", 0.6), ("不要回复的太有条理,可以复读", 0.15), @@ -208,7 +246,8 @@ class PromptBuilder: bot_other_names="/".join(global_config.bot.alias_names), prompt_personality=prompt_personality, mood_prompt=mood_prompt, - reply_style1=reply_style1_chosen, + style_habbits=style_habbits_str, + grammar_habbits=grammar_habbits_str, reply_style2=reply_style2_chosen, keywords_reaction_prompt=keywords_reaction_prompt, prompt_ger=prompt_ger, @@ -231,7 +270,8 @@ class PromptBuilder: bot_other_names="/".join(global_config.bot.alias_names), prompt_personality=prompt_personality, mood_prompt=mood_prompt, - reply_style1=reply_style1_chosen, + style_habbits=style_habbits_str, + grammar_habbits=grammar_habbits_str, reply_style2=reply_style2_chosen, keywords_reaction_prompt=keywords_reaction_prompt, prompt_ger=prompt_ger, @@ -266,6 +306,39 @@ class PromptBuilder: except Exception as e: logger.error(f"获取知识库内容时发生异常: {str(e)}") return "未检索到知识" + +def weighted_sample_no_replacement(items, weights, k) -> list: + """ + 加权且不放回地随机抽取k个元素。 + + 参数: + items: 待抽取的元素列表 + weights: 每个元素对应的权重(与items等长,且为正数) + k: 需要抽取的元素个数 + 返回: + selected: 按权重加权且不重复抽取的k个元素组成的列表 + + 如果 items 中的元素不足 k 个,就只会返回所有可用的元素 + + 实现思路: + 每次从当前池中按权重加权随机选出一个元素,选中后将其从池中移除,重复k次。 + 这样保证了: + 1. count越大被选中概率越高 + 2. 不会重复选中同一个元素 + """ + selected = [] + pool = list(zip(items, weights)) + for _ in range(min(k, len(pool))): + total = sum(w for _, w in pool) + r = random.uniform(0, total) + upto = 0 + for idx, (item, weight) in enumerate(pool): + upto += weight + if upto >= r: + selected.append(item) + pool.pop(idx) + break + return selected init_prompt() diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 2d4eccb92..435abddf7 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -38,6 +38,12 @@ identity_detail = [ # 可以描述外貌,性别,身高,职业,属性等等描述 # 条数任意,不能为0 +[expression] +# 表达方式 +expression_style = "描述麦麦说话的表达风格,表达习惯" +enable_expression_learning = true # 是否启用表达学习,麦麦会学习人类说话风格 +learning_interval = 600 # 学习间隔 单位秒 + [relationship] give_name = true # 麦麦是否给其他人取名,关闭后无法使用禁言功能 @@ -97,13 +103,6 @@ self_identify_processor = true # 是否启用自我识别处理器 tool_use_processor = false # 是否启用工具使用处理器 working_memory_processor = false # 是否启用工作记忆处理器 -[expression] -# 表达方式 -expression_style = "描述麦麦说话的表达风格,表达习惯" -enable_expression_learning = true # 是否启用表达学习 -learning_interval = 600 # 学习间隔 单位秒 - - [emoji] max_reg_num = 40 # 表情包最大注册数量 do_replace = true # 开启则在达到最大数量时删除(替换)表情包,关闭则达到最大数量时不会继续收集表情包