From 3304dc6b290ad8d45be8bbc4113a2f7594a94685 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 2 May 2025 11:13:59 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/PFC/action_planner.py | 115 +++++++++++++++++------------ src/plugins/PFC/conversation.py | 46 ++++++------ src/plugins/PFC/reply_generator.py | 102 +++++++++++++++---------- 3 files changed, 154 insertions(+), 109 deletions(-) diff --git a/src/plugins/PFC/action_planner.py b/src/plugins/PFC/action_planner.py index 40333fc4..8a45bf13 100644 --- a/src/plugins/PFC/action_planner.py +++ b/src/plugins/PFC/action_planner.py @@ -4,6 +4,7 @@ from src.plugins.memory_system.Hippocampus import HippocampusManager from src.plugins.knowledge.knowledge_lib import qa_manager from src.common.database import db from src.plugins.chat.utils import get_embedding + # import jieba # 如果需要旧版知识库的回退,可能需要 # import re # 如果需要旧版知识库的回退,可能需要 from src.common.logger_manager import get_logger @@ -124,7 +125,7 @@ class ActionPlanner: self.name = global_config.BOT_NICKNAME self.private_name = private_name self.chat_observer = ChatObserver.get_instance(stream_id, private_name) - + async def _get_memory_info(self, text: str) -> str: """根据文本自动检索相关记忆""" memory_prompt = "" @@ -132,18 +133,20 @@ class ActionPlanner: try: related_memory = await HippocampusManager.get_instance().get_memory_from_text( text=text, - max_memory_num=2, # 最多获取 2 条记忆 - max_memory_length=2, # 每条记忆长度限制(这个参数含义可能需确认) - max_depth=3, # 搜索深度 - fast_retrieval=False # 是否快速检索 + max_memory_num=2, # 最多获取 2 条记忆 + max_memory_length=2, # 每条记忆长度限制(这个参数含义可能需确认) + max_depth=3, # 搜索深度 + fast_retrieval=False, # 是否快速检索 ) if related_memory: for memory in related_memory: # memory[0] 是记忆ID, memory[1] 是记忆内容 - related_memory_info += memory[1] + "\n" # 将记忆内容拼接起来 + related_memory_info += memory[1] + "\n" # 将记忆内容拼接起来 if related_memory_info: memory_prompt = f"你回忆起:\n{related_memory_info.strip()}\n(以上是你的回忆,供参考)\n" - logger.debug(f"[私聊]决策层[{self.private_name}]自动检索到记忆: {related_memory_info.strip()[:100]}...") + logger.debug( + f"[私聊]决策层[{self.private_name}]自动检索到记忆: {related_memory_info.strip()[:100]}..." + ) else: logger.debug(f"[私聊]决策层[{self.private_name}]自动检索记忆返回为空。") else: @@ -179,9 +182,11 @@ class ActionPlanner: return "" # 调用我们之前添加的 get_info_from_db 函数 - results = get_info_from_db(embedding, limit=5, threshold=threshold, return_raw=True) # 最多查 5 条 + results = get_info_from_db(embedding, limit=5, threshold=threshold, return_raw=True) # 最多查 5 条 - logger.info(f"[私聊][{self.private_name}]旧版知识库查询完成,耗时: {time.time() - start_time:.3f}秒,获取{len(results)}条结果") + logger.info( + f"[私聊][{self.private_name}]旧版知识库查询完成,耗时: {time.time() - start_time:.3f}秒,获取{len(results)}条结果" + ) # 去重和格式化 unique_contents = set() @@ -231,7 +236,9 @@ class ActionPlanner: else: logger.debug(f"[私聊][{self.private_name}]LPMM 知识库未返回有效知识,尝试旧版数据库检索。") except Exception as e: - logger.error(f"[私聊][{self.private_name}]调用 LPMM 知识库 (qa_manager.get_knowledge) 时发生异常: {str(e)},尝试旧版数据库检索。") + logger.error( + f"[私聊][{self.private_name}]调用 LPMM 知识库 (qa_manager.get_knowledge) 时发生异常: {str(e)},尝试旧版数据库检索。" + ) # 2. 如果 LPMM 失败或无结果,尝试旧版数据库 try: @@ -246,11 +253,15 @@ class ActionPlanner: logger.debug(f"[私聊][{self.private_name}]旧版数据库也未检索到有效知识。") except Exception as e2: - logger.error(f"[私聊][{self.private_name}]调用旧版知识库检索 (_get_prompt_info_old) 时也发生异常: {str(e2)}") + logger.error( + f"[私聊][{self.private_name}]调用旧版知识库检索 (_get_prompt_info_old) 时也发生异常: {str(e2)}" + ) # 如果两种方法都失败或无结果 - logger.info(f"[私聊][{self.private_name}]自动知识检索总耗时: {time.time() - start_time:.3f}秒,未找到任何相关知识。") - return "" # 返回空字符串 + logger.info( + f"[私聊][{self.private_name}]自动知识检索总耗时: {time.time() - start_time:.3f}秒,未找到任何相关知识。" + ) + return "" # 返回空字符串 # 修改 plan 方法签名,增加 last_successful_reply_action 参数 async def plan( @@ -362,36 +373,36 @@ class ActionPlanner: # --- 知识信息字符串构建开始 --- # knowledge_info_str = "【已获取的相关知识和记忆】\n" # try: - # 检查 conversation_info 是否有 knowledge_list 并且不为空 - # if hasattr(conversation_info, "knowledge_list") and conversation_info.knowledge_list: - # 最多只显示最近的 5 条知识,防止 Prompt 过长 - # recent_knowledge = conversation_info.knowledge_list[-5:] - # for i, knowledge_item in enumerate(recent_knowledge): - # if isinstance(knowledge_item, dict): - # query = knowledge_item.get("query", "未知查询") - # knowledge = knowledge_item.get("knowledge", "无知识内容") - # source = knowledge_item.get("source", "未知来源") - # 只取知识内容的前 2000 个字,避免太长 - # knowledge_snippet = knowledge[:2000] + "..." if len(knowledge) > 2000 else knowledge - # knowledge_info_str += ( - # f"{i + 1}. 关于 '{query}' 的知识 (来源: {source}):\n {knowledge_snippet}\n" - # ) - # else: - # 处理列表里不是字典的异常情况 - # knowledge_info_str += f"{i + 1}. 发现一条格式不正确的知识记录。\n" + # 检查 conversation_info 是否有 knowledge_list 并且不为空 + # if hasattr(conversation_info, "knowledge_list") and conversation_info.knowledge_list: + # 最多只显示最近的 5 条知识,防止 Prompt 过长 + # recent_knowledge = conversation_info.knowledge_list[-5:] + # for i, knowledge_item in enumerate(recent_knowledge): + # if isinstance(knowledge_item, dict): + # query = knowledge_item.get("query", "未知查询") + # knowledge = knowledge_item.get("knowledge", "无知识内容") + # source = knowledge_item.get("source", "未知来源") + # 只取知识内容的前 2000 个字,避免太长 + # knowledge_snippet = knowledge[:2000] + "..." if len(knowledge) > 2000 else knowledge + # knowledge_info_str += ( + # f"{i + 1}. 关于 '{query}' 的知识 (来源: {source}):\n {knowledge_snippet}\n" + # ) + # else: + # 处理列表里不是字典的异常情况 + # knowledge_info_str += f"{i + 1}. 发现一条格式不正确的知识记录。\n" - # if not recent_knowledge: # 如果 knowledge_list 存在但为空 - # knowledge_info_str += "- 暂无相关知识和记忆。\n" + # if not recent_knowledge: # 如果 knowledge_list 存在但为空 + # knowledge_info_str += "- 暂无相关知识和记忆。\n" - # else: - # 如果 conversation_info 没有 knowledge_list 属性,或者列表为空 - # knowledge_info_str += "- 暂无相关知识记忆。\n" + # else: + # 如果 conversation_info 没有 knowledge_list 属性,或者列表为空 + # knowledge_info_str += "- 暂无相关知识记忆。\n" # except AttributeError: - # logger.warning(f"[私聊][{self.private_name}]ConversationInfo 对象可能缺少 knowledge_list 属性。") - # knowledge_info_str += "- 获取知识列表时出错。\n" + # logger.warning(f"[私聊][{self.private_name}]ConversationInfo 对象可能缺少 knowledge_list 属性。") + # knowledge_info_str += "- 获取知识列表时出错。\n" # except Exception as e: - # logger.error(f"[私聊][{self.private_name}]构建知识信息字符串时出错: {e}") - # knowledge_info_str += "- 处理知识列表时出错。\n" + # logger.error(f"[私聊][{self.private_name}]构建知识信息字符串时出错: {e}") + # knowledge_info_str += "- 处理知识列表时出错。\n" # --- 知识信息字符串构建结束 --- # 获取聊天历史记录 (chat_history_text) @@ -503,16 +514,20 @@ class ActionPlanner: retrieved_memory_str_planner = "" retrieved_knowledge_str_planner = "" - retrieval_context = chat_history_text # 使用聊天记录作为检索上下文 + retrieval_context = chat_history_text # 使用聊天记录作为检索上下文 if retrieval_context and retrieval_context != "还没有聊天记录。" and retrieval_context != "[构建聊天记录出错]": try: logger.debug(f"[私聊][{self.private_name}] (ActionPlanner) 开始自动检索记忆...") retrieved_memory_str_planner = await self._get_memory_info(text=retrieval_context) - logger.info(f"[私聊][{self.private_name}] (ActionPlanner) 自动检索记忆 {'完成' if retrieved_memory_str_planner else '无结果'}。") + logger.info( + f"[私聊][{self.private_name}] (ActionPlanner) 自动检索记忆 {'完成' if retrieved_memory_str_planner else '无结果'}。" + ) logger.debug(f"[私聊][{self.private_name}] (ActionPlanner) 开始自动知识检索...") retrieved_knowledge_str_planner = await self._get_prompt_info(message=retrieval_context) - logger.info(f"[私聊][{self.private_name}] (ActionPlanner) 自动检索知识 {'完成' if retrieved_knowledge_str_planner else '无结果'}。") + logger.info( + f"[私聊][{self.private_name}] (ActionPlanner) 自动检索知识 {'完成' if retrieved_knowledge_str_planner else '无结果'}。" + ) except Exception as retrieval_err: logger.error(f"[私聊][{self.private_name}] (ActionPlanner) 自动检索时出错: {retrieval_err}") retrieved_memory_str_planner = "检索记忆时出错。\n" @@ -541,7 +556,9 @@ class ActionPlanner: chat_history_text=chat_history_text if chat_history_text.strip() else "还没有聊天记录。", # knowledge_info_str=knowledge_info_str, retrieved_memory_str=retrieved_memory_str_planner if retrieved_memory_str_planner else "无相关记忆。", - retrieved_knowledge_str=retrieved_knowledge_str_planner if retrieved_knowledge_str_planner else "无相关知识。" + retrieved_knowledge_str=retrieved_knowledge_str_planner + if retrieved_knowledge_str_planner + else "无相关知识。", ) logger.debug(f"[私聊][{self.private_name}]发送到LLM的最终提示词:\n------\n{prompt}\n------") @@ -644,7 +661,8 @@ class ActionPlanner: # 外层异常处理保持不变 logger.error(f"[私聊][{self.private_name}]规划行动时调用 LLM 或处理结果出错: {str(e)}") return "wait", f"行动规划处理中发生错误,暂时等待: {str(e)}" - + + def get_info_from_db( query_embedding: list, limit: int = 1, threshold: float = 0.5, return_raw: bool = False ) -> Union[str, list]: @@ -696,7 +714,13 @@ def get_info_from_db( } }, # 防止除以零错误,添加一个小的 epsilon - {"$addFields": {"similarity": {"$divide": ["$dotProduct", {"$max": [{"$multiply": ["$magnitude1", "$magnitude2"]}, 1e-9]}]}}}, + { + "$addFields": { + "similarity": { + "$divide": ["$dotProduct", {"$max": [{"$multiply": ["$magnitude1", "$magnitude2"]}, 1e-9]}] + } + } + }, { "$match": { "similarity": {"$gte": threshold} # 只保留相似度大于等于阈值的结果 @@ -723,4 +747,3 @@ def get_info_from_db( else: # 返回所有找到的内容,用换行分隔 return "\n".join(str(result["content"]) for result in results) - diff --git a/src/plugins/PFC/conversation.py b/src/plugins/PFC/conversation.py index 2ecd6824..b2541ebb 100644 --- a/src/plugins/PFC/conversation.py +++ b/src/plugins/PFC/conversation.py @@ -506,30 +506,30 @@ class Conversation: conversation_info.done_action.append(wait_action_record) # elif action == "fetch_knowledge": - # self.state = ConversationState.FETCHING - # knowledge_query = reason - # try: - # 检查 knowledge_fetcher 是否存在 - # if not hasattr(self, "knowledge_fetcher"): - # logger.error(f"[私聊][{self.private_name}]KnowledgeFetcher 未初始化,无法获取知识。") - # raise AttributeError("KnowledgeFetcher not initialized") + # self.state = ConversationState.FETCHING + # knowledge_query = reason + # try: + # 检查 knowledge_fetcher 是否存在 + # if not hasattr(self, "knowledge_fetcher"): + # logger.error(f"[私聊][{self.private_name}]KnowledgeFetcher 未初始化,无法获取知识。") + # raise AttributeError("KnowledgeFetcher not initialized") - # knowledge, source = await self.knowledge_fetcher.fetch(knowledge_query, observation_info.chat_history) - # logger.info(f"[私聊][{self.private_name}]获取到知识: {knowledge[:100]}..., 来源: {source}") - # if knowledge: - # 确保 knowledge_list 存在 - # if not hasattr(conversation_info, "knowledge_list"): - # conversation_info.knowledge_list = [] - # conversation_info.knowledge_list.append( - # {"query": knowledge_query, "knowledge": knowledge, "source": source} - # ) - # action_successful = True - # except Exception as fetch_err: - # logger.error(f"[私聊][{self.private_name}]获取知识时出错: {str(fetch_err)}") - # conversation_info.done_action[action_index].update( - # {"status": "recall", "final_reason": f"获取知识失败: {str(fetch_err)}"} - # ) - # self.conversation_info.last_successful_reply_action = None # 重置状态 + # knowledge, source = await self.knowledge_fetcher.fetch(knowledge_query, observation_info.chat_history) + # logger.info(f"[私聊][{self.private_name}]获取到知识: {knowledge[:100]}..., 来源: {source}") + # if knowledge: + # 确保 knowledge_list 存在 + # if not hasattr(conversation_info, "knowledge_list"): + # conversation_info.knowledge_list = [] + # conversation_info.knowledge_list.append( + # {"query": knowledge_query, "knowledge": knowledge, "source": source} + # ) + # action_successful = True + # except Exception as fetch_err: + # logger.error(f"[私聊][{self.private_name}]获取知识时出错: {str(fetch_err)}") + # conversation_info.done_action[action_index].update( + # {"status": "recall", "final_reason": f"获取知识失败: {str(fetch_err)}"} + # ) + # self.conversation_info.last_successful_reply_action = None # 重置状态 elif action == "rethink_goal": self.state = ConversationState.RETHINKING diff --git a/src/plugins/PFC/reply_generator.py b/src/plugins/PFC/reply_generator.py index 3599e6e1..2c01cc0e 100644 --- a/src/plugins/PFC/reply_generator.py +++ b/src/plugins/PFC/reply_generator.py @@ -1,15 +1,19 @@ # 用于访问记忆系统 from src.plugins.memory_system.Hippocampus import HippocampusManager + # 用于访问新的知识库 (LPMM) from src.plugins.knowledge.knowledge_lib import qa_manager + # 用于访问数据库 (旧知识库需要) from src.common.database import db + # 用于获取文本的嵌入向量 (旧知识库需要) from src.plugins.chat.utils import get_embedding + # 可能用于旧知识库提取主题 (如果需要回退到旧方法) # import jieba # 如果报错说找不到 jieba,可能需要安装: pip install jieba # import re # 正则表达式库,通常 Python 自带 -from typing import Tuple, List, Dict, Any,Union +from typing import Tuple, List, Dict, Any, Union from src.common.logger import get_module_logger from ..models.utils_model import LLMRequest from ...config.config import global_config @@ -116,6 +120,7 @@ class ReplyGenerator: self.private_name = private_name self.chat_observer = ChatObserver.get_instance(stream_id, private_name) self.reply_checker = ReplyChecker(stream_id, private_name) + async def _get_memory_info(self, text: str) -> str: """根据文本自动检索相关记忆""" memory_prompt = "" @@ -123,15 +128,15 @@ class ReplyGenerator: try: related_memory = await HippocampusManager.get_instance().get_memory_from_text( text=text, - max_memory_num=2, # 最多获取 2 条记忆 - max_memory_length=2, # 每条记忆长度限制(这个参数含义可能需确认) - max_depth=3, # 搜索深度 - fast_retrieval=False # 是否快速检索 + max_memory_num=2, # 最多获取 2 条记忆 + max_memory_length=2, # 每条记忆长度限制(这个参数含义可能需确认) + max_depth=3, # 搜索深度 + fast_retrieval=False, # 是否快速检索 ) if related_memory: for memory in related_memory: # memory[0] 是记忆ID, memory[1] 是记忆内容 - related_memory_info += memory[1] + "\n" # 将记忆内容拼接起来 + related_memory_info += memory[1] + "\n" # 将记忆内容拼接起来 if related_memory_info: memory_prompt = f"你回忆起:\n{related_memory_info.strip()}\n(以上是你的回忆,不一定是目前聊天里的人说的,回忆中别人说的事情也不一定是准确的,请记住)\n" logger.debug(f"[私聊][{self.private_name}]自动检索到记忆: {related_memory_info.strip()[:100]}...") @@ -170,9 +175,11 @@ class ReplyGenerator: return "" # 调用我们之前添加的 get_info_from_db 函数 - results = get_info_from_db(embedding, limit=5, threshold=threshold, return_raw=True) # 最多查 5 条 + results = get_info_from_db(embedding, limit=5, threshold=threshold, return_raw=True) # 最多查 5 条 - logger.info(f"[私聊][{self.private_name}]旧版知识库查询完成,耗时: {time.time() - start_time:.3f}秒,获取{len(results)}条结果") + logger.info( + f"[私聊][{self.private_name}]旧版知识库查询完成,耗时: {time.time() - start_time:.3f}秒,获取{len(results)}条结果" + ) # 去重和格式化 unique_contents = set() @@ -222,7 +229,9 @@ class ReplyGenerator: else: logger.debug(f"[私聊][{self.private_name}]LPMM 知识库未返回有效知识,尝试旧版数据库检索。") except Exception as e: - logger.error(f"[私聊][{self.private_name}]调用 LPMM 知识库 (qa_manager.get_knowledge) 时发生异常: {str(e)},尝试旧版数据库检索。") + logger.error( + f"[私聊][{self.private_name}]调用 LPMM 知识库 (qa_manager.get_knowledge) 时发生异常: {str(e)},尝试旧版数据库检索。" + ) # 2. 如果 LPMM 失败或无结果,尝试旧版数据库 try: @@ -237,11 +246,15 @@ class ReplyGenerator: logger.debug(f"[私聊][{self.private_name}]旧版数据库也未检索到有效知识。") except Exception as e2: - logger.error(f"[私聊][{self.private_name}]调用旧版知识库检索 (_get_prompt_info_old) 时也发生异常: {str(e2)}") + logger.error( + f"[私聊][{self.private_name}]调用旧版知识库检索 (_get_prompt_info_old) 时也发生异常: {str(e2)}" + ) # 如果两种方法都失败或无结果 - logger.info(f"[私聊][{self.private_name}]自动知识检索总耗时: {time.time() - start_time:.3f}秒,未找到任何相关知识。") - return "" # 返回空字符串 - + logger.info( + f"[私聊][{self.private_name}]自动知识检索总耗时: {time.time() - start_time:.3f}秒,未找到任何相关知识。" + ) + return "" # 返回空字符串 + # 修改 generate 方法签名,增加 action_type 参数 async def generate( self, observation_info: ObservationInfo, conversation_info: ConversationInfo, action_type: str @@ -284,34 +297,34 @@ class ReplyGenerator: # --- 新增:构建知识信息字符串 --- # knowledge_info_str = "【供参考的相关知识和记忆】\n" # 稍微改下标题,表明是供参考 # try: - # 检查 conversation_info 是否有 knowledge_list 并且不为空 - # if hasattr(conversation_info, "knowledge_list") and conversation_info.knowledge_list: - # 最多只显示最近的 5 条知识 - # recent_knowledge = conversation_info.knowledge_list[-5:] - # for i, knowledge_item in enumerate(recent_knowledge): - # if isinstance(knowledge_item, dict): - # query = knowledge_item.get("query", "未知查询") - # knowledge = knowledge_item.get("knowledge", "无知识内容") - # source = knowledge_item.get("source", "未知来源") - # 只取知识内容的前 2000 个字 - # knowledge_snippet = knowledge[:2000] + "..." if len(knowledge) > 2000 else knowledge - # knowledge_info_str += ( - # f"{i + 1}. 关于 '{query}' (来源: {source}): {knowledge_snippet}\n" # 格式微调,更简洁 - # ) - # else: - # knowledge_info_str += f"{i + 1}. 发现一条格式不正确的知识记录。\n" + # 检查 conversation_info 是否有 knowledge_list 并且不为空 + # if hasattr(conversation_info, "knowledge_list") and conversation_info.knowledge_list: + # 最多只显示最近的 5 条知识 + # recent_knowledge = conversation_info.knowledge_list[-5:] + # for i, knowledge_item in enumerate(recent_knowledge): + # if isinstance(knowledge_item, dict): + # query = knowledge_item.get("query", "未知查询") + # knowledge = knowledge_item.get("knowledge", "无知识内容") + # source = knowledge_item.get("source", "未知来源") + # 只取知识内容的前 2000 个字 + # knowledge_snippet = knowledge[:2000] + "..." if len(knowledge) > 2000 else knowledge + # knowledge_info_str += ( + # f"{i + 1}. 关于 '{query}' (来源: {source}): {knowledge_snippet}\n" # 格式微调,更简洁 + # ) + # else: + # knowledge_info_str += f"{i + 1}. 发现一条格式不正确的知识记录。\n" - # if not recent_knowledge: - # knowledge_info_str += "- 暂无。\n" # 更简洁的提示 + # if not recent_knowledge: + # knowledge_info_str += "- 暂无。\n" # 更简洁的提示 - # else: - # knowledge_info_str += "- 暂无。\n" + # else: + # knowledge_info_str += "- 暂无。\n" # except AttributeError: - # logger.warning(f"[私聊][{self.private_name}]ConversationInfo 对象可能缺少 knowledge_list 属性。") - # knowledge_info_str += "- 获取知识列表时出错。\n" + # logger.warning(f"[私聊][{self.private_name}]ConversationInfo 对象可能缺少 knowledge_list 属性。") + # knowledge_info_str += "- 获取知识列表时出错。\n" # except Exception as e: - # logger.error(f"[私聊][{self.private_name}]构建知识信息字符串时出错: {e}") - # knowledge_info_str += "- 处理知识列表时出错。\n" + # logger.error(f"[私聊][{self.private_name}]构建知识信息字符串时出错: {e}") + # knowledge_info_str += "- 处理知识列表时出错。\n" # 获取聊天历史记录 (chat_history_text) chat_history_text = observation_info.chat_history_str @@ -378,8 +391,10 @@ class ReplyGenerator: goals_str=goals_str, chat_history_text=chat_history_text, # knowledge_info_str=knowledge_info_str, - retrieved_memory_str=retrieved_memory_str if retrieved_memory_str else "无相关记忆。", # 如果为空则提示无 - retrieved_knowledge_str=retrieved_knowledge_str if retrieved_knowledge_str else "无相关知识。" # 如果为空则提示无 + retrieved_memory_str=retrieved_memory_str if retrieved_memory_str else "无相关记忆。", # 如果为空则提示无 + retrieved_knowledge_str=retrieved_knowledge_str + if retrieved_knowledge_str + else "无相关知识。", # 如果为空则提示无 ) # --- 调用 LLM 生成 --- @@ -403,6 +418,7 @@ class ReplyGenerator: """ return await self.reply_checker.check(reply, goal, chat_history, chat_history_str, retry_count) + def get_info_from_db( query_embedding: list, limit: int = 1, threshold: float = 0.5, return_raw: bool = False ) -> Union[str, list]: @@ -454,7 +470,13 @@ def get_info_from_db( } }, # 防止除以零错误,添加一个小的 epsilon - {"$addFields": {"similarity": {"$divide": ["$dotProduct", {"$max": [{"$multiply": ["$magnitude1", "$magnitude2"]}, 1e-9]}]}}}, + { + "$addFields": { + "similarity": { + "$divide": ["$dotProduct", {"$max": [{"$multiply": ["$magnitude1", "$magnitude2"]}, 1e-9]}] + } + } + }, { "$match": { "similarity": {"$gte": threshold} # 只保留相似度大于等于阈值的结果