diff --git a/src/chat/frequency_control/frequency_control.py b/src/chat/frequency_control/frequency_control.py index 905e0da5..ca63e503 100644 --- a/src/chat/frequency_control/frequency_control.py +++ b/src/chat/frequency_control/frequency_control.py @@ -112,10 +112,10 @@ class FrequencyControl: if len(response) < 20: if "过于频繁" in response: logger.info(f"频率调整: 过于频繁,调整值到{final_value_by_api}") - self.talk_frequency_adjust = max(0.1, min(3.0, self.talk_frequency_adjust * 0.8)) + self.talk_frequency_adjust = max(0.1, min(1.5, self.talk_frequency_adjust * 0.8)) elif "过少" in response: logger.info(f"频率调整: 过少,调整值到{final_value_by_api}") - self.talk_frequency_adjust = max(0.1, min(3.0, self.talk_frequency_adjust * 1.2)) + self.talk_frequency_adjust = max(0.1, min(1.5, self.talk_frequency_adjust * 1.2)) self.last_frequency_adjust_time = time.time() else: logger.info(f"频率调整:response不符合要求,取消本次调整") diff --git a/src/chat/utils/statistic.py b/src/chat/utils/statistic.py index 8cc2071a..bcd0a1f8 100644 --- a/src/chat/utils/statistic.py +++ b/src/chat/utils/statistic.py @@ -690,9 +690,6 @@ class StatisticOutputTask(AsyncTask): online_hours = stats[ONLINE_TIME] / 3600.0 if stats[ONLINE_TIME] > 0 else 0.0 cost_per_hour = stats[TOTAL_COST] / online_hours if online_hours > 0 else 0.0 - # 计算token/消息数量指标(每100条) - tokens_per_100_messages = (total_tokens / stats[TOTAL_MSG_CNT] * 100) if stats[TOTAL_MSG_CNT] > 0 else 0.0 - # 计算token/时间指标(token/小时) tokens_per_hour = (total_tokens / online_hours) if online_hours > 0 else 0.0 @@ -700,8 +697,9 @@ class StatisticOutputTask(AsyncTask): total_replies = stats.get(TOTAL_REPLY_CNT, 0) cost_per_100_replies = (stats[TOTAL_COST] / total_replies * 100) if total_replies > 0 else 0.0 - # 计算token/回复数量指标(每100条) - tokens_per_100_replies = (total_tokens / total_replies * 100) if total_replies > 0 else 0.0 + # 计算花费/消息数量(排除自己回复)指标(每100条) + total_messages_excluding_replies = stats[TOTAL_MSG_CNT] - total_replies + cost_per_100_messages_excluding_replies = (stats[TOTAL_COST] / total_messages_excluding_replies * 100) if total_messages_excluding_replies > 0 else 0.0 output = [ f"总在线时间: {_format_online_time(stats[ONLINE_TIME])}", @@ -711,10 +709,9 @@ class StatisticOutputTask(AsyncTask): f"总Token数: {_format_large_number(total_tokens)}", f"总花费: {stats[TOTAL_COST]:.2f}¥", f"花费/消息数量: {cost_per_100_messages:.4f}¥/100条" if stats[TOTAL_MSG_CNT] > 0 else "花费/消息数量: N/A", - f"花费/回复数量: {cost_per_100_replies:.4f}¥/100条" if total_replies > 0 else "花费/回复数量: N/A", + f"花费/接受消息数量: {cost_per_100_messages_excluding_replies:.4f}¥/100条" if total_messages_excluding_replies > 0 else "花费/消息数量(排除回复): N/A", + f"花费/回复消息数量: {cost_per_100_replies:.4f}¥/100条" if total_replies > 0 else "花费/回复数量: N/A", f"花费/时间: {cost_per_hour:.2f}¥/小时" if online_hours > 0 else "花费/时间: N/A", - f"Token/消息数量: {_format_large_number(tokens_per_100_messages)}/100条" if stats[TOTAL_MSG_CNT] > 0 else "Token/消息数量: N/A", - f"Token/回复数量: {_format_large_number(tokens_per_100_replies)}/100条" if total_replies > 0 else "Token/回复数量: N/A", f"Token/时间: {_format_large_number(tokens_per_hour)}/小时" if online_hours > 0 else "Token/时间: N/A", "", ] @@ -933,25 +930,21 @@ class StatisticOutputTask(AsyncTask):
{(stat_data[TOTAL_COST] / stat_data[TOTAL_MSG_CNT] * 100 if stat_data[TOTAL_MSG_CNT] > 0 else 0.0):.4f} ¥/100条
-
花费/时间
-
{(stat_data[TOTAL_COST] / (stat_data[ONLINE_TIME] / 3600.0) if stat_data[ONLINE_TIME] > 0 else 0.0):.2f} ¥/小时
-
-
-
Token/消息数量
-
{_format_large_number(sum(stat_data[TOTAL_TOK_BY_MODEL].values()) / stat_data[TOTAL_MSG_CNT] * 100 if stat_data[TOTAL_MSG_CNT] > 0 and stat_data[TOTAL_TOK_BY_MODEL] else 0.0, html=True)}/100条
-
-
-
Token/回复数量
-
{_format_large_number(sum(stat_data[TOTAL_TOK_BY_MODEL].values()) / stat_data.get(TOTAL_REPLY_CNT, 0) * 100 if stat_data.get(TOTAL_REPLY_CNT, 0) > 0 and stat_data[TOTAL_TOK_BY_MODEL] else 0.0, html=True)}/100条
-
-
-
Token/时间
-
{_format_large_number(sum(stat_data[TOTAL_TOK_BY_MODEL].values()) / (stat_data[ONLINE_TIME] / 3600.0) if stat_data[ONLINE_TIME] > 0 and stat_data[TOTAL_TOK_BY_MODEL] else 0.0, html=True)}/小时
+
花费/消息数量(排除回复)
+
{(stat_data[TOTAL_COST] / (stat_data[TOTAL_MSG_CNT] - stat_data.get(TOTAL_REPLY_CNT, 0)) * 100 if (stat_data[TOTAL_MSG_CNT] - stat_data.get(TOTAL_REPLY_CNT, 0)) > 0 else 0.0):.4f} ¥/100条
花费/回复数量
{(stat_data[TOTAL_COST] / stat_data.get(TOTAL_REPLY_CNT, 0) * 100 if stat_data.get(TOTAL_REPLY_CNT, 0) > 0 else 0.0):.4f} ¥/100条
+
+
花费/时间
+
{(stat_data[TOTAL_COST] / (stat_data[ONLINE_TIME] / 3600.0) if stat_data[ONLINE_TIME] > 0 else 0.0):.2f} ¥/小时
+
+
+
Token/时间
+
{_format_large_number(sum(stat_data[TOTAL_TOK_BY_MODEL].values()) / (stat_data[ONLINE_TIME] / 3600.0) if stat_data[ONLINE_TIME] > 0 and stat_data[TOTAL_TOK_BY_MODEL] else 0.0, html=True)}/小时
+

