diff --git a/src/bw_learner/expression_reflector.py b/src/bw_learner/expression_reflector.py index dc651e55..5c165b9f 100644 --- a/src/bw_learner/expression_reflector.py +++ b/src/bw_learner/expression_reflector.py @@ -147,7 +147,7 @@ expression_reflector_manager = ExpressionReflectorManager() async def _check_tracker_exists(operator_config: str) -> bool: """检查指定 Operator 是否已有活跃的 Tracker""" - from src.express.reflect_tracker import reflect_tracker_manager + from src.bw_learner.reflect_tracker import reflect_tracker_manager chat_manager = get_chat_manager() chat_stream = None @@ -242,7 +242,7 @@ async def _send_to_operator(operator_config: str, text: str, expr: Expression): stream_id = chat_stream.stream_id # 注册 Tracker - from src.express.reflect_tracker import ReflectTracker, reflect_tracker_manager + from src.bw_learner.reflect_tracker import ReflectTracker, reflect_tracker_manager tracker = ReflectTracker(chat_stream=chat_stream, expression=expr, created_time=time.time()) reflect_tracker_manager.add_tracker(stream_id, tracker) diff --git a/src/chat/heart_flow/heartFC_chat.py b/src/chat/heart_flow/heartFC_chat.py index 725960bd..f700ff47 100644 --- a/src/chat/heart_flow/heartFC_chat.py +++ b/src/chat/heart_flow/heartFC_chat.py @@ -605,7 +605,18 @@ class HeartFChatting: self.consecutive_no_reply_count = 0 reason = action_planner_info.reasoning or "" - think_level = action_planner_info.action_data.get("think_level", 1) + # 根据 think_mode 配置决定 think_level 的值 + think_mode = global_config.chat.think_mode + if think_mode == "default": + think_level = 0 + elif think_mode == "deep": + think_level = 1 + elif think_mode == "dynamic": + # dynamic 模式:从 planner 返回的 action_data 中获取 + think_level = action_planner_info.action_data.get("think_level", 1) + else: + # 默认使用 default 模式 + think_level = 0 # 使用 action_reasoning(planner 的整体思考理由)作为 reply_reason planner_reasoning = action_planner_info.action_reasoning or reason diff --git a/src/chat/planner_actions/planner.py b/src/chat/planner_actions/planner.py index 298791f6..2c58de7d 100644 --- a/src/chat/planner_actions/planner.py +++ b/src/chat/planner_actions/planner.py @@ -47,8 +47,7 @@ reply 2.你可以自然的顺着正在进行的聊天内容进行回复或自然的提出一个问题 3.不要选择回复你自己发送的消息 4.不要单独对表情包进行回复 -5.think_level表示思考深度,0表示该回复不需要思考和回忆,1表示该回复需要进行回忆和思考 -{{"action":"reply", "think_level":数值等级(0或1), "target_messamge_id":"消息id(m+数字)"}} +{reply_action_example} no_reply 动作描述: @@ -69,7 +68,7 @@ no_reply {moderation_prompt} target_message_id为必填,表示触发消息的id -请选择所有符合使用要求的action,动作用json格式输出,用```json包裹,如果输出多个json,每个json都要单独一行放在同一个```json代码块内,你可以重复使用同一个动作或不同动作: +请选择所有符合使用要求的action,动作用json格式输出,用```json包裹,如果输出多个json,每个json都要单独一行放在同一个```json代码块内: **示例** // 理由文本(简短) ```json @@ -78,43 +77,8 @@ target_message_id为必填,表示触发消息的id ```""", "planner_prompt", ) - - Prompt( - """{time_block} -{name_block} -{chat_context_description},以下是具体的聊天内容 -**聊天内容** -{chat_content_block} - -**可选的action** -no_reply -动作描述: -没有合适的可以使用的动作,不使用action -{{"action":"no_reply"}} - -{action_options_text} - -**你之前的action执行和思考记录** -{actions_before_now_block} - -请选择**可选的**且符合使用条件的action,并说明触发action的消息id(消息id格式:m+数字) -先输出你的简短的选择思考理由,再输出你选择的action,理由不要分点,精简。 -**动作选择要求** -请你根据聊天内容,用户的最新消息和以下标准选择合适的动作: -1.思考**所有**的可用的action中的**每个动作**是否符合当下条件,如果动作使用条件符合聊天内容就使用 -2.如果相同的内容已经被执行,请不要重复执行 -{moderation_prompt} - -请选择所有符合使用要求的action,动作用json格式输出,用```json包裹,如果输出多个json,每个json都要单独一行放在同一个```json代码块内,你可以重复使用同一个动作或不同动作: -**示例** -// 理由文本(简短) -```json -{{"action":"动作名", "target_message_id":"m123"}} -{{"action":"动作名", "target_message_id":"m456"}} -```""", - "planner_prompt_mentioned", - ) - + + Prompt( """ {action_name} @@ -450,6 +414,12 @@ class ActionPlanner: ) name_block = f"你的名字是{bot_name}{bot_nickname},请注意哪些是你自己的发言。" + # 根据 think_mode 配置决定 reply action 的示例 JSON + if global_config.chat.think_mode == "classic": + reply_action_example = '{{"action":"reply", "target_messamge_id":"消息id(m+数字)"}}' + else: + reply_action_example = '5.think_level表示思考深度,0表示该回复不需要思考和回忆,1表示该回复需要进行回忆和思考\n{{"action":"reply", "think_level":数值等级(0或1), "target_messamge_id":"消息id(m+数字)"}}' + planner_prompt_template = await global_prompt_manager.get_prompt_async("planner_prompt") prompt = planner_prompt_template.format( time_block=time_block, @@ -461,6 +431,7 @@ class ActionPlanner: name_block=name_block, interest=interest, plan_style=global_config.personality.plan_style, + reply_action_example=reply_action_example, ) return prompt, message_id_list diff --git a/src/chat/replyer/prompt/replyer_prompt.py b/src/chat/replyer/prompt/replyer_prompt.py index b9a6addf..e17e07f5 100644 --- a/src/chat/replyer/prompt/replyer_prompt.py +++ b/src/chat/replyer/prompt/replyer_prompt.py @@ -16,7 +16,8 @@ def init_replyer_prompt(): {planner_reasoning} {identity} {chat_prompt}你正在群里聊天,现在请你读读之前的聊天记录,然后给出日常且口语化的回复, -尽量简短一些。{keywords_reaction_prompt}请注意把握聊天内容,不要回复的太有条理。 +尽量简短一些。{keywords_reaction_prompt} +请注意把握聊天内容,不要回复的太有条理。 {reply_style} 请注意不要输出多余内容(包括不必要的前后缀,冒号,括号,表情包,at或 @等 ),只输出发言内容就好。 现在,你说:""", @@ -35,8 +36,9 @@ def init_replyer_prompt(): {reply_target_block}。 {planner_reasoning} {identity} -{chat_prompt}你正在群里聊天,现在请你读读之前的聊天记录,把握当前的话题,然后给出回复, -{keywords_reaction_prompt}请注意把握聊天内容。 +{chat_prompt}你正在群里聊天,现在请你读读之前的聊天记录,把握当前的话题,然后给出口语化回复, +{keywords_reaction_prompt} +请注意把握聊天内容。 {reply_style} 请注意不要输出多余内容(包括不必要的前后缀,冒号,括号,at或 @等 ),只输出发言内容就好。 现在,你说:""", diff --git a/src/config/official_configs.py b/src/config/official_configs.py index e9115a1b..55528d51 100644 --- a/src/config/official_configs.py +++ b/src/config/official_configs.py @@ -108,6 +108,14 @@ class ChatConfig(ConfigBase): 时间区间支持跨夜,例如 "23:00-02:00"。 """ + think_mode: Literal["classic", "deep", "dynamic"] = "classic" + """ + 思考模式配置 + - classic: 默认think_level为0(轻量回复,不需要思考和回忆) + - deep: 默认think_level为1(深度回复,需要进行回忆和思考) + - dynamic: think_level由planner动态给出(根据planner返回的think_level决定) + """ + def _parse_stream_config_to_chat_id(self, stream_config_str: str) -> Optional[str]: """与 ChatStream.get_stream_id 一致地从 "platform:id:type" 生成 chat_id。""" try: diff --git a/src/llm_models/model_client/gemini_client.py b/src/llm_models/model_client/gemini_client.py index b52474e4..33afacaa 100644 --- a/src/llm_models/model_client/gemini_client.py +++ b/src/llm_models/model_client/gemini_client.py @@ -98,7 +98,10 @@ def _convert_messages( content: List[Part] = [] for item in message.content: if isinstance(item, tuple): - image_format = "jpeg" if item[0].lower() == "jpg" else item[0].lower() + image_format = item[0].lower() + # 规范 JPEG MIME 类型后缀,统一使用 image/jpeg + if image_format in ("jpg", "jpeg"): + image_format = "jpeg" content.append(Part.from_bytes(data=base64.b64decode(item[1]), mime_type=f"image/{image_format}")) elif isinstance(item, str): content.append(Part.from_text(text=item)) diff --git a/src/llm_models/model_client/openai_client.py b/src/llm_models/model_client/openai_client.py index 5793d4f9..5372d95b 100644 --- a/src/llm_models/model_client/openai_client.py +++ b/src/llm_models/model_client/openai_client.py @@ -61,10 +61,16 @@ def _convert_messages(messages: list[Message]) -> list[ChatCompletionMessagePara content = [] for item in message.content: if isinstance(item, tuple): + image_format = item[0].lower() + # 规范 JPEG MIME 类型后缀,统一使用 image/jpeg + if image_format in ("jpg", "jpeg"): + mime_suffix = "jpeg" + else: + mime_suffix = image_format content.append( { "type": "image_url", - "image_url": {"url": f"data:image/{item[0].lower()};base64,{item[1]}"}, + "image_url": {"url": f"data:image/{mime_suffix};base64,{item[1]}"}, } ) elif isinstance(item, str): diff --git a/src/plugins/built_in/emoji_plugin/emoji.py b/src/plugins/built_in/emoji_plugin/emoji.py index 3783886a..8599620a 100644 --- a/src/plugins/built_in/emoji_plugin/emoji.py +++ b/src/plugins/built_in/emoji_plugin/emoji.py @@ -47,7 +47,7 @@ class EmojiAction(BaseAction): try: # 1. 获取发送表情的原因 # reason = self.action_data.get("reason", "表达当前情绪") - reason = self.reasoning + reason = self.action_reasoning # 2. 随机获取20个表情包 sampled_emojis = await emoji_api.get_random(30) diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index a381104b..d968f7d0 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -1,5 +1,5 @@ [inner] -version = "7.1.5" +version = "7.1.6" #----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读---- # 如果你想要修改配置文件,请递增version的值 @@ -22,10 +22,10 @@ alias_names = ["麦叠", "牢麦"] # 麦麦的别名 [personality] # 建议120字以内,描述人格特质 和 身份特征 -personality = "是一个女大学生,现在在读大二,会刷贴吧。" +personality = "是一个大二在读女大学生,现在正在上网和群友聊天,有时有点攻击性,有时比较温柔" # アイデンティティがない 生まれないらららら -# 描述麦麦说话的表达风格,表达习惯,如要修改,可以酌情新增内容 -reply_style = "请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景。可以参考贴吧,知乎和微博的回复风格。" +# 描述麦麦说话的表达风格,表达习惯,如要修改,可以酌情新增内容,建议1-2行 +reply_style = "请不要刻意突出自身学科背景。可以参考贴吧,知乎和微博的回复风格。" # 麦麦的说话规则,行为风格: plan_style = """ @@ -34,7 +34,7 @@ plan_style = """ 3.你对技术相关话题,游戏和动漫相关话题感兴趣,也对日常话题感兴趣,不喜欢太过沉重严肃的话题 4.请控制你的发言频率,不要太过频繁的发言 5.如果有人对你感到厌烦,请减少回复 -6.如果有人对你进行攻击,或者情绪激动,请你以合适的方法应对""" +6.如果有人在追问你,或者话题没有说完,请你继续回复""" # 麦麦识图规则,不建议修改 visual_style = "请用中文描述这张图片的内容。如果有文字,请把文字描述概括出来,请留意其主题,直观感受,输出为一段平文本,最多30字,请注意不要分点,就输出一段文本" @@ -55,6 +55,8 @@ states = [ # 替换概率,每次构建人格时替换personality的概率(0.0-1.0) state_probability = 0.3 + + [expression] # 表达学习配置 learning_list = [ # 表达学习配置列表,支持按聊天流配置 @@ -89,6 +91,7 @@ talk_value = 1 # 聊天频率,越小越沉默,范围0-1 mentioned_bot_reply = true # 是否启用提及必回复 max_context_size = 30 # 上下文长度 planner_smooth = 3 # 规划器平滑,增大数值会减小planner负荷,略微降低反应速度,推荐1-5,0为关闭,必须大于等于0 +think_mode = "dynamic" # 思考模式,可选:classic(默认浅度思考和回复)、deep(会进行比较长的,深度回复)、dynamic(动态选择两种模式) enable_talk_value_rules = true # 是否启用动态发言频率规则