From 1e159213cf480c75e11073978e2033282c8d5b2e Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Thu, 18 Dec 2025 10:52:58 +0800 Subject: [PATCH] =?UTF-8?q?better=EF=BC=9A=E4=BC=98=E5=8C=96=E5=88=86?= =?UTF-8?q?=E5=89=B2=EF=BC=8C=E4=BC=98=E5=8C=96=E8=A1=A8=E8=BE=BE=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=EF=BC=8C=E4=BC=98=E5=8C=96Planner=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E5=92=8C=E8=81=94=E5=8A=A8=EF=BC=8C=E4=BC=98=E5=8C=96=E8=AE=B0?= =?UTF-8?q?=E5=BF=86=E6=80=BB=E7=BB=93=EF=BC=8C=E4=BC=98=E5=8C=96=E5=9B=9E?= =?UTF-8?q?=E5=A4=8DLog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bw_learner/expression_selector.py | 47 ++++++-- src/bw_learner/jargon_miner.py | 6 +- src/bw_learner/message_recorder.py | 2 +- src/chat/planner_actions/planner.py | 38 ++++++- src/chat/replyer/group_generator.py | 54 +++++++-- src/chat/utils/utils.py | 48 +++++--- .../chat_history_summarizer.py | 105 ++++++++++++++++-- src/plugin_system/apis/llm_api.py | 6 +- template/model_config_template.toml | 4 +- 9 files changed, 252 insertions(+), 58 deletions(-) diff --git a/src/bw_learner/expression_selector.py b/src/bw_learner/expression_selector.py index 386d4fdf..1beaba14 100644 --- a/src/bw_learner/expression_selector.py +++ b/src/bw_learner/expression_selector.py @@ -145,16 +145,33 @@ class ExpressionSelector: for expr in style_query ] - # 要求至少有10个 count > 1 的表达方式才进行选择 - min_required = 10 + # 要求至少有一定数量的 count > 1 的表达方式才进行“完整简单模式”选择 + min_required = 8 if len(style_exprs) < min_required: + # 高 count 样本不足:如果还有候选,就降级为随机选 3 个;如果一个都没有,则直接返回空 + if not style_exprs: + logger.info( + f"聊天流 {chat_id} 没有满足 count > 1 且未被拒绝的表达方式,简单模式不进行选择" + ) + # 完全没有高 count 样本时,退化为全量随机抽样(不进入LLM流程) + fallback_num = min(3, max_num) if max_num > 0 else 3 + fallback_selected = self._random_expressions(chat_id, fallback_num) + if fallback_selected: + self.update_expressions_last_active_time(fallback_selected) + selected_ids = [expr["id"] for expr in fallback_selected] + logger.info( + f"聊天流 {chat_id} 使用简单模式降级随机抽选 {len(fallback_selected)} 个表达(无 count>1 样本)" + ) + return fallback_selected, selected_ids + return [], [] logger.info( - f"聊天流 {chat_id} count > 1 的表达方式不足 {min_required} 个(实际 {len(style_exprs)} 个),不进行选择" + f"聊天流 {chat_id} count > 1 的表达方式不足 {min_required} 个(实际 {len(style_exprs)} 个)," + f"简单模式降级为随机选择 3 个" ) - return [], [] - - # 固定选择5个 - select_count = 5 + select_count = min(3, len(style_exprs)) + else: + # 高 count 数量达标时,固定选择 5 个 + select_count = 5 import random selected_style = random.sample(style_exprs, select_count) @@ -308,20 +325,28 @@ class ExpressionSelector: select_random_count = 5 # 检查数量要求 + # 对于高 count 表达:如果数量不足,不再直接停止,而是仅跳过“高 count 优先选择” if len(high_count_exprs) < min_high_count: logger.info( - f"聊天流 {chat_id} count > 1 的表达方式不足 {min_high_count} 个(实际 {len(high_count_exprs)} 个),不进行选择" + f"聊天流 {chat_id} count > 1 的表达方式不足 {min_high_count} 个(实际 {len(high_count_exprs)} 个)," + f"将跳过高 count 优先选择,仅从全部表达中随机抽样" ) - return [], [] + high_count_valid = False + else: + high_count_valid = True + # 总量不足仍然直接返回,避免样本过少导致选择质量过低 if len(all_style_exprs) < min_total_count: logger.info( f"聊天流 {chat_id} 总表达方式不足 {min_total_count} 个(实际 {len(all_style_exprs)} 个),不进行选择" ) return [], [] - # 先选取高count的表达方式 - selected_high = weighted_sample(high_count_exprs, min(len(high_count_exprs), select_high_count)) + # 先选取高count的表达方式(如果数量达标) + if high_count_valid: + selected_high = weighted_sample(high_count_exprs, min(len(high_count_exprs), select_high_count)) + else: + selected_high = [] # 然后从所有表达方式中随机抽样(使用加权抽样) remaining_num = select_random_count diff --git a/src/bw_learner/jargon_miner.py b/src/bw_learner/jargon_miner.py index af2c6361..f1580fc4 100644 --- a/src/bw_learner/jargon_miner.py +++ b/src/bw_learner/jargon_miner.py @@ -759,8 +759,8 @@ class JargonMiner: content_key = entry["content"] # 检查是否包含人物名称 - logger.info(f"process_extracted_entries 检查是否包含人物名称: {content_key}") - logger.info(f"person_name_filter: {person_name_filter}") + # logger.info(f"process_extracted_entries 检查是否包含人物名称: {content_key}") + # logger.info(f"person_name_filter: {person_name_filter}") if person_name_filter and person_name_filter(content_key): logger.info(f"process_extracted_entries 跳过包含人物名称的黑话: {content_key}") continue @@ -885,7 +885,7 @@ class JargonMiner: logger.info(f"[{self.stream_name}]疑似黑话: {jargon_str}") if saved or updated: - logger.info(f"jargon写入: 新增 {saved} 条,更新 {updated} 条,chat_id={self.chat_id}") + logger.debug(f"jargon写入: 新增 {saved} 条,更新 {updated} 条,chat_id={self.chat_id}") except Exception as e: logger.error(f"处理已提取的黑话条目失败: {e}") diff --git a/src/bw_learner/message_recorder.py b/src/bw_learner/message_recorder.py index b31ab153..4d8a5015 100644 --- a/src/bw_learner/message_recorder.py +++ b/src/bw_learner/message_recorder.py @@ -95,7 +95,7 @@ class MessageRecorder: self.last_extraction_time = extraction_end_time try: - logger.info(f"在聊天流 {self.chat_name} 开始统一消息提取和分发") + # logger.info(f"在聊天流 {self.chat_name} 开始统一消息提取和分发") # 拉取提取窗口内的消息 messages = get_raw_msg_by_timestamp_with_chat_inclusive( diff --git a/src/chat/planner_actions/planner.py b/src/chat/planner_actions/planner.py index 4740c08f..57ae6d11 100644 --- a/src/chat/planner_actions/planner.py +++ b/src/chat/planner_actions/planner.py @@ -15,12 +15,15 @@ from src.chat.utils.prompt_builder import Prompt, global_prompt_manager from src.chat.utils.chat_message_builder import ( build_readable_messages_with_id, get_raw_msg_before_timestamp_with_chat, + replace_user_references, ) from src.chat.utils.utils import get_chat_type_and_target_info from src.chat.planner_actions.action_manager import ActionManager from src.chat.message_receive.chat_stream import get_chat_manager from src.plugin_system.base.component_types import ActionInfo, ComponentType, ActionActivationType from src.plugin_system.core.component_registry import component_registry +from src.plugin_system.apis.message_api import translate_pid_to_description +from src.person_info.person_info import Person if TYPE_CHECKING: from src.common.data_models.info_data_model import TargetPersonInfo @@ -68,7 +71,8 @@ no_reply {moderation_prompt} target_message_id为必填,表示触发消息的id -请选择所有符合使用要求的action,动作用json格式输出,用```json包裹,如果输出多个json,每个json都要单独一行放在同一个```json代码块内: +请选择所有符合使用要求的action,每个动作最多选择一次,但是可以选择多个动作; +动作用json格式输出,用```json包裹,如果输出多个json,每个json都要单独一行放在同一个```json代码块内: **示例** // 理由文本(简短) ```json @@ -155,11 +159,41 @@ class ActionPlanner: logger.warning(f"{self.log_prefix}planner理由引用 {msg_id} 未找到对应消息,保持原样") return msg_id - msg_text = (message.processed_plain_text or message.display_message or "").strip() + msg_text = (message.processed_plain_text or "").strip() if not msg_text: logger.warning(f"{self.log_prefix}planner理由引用 {msg_id} 的消息内容为空,保持原样") return msg_id + # 替换 [picid:xxx] 为 [图片:描述] + pic_pattern = r"\[picid:([^\]]+)\]" + def replace_pic_id(pic_match: re.Match) -> str: + pic_id = pic_match.group(1) + description = translate_pid_to_description(pic_id) + return f"[图片:{description}]" + msg_text = re.sub(pic_pattern, replace_pic_id, msg_text) + + # 替换用户引用格式:回复 和 @ + platform = getattr(message, "user_info", None) and message.user_info.platform or getattr(message, "chat_info", None) and message.chat_info.platform or "qq" + msg_text = replace_user_references(msg_text, platform, replace_bot_name=True) + + # 替换单独的 <用户名:用户ID> 格式(replace_user_references 已处理回复<和@<格式) + # 匹配所有 格式,由于 replace_user_references 已经替换了回复<和@<格式, + # 这里匹配到的应该都是单独的格式 + user_ref_pattern = r"<([^:<>]+):([^:<>]+)>" + def replace_user_ref(user_match: re.Match) -> str: + user_name = user_match.group(1) + user_id = user_match.group(2) + try: + # 检查是否是机器人自己 + if user_id == global_config.bot.qq_account: + return f"{global_config.bot.nickname}(你)" + person = Person(platform=platform, user_id=user_id) + return person.person_name or user_name + except Exception: + # 如果解析失败,使用原始昵称 + return user_name + msg_text = re.sub(user_ref_pattern, replace_user_ref, msg_text) + preview = msg_text if len(msg_text) <= 100 else f"{msg_text[:97]}..." logger.info(f"{self.log_prefix}planner理由引用 {msg_id} -> 消息({preview})") return f"消息({msg_text})" diff --git a/src/chat/replyer/group_generator.py b/src/chat/replyer/group_generator.py index 3c4c5440..e8ba1f24 100644 --- a/src/chat/replyer/group_generator.py +++ b/src/chat/replyer/group_generator.py @@ -98,8 +98,10 @@ class DefaultReplyer: available_actions = {} try: # 3. 构建 Prompt + timing_logs = [] + almost_zero_str = "" with Timer("构建Prompt", {}): # 内部计时器,可选保留 - prompt, selected_expressions = await self.build_prompt_reply_context( + prompt, selected_expressions, timing_logs, almost_zero_str = await self.build_prompt_reply_context( extra_info=extra_info, available_actions=available_actions, chosen_actions=chosen_actions, @@ -136,9 +138,22 @@ class DefaultReplyer: content, reasoning_content, model_name, tool_call = await self.llm_generate_content(prompt) # logger.debug(f"replyer生成内容: {content}") - logger.info(f"模型: [{model_name}][思考等级:{think_level}]生成内容: {content}") - if global_config.debug.show_replyer_reasoning and reasoning_content: - logger.info(f"模型: [{model_name}][思考等级:{think_level}]生成推理:\n{reasoning_content}") + # 统一输出所有日志信息,使用try-except确保即使某个步骤出错也能输出 + try: + # 1. 输出回复准备日志 + timing_log_str = f"回复准备: {'; '.join(timing_logs)}; {almost_zero_str} <0.1s" if timing_logs or almost_zero_str else "回复准备: 无计时信息" + logger.info(timing_log_str) + # 2. 输出Prompt日志 + if global_config.debug.show_replyer_prompt: + logger.info(f"\n{prompt}\n") + else: + logger.debug(f"\nreplyer_Prompt:{prompt}\n") + # 3. 输出模型生成内容和推理日志 + logger.info(f"模型: [{model_name}][思考等级:{think_level}]生成内容: {content}") + if global_config.debug.show_replyer_reasoning and reasoning_content: + logger.info(f"模型: [{model_name}][思考等级:{think_level}]生成推理:\n{reasoning_content}") + except Exception as e: + logger.warning(f"输出日志时出错: {e}") llm_response.content = content llm_response.reasoning = reasoning_content @@ -162,6 +177,21 @@ class DefaultReplyer: except Exception as llm_e: # 精简报错信息 logger.error(f"LLM 生成失败: {llm_e}") + # 即使LLM生成失败,也尝试输出已收集的日志信息 + try: + # 1. 输出回复准备日志 + timing_log_str = f"回复准备: {'; '.join(timing_logs)}; {almost_zero_str} <0.1s" if timing_logs or almost_zero_str else "回复准备: 无计时信息" + logger.info(timing_log_str) + # 2. 输出Prompt日志 + if global_config.debug.show_replyer_prompt: + logger.info(f"\n{prompt}\n") + else: + logger.debug(f"\nreplyer_Prompt:{prompt}\n") + # 3. 输出模型生成失败信息 + logger.info("模型生成失败,无法输出生成内容和推理") + except Exception as log_e: + logger.warning(f"输出日志时出错: {log_e}") + return False, llm_response # LLM 调用失败则无法生成回复 return True, llm_response @@ -705,7 +735,7 @@ class DefaultReplyer: enable_tool: bool = True, reply_time_point: Optional[float] = time.time(), think_level: int = 1, - ) -> Tuple[str, List[int]]: + ) -> Tuple[str, List[int], List[str], str]: """ 构建回复器上下文 @@ -838,7 +868,8 @@ class DefaultReplyer: continue timing_logs.append(f"{chinese_name}: {duration:.1f}s") - logger.info(f"回复准备: {'; '.join(timing_logs)}; {almost_zero_str} <0.1s") + # 不再在这里输出日志,而是返回给调用者统一输出 + # logger.info(f"回复准备: {'; '.join(timing_logs)}; {almost_zero_str} <0.1s") expression_habits_block, selected_expressions = results_dict["expression_habits"] expression_habits_block: str @@ -915,7 +946,7 @@ class DefaultReplyer: memory_retrieval=memory_retrieval, chat_prompt=chat_prompt_block, planner_reasoning=planner_reasoning, - ), selected_expressions + ), selected_expressions, timing_logs, almost_zero_str async def build_prompt_rewrite_context( self, @@ -1046,10 +1077,11 @@ class DefaultReplyer: # 直接使用已初始化的模型实例 # logger.info(f"\n{prompt}\n") - if global_config.debug.show_replyer_prompt: - logger.info(f"\n{prompt}\n") - else: - logger.debug(f"\nreplyer_Prompt:{prompt}\n") + # 不再在这里输出日志,而是返回给调用者统一输出 + # if global_config.debug.show_replyer_prompt: + # logger.info(f"\n{prompt}\n") + # else: + # logger.debug(f"\nreplyer_Prompt:{prompt}\n") content, (reasoning_content, model_name, tool_calls) = await self.express_model.generate_response_async( prompt diff --git a/src/chat/utils/utils.py b/src/chat/utils/utils.py index bb0c01f7..5f7fe651 100644 --- a/src/chat/utils/utils.py +++ b/src/chat/utils/utils.py @@ -198,21 +198,21 @@ def split_into_sentences_w_remove_punctuation(text: str) -> list[str]: List[str]: 分割和合并后的句子列表 """ # 预处理:处理多余的换行符 - # 1. 将连续的换行符替换为单个换行符 + # 1. 将连续的换行符替换为单个换行符(保留换行符用于分割) text = re.sub(r"\n\s*\n+", "\n", text) - # 2. 处理换行符和其他分隔符的组合 - text = re.sub(r"\n\s*([,,。;\s])", r"\1", text) - text = re.sub(r"([,,。;\s])\s*\n", r"\1", text) + # 2. 处理换行符和其他分隔符的组合(保留换行符,删除其他分隔符) + text = re.sub(r"\n\s*([,,。;\s])", r"\n\1", text) + text = re.sub(r"([,,。;\s])\s*\n", r"\1\n", text) - # 处理两个汉字中间的换行符 - text = re.sub(r"([\u4e00-\u9fff])\n([\u4e00-\u9fff])", r"\1。\2", text) + # 处理两个汉字中间的换行符(保留换行符,不替换为句号,让换行符强制分割) + # text = re.sub(r"([\u4e00-\u9fff])\n([\u4e00-\u9fff])", r"\1。\2", text) # 注释掉,保留换行符用于分割 len_text = len(text) if len_text < 3: return list(text) if random.random() < 0.01 else [text] - # 定义分隔符 - separators = {",", ",", " ", "。", ";"} + # 定义分隔符(包含换行符,换行符必须强制分割) + separators = {",", ",", " ", "。", ";", "\n"} segments = [] current_segment = "" @@ -221,13 +221,27 @@ def split_into_sentences_w_remove_punctuation(text: str) -> list[str]: while i < len(text): char = text[i] if char in separators: - # 检查分割条件:如果空格左右都是英文字母、数字,或数字和英文之间,则不分割(仅对空格应用此规则) - can_split = True - if 0 < i < len(text) - 1: - prev_char = text[i - 1] - next_char = text[i + 1] - # 只对空格应用"不分割数字和数字、数字和英文、英文和数字、英文和英文之间的空格"规则 - if char == " ": + # 换行符必须强制分割,不受其他规则影响 + if char == "\n": + can_split = True + else: + # 检查分割条件 + can_split = True + # 检查分隔符左右是否有冒号(中英文),如果有则不分割 + if i > 0: + prev_char = text[i - 1] + if prev_char in {":", ":"}: + can_split = False + if i < len(text) - 1: + next_char = text[i + 1] + if next_char in {":", ":"}: + can_split = False + + # 如果左右没有冒号,再检查空格的特殊情况 + if can_split and char == " " and i > 0 and i < len(text) - 1: + prev_char = text[i - 1] + next_char = text[i + 1] + # 不分割数字和数字、数字和英文、英文和数字、英文和英文之间的空格 prev_is_alnum = prev_char.isdigit() or is_english_letter(prev_char) next_is_alnum = next_char.isdigit() or is_english_letter(next_char) if prev_is_alnum and next_is_alnum: @@ -237,8 +251,8 @@ def split_into_sentences_w_remove_punctuation(text: str) -> list[str]: # 只有当当前段不为空时才添加 if current_segment: segments.append((current_segment, char)) - # 如果当前段为空,但分隔符是空格,则也添加一个空段(保留空格) - elif char == " ": + # 如果当前段为空,但分隔符是空格或换行符,则也添加一个空段(保留分隔符) + elif char in {" ", "\n"}: segments.append(("", char)) current_segment = "" else: diff --git a/src/hippo_memorizer/chat_history_summarizer.py b/src/hippo_memorizer/chat_history_summarizer.py index 241a2af8..c8d4a943 100644 --- a/src/hippo_memorizer/chat_history_summarizer.py +++ b/src/hippo_memorizer/chat_history_summarizer.py @@ -7,6 +7,7 @@ import asyncio import json import time import re +import difflib from pathlib import Path from typing import Any, Dict, List, Optional, Set from dataclasses import dataclass, field @@ -30,16 +31,18 @@ HIPPO_CACHE_DIR = Path(__file__).resolve().parents[2] / "data" / "hippo_memorize def init_prompt(): """初始化提示词模板""" - topic_analysis_prompt = """ -【历史话题标题列表】(仅标题,不含具体内容): + topic_analysis_prompt = """【历史话题标题列表】(仅标题,不含具体内容): {history_topics_block} +【历史话题标题列表结束】 【本次聊天记录】(每条消息前有编号,用于后续引用): {messages_block} +【本次聊天记录结束】 请完成以下任务: **识别话题** 1. 识别【本次聊天记录】中正在进行的一个或多个话题; +2. 【本次聊天记录】的中的消息可能与历史话题有关,也可能毫无关联。 2. 判断【历史话题标题列表】中的话题是否在【本次聊天记录】中出现,如果出现,则直接使用该历史话题标题字符串; **选取消息** @@ -374,10 +377,10 @@ class ChatHistorySummarizer: should_check = True logger.info(f"{self.log_prefix} 触发检查条件: 消息数量达到 {message_count} 条(阈值: 100条)") - # 条件2: 距离上一次检查 > 3600 秒(1小时),触发一次检查 - elif time_since_last_check > 2400: + # 条件2: 距离上一次检查 > 3600 * 8 秒(8小时)且消息数量 >= 20 条,触发一次检查 + elif time_since_last_check > 3600 * 8 and message_count >= 20: should_check = True - logger.info(f"{self.log_prefix} 触发检查条件: 距上次检查 {time_str}(阈值: 1小时)") + logger.info(f"{self.log_prefix} 触发检查条件: 距上次检查 {time_str}(阈值: 8小时)且消息数量达到 {message_count} 条(阈值: 20条)") if should_check: await self._run_topic_check_and_update_cache(messages) @@ -459,9 +462,31 @@ class ChatHistorySummarizer: if not success or not topic_to_indices: logger.error(f"{self.log_prefix} 话题识别连续 {max_retries} 次失败或始终无有效话题,本次检查放弃") - # 即使识别失败,也认为是一次“检查”,但不更新 no_update_checks(保持原状) + # 即使识别失败,也认为是一次"检查",但不更新 no_update_checks(保持原状) return + # 3.5. 检查新话题是否与历史话题相似(相似度>=90%则使用历史标题) + topic_mapping = self._build_topic_mapping(topic_to_indices, similarity_threshold=0.9) + + # 应用话题映射:将相似的新话题标题替换为历史话题标题 + if topic_mapping: + new_topic_to_indices: Dict[str, List[int]] = {} + for new_topic, indices in topic_to_indices.items(): + # 如果这个新话题需要映射到历史话题 + if new_topic in topic_mapping: + historical_topic = topic_mapping[new_topic] + # 如果历史话题已经存在,合并消息索引 + if historical_topic in new_topic_to_indices: + # 合并索引并去重 + combined_indices = list(set(new_topic_to_indices[historical_topic] + indices)) + new_topic_to_indices[historical_topic] = combined_indices + else: + new_topic_to_indices[historical_topic] = indices + else: + # 不需要映射,保持原样 + new_topic_to_indices[new_topic] = indices + topic_to_indices = new_topic_to_indices + # 4. 统计哪些话题在本次检查中有新增内容 updated_topics: Set[str] = set() @@ -528,6 +553,71 @@ class ChatHistorySummarizer: # 无论成功与否,都从缓存中删除,避免重复 self.topic_cache.pop(topic, None) + def _find_most_similar_topic( + self, new_topic: str, existing_topics: List[str], similarity_threshold: float = 0.9 + ) -> Optional[tuple[str, float]]: + """ + 查找与给定新话题最相似的历史话题 + + Args: + new_topic: 新话题标题 + existing_topics: 历史话题标题列表 + similarity_threshold: 相似度阈值,默认0.9(90%) + + Returns: + Optional[tuple[str, float]]: 如果找到相似度>=阈值的历史话题,返回(历史话题标题, 相似度), + 否则返回None + """ + if not existing_topics: + return None + + best_match = None + best_similarity = 0.0 + + for existing_topic in existing_topics: + similarity = difflib.SequenceMatcher(None, new_topic, existing_topic).ratio() + if similarity > best_similarity: + best_similarity = similarity + best_match = existing_topic + + # 如果相似度达到阈值,返回匹配结果 + if best_match and best_similarity >= similarity_threshold: + return (best_match, best_similarity) + + return None + + def _build_topic_mapping( + self, topic_to_indices: Dict[str, List[int]], similarity_threshold: float = 0.9 + ) -> Dict[str, str]: + """ + 构建新话题到历史话题的映射(如果相似度>=阈值) + + Args: + topic_to_indices: 新话题到消息索引的映射 + similarity_threshold: 相似度阈值,默认0.9(90%) + + Returns: + Dict[str, str]: 新话题 -> 历史话题的映射字典 + """ + existing_topics_list = list(self.topic_cache.keys()) + topic_mapping: Dict[str, str] = {} + + for new_topic in topic_to_indices.keys(): + # 如果新话题已经在历史话题中,不需要检查 + if new_topic in existing_topics_list: + continue + + # 查找最相似的历史话题 + result = self._find_most_similar_topic(new_topic, existing_topics_list, similarity_threshold) + if result: + historical_topic, similarity = result + topic_mapping[new_topic] = historical_topic + logger.info( + f"{self.log_prefix} 话题相似度检查: '{new_topic}' 与历史话题 '{historical_topic}' 相似度 {similarity:.2%},使用历史标题" + ) + + return topic_mapping + def _build_numbered_messages_for_llm( self, messages: List[DatabaseMessages] ) -> tuple[List[str], Dict[int, str], Dict[int, str], Dict[int, Set[str]]]: @@ -622,8 +712,7 @@ class ChatHistorySummarizer: try: response, _ = await self.summarizer_llm.generate_response_async( prompt=prompt, - temperature=0.2, - max_tokens=800, + temperature=0.3, ) logger.info(f"{self.log_prefix} 话题识别LLM Prompt: {prompt}") diff --git a/src/plugin_system/apis/llm_api.py b/src/plugin_system/apis/llm_api.py index a4e2dd30..fb4de48d 100644 --- a/src/plugin_system/apis/llm_api.py +++ b/src/plugin_system/apis/llm_api.py @@ -108,8 +108,8 @@ async def generate_with_model_with_tools( """ try: model_name_list = model_config.model_list - logger.info(f"[LLMAPI] 使用模型集合 {model_name_list} 生成内容") - logger.debug(f"[LLMAPI] 完整提示词: {prompt}") + logger.info(f"使用模型{model_name_list}生成内容") + logger.debug(f"完整提示词: {prompt}") llm_request = LLMRequest(model_set=model_config, request_type=request_type) @@ -147,7 +147,7 @@ async def generate_with_model_with_tools_by_message_factory( """ try: model_name_list = model_config.model_list - logger.info(f"[LLMAPI] 使用模型集合 {model_name_list} 生成内容(消息工厂)") + logger.info(f"使用模型 {model_name_list} 生成内容") llm_request = LLMRequest(model_set=model_config, request_type=request_type) diff --git a/template/model_config_template.toml b/template/model_config_template.toml index 225b94f1..669488f6 100644 --- a/template/model_config_template.toml +++ b/template/model_config_template.toml @@ -1,5 +1,5 @@ [inner] -version = "1.9.0" +version = "1.9.1" # 配置文件版本号迭代规则同bot_config.toml @@ -138,7 +138,7 @@ price_out = 0 [model_task_config.utils] # 在麦麦的一些组件中使用的模型,例如表情包模块,取名模块,关系模块,麦麦的情绪变化等,是麦麦必须的模型 model_list = ["siliconflow-deepseek-v3.2"] # 使用的模型列表,每个子项对应上面的模型名称(name) temperature = 0.2 # 模型温度,新V3建议0.1-0.3 -max_tokens = 2048 # 最大输出token数 +max_tokens = 4096 # 最大输出token数 slow_threshold = 15.0 # 慢请求阈值(秒),模型等待回复时间超过此值会输出警告日志 [model_task_config.utils_small] # 在麦麦的一些组件中使用的小模型,消耗量较大,建议使用速度较快的小模型