按模型分类统计

@@ -1805,10 +1798,8 @@ class StatisticOutputTask(AsyncTask): # 初始化数据结构 cost_per_100_messages = [0.0] * len(time_points) # 花费/消息数量(每100条) cost_per_hour = [0.0] * len(time_points) # 花费/时间(每小时) - tokens_per_100_messages = [0.0] * len(time_points) # Token/消息数量(每100条) tokens_per_hour = [0.0] * len(time_points) # Token/时间(每小时) cost_per_100_replies = [0.0] * len(time_points) # 花费/回复数量(每100条) - tokens_per_100_replies = [0.0] * len(time_points) # Token/回复数量(每100条) # 每个时间点的累计数据 total_costs = [0.0] * len(time_points) @@ -1882,10 +1873,6 @@ class StatisticOutputTask(AsyncTask): if total_online_hours[idx] > 0: cost_per_hour[idx] = (total_costs[idx] / total_online_hours[idx]) - # Token/消息数量(每100条) - if total_messages[idx] > 0: - tokens_per_100_messages[idx] = (total_tokens[idx] / total_messages[idx] * 100) - # Token/时间(每小时) if total_online_hours[idx] > 0: tokens_per_hour[idx] = (total_tokens[idx] / total_online_hours[idx]) @@ -1893,10 +1880,6 @@ class StatisticOutputTask(AsyncTask): # 花费/回复数量(每100条) if total_replies[idx] > 0: cost_per_100_replies[idx] = (total_costs[idx] / total_replies[idx] * 100) - - # Token/回复数量(每100条) - if total_replies[idx] > 0: - tokens_per_100_replies[idx] = (total_tokens[idx] / total_replies[idx] * 100) # 生成时间标签 if interval_hours == 1: @@ -1908,10 +1891,8 @@ class StatisticOutputTask(AsyncTask): "time_labels": time_labels, "cost_per_100_messages": cost_per_100_messages, "cost_per_hour": cost_per_hour, - "tokens_per_100_messages": tokens_per_100_messages, "tokens_per_hour": tokens_per_hour, "cost_per_100_replies": cost_per_100_replies, - "tokens_per_100_replies": tokens_per_100_replies, } def _generate_metrics_tab(self, metrics_data: dict) -> str: @@ -1919,10 +1900,8 @@ class StatisticOutputTask(AsyncTask): colors = { "cost_per_100_messages": "#8b5cf6", "cost_per_hour": "#9f8efb", - "tokens_per_100_messages": "#b5a6ff", "tokens_per_hour": "#c7bbff", "cost_per_100_replies": "#d9ceff", - "tokens_per_100_replies": "#a78bfa", } return f""" @@ -1944,17 +1923,11 @@ class StatisticOutputTask(AsyncTask):
-
- -
-
- -
- +
@@ -2001,13 +1974,6 @@ class StatisticOutputTask(AsyncTask): dataKey: 'cost_per_hour', color: '{colors["cost_per_hour"]}' }}, - tokensPer100Messages: {{ - id: 'tokensPer100MessagesChart', - title: 'Token/消息数量', - yAxisLabel: 'Token (/100条)', - dataKey: 'tokens_per_100_messages', - color: '{colors["tokens_per_100_messages"]}' - }}, tokensPerHour: {{ id: 'tokensPerHourChart', title: 'Token/时间', @@ -2021,13 +1987,6 @@ class StatisticOutputTask(AsyncTask): yAxisLabel: '花费 (¥/100条)', dataKey: 'cost_per_100_replies', color: '{colors["cost_per_100_replies"]}' - }}, - tokensPer100Replies: {{ - id: 'tokensPer100RepliesChart', - title: 'Token/回复数量', - yAxisLabel: 'Token (/100条)', - dataKey: 'tokens_per_100_replies', - color: '{colors["tokens_per_100_replies"]}' }} }}; @@ -2054,10 +2013,8 @@ class StatisticOutputTask(AsyncTask): // 重新创建图表 createMetricsChart('costPer100Messages', data, timeScale); createMetricsChart('costPerHour', data, timeScale); - createMetricsChart('tokensPer100Messages', data, timeScale); createMetricsChart('tokensPerHour', data, timeScale); createMetricsChart('costPer100Replies', data, timeScale); - createMetricsChart('tokensPer100Replies', data, timeScale); }} function createMetricsChart(chartType, data, timeScale) {{ diff --git a/src/memory_system/memory_retrieval.py b/src/memory_system/memory_retrieval.py index ed1e0b66..4014826c 100644 --- a/src/memory_system/memory_retrieval.py +++ b/src/memory_system/memory_retrieval.py @@ -23,7 +23,7 @@ def init_memory_retrieval_prompt(): # 第一步:问题生成prompt Prompt( """ -你是一个专门检测是否需要回忆的助手。你的名字是{bot_name}。现在是{time_now}。 +你的名字是{bot_name}。现在是{time_now}。 群里正在进行的聊天内容: {chat_history} @@ -34,6 +34,7 @@ def init_memory_retrieval_prompt(): 1. 对话中是否提到了过去发生的事情、人物、事件或信息 2. 是否有需要回忆的内容(比如"之前说过"、"上次"、"以前"等) 3. 是否有需要查找历史信息的问题 +4. 是否有问题可以搜集信息帮助你聊天 重要提示: - 如果"最近已查询的问题和结果"中已经包含了类似的问题,请避免重复生成相同或相似的问题 @@ -65,7 +66,9 @@ def init_memory_retrieval_prompt(): # 第二步:ReAct Agent prompt(工具描述会在运行时动态生成) Prompt( - """你需要通过思考(Think)、行动(Action)、观察(Observation)的循环来回答问题。 + """ +你的名字是{bot_name},你正在参与聊天,你需要搜集信息来回答问题,帮助你参与聊天。 +你需要通过思考(Think)、行动(Action)、观察(Observation)的循环来回答问题。 当前问题:{question} 已收集的信息: @@ -78,14 +81,20 @@ def init_memory_retrieval_prompt(): ```json {{ "thought": "你的思考过程,分析当前情况,决定下一步行动", - "action_type": {action_types_list}, - "action_params": {{参数名: 参数值}} 或 null + "actions": [ + {{ + "action_type": {action_types_list}, + "action_params": {{参数名: 参数值}} 或 null + }} + ] }} ``` -你可以选择以下动作: -1. 如果已经收集到足够的信息可以回答问题,请设置action_type为"final_answer",并在thought中说明答案。除非明确找到答案,否则不要设置为final_answer。 -2. 如果经过多次查询后,确认无法找到相关信息或答案,请设置action_type为"no_answer",并在thought中说明原因。 +重要说明: +- 你可以在一次迭代中执行多个查询,将多个action放在actions数组中 +- 如果只需要执行一个查询,actions数组中只包含一个action即可 +- 如果已经收集到足够的信息可以回答问题,请设置actions为包含一个action_type为"final_answer"的数组,并在thought中说明答案。除非明确找到答案,否则不要设置为final_answer。 +- 如果经过多次查询后,确认无法找到相关信息或答案,请设置actions为包含一个action_type为"no_answer"的数组,并在thought中说明原因。 请只输出JSON,不要输出其他内容: """, @@ -101,6 +110,8 @@ def _parse_react_response(response: str) -> Optional[Dict[str, Any]]: Returns: Dict[str, Any]: 解析后的动作信息,如果解析失败返回None + 格式: {"thought": str, "actions": List[Dict[str, Any]]} + 每个action格式: {"action_type": str, "action_params": dict} """ try: # 尝试提取JSON(可能包含在```json代码块中) @@ -123,6 +134,20 @@ def _parse_react_response(response: str) -> Optional[Dict[str, Any]]: logger.warning(f"解析的JSON不是对象格式: {action_info}") return None + # 确保actions字段存在且为列表 + if "actions" not in action_info: + logger.warning(f"响应中缺少actions字段: {action_info}") + return None + + if not isinstance(action_info["actions"], list): + logger.warning(f"actions字段不是数组格式: {action_info['actions']}") + return None + + # 确保actions不为空 + if len(action_info["actions"]) == 0: + logger.warning("actions数组为空") + return None + return action_info except Exception as e: @@ -163,9 +188,13 @@ async def _react_agent_solve_question( # 获取工具注册器 tool_registry = get_tool_registry() + # 获取bot_name + bot_name = global_config.bot.nickname + # 构建prompt(动态生成工具描述) prompt = await global_prompt_manager.format_prompt( "memory_retrieval_react_prompt", + bot_name=bot_name, question=question, collected_info=collected_info if collected_info else "暂无信息", tools_description=tool_registry.get_tools_description(), @@ -196,45 +225,50 @@ async def _react_agent_solve_question( break thought = action_info.get("thought", "") - action_type = action_info.get("action_type", "") - action_params = action_info.get("action_params", {}) + actions = action_info.get("actions", []) logger.info(f"ReAct Agent 第 {iteration + 1} 次迭代 思考: {thought}") - logger.info(f"ReAct Agent 第 {iteration + 1} 次迭代 动作类型: {action_type}") - logger.info(f"ReAct Agent 第 {iteration + 1} 次迭代 动作参数: {action_params}") + logger.info(f"ReAct Agent 第 {iteration + 1} 次迭代 动作数量: {len(actions)}") - # 记录思考步骤 + # 记录思考步骤(包含所有actions) step = { "iteration": iteration + 1, "thought": thought, - "action_type": action_type, - "action_params": action_params, - "observation": "" + "actions": actions, + "observations": [] } - # 执行动作 - if action_type == "final_answer": - # Agent认为已经找到答案 - answer = thought # 使用thought作为答案 - step["observation"] = "找到答案" - thinking_steps.append(step) - logger.info(f"ReAct Agent 第 {iteration + 1} 次迭代 找到最终答案: {answer}") - return True, answer, thinking_steps + # 检查是否有final_answer或no_answer + for action in actions: + action_type = action.get("action_type", "") + if action_type == "final_answer": + # Agent认为已经找到答案 + answer = thought # 使用thought作为答案 + step["observations"] = ["找到答案"] + thinking_steps.append(step) + logger.info(f"ReAct Agent 第 {iteration + 1} 次迭代 找到最终答案: {answer}") + return True, answer, thinking_steps + elif action_type == "no_answer": + # Agent确认无法找到答案 + answer = thought # 使用thought说明无法找到答案的原因 + step["observations"] = ["确认无法找到答案"] + thinking_steps.append(step) + logger.info(f"ReAct Agent 第 {iteration + 1} 次迭代 确认无法找到答案: {answer}") + return False, answer, thinking_steps - elif action_type == "no_answer": - # Agent确认无法找到答案 - answer = thought # 使用thought说明无法找到答案的原因 - step["observation"] = "确认无法找到答案" - thinking_steps.append(step) - logger.info(f"ReAct Agent 第 {iteration + 1} 次迭代 确认无法找到答案: {answer}") - return False, answer, thinking_steps - - # 使用工具注册器执行工具 + # 并行执行所有工具 tool_registry = get_tool_registry() - tool = tool_registry.get_tool(action_type) + tool_tasks = [] - if tool: - try: + for i, action in enumerate(actions): + action_type = action.get("action_type", "") + action_params = action.get("action_params", {}) + + logger.info(f"ReAct Agent 第 {iteration + 1} 次迭代 动作 {i+1}/{len(actions)}: {action_type}({action_params})") + + tool = tool_registry.get_tool(action_type) + + if tool: # 准备工具参数(需要添加chat_id如果工具需要) tool_params = action_params.copy() @@ -244,31 +278,38 @@ async def _react_agent_solve_question( if "chat_id" in sig.parameters: tool_params["chat_id"] = chat_id - logger.info(f"ReAct Agent 第 {iteration + 1} 次迭代 执行工具: {action_type}({tool_params})") + # 创建异步任务 + async def execute_single_tool(tool_instance, params, act_type, act_params, iter_num): + try: + observation = await tool_instance.execute(**params) + param_str = ", ".join([f"{k}={v}" for k, v in act_params.items()]) + return f"查询{act_type}({param_str})的结果:{observation}" + except Exception as e: + error_msg = f"工具执行失败: {str(e)}" + logger.error(f"ReAct Agent 第 {iter_num + 1} 次迭代 动作 {act_type} {error_msg}") + return f"查询{act_type}失败: {error_msg}" - # 执行工具 - observation = await tool.execute(**tool_params) - step["observation"] = observation + tool_tasks.append(execute_single_tool(tool, tool_params, action_type, action_params, iteration)) + else: + error_msg = f"未知的工具类型: {action_type}" + logger.warning(f"ReAct Agent 第 {iteration + 1} 次迭代 动作 {i+1}/{len(actions)} {error_msg}") + tool_tasks.append(asyncio.create_task(asyncio.sleep(0, result=f"查询{action_type}失败: {error_msg}"))) + + # 并行执行所有工具 + if tool_tasks: + observations = await asyncio.gather(*tool_tasks, return_exceptions=True) + + # 处理执行结果 + for i, observation in enumerate(observations): + if isinstance(observation, Exception): + observation = f"工具执行异常: {str(observation)}" + logger.error(f"ReAct Agent 第 {iteration + 1} 次迭代 动作 {i+1} 执行异常: {observation}") - # 构建收集信息的描述 - param_str = ", ".join([f"{k}={v}" for k, v in action_params.items()]) - collected_info += f"\n查询{action_type}({param_str})的结果:{observation}\n" - - logger.info(f"ReAct Agent 第 {iteration + 1} 次迭代 工具执行结果: {observation}") - except Exception as e: - error_msg = f"工具执行失败: {str(e)}" - step["observation"] = error_msg - logger.error(f"ReAct Agent 第 {iteration + 1} 次迭代 {error_msg}") - else: - step["observation"] = f"未知的工具类型: {action_type}" - logger.warning(f"ReAct Agent 第 {iteration + 1} 次迭代 未知的工具类型: {action_type}") + step["observations"].append(observation) + collected_info += f"\n{observation}\n" + logger.info(f"ReAct Agent 第 {iteration + 1} 次迭代 动作 {i+1} 执行结果: {observation}") thinking_steps.append(step) - - # 如果观察结果为空或无效,继续下一轮 - if step["observation"] and "无有效信息" not in step["observation"] and "未找到" not in step["observation"]: - # 有有效信息,继续思考 - pass # 达到最大迭代次数或超时,但Agent没有明确返回final_answer # 迭代超时应该直接视为no_answer,而不是使用已有信息 @@ -333,6 +374,47 @@ def _get_recent_query_history(chat_id: str, time_window_seconds: float = 300.0) return "" +def _get_cached_memories(chat_id: str, time_window_seconds: float = 300.0) -> List[str]: + """获取最近一段时间内缓存的记忆(只返回找到答案的记录) + + Args: + chat_id: 聊天ID + time_window_seconds: 时间窗口(秒),默认300秒(5分钟) + + Returns: + List[str]: 格式化的记忆列表,每个元素格式为 "问题:xxx\n答案:xxx" + """ + try: + current_time = time.time() + start_time = current_time - time_window_seconds + + # 查询最近时间窗口内找到答案的记录,按更新时间倒序 + records = ( + ThinkingBack.select() + .where( + (ThinkingBack.chat_id == chat_id) & + (ThinkingBack.update_time >= start_time) & + (ThinkingBack.found_answer == 1) + ) + .order_by(ThinkingBack.update_time.desc()) + .limit(5) # 最多返回5条最近的记录 + ) + + if not records.exists(): + return [] + + cached_memories = [] + for record in records: + if record.answer: + cached_memories.append(f"问题:{record.question}\n答案:{record.answer}") + + return cached_memories + + except Exception as e: + logger.error(f"获取缓存记忆失败: {e}") + return [] + + def _query_thinking_back(chat_id: str, question: str) -> Optional[Tuple[bool, str]]: """从thinking_back数据库中查询是否有现成的答案 @@ -441,11 +523,11 @@ def _get_max_iterations_by_question_count(question_count: int) -> int: int: 最大迭代次数 """ if question_count == 1: - return 5 + return 6 elif question_count == 2: return 3 else: # 3个或以上 - return 1 + return 2 async def _process_single_question( @@ -587,9 +669,19 @@ async def build_memory_retrieval_prompt( # 解析问题列表 questions = _parse_questions_json(response) + # 获取缓存的记忆(与question时使用相同的时间窗口和数量限制) + cached_memories = _get_cached_memories(chat_id, time_window_seconds=300.0) + if not questions: logger.debug("模型认为不需要检索记忆或解析失败") - return "" + # 即使没有当次查询,也返回缓存的记忆 + if cached_memories: + retrieved_memory = "\n\n".join(cached_memories) + end_time = time.time() + logger.info(f"无当次查询,返回缓存记忆,耗时: {(end_time - start_time):.3f}秒,包含 {len(cached_memories)} 条缓存记忆") + return f"你回忆起了以下信息:\n{retrieved_memory}\n请在回复时参考这些回忆的信息。\n" + else: + return "" logger.info(f"解析到 {len(questions)} 个问题: {questions}") @@ -613,20 +705,34 @@ async def build_memory_retrieval_prompt( # 收集所有有效结果 all_results = [] + current_questions = set() # 用于去重,避免缓存和当次查询重复 for i, result in enumerate(results): if isinstance(result, Exception): logger.error(f"处理问题 '{questions[i]}' 时发生异常: {result}") elif result is not None: all_results.append(result) + # 提取问题用于去重 + if result.startswith("问题:"): + question = result.split("\n")[0].replace("问题:", "").strip() + current_questions.add(question) + + # 将缓存的记忆添加到结果中(排除当次查询已包含的问题,避免重复) + for cached_memory in cached_memories: + if cached_memory.startswith("问题:"): + question = cached_memory.split("\n")[0].replace("问题:", "").strip() + # 只有当次查询中没有相同问题时,才添加缓存记忆 + if question not in current_questions: + all_results.append(cached_memory) + logger.debug(f"添加缓存记忆: {question[:50]}...") end_time = time.time() if all_results: retrieved_memory = "\n\n".join(all_results) - logger.info(f"记忆检索成功,耗时: {(end_time - start_time):.3f}秒") + logger.info(f"记忆检索成功,耗时: {(end_time - start_time):.3f}秒,包含 {len(all_results)} 条记忆(含缓存)") return f"你回忆起了以下信息:\n{retrieved_memory}\n请在回复时参考这些回忆的信息。\n" else: - logger.debug("所有问题均未找到答案") + logger.debug("所有问题均未找到答案,且无缓存记忆") return "" except Exception as e: