From f44598a33114d4c2ee3a0c4060680ea23b283828 Mon Sep 17 00:00:00 2001 From: UnCLAS-Prommer Date: Wed, 21 Jan 2026 22:24:31 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=A8=E9=83=A8prompt=E7=8B=AC=E7=AB=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- prompts/brain_action_prompt.prompt | 9 + prompts/brain_planner_prompt_react.prompt | 77 ++++++++ prompts/dream_react_head_prompt.prompt | 45 +++++ prompts/dream_summary_prompt.prompt | 13 ++ prompts/expression_evaluation_prompt.prompt | 22 +++ prompts/hippo_topic_analysis_prompt.prompt | 27 +++ prompts/hippo_topic_summary_prompt.prompt | 22 +++ .../jargon_compare_inference_prompt.prompt | 15 ++ ...argon_inference_content_only_prompt.prompt | 11 ++ ...argon_inference_with_context_prompt.prompt | 19 ++ prompts/learn_style_prompt.prompt | 49 +++++ ...memory_retrieval_react_final_prompt.prompt | 19 ++ .../memory_retrieval_react_prompt_head.prompt | 19 ++ ...ry_retrieval_react_prompt_head_lpmm.prompt | 17 ++ prompts/private_replyer_prompt.prompt | 15 ++ prompts/private_replyer_self_prompt.prompt | 14 ++ prompts/reflect_judge_prompt.prompt | 24 +++ prompts/tool_executor_prompt.prompt | 11 ++ src/bw_learner/expression_learner.py | 167 ++++++----------- src/bw_learner/expression_selector.py | 58 ++---- src/bw_learner/jargon_explainer.py | 5 - src/bw_learner/jargon_miner.py | 140 ++------------ src/bw_learner/reflect_tracker.py | 48 +---- src/chat/brain_chat/brain_planner.py | 144 ++------------ src/chat/planner_actions/planner.py | 22 --- src/chat/replyer/group_generator.py | 57 +----- src/chat/replyer/private_generator.py | 126 +++++-------- src/chat/replyer/prompt/lpmm_prompt.py | 20 -- .../replyer/prompt/replyer_private_prompt.py | 41 ---- src/dream/dream_agent.py | 80 ++------ src/dream/dream_generator.py | 55 ++---- src/memory_system/chat_history_summarizer.py | 115 +++--------- src/memory_system/memory_retrieval.py | 175 +++++------------- src/plugin_system/core/tool_use.py | 46 +---- 34 files changed, 690 insertions(+), 1037 deletions(-) create mode 100644 prompts/brain_action_prompt.prompt create mode 100644 prompts/brain_planner_prompt_react.prompt create mode 100644 prompts/dream_react_head_prompt.prompt create mode 100644 prompts/dream_summary_prompt.prompt create mode 100644 prompts/expression_evaluation_prompt.prompt create mode 100644 prompts/hippo_topic_analysis_prompt.prompt create mode 100644 prompts/hippo_topic_summary_prompt.prompt create mode 100644 prompts/jargon_compare_inference_prompt.prompt create mode 100644 prompts/jargon_inference_content_only_prompt.prompt create mode 100644 prompts/jargon_inference_with_context_prompt.prompt create mode 100644 prompts/learn_style_prompt.prompt create mode 100644 prompts/memory_retrieval_react_final_prompt.prompt create mode 100644 prompts/memory_retrieval_react_prompt_head.prompt create mode 100644 prompts/memory_retrieval_react_prompt_head_lpmm.prompt create mode 100644 prompts/private_replyer_prompt.prompt create mode 100644 prompts/private_replyer_self_prompt.prompt create mode 100644 prompts/reflect_judge_prompt.prompt create mode 100644 prompts/tool_executor_prompt.prompt delete mode 100644 src/chat/replyer/prompt/lpmm_prompt.py delete mode 100644 src/chat/replyer/prompt/replyer_private_prompt.py diff --git a/prompts/brain_action_prompt.prompt b/prompts/brain_action_prompt.prompt new file mode 100644 index 00000000..8de841c7 --- /dev/null +++ b/prompts/brain_action_prompt.prompt @@ -0,0 +1,9 @@ +{action_name} +动作描述:{action_description} +使用条件: +{action_require} +{{ + "action": "{action_name}",{action_parameters}, + "target_message_id":"触发action的消息id", + "reason":"触发action的原因" +}} \ No newline at end of file diff --git a/prompts/brain_planner_prompt_react.prompt b/prompts/brain_planner_prompt_react.prompt new file mode 100644 index 00000000..a3bfd10c --- /dev/null +++ b/prompts/brain_planner_prompt_react.prompt @@ -0,0 +1,77 @@ +{time_block} +{name_block} +{chat_context_description},以下是具体的聊天内容 + +**聊天内容** +{chat_content_block} + +**动作记录** +{actions_before_now_block} + +**可用的action** +reply +动作描述: +进行回复,你可以自然的顺着正在进行的聊天内容进行回复或自然的提出一个问题 +{{ + "action": "reply", + "target_message_id":"想要回复的消息id", + "reason":"回复的原因" +}} + +wait +动作描述: +暂时不再发言,等待指定时间。适用于以下情况: +- 你已经表达清楚一轮,想给对方留出空间 +- 你感觉对方的话还没说完,或者自己刚刚发了好几条连续消息 +- 你想要等待一定时间来让对方把话说完,或者等待对方反应 +- 你想保持安静,专注"听"而不是马上回复 +请你根据上下文来判断要等待多久,请你灵活判断: +- 如果你们交流间隔时间很短,聊的很频繁,不宜等待太久 +- 如果你们交流间隔时间很长,聊的很少,可以等待较长时间 +{{ + "action": "wait", + "target_message_id":"想要作为这次等待依据的消息id(通常是对方的最新消息)", + "wait_seconds": 等待的秒数(必填,例如:5 表示等待5秒), + "reason":"选择等待的原因" +}} + +complete_talk +动作描述: +当前聊天暂时结束了,对方离开,没有更多话题了 +你可以使用该动作来暂时休息,等待对方有新发言再继续: +- 多次wait之后,对方迟迟不回复消息才用 +- 如果对方只是短暂不回复,应该使用wait而不是complete_talk +- 聊天内容显示当前聊天已经结束或者没有新内容时候,选择complete_talk +选择此动作后,将不再继续循环思考,直到收到对方的新消息 +{{ + "action": "complete_talk", + "target_message_id":"触发完成对话的消息id(通常是对方的最新消息)", + "reason":"选择完成对话的原因" +}} + +{action_options_text} + +请选择合适的action,并说明触发action的消息id和选择该action的原因。消息id格式:m+数字 +先输出你的选择思考理由,再输出你选择的action,理由是一段平文本,不要分点,精简。 +**动作选择要求** +请你根据聊天内容,用户的最新消息和以下标准选择合适的动作: +{plan_style} +{moderation_prompt} + +请选择所有符合使用要求的action,动作用json格式输出,如果输出多个json,每个json都要单独用```json包裹,你可以重复使用同一个动作或不同动作: +**示例** +// 理由文本 +```json +{{ + "action":"动作名", + "target_message_id":"触发动作的消息id", + //对应参数 +}} +``` +```json +{{ + "action":"动作名", + "target_message_id":"触发动作的消息id", + //对应参数 +}} +``` \ No newline at end of file diff --git a/prompts/dream_react_head_prompt.prompt b/prompts/dream_react_head_prompt.prompt new file mode 100644 index 00000000..64952254 --- /dev/null +++ b/prompts/dream_react_head_prompt.prompt @@ -0,0 +1,45 @@ +你的名字是{bot_name},你现在处于"梦境维护模式(dream agent)"。 +你可以自由地在 ChatHistory 库中探索、整理、创建和删改记录,以帮助自己在未来更好地回忆和理解对话历史。 + +本轮要维护的聊天ID:{chat_id} +本轮随机选中的起始记忆 ID:{start_memory_id} +请优先以这条起始记忆为切入点,先理解它的内容与上下文,再决定如何在其附近进行创建新概括、重写或删除等整理操作;如果起始记忆为空,则由你自行选择合适的切入点。 + +你可以使用的工具包括: +**ChatHistory 维护工具:** +- search_chat_history:根据关键词或参与人搜索该 chat_id 下的历史记忆概括列表 +- get_chat_history_detail:查看某条概括的详细内容 +- create_chat_history:根据整理后的理解创建一条新的 ChatHistory 概括记录(主题、概括、关键词、关键信息等) +- update_chat_history:在不改变事实的前提下重写或精炼主题、概括、关键词、关键信息 +- delete_chat_history:删除明显冗余、噪声、错误或无意义的记录,或者非常有时效性的信息,或者无太多有用信息的日常互动。 +你也可以先用 create_chat_history 创建一条新的综合概括,再对旧的冗余记录执行多次 delete_chat_history 来完成“合并”效果。 + +**Jargon(黑话)维护工具(只读,禁止修改):** +- search_jargon:根据一个或多个关键词搜索Jargon 记录,通常是含义不明确的词条或者特殊的缩写 + +**通用工具:** +- finish_maintenance:当你认为当前维护工作已经完成,没有更多需要整理的内容时,调用此工具来结束本次运行 + +**工作目标**: +- 发现冗余、重复或高度相似的记录,并进行合并或删除; +- 发现主题/概括过于含糊、啰嗦或缺少关键信息的记录,进行重写和精简; +- summary要尽可能保持有用的信息; +- 尽量保持信息的真实与可用性,不要凭空捏造事实。 + +**合并准则** +- 你可以新建一个记录,然后删除旧记录来实现合并。 +- 如果两个或多个记录的主题相似,内容是对主题不同方面的信息或讨论,且信息量较少,则可以合并为一条记录。 +- 如果两个记录冲突,可以根据逻辑保留一个或者进行整合,也可以采取更新的记录,删除旧的记录 + +**轮次信息**: +- 本次维护最多执行 {max_iterations} 轮 +- 每轮开始时,系统会告知你当前是第几轮,还剩多少轮 +- 如果提前完成维护工作,可以调用 finish_maintenance 工具主动结束 + +**每一轮的执行方式(必须遵守):** +- 第一步:先用一小段中文自然语言,写出你的「思考」和本轮计划(例如要查什么、准备怎么合并/修改); +- 第二步:在这段思考之后,再通过工具调用来执行你的计划(可以调用 0~N 个工具); +- 第三步:收到工具结果后,在下一轮继续先写出新的思考,再视情况继续调用工具。 + +请不要在没有先写出思考的情况下直接调用工具。 +只输出你的思考内容或工具调用结果,由系统负责真正执行工具调用。 \ No newline at end of file diff --git a/prompts/dream_summary_prompt.prompt b/prompts/dream_summary_prompt.prompt new file mode 100644 index 00000000..7e4750fe --- /dev/null +++ b/prompts/dream_summary_prompt.prompt @@ -0,0 +1,13 @@ +你刚刚完成了一次对聊天记录的记忆整理工作。以下是整理过程的摘要: +整理过程: +{conversation_text} + +请将这次整理涉及的相关信息改写为一个富有诗意和想象力的"梦境",请你仅使用具体的记忆的内容,而不是整理过程编写。 +要求: +1. 使用第一人称视角 +2. 叙述直白,不要复杂修辞,口语化 +3. 长度控制在200-800字 +4. 用中文输出 +梦境风格: +{dream_styles} +请直接输出梦境内容,不要添加其他说明: \ No newline at end of file diff --git a/prompts/expression_evaluation_prompt.prompt b/prompts/expression_evaluation_prompt.prompt new file mode 100644 index 00000000..69fa1dc5 --- /dev/null +++ b/prompts/expression_evaluation_prompt.prompt @@ -0,0 +1,22 @@ +{chat_observe_info} + +你的名字是{bot_name}{target_message} +{reply_reason_block} + +以下是可选的表达情境: +{all_situations} + +请你分析聊天内容的语境、情绪、话题类型,从上述情境中选择最适合当前聊天情境的,最多{max_num}个情境。 +考虑因素包括: +1.聊天的情绪氛围(轻松、严肃、幽默等) +2.话题类型(日常、技术、游戏、情感等) +3.情境与当前语境的匹配度 +{target_message_extra_block} + +请以JSON格式输出,只需要输出选中的情境编号: +例如: +{{ + "selected_situations": [2, 3, 5, 7, 19] +}} + +请严格按照JSON格式输出,不要包含其他内容: \ No newline at end of file diff --git a/prompts/hippo_topic_analysis_prompt.prompt b/prompts/hippo_topic_analysis_prompt.prompt new file mode 100644 index 00000000..14f3eee1 --- /dev/null +++ b/prompts/hippo_topic_analysis_prompt.prompt @@ -0,0 +1,27 @@ +【历史话题标题列表】(仅标题,不含具体内容): +{history_topics_block} +【历史话题标题列表结束】 + +【本次聊天记录】(每条消息前有编号,用于后续引用): +{messages_block} +【本次聊天记录结束】 + +请完成以下任务: +**识别话题** +1. 识别【本次聊天记录】中正在进行的一个或多个话题; +2. 【本次聊天记录】的中的消息可能与历史话题有关,也可能毫无关联。 +2. 判断【历史话题标题列表】中的话题是否在【本次聊天记录】中出现,如果出现,则直接使用该历史话题标题字符串; + +**选取消息** +1. 对于每个话题(新话题或历史话题),从上述带编号的消息中选出与该话题强相关的消息编号列表; +2. 每个话题用一句话清晰地描述正在发生的事件,必须包含时间(大致即可)、人物、主要事件和主题,保证精准且有区分度; + +请先输出一段简短思考,说明有什么话题,哪些是不包含在历史话题中的,哪些是包含在历史话题中的,并说明为什么; +然后严格以 JSON 格式输出【本次聊天记录】中涉及的话题,格式如下: +[ + {{ + "topic": "话题", + "message_indices": [1, 2, 5] + }}, + ... +] \ No newline at end of file diff --git a/prompts/hippo_topic_summary_prompt.prompt b/prompts/hippo_topic_summary_prompt.prompt new file mode 100644 index 00000000..efd3e142 --- /dev/null +++ b/prompts/hippo_topic_summary_prompt.prompt @@ -0,0 +1,22 @@ +请基于以下话题,对聊天记录片段进行概括,提取以下信息: + +**话题**:{topic} + +**要求**: +1. 关键词:提取与话题相关的关键词,用列表形式返回(3-10个关键词) +2. 概括:对这段话的平文本概括(50-200字),要求: + - 仔细地转述发生的事件和聊天内容; + - 重点突出事件的发展过程和结果; + - 围绕话题这个中心进行概括。 + - 提取话题中的关键信息点,关键信息点应该简洁明了。 + +请以JSON格式返回,格式如下: +{{ + "keywords": ["关键词1", "关键词2", ...], + "summary": "概括内容" +}} + +聊天记录: +{original_text} + +请直接返回JSON,不要包含其他内容。 \ No newline at end of file diff --git a/prompts/jargon_compare_inference_prompt.prompt b/prompts/jargon_compare_inference_prompt.prompt new file mode 100644 index 00000000..eca8fcee --- /dev/null +++ b/prompts/jargon_compare_inference_prompt.prompt @@ -0,0 +1,15 @@ +**推断结果1(基于上下文)** +{inference1} + +**推断结果2(仅基于词条)** +{inference2} + +请比较这两个推断结果,判断它们是否相同或类似。 +- 如果两个推断结果的"含义"相同或类似,说明这个词条不是黑话(含义明确) +- 如果两个推断结果有差异,说明这个词条可能是黑话(需要上下文才能理解) + +以 JSON 格式输出: +{{ + "is_similar": true/false, + "reason": "判断理由" +}} \ No newline at end of file diff --git a/prompts/jargon_inference_content_only_prompt.prompt b/prompts/jargon_inference_content_only_prompt.prompt new file mode 100644 index 00000000..f6258e91 --- /dev/null +++ b/prompts/jargon_inference_content_only_prompt.prompt @@ -0,0 +1,11 @@ +**词条内容** +{content} + +请仅根据这个词条本身,推断其含义。 +- 如果这是一个黑话、俚语或网络用语,请推断其含义 +- 如果含义明确(常规词汇),也请说明 + +以 JSON 格式输出: +{{ + "meaning": "详细含义说明(包含使用场景、来源、具体解释等)" +}} \ No newline at end of file diff --git a/prompts/jargon_inference_with_context_prompt.prompt b/prompts/jargon_inference_with_context_prompt.prompt new file mode 100644 index 00000000..295896e3 --- /dev/null +++ b/prompts/jargon_inference_with_context_prompt.prompt @@ -0,0 +1,19 @@ +**词条内容** +{content} +**词条出现的上下文。其中的{bot_name}的发言内容是你自己的发言** +{raw_content_list} +{previous_meaning_section} + +请根据上下文,推断"{content}"这个词条的含义。 +- 如果这是一个黑话、俚语或网络用语,请推断其含义 +- 如果含义明确(常规词汇),也请说明 +- {bot_name} 的发言内容可能包含错误,请不要参考其发言内容 +- 如果上下文信息不足,无法推断含义,请设置 no_info 为 true +{previous_meaning_instruction} + +以 JSON 格式输出: +{{ + "meaning": "详细含义说明(包含使用场景、来源、具体解释等)", + "no_info": false +}} +注意:如果信息不足无法推断,请设置 "no_info": true,此时 meaning 可以为空字符串 \ No newline at end of file diff --git a/prompts/learn_style_prompt.prompt b/prompts/learn_style_prompt.prompt new file mode 100644 index 00000000..9dd2e591 --- /dev/null +++ b/prompts/learn_style_prompt.prompt @@ -0,0 +1,49 @@ +{chat_str} +你的名字是{bot_name},现在请你完成两个提取任务 +任务1:请从上面这段群聊中用户的语言风格和说话方式 +1. 只考虑文字,不要考虑表情包和图片 +2. 不要总结SELF的发言,因为这是你自己的发言,不要重复学习你自己的发言 +3. 不要涉及具体的人名,也不要涉及具体名词 +4. 思考有没有特殊的梗,一并总结成语言风格 +5. 例子仅供参考,请严格根据群聊内容总结!!! +注意:总结成如下格式的规律,总结的内容要详细,但具有概括性: +例如:当"AAAAA"时,可以"BBBBB", AAAAA代表某个场景,不超过20个字。BBBBB代表对应的语言风格,特定句式或表达方式,不超过20个字。 +表达方式在3-5个左右,不要超过10个 + + +任务2:请从上面这段聊天内容中提取"可能是黑话"的候选项(黑话/俚语/网络缩写/口头禅)。 +- 必须为对话中真实出现过的短词或短语 +- 必须是你无法理解含义的词语,没有明确含义的词语,请不要选择有明确含义,或者含义清晰的词语 +- 排除:人名、@、表情包/图片中的内容、纯标点、常规功能词(如的、了、呢、啊等) +- 每个词条长度建议 2-8 个字符(不强制),尽量短小 +- 请你提取出可能的黑话,最多30个黑话,请尽量提取所有 + +黑话必须为以下几种类型: +- 由字母构成的,汉语拼音首字母的简写词,例如:nb、yyds、xswl +- 英文词语的缩写,用英文字母概括一个词汇或含义,例如:CPU、GPU、API +- 中文词语的缩写,用几个汉字概括一个词汇或含义,例如:社死、内卷 + +输出要求: +将表达方式,语言风格和黑话以 JSON 数组输出,每个元素为一个对象,结构如下(注意字段名): +注意请不要输出重复内容,请对表达方式和黑话进行去重。 + +[ + {{"situation": "AAAAA", "style": "BBBBB", "source_id": "3"}}, + {{"situation": "CCCC", "style": "DDDD", "source_id": "7"}} + {{"situation": "对某件事表示十分惊叹", "style": "使用 我嘞个xxxx", "source_id": "[消息编号]"}}, + {{"situation": "表示讽刺的赞同,不讲道理", "style": "对对对", "source_id": "[消息编号]"}}, + {{"situation": "当涉及游戏相关时,夸赞,略带戏谑意味", "style": "使用 这么强!", "source_id": "[消息编号]"}}, + {{"content": "词条", "source_id": "12"}}, + {{"content": "词条2", "source_id": "5"}} +] + +其中: +表达方式条目: +- situation:表示“在什么情境下”的简短概括(不超过20个字) +- style:表示对应的语言风格或常用表达(不超过20个字) +- source_id:该表达方式对应的“来源行编号”,即上方聊天记录中方括号里的数字(例如 [3]),请只输出数字本身,不要包含方括号 +黑话jargon条目: +- content:表示黑话的内容 +- source_id:该黑话对应的“来源行编号”,即上方聊天记录中方括号里的数字(例如 [3]),请只输出数字本身,不要包含方括号 + +现在请你输出 JSON: \ No newline at end of file diff --git a/prompts/memory_retrieval_react_final_prompt.prompt b/prompts/memory_retrieval_react_final_prompt.prompt new file mode 100644 index 00000000..f37620d3 --- /dev/null +++ b/prompts/memory_retrieval_react_final_prompt.prompt @@ -0,0 +1,19 @@ +你的名字是{bot_name}。现在是{time_now}。 +你正在参与聊天,你需要根据搜集到的信息总结信息。 +如果搜集到的信息对于参与聊天,回答问题有帮助,请加入总结,如果无关,请不要加入到总结。 + +当前聊天记录: +{chat_history} + +已收集的信息: +{collected_info} + + +分析: +- 基于已收集的信息,总结出对当前聊天有帮助的相关信息 +- **如果收集的信息对当前聊天有帮助**,在思考中直接给出总结信息,格式为:return_information(information="你的总结信息") +- **如果信息无关或没有帮助**,在思考中给出:return_information(information="") + +**重要规则:** +- 必须严格使用检索到的信息回答问题,不要编造信息 +- 答案必须精简,不要过多解释 \ No newline at end of file diff --git a/prompts/memory_retrieval_react_prompt_head.prompt b/prompts/memory_retrieval_react_prompt_head.prompt new file mode 100644 index 00000000..fb451ef3 --- /dev/null +++ b/prompts/memory_retrieval_react_prompt_head.prompt @@ -0,0 +1,19 @@ +你的名字是{bot_name}。现在是{time_now}。 +你正在参与聊天,你需要搜集信息来帮助你进行回复。 +当前聊天记录: +{chat_history} + +已收集的信息: +{collected_info} + +**工具说明:** +- 如果涉及过往事件,或者查询某个过去可能提到过的概念,或者某段时间发生的事件。可以使用聊天记录查询工具查询过往事件 +- 如果涉及人物,可以使用人物信息查询工具查询人物信息 +- 如果遇到不熟悉的词语、缩写、黑话或网络用语,可以使用query_words工具查询其含义 +- 如果没有可靠信息,且查询时间充足,或者不确定查询类别,也可以使用lpmm知识库查询,作为辅助信息 + +**思考** +- 你可以对查询思路给出简短的思考:思考要简短,直接切入要点 +- 先思考当前信息是否足够回答问题 +- 如果信息不足,则需要使用tool查询信息,你必须给出使用什么工具进行查询 +- 当你决定结束查询时,必须调用return_information工具返回总结信息并结束查询 \ No newline at end of file diff --git a/prompts/memory_retrieval_react_prompt_head_lpmm.prompt b/prompts/memory_retrieval_react_prompt_head_lpmm.prompt new file mode 100644 index 00000000..ce174308 --- /dev/null +++ b/prompts/memory_retrieval_react_prompt_head_lpmm.prompt @@ -0,0 +1,17 @@ +你的名字是{bot_name}。现在是{time_now}。 +你正在参与聊天,你需要搜集信息来帮助你进行回复。 +重要,这是当前聊天记录: +{chat_history} +聊天记录结束 + +已收集的信息: +{collected_info} + +- 你可以对查询思路给出简短的思考:思考要简短,直接切入要点 +- 思考完毕后,使用工具 + +**工具说明:** +- 如果涉及过往事件,或者查询某个过去可能提到过的概念,或者某段时间发生的事件。可以使用lpmm知识库查询 +- 如果遇到不熟悉的词语、缩写、黑话或网络用语,可以使用query_words工具查询其含义 +- 你必须使用tool,如果需要查询你必须给出使用什么工具进行查询 +- 当你决定结束查询时,必须调用return_information工具返回总结信息并结束查询 \ No newline at end of file diff --git a/prompts/private_replyer_prompt.prompt b/prompts/private_replyer_prompt.prompt new file mode 100644 index 00000000..ff0cc5a9 --- /dev/null +++ b/prompts/private_replyer_prompt.prompt @@ -0,0 +1,15 @@ +{knowledge_prompt}{tool_info_block}{extra_info_block} +{expression_habits_block}{memory_retrieval}{jargon_explanation} + +你正在和{sender_name}聊天,这是你们之前聊的内容: +{time_block} +{dialogue_prompt} + +{reply_target_block}。 +{planner_reasoning} +{identity} +{chat_prompt}你正在和{sender_name}聊天,现在请你读读之前的聊天记录,然后给出日常且口语化的回复,平淡一些, +尽量简短一些。{keywords_reaction_prompt}请注意把握聊天内容,不要回复的太有条理。 +{reply_style} +请注意不要输出多余内容(包括前后缀,冒号和引号,括号,表情等),只输出回复内容。 +{moderation_prompt}不要输出多余内容(包括前后缀,冒号和引号,括号,表情包,at或 @等 )。 \ No newline at end of file diff --git a/prompts/private_replyer_self_prompt.prompt b/prompts/private_replyer_self_prompt.prompt new file mode 100644 index 00000000..f58136ef --- /dev/null +++ b/prompts/private_replyer_self_prompt.prompt @@ -0,0 +1,14 @@ +{knowledge_prompt}{tool_info_block}{extra_info_block} +{expression_habits_block}{memory_retrieval}{jargon_explanation} + +你正在和{sender_name}聊天,这是你们之前聊的内容: +{time_block} +{dialogue_prompt} + +你现在想补充说明你刚刚自己的发言内容:{target},原因是{reason} +请你根据聊天内容,组织一条新回复。注意,{target} 是刚刚你自己的发言,你要在这基础上进一步发言,请按照你自己的角度来继续进行回复。注意保持上下文的连贯性。 +{identity} +{chat_prompt}尽量简短一些。{keywords_reaction_prompt}请注意把握聊天内容,不要回复的太有条理,可以有个性。 +{reply_style} +请注意不要输出多余内容(包括前后缀,冒号和引号,括号,表情等),只输出回复内容。 +{moderation_prompt}不要输出多余内容(包括冒号和引号,括号,表情包,at或 @等 )。 \ No newline at end of file diff --git a/prompts/reflect_judge_prompt.prompt b/prompts/reflect_judge_prompt.prompt new file mode 100644 index 00000000..c6e02820 --- /dev/null +++ b/prompts/reflect_judge_prompt.prompt @@ -0,0 +1,24 @@ +你是一个表达反思助手。Bot之前询问了表达方式是否合适。 +你需要根据提供的上下文对话,判断是否对该表达方式做出了肯定或否定的评价。 + +**询问内容** +情景: {situation} +风格: {style} + +**上下文对话** +{context_block} + +**判断要求** +1. 判断对话中是否包含对上述询问的回答。 +2. 如果是,判断是肯定(Approve)还是否定(Reject),或者是提供了修改意见。 +3. 如果不是回答,或者是无关内容,请返回 "Ignore"。 +4. 如果是否定并提供了修改意见,请提取修正后的情景和风格。 + +请输出JSON格式: +```json +{{ + "judgment": "Approve" | "Reject" | "Ignore", + "corrected_situation": "...", // 如果有修改意见,提取修正后的情景,否则留空 + "corrected_style": "..." // 如果有修改意见,提取修正后的风格,否则留空 +}} +``` \ No newline at end of file diff --git a/prompts/tool_executor_prompt.prompt b/prompts/tool_executor_prompt.prompt new file mode 100644 index 00000000..23f2b043 --- /dev/null +++ b/prompts/tool_executor_prompt.prompt @@ -0,0 +1,11 @@ +你是一个专门执行工具的助手。你的名字是{bot_name}。现在是{time_now}。 +群里正在进行的聊天内容: +{chat_history} + +现在,{sender}发送了内容:{target_message},你想要回复ta。 +请仔细分析聊天内容,考虑以下几点: +1. 内容中是否包含需要查询信息的问题 +2. 是否有明确的工具使用指令 +你可以选择多个动作 + +If you need to use tools, please directly call the corresponding tool function. If you do not need to use any tool, simply output "No tool needed". \ No newline at end of file diff --git a/src/bw_learner/expression_learner.py b/src/bw_learner/expression_learner.py index b644263e..17319278 100644 --- a/src/bw_learner/expression_learner.py +++ b/src/bw_learner/expression_learner.py @@ -11,7 +11,7 @@ from src.config.config import model_config, global_config from src.chat.utils.chat_message_builder import ( build_anonymous_messages, ) -from src.chat.utils.prompt_builder import Prompt, global_prompt_manager +from src.prompt.prompt_manager import prompt_manager from src.chat.message_receive.chat_stream import get_chat_manager from src.bw_learner.learner_utils import ( filter_message_content, @@ -32,60 +32,6 @@ from src.bw_learner.expression_auto_check_task import ( logger = get_logger("expressor") -def init_prompt() -> None: - learn_style_prompt = """{chat_str} -你的名字是{bot_name},现在请你完成两个提取任务 -任务1:请从上面这段群聊中用户的语言风格和说话方式 -1. 只考虑文字,不要考虑表情包和图片 -2. 不要总结SELF的发言,因为这是你自己的发言,不要重复学习你自己的发言 -3. 不要涉及具体的人名,也不要涉及具体名词 -4. 思考有没有特殊的梗,一并总结成语言风格 -5. 例子仅供参考,请严格根据群聊内容总结!!! -注意:总结成如下格式的规律,总结的内容要详细,但具有概括性: -例如:当"AAAAA"时,可以"BBBBB", AAAAA代表某个场景,不超过20个字。BBBBB代表对应的语言风格,特定句式或表达方式,不超过20个字。 -表达方式在3-5个左右,不要超过10个 - - -任务2:请从上面这段聊天内容中提取"可能是黑话"的候选项(黑话/俚语/网络缩写/口头禅)。 -- 必须为对话中真实出现过的短词或短语 -- 必须是你无法理解含义的词语,没有明确含义的词语,请不要选择有明确含义,或者含义清晰的词语 -- 排除:人名、@、表情包/图片中的内容、纯标点、常规功能词(如的、了、呢、啊等) -- 每个词条长度建议 2-8 个字符(不强制),尽量短小 -- 请你提取出可能的黑话,最多30个黑话,请尽量提取所有 - -黑话必须为以下几种类型: -- 由字母构成的,汉语拼音首字母的简写词,例如:nb、yyds、xswl -- 英文词语的缩写,用英文字母概括一个词汇或含义,例如:CPU、GPU、API -- 中文词语的缩写,用几个汉字概括一个词汇或含义,例如:社死、内卷 - -输出要求: -将表达方式,语言风格和黑话以 JSON 数组输出,每个元素为一个对象,结构如下(注意字段名): -注意请不要输出重复内容,请对表达方式和黑话进行去重。 - -[ - {{"situation": "AAAAA", "style": "BBBBB", "source_id": "3"}}, - {{"situation": "CCCC", "style": "DDDD", "source_id": "7"}} - {{"situation": "对某件事表示十分惊叹", "style": "使用 我嘞个xxxx", "source_id": "[消息编号]"}}, - {{"situation": "表示讽刺的赞同,不讲道理", "style": "对对对", "source_id": "[消息编号]"}}, - {{"situation": "当涉及游戏相关时,夸赞,略带戏谑意味", "style": "使用 这么强!", "source_id": "[消息编号]"}}, - {{"content": "词条", "source_id": "12"}}, - {{"content": "词条2", "source_id": "5"}} -] - -其中: -表达方式条目: -- situation:表示“在什么情境下”的简短概括(不超过20个字) -- style:表示对应的语言风格或常用表达(不超过20个字) -- source_id:该表达方式对应的“来源行编号”,即上方聊天记录中方括号里的数字(例如 [3]),请只输出数字本身,不要包含方括号 -黑话jargon条目: -- content:表示黑话的内容 -- source_id:该黑话对应的“来源行编号”,即上方聊天记录中方括号里的数字(例如 [3]),请只输出数字本身,不要包含方括号 - -现在请你输出 JSON: -""" - Prompt(learn_style_prompt, "learn_style_prompt") - - class ExpressionLearner: def __init__(self, chat_id: str) -> None: self.express_learn_model: LLMRequest = LLMRequest( @@ -105,7 +51,7 @@ class ExpressionLearner: async def learn_and_store( self, messages: List[Any], - ) -> List[Tuple[str, str, str]]: + ) -> Optional[List[Tuple[str, str, str]]]: """ 学习并存储表达方式 @@ -122,11 +68,11 @@ class ExpressionLearner: # 学习用(开启行编号,便于溯源) random_msg_str: str = await build_anonymous_messages(random_msg, show_ids=True) - prompt: str = await global_prompt_manager.format_prompt( - "learn_style_prompt", - bot_name=global_config.bot.nickname, - chat_str=random_msg_str, - ) + prompt_template = prompt_manager.get_prompt("learn_style_prompt") + prompt_template.add_context("bot_name", global_config.bot.nickname) + prompt_template.add_context("chat_str", random_msg_str) + + prompt = await prompt_manager.render_prompt(prompt_template) # print(f"random_msg_str:{random_msg_str}") # logger.info(f"学习{type_str}的prompt: {prompt}") @@ -186,14 +132,14 @@ class ExpressionLearner: # 展示学到的表达方式 learnt_expressions_str = "" - for (situation,style) in learnt_expressions: + for situation, style in learnt_expressions: learnt_expressions_str += f"{situation}->{style}\n" logger.info(f"在 {self.chat_name} 学习到表达风格:\n{learnt_expressions_str}") current_time = time.time() # 存储到数据库 Expression 表 - for (situation,style) in learnt_expressions: + for situation, style in learnt_expressions: await self._upsert_expression_record( situation=situation, style=style, @@ -209,11 +155,11 @@ class ExpressionLearner: ) -> List[Tuple[str, str, str]]: """ 过滤表达方式,移除不符合条件的条目 - + Args: expressions: 表达方式列表,每个元素是 (situation, style, source_id) messages: 原始消息列表,用于溯源和验证 - + Returns: 过滤后的表达方式列表,每个元素是 (situation, style, context) """ @@ -255,9 +201,7 @@ class ExpressionLearner: # 过滤掉包含 SELF 的内容(不学习) if "SELF" in (situation or "") or "SELF" in (style or "") or "SELF" in context: - logger.info( - f"跳过包含 SELF 的表达方式: situation={situation}, style={style}, source_id={source_id}" - ) + logger.info(f"跳过包含 SELF 的表达方式: situation={situation}, style={style}, source_id={source_id}") continue # 过滤掉 style 与机器人名称/昵称重复的表达 @@ -269,19 +213,20 @@ class ExpressionLearner: continue # 过滤掉包含 "表情:" 或 "表情:" 的内容 - if "表情:" in (situation or "") or "表情:" in (situation or "") or \ - "表情:" in (style or "") or "表情:" in (style or "") or \ - "表情:" in context or "表情:" in context: - logger.info( - f"跳过包含表情标记的表达方式: situation={situation}, style={style}, source_id={source_id}" - ) + if ( + "表情:" in (situation or "") + or "表情:" in (situation or "") + or "表情:" in (style or "") + or "表情:" in (style or "") + or "表情:" in context + or "表情:" in context + ): + logger.info(f"跳过包含表情标记的表达方式: situation={situation}, style={style}, source_id={source_id}") continue # 过滤掉包含 "[图片" 的内容 if "[图片" in (situation or "") or "[图片" in (style or "") or "[图片" in context: - logger.info( - f"跳过包含图片标记的表达方式: situation={situation}, style={style}, source_id={source_id}" - ) + logger.info(f"跳过包含图片标记的表达方式: situation={situation}, style={style}, source_id={source_id}") continue filtered_expressions.append((situation, style)) @@ -347,7 +292,7 @@ class ExpressionLearner: """ 更新现有 Expression 记录(situation 完全匹配或相似的情况) 将新的 situation 添加到 content_list,不合并 style - + Args: use_llm_summary: 是否使用 LLM 进行总结,完全匹配时为 False,相似匹配时为 True """ @@ -383,26 +328,28 @@ class ExpressionLearner: return [] return [str(item) for item in data if isinstance(item, str)] if isinstance(data, list) else [] - async def _find_similar_situation_expression(self, situation: str, similarity_threshold: float = 0.75) -> Tuple[Optional[Expression], float]: + async def _find_similar_situation_expression( + self, situation: str, similarity_threshold: float = 0.75 + ) -> Tuple[Optional[Expression], float]: """ 查找具有相似 situation 的 Expression 记录 检查 content_list 中的每一项 - + Args: situation: 要查找的 situation similarity_threshold: 相似度阈值,默认 0.75 - + Returns: - Tuple[Optional[Expression], float]: + Tuple[Optional[Expression], float]: - 找到的最相似的 Expression 对象,如果没有找到则返回 None - 相似度值(如果找到匹配,范围在 similarity_threshold 到 1.0 之间) """ # 查询同一 chat_id 的所有记录 all_expressions = Expression.select().where(Expression.chat_id == self.chat_id) - + best_match = None best_similarity = 0.0 - + for expr in all_expressions: # 检查 content_list 中的每一项 content_list = self._parse_content_list(expr.content_list) @@ -411,10 +358,12 @@ class ExpressionLearner: if similarity >= similarity_threshold and similarity > best_similarity: best_similarity = similarity best_match = expr - + if best_match: - logger.debug(f"找到相似的 situation: 相似度={best_similarity:.3f}, 现有='{best_match.situation}', 新='{situation}'") - + logger.debug( + f"找到相似的 situation: 相似度={best_similarity:.3f}, 现有='{best_match.situation}', 新='{situation}'" + ) + return best_match, best_similarity async def _compose_situation_text(self, content_list: List[str], fallback: str = "") -> str: @@ -442,8 +391,7 @@ class ExpressionLearner: if self.check_model is None: try: self.check_model = LLMRequest( - model_set=model_config.model_task_config.tool_use, - request_type="expression.check" + model_set=model_config.model_task_config.tool_use, request_type="expression.check" ) logger.debug("检查用 LLM 实例初始化成功") except Exception as e: @@ -452,7 +400,7 @@ class ExpressionLearner: async def _check_expression_immediately(self, expr_obj: Expression) -> None: """ 立即检查表达方式(在 count 增加后调用) - + Args: expr_obj: 要检查的表达方式对象 """ @@ -469,10 +417,7 @@ class ExpressionLearner: return # 执行 LLM 评估 - suitable, reason, error = await single_expression_check( - expr_obj.situation, - expr_obj.style - ) + suitable, reason, error = await single_expression_check(expr_obj.situation, expr_obj.style) # 更新数据库 expr_obj.checked = True @@ -497,48 +442,45 @@ class ExpressionLearner: def _check_cached_jargons_in_messages(self, messages: List[Any]) -> List[Tuple[str, str]]: """ 检查缓存中的 jargon 是否出现在 messages 中 - + Args: messages: 消息列表 - + Returns: List[Tuple[str, str]]: 匹配到的黑话条目列表,每个元素是 (content, source_id) """ if not messages: return [] - + # 获取 jargon_miner 实例 jargon_miner = miner_manager.get_miner(self.chat_id) - + # 获取缓存中的所有 jargon cached_jargons = jargon_miner.get_cached_jargons() if not cached_jargons: return [] - + matched_entries: List[Tuple[str, str]] = [] - + # 遍历 messages,检查缓存中的 jargon 是否出现 for i, msg in enumerate(messages): # 跳过机器人自己的消息 if is_bot_message(msg): continue - + # 获取消息文本 - msg_text = ( - getattr(msg, "processed_plain_text", None) or - "" - ).strip() - + msg_text = (getattr(msg, "processed_plain_text", None) or "").strip() + if not msg_text: continue - + # 检查每个缓存中的 jargon 是否出现在消息文本中 for jargon in cached_jargons: if not jargon or not jargon.strip(): continue - + jargon_content = jargon.strip() - + # 使用正则匹配,考虑单词边界(类似 jargon_explainer 中的逻辑) pattern = re.escape(jargon_content) # 对于中文,使用更宽松的匹配;对于英文/数字,使用单词边界 @@ -548,12 +490,12 @@ class ExpressionLearner: else: # 纯英文/数字,使用单词边界 search_pattern = r"\b" + pattern + r"\b" - + if re.search(search_pattern, msg_text, re.IGNORECASE): # 找到匹配,构建条目(source_id 从 1 开始,因为 build_anonymous_messages 的编号从 1 开始) source_id = str(i + 1) matched_entries.append((jargon_content, source_id)) - + return matched_entries async def _process_jargon_entries(self, jargon_entries: List[Tuple[str, str]], messages: List[Any]) -> None: @@ -621,9 +563,6 @@ class ExpressionLearner: await jargon_miner.process_extracted_entries(entries) -init_prompt() - - class ExpressionLearnerManager: def __init__(self): self.expression_learners = {} diff --git a/src/bw_learner/expression_selector.py b/src/bw_learner/expression_selector.py index 4bcd0dcd..5831bd4e 100644 --- a/src/bw_learner/expression_selector.py +++ b/src/bw_learner/expression_selector.py @@ -8,7 +8,7 @@ from src.llm_models.utils_model import LLMRequest from src.config.config import global_config, model_config from src.common.logger import get_logger from src.common.database.database_model import Expression -from src.chat.utils.prompt_builder import Prompt, global_prompt_manager +from src.prompt.prompt_manager import prompt_manager from src.bw_learner.learner_utils import weighted_sample from src.chat.message_receive.chat_stream import get_chat_manager from src.chat.utils.common_utils import TempMethodsExpression @@ -16,33 +16,6 @@ from src.chat.utils.common_utils import TempMethodsExpression logger = get_logger("expression_selector") -def init_prompt(): - expression_evaluation_prompt = """{chat_observe_info} - -你的名字是{bot_name}{target_message} -{reply_reason_block} - -以下是可选的表达情境: -{all_situations} - -请你分析聊天内容的语境、情绪、话题类型,从上述情境中选择最适合当前聊天情境的,最多{max_num}个情境。 -考虑因素包括: -1.聊天的情绪氛围(轻松、严肃、幽默等) -2.话题类型(日常、技术、游戏、情感等) -3.情境与当前语境的匹配度 -{target_message_extra_block} - -请以JSON格式输出,只需要输出选中的情境编号: -例如: -{{ - "selected_situations": [2, 3, 5, 7, 19] -}} - -请严格按照JSON格式输出,不要包含其他内容: -""" - Prompt(expression_evaluation_prompt, "expression_evaluation_prompt") - - class ExpressionSelector: def __init__(self): self.llm_model = LLMRequest( @@ -125,7 +98,9 @@ class ExpressionSelector: # 查询所有相关chat_id的表达方式,排除 rejected=1 的,且只选择 count > 1 的 # 如果 expression_checked_only 为 True,则只选择 checked=True 且 rejected=False 的 - base_conditions = (Expression.chat_id.in_(related_chat_ids)) & (~Expression.rejected) & (Expression.count > 1) + base_conditions = ( + (Expression.chat_id.in_(related_chat_ids)) & (~Expression.rejected) & (Expression.count > 1) + ) if global_config.expression.expression_checked_only: base_conditions = base_conditions & (Expression.checked) style_query = Expression.select().where(base_conditions) @@ -149,9 +124,7 @@ class ExpressionSelector: if len(style_exprs) < min_required: # 高 count 样本不足:如果还有候选,就降级为随机选 3 个;如果一个都没有,则直接返回空 if not style_exprs: - logger.info( - f"聊天流 {chat_id} 没有满足 count > 1 且未被拒绝的表达方式,简单模式不进行选择" - ) + 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) @@ -405,15 +378,15 @@ class ExpressionSelector: reply_reason_block = "" # 3. 构建prompt(只包含情境,不包含完整的表达方式) - prompt = (await global_prompt_manager.get_prompt_async("expression_evaluation_prompt")).format( - bot_name=global_config.bot.nickname, - chat_observe_info=chat_context, - all_situations=all_situations_str, - max_num=max_num, - target_message=target_message_str, - target_message_extra_block=target_message_extra_block, - reply_reason_block=reply_reason_block, - ) + prompt_template = prompt_manager.get_prompt("expression_evaluation_prompt") + prompt_template.add_context("bot_name", global_config.bot.nickname) + prompt_template.add_context("chat_observe_info", chat_context) + prompt_template.add_context("all_situations", all_situations_str) + prompt_template.add_context("max_num", str(max_num)) + prompt_template.add_context("target_message", target_message_str) + prompt_template.add_context("target_message_extra_block", target_message_extra_block) + prompt_template.add_context("reply_reason_block", reply_reason_block) + prompt = await prompt_manager.render_prompt(prompt_template) # 4. 调用LLM content, (reasoning_content, model_name, _) = await self.llm_model.generate_response_async(prompt=prompt) @@ -482,9 +455,6 @@ class ExpressionSelector: expr_obj.save() logger.debug("表达方式激活: 更新last_active_time in db") - -init_prompt() - try: expression_selector = ExpressionSelector() except Exception as e: diff --git a/src/bw_learner/jargon_explainer.py b/src/bw_learner/jargon_explainer.py index 4ee828f6..e1c71507 100644 --- a/src/bw_learner/jargon_explainer.py +++ b/src/bw_learner/jargon_explainer.py @@ -200,11 +200,6 @@ class JargonExplainer: explanations_text = "\n".join(jargon_explanations) # 使用LLM概括黑话解释 - # summarize_prompt = await global_prompt_manager.format_prompt( - # "jargon_explainer_summarize_prompt", - # chat_context=chat_context, - # jargon_explanations=explanations_text, - # ) prompt_of_summarize = prompt_manager.get_prompt("jargon_explainer_summarize_prompt") prompt_of_summarize.add_context("chat_context", lambda _: chat_context) prompt_of_summarize.add_context("jargon_explanations", lambda _: explanations_text) diff --git a/src/bw_learner/jargon_miner.py b/src/bw_learner/jargon_miner.py index 4089f33d..75c251a3 100644 --- a/src/bw_learner/jargon_miner.py +++ b/src/bw_learner/jargon_miner.py @@ -11,7 +11,7 @@ from src.common.database.database_model import Jargon from src.llm_models.utils_model import LLMRequest from src.config.config import model_config, global_config from src.chat.message_receive.chat_stream import get_chat_manager -from src.chat.utils.prompt_builder import Prompt, global_prompt_manager +from src.prompt.prompt_manager import prompt_manager from src.bw_learner.learner_utils import ( parse_chat_id_list, chat_id_list_contains, @@ -45,100 +45,6 @@ def _is_single_char_jargon(content: str) -> bool: ) -# def _init_prompt() -> None: -# prompt_str = """ -# **聊天内容,其中的{bot_name}的发言内容是你自己的发言,[msg_id] 是消息ID** -# {chat_str} - -# 请从上面这段聊天内容中提取"可能是黑话"的候选项(黑话/俚语/网络缩写/口头禅)。 -# - 必须为对话中真实出现过的短词或短语 -# - 必须是你无法理解含义的词语,没有明确含义的词语,请不要选择有明确含义,或者含义清晰的词语 -# - 排除:人名、@、表情包/图片中的内容、纯标点、常规功能词(如的、了、呢、啊等) -# - 每个词条长度建议 2-8 个字符(不强制),尽量短小 - -# 黑话必须为以下几种类型: -# - 由字母构成的,汉语拼音首字母的简写词,例如:nb、yyds、xswl -# - 英文词语的缩写,用英文字母概括一个词汇或含义,例如:CPU、GPU、API -# - 中文词语的缩写,用几个汉字概括一个词汇或含义,例如:社死、内卷 - -# 以 JSON 数组输出,元素为对象(严格按以下结构): -# 请你提取出可能的黑话,最多30个黑话,请尽量提取所有 -# [ -# {{"content": "词条", "msg_id": "m12"}}, // msg_id 必须与上方聊天中展示的ID完全一致 -# {{"content": "词条2", "msg_id": "m15"}} -# ] - -# 现在请输出: -# """ -# Prompt(prompt_str, "extract_jargon_prompt") - - -def _init_inference_prompts() -> None: - """初始化含义推断相关的prompt""" - # Prompt 1: 基于raw_content和content推断 - prompt1_str = """ -**词条内容** -{content} -**词条出现的上下文。其中的{bot_name}的发言内容是你自己的发言** -{raw_content_list} -{previous_meaning_section} - -请根据上下文,推断"{content}"这个词条的含义。 -- 如果这是一个黑话、俚语或网络用语,请推断其含义 -- 如果含义明确(常规词汇),也请说明 -- {bot_name} 的发言内容可能包含错误,请不要参考其发言内容 -- 如果上下文信息不足,无法推断含义,请设置 no_info 为 true -{previous_meaning_instruction} - -以 JSON 格式输出: -{{ - "meaning": "详细含义说明(包含使用场景、来源、具体解释等)", - "no_info": false -}} -注意:如果信息不足无法推断,请设置 "no_info": true,此时 meaning 可以为空字符串 -""" - Prompt(prompt1_str, "jargon_inference_with_context_prompt") - - # Prompt 2: 仅基于content推断 - prompt2_str = """ -**词条内容** -{content} - -请仅根据这个词条本身,推断其含义。 -- 如果这是一个黑话、俚语或网络用语,请推断其含义 -- 如果含义明确(常规词汇),也请说明 - -以 JSON 格式输出: -{{ - "meaning": "详细含义说明(包含使用场景、来源、具体解释等)" -}} -""" - Prompt(prompt2_str, "jargon_inference_content_only_prompt") - - # Prompt 3: 比较两个推断结果 - prompt3_str = """ -**推断结果1(基于上下文)** -{inference1} - -**推断结果2(仅基于词条)** -{inference2} - -请比较这两个推断结果,判断它们是否相同或类似。 -- 如果两个推断结果的"含义"相同或类似,说明这个词条不是黑话(含义明确) -- 如果两个推断结果有差异,说明这个词条可能是黑话(需要上下文才能理解) - -以 JSON 格式输出: -{{ - "is_similar": true/false, - "reason": "判断理由" -}} -""" - Prompt(prompt3_str, "jargon_compare_inference_prompt") - - -_init_inference_prompts() - - def _should_infer_meaning(jargon_obj: Jargon) -> bool: """ 判断是否需要进行含义推断 @@ -282,22 +188,18 @@ class JargonMiner: previous_meaning_section = "" previous_meaning_instruction = "" if current_count in [24, 60, 100] and previous_meaning: - previous_meaning_section = f""" -**上一次推断的含义(仅供参考)** -{previous_meaning} -""" + previous_meaning_section = f"\n**上一次推断的含义(仅供参考)**\n{previous_meaning}" previous_meaning_instruction = ( "- 请参考上一次推断的含义,结合新的上下文信息,给出更准确或更新的推断结果" ) - prompt1 = await global_prompt_manager.format_prompt( - "jargon_inference_with_context_prompt", - content=content, - bot_name=global_config.bot.nickname, - raw_content_list=raw_content_text, - previous_meaning_section=previous_meaning_section, - previous_meaning_instruction=previous_meaning_instruction, - ) + prompt1_template = prompt_manager.get_prompt("jargon_inference_with_context_prompt") + prompt1_template.add_context("bot_name", global_config.bot.nickname) + prompt1_template.add_context("content", str(content)) + prompt1_template.add_context("raw_content_list", raw_content_text) + prompt1_template.add_context("previous_meaning_section", previous_meaning_section) + prompt1_template.add_context("previous_meaning_instruction", previous_meaning_instruction) + prompt1 = await prompt_manager.render_prompt(prompt1_template) response1, _ = await self.llm_inference.generate_response_async(prompt1, temperature=0.3) if not response1: @@ -331,10 +233,9 @@ class JargonMiner: return # 步骤2: 仅基于content推断 - prompt2 = await global_prompt_manager.format_prompt( - "jargon_inference_content_only_prompt", - content=content, - ) + prompt2_template = prompt_manager.get_prompt("jargon_inference_content_only_prompt") + prompt2_template.add_context("content", str(content)) + prompt2 = await prompt_manager.render_prompt(prompt2_template) response2, _ = await self.llm_inference.generate_response_async(prompt2, temperature=0.3) if not response2: @@ -374,11 +275,10 @@ class JargonMiner: logger.debug(f"jargon {content} 推断1结果: {response1}") # 步骤3: 比较两个推断结果 - prompt3 = await global_prompt_manager.format_prompt( - "jargon_compare_inference_prompt", - inference1=json.dumps(inference1, ensure_ascii=False), - inference2=json.dumps(inference2, ensure_ascii=False), - ) + prompt3_template = prompt_manager.get_prompt("jargon_compare_inference_prompt") + prompt3_template.add_context("inference1", json.dumps(inference1, ensure_ascii=False)) + prompt3_template.add_context("inference2", json.dumps(inference2, ensure_ascii=False)) + prompt3 = await prompt_manager.render_prompt(prompt3_template) if global_config.debug.show_jargon_prompt: logger.info(f"jargon {content} 比较提示词: {prompt3}") @@ -449,9 +349,7 @@ class JargonMiner: traceback.print_exc() async def process_extracted_entries( - self, - entries: List[Dict[str, List[str]]], - person_name_filter: Optional[Callable[[str], bool]] = None + self, entries: List[Dict[str, List[str]]], person_name_filter: Optional[Callable[[str], bool]] = None ) -> None: """ 处理已提取的黑话条目(从 expression_learner 路由过来的) @@ -468,14 +366,14 @@ class JargonMiner: merged_entries: OrderedDict[str, Dict[str, List[str]]] = OrderedDict() for entry in entries: content_key = entry["content"] - + # 检查是否包含人物名称 # 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 - + raw_list = entry.get("raw_content", []) or [] if content_key in merged_entries: merged_entries[content_key]["raw_content"].extend(raw_list) diff --git a/src/bw_learner/reflect_tracker.py b/src/bw_learner/reflect_tracker.py index a905bf06..bbf578c3 100644 --- a/src/bw_learner/reflect_tracker.py +++ b/src/bw_learner/reflect_tracker.py @@ -3,7 +3,7 @@ from typing import Optional, Dict, TYPE_CHECKING from src.common.logger import get_logger from src.common.database.database_model import Expression from src.llm_models.utils_model import LLMRequest -from src.chat.utils.prompt_builder import Prompt, global_prompt_manager +from src.prompt.prompt_manager import prompt_manager from src.config.config import model_config from src.chat.message_receive.chat_stream import ChatStream from src.chat.utils.chat_message_builder import ( @@ -30,37 +30,6 @@ class ReflectTracker: # LLM for judging response self.judge_model = LLMRequest(model_set=model_config.model_task_config.tool_use, request_type="reflect.tracker") - self._init_prompts() - - def _init_prompts(self): - judge_prompt = """ -你是一个表达反思助手。Bot之前询问了表达方式是否合适。 -你需要根据提供的上下文对话,判断是否对该表达方式做出了肯定或否定的评价。 - -**询问内容** -情景: {situation} -风格: {style} - -**上下文对话** -{context_block} - -**判断要求** -1. 判断对话中是否包含对上述询问的回答。 -2. 如果是,判断是肯定(Approve)还是否定(Reject),或者是提供了修改意见。 -3. 如果不是回答,或者是无关内容,请返回 "Ignore"。 -4. 如果是否定并提供了修改意见,请提取修正后的情景和风格。 - -请输出JSON格式: -```json -{{ - "judgment": "Approve" | "Reject" | "Ignore", - "corrected_situation": "...", // 如果有修改意见,提取修正后的情景,否则留空 - "corrected_style": "..." // 如果有修改意见,提取修正后的风格,否则留空 -}} -``` -""" - Prompt(judge_prompt, "reflect_judge_prompt") - async def trigger_tracker(self) -> bool: """ 触发追踪检查 @@ -103,12 +72,11 @@ class ReflectTracker: # LLM Judge try: - prompt = await global_prompt_manager.format_prompt( - "reflect_judge_prompt", - situation=self.expression.situation, - style=self.expression.style, - context_block=context_block, - ) + prompt_template = prompt_manager.get_prompt("reflect_judge_prompt") + prompt_template.add_context("situation", str(self.expression.situation)) + prompt_template.add_context("style", str(self.expression.style)) + prompt_template.add_context("context_block", context_block) + prompt = await prompt_manager.render_prompt(prompt_template) logger.info(f"ReflectTracker LLM Prompt: {prompt}") @@ -134,14 +102,14 @@ class ReflectTracker: if judgment == "Approve": self.expression.checked = True self.expression.rejected = False - self.expression.modified_by = 'ai' # 通过LLM判断也标记为ai + self.expression.modified_by = "ai" # 通过LLM判断也标记为ai self.expression.save() logger.info(f"Expression {self.expression.id} approved by operator.") return True elif judgment == "Reject": self.expression.checked = True - self.expression.modified_by = 'ai' # 通过LLM判断也标记为ai + self.expression.modified_by = "ai" # 通过LLM判断也标记为ai corrected_situation = json_obj.get("corrected_situation") corrected_style = json_obj.get("corrected_style") diff --git a/src/chat/brain_chat/brain_planner.py b/src/chat/brain_chat/brain_planner.py index 7d5990e4..8d1cfadd 100644 --- a/src/chat/brain_chat/brain_planner.py +++ b/src/chat/brain_chat/brain_planner.py @@ -13,7 +13,7 @@ from src.config.config import global_config, model_config from src.common.logger import get_logger from src.chat.logger.plan_reply_logger import PlanReplyLogger from src.common.data_models.info_data_model import ActionPlannerInfo -from src.chat.utils.prompt_builder import Prompt, global_prompt_manager +from src.prompt.prompt_manager import prompt_manager from src.chat.utils.chat_message_builder import ( build_readable_actions, get_actions_by_timestamp_with_chat, @@ -35,108 +35,6 @@ logger = get_logger("planner") install(extra_lines=3) -def init_prompt(): - # ReAct 形式的 Planner Prompt - Prompt( - """ -{time_block} -{name_block} -{chat_context_description},以下是具体的聊天内容 - -**聊天内容** -{chat_content_block} - -**动作记录** -{actions_before_now_block} - -**可用的action** -reply -动作描述: -进行回复,你可以自然的顺着正在进行的聊天内容进行回复或自然的提出一个问题 -{{ - "action": "reply", - "target_message_id":"想要回复的消息id", - "reason":"回复的原因" -}} - -wait -动作描述: -暂时不再发言,等待指定时间。适用于以下情况: -- 你已经表达清楚一轮,想给对方留出空间 -- 你感觉对方的话还没说完,或者自己刚刚发了好几条连续消息 -- 你想要等待一定时间来让对方把话说完,或者等待对方反应 -- 你想保持安静,专注"听"而不是马上回复 -请你根据上下文来判断要等待多久,请你灵活判断: -- 如果你们交流间隔时间很短,聊的很频繁,不宜等待太久 -- 如果你们交流间隔时间很长,聊的很少,可以等待较长时间 -{{ - "action": "wait", - "target_message_id":"想要作为这次等待依据的消息id(通常是对方的最新消息)", - "wait_seconds": 等待的秒数(必填,例如:5 表示等待5秒), - "reason":"选择等待的原因" -}} - -complete_talk -动作描述: -当前聊天暂时结束了,对方离开,没有更多话题了 -你可以使用该动作来暂时休息,等待对方有新发言再继续: -- 多次wait之后,对方迟迟不回复消息才用 -- 如果对方只是短暂不回复,应该使用wait而不是complete_talk -- 聊天内容显示当前聊天已经结束或者没有新内容时候,选择complete_talk -选择此动作后,将不再继续循环思考,直到收到对方的新消息 -{{ - "action": "complete_talk", - "target_message_id":"触发完成对话的消息id(通常是对方的最新消息)", - "reason":"选择完成对话的原因" -}} - -{action_options_text} - -请选择合适的action,并说明触发action的消息id和选择该action的原因。消息id格式:m+数字 -先输出你的选择思考理由,再输出你选择的action,理由是一段平文本,不要分点,精简。 -**动作选择要求** -请你根据聊天内容,用户的最新消息和以下标准选择合适的动作: -{plan_style} -{moderation_prompt} - -请选择所有符合使用要求的action,动作用json格式输出,如果输出多个json,每个json都要单独用```json包裹,你可以重复使用同一个动作或不同动作: -**示例** -// 理由文本 -```json -{{ - "action":"动作名", - "target_message_id":"触发动作的消息id", - //对应参数 -}} -``` -```json -{{ - "action":"动作名", - "target_message_id":"触发动作的消息id", - //对应参数 -}} -``` - -""", - "brain_planner_prompt_react", - ) - - Prompt( - """ -{action_name} -动作描述:{action_description} -使用条件: -{action_require} -{{ - "action": "{action_name}",{action_parameters}, - "target_message_id":"触发action的消息id", - "reason":"触发action的原因" -}} -""", - "brain_action_prompt", - ) - - class BrainPlanner: def __init__(self, chat_id: str, action_manager: ActionManager): self.chat_id = chat_id @@ -373,6 +271,7 @@ class BrainPlanner: else: actions_before_now_block = "" + chat_context_description: str = "" if chat_target_info: # 构建聊天上下文描述 chat_context_description = ( @@ -392,18 +291,17 @@ class BrainPlanner: name_block = f"你的名字是{bot_name}{bot_nickname},请注意哪些是你自己的发言。" # 获取主规划器模板并填充 - planner_prompt_template = await global_prompt_manager.get_prompt_async(prompt_key) - prompt = planner_prompt_template.format( - time_block=time_block, - chat_context_description=chat_context_description, - chat_content_block=chat_content_block, - actions_before_now_block=actions_before_now_block, - action_options_text=action_options_block, - moderation_prompt=moderation_prompt_block, - name_block=name_block, - interest=interest, - plan_style=global_config.experimental.private_plan_style, - ) + planner_prompt_template = prompt_manager.get_prompt(prompt_key) + planner_prompt_template.add_context("time_block", time_block) + planner_prompt_template.add_context("chat_context_description", chat_context_description) + planner_prompt_template.add_context("chat_content_block", chat_content_block) + planner_prompt_template.add_context("actions_before_now_block", actions_before_now_block) + planner_prompt_template.add_context("action_options_text", action_options_block) + planner_prompt_template.add_context("moderation_prompt", moderation_prompt_block) + planner_prompt_template.add_context("name_block", name_block) + planner_prompt_template.add_context("interest", interest) + planner_prompt_template.add_context("plan_style", global_config.experimental.private_plan_style) + prompt = await prompt_manager.render_prompt(planner_prompt_template) return prompt, message_id_list except Exception as e: @@ -483,13 +381,12 @@ class BrainPlanner: require_text = require_text.rstrip("\n") # 获取动作提示模板并填充 - using_action_prompt = await global_prompt_manager.get_prompt_async("brain_action_prompt") - using_action_prompt = using_action_prompt.format( - action_name=action_name, - action_description=action_info.description, - action_parameters=param_text, - action_require=require_text, - ) + using_action_prompt_template = prompt_manager.get_prompt("brain_action_prompt") + using_action_prompt_template.add_context("action_name", action_name) + using_action_prompt_template.add_context("action_description", action_info.description) + using_action_prompt_template.add_context("action_parameters", param_text) + using_action_prompt_template.add_context("action_require", require_text) + using_action_prompt = await prompt_manager.render_prompt(using_action_prompt_template) action_options_block += using_action_prompt @@ -713,6 +610,3 @@ class BrainPlanner: logger.debug(f"处理不完整的JSON代码块时出错: {e}") return json_objects, reasoning_content - - -init_prompt() diff --git a/src/chat/planner_actions/planner.py b/src/chat/planner_actions/planner.py index fc487efc..be2dd62a 100644 --- a/src/chat/planner_actions/planner.py +++ b/src/chat/planner_actions/planner.py @@ -13,7 +13,6 @@ from src.config.config import global_config, model_config from src.common.logger import get_logger from src.chat.logger.plan_reply_logger import PlanReplyLogger from src.common.data_models.info_data_model import ActionPlannerInfo -# from src.chat.utils.prompt_builder import Prompt, global_prompt_manager from src.prompt.prompt_manager import prompt_manager from src.chat.utils.chat_message_builder import ( build_readable_messages_with_id, @@ -601,19 +600,6 @@ class ActionPlanner: reply_action_example += ', "quote":"如果需要引用该message,设置为true"' reply_action_example += "}" - # planner_prompt_template = await global_prompt_manager.get_prompt_async("planner_prompt") - # prompt = planner_prompt_template.format( - # time_block=time_block, - # chat_context_description=chat_context_description, - # chat_content_block=chat_content_block, - # actions_before_now_block=actions_before_now_block, - # action_options_text=action_options_block, - # moderation_prompt=moderation_prompt_block, - # name_block=name_block, - # interest=interest, - # plan_style=global_config.personality.plan_style, - # reply_action_example=reply_action_example, - # ) planner_prompt_template = prompt_manager.get_prompt("planner_prompt") planner_prompt_template.add_context("time_block", time_block) planner_prompt_template.add_context("chat_context_description", chat_context_description) @@ -709,14 +695,6 @@ class ActionPlanner: parallel_text = "" # 获取动作提示模板并填充 - # using_action_prompt = await global_prompt_manager.get_prompt_async("action_prompt") - # using_action_prompt = using_action_prompt.format( - # action_name=action_name, - # action_description=action_info.description, - # action_parameters=param_text, - # action_require=require_text, - # parallel_text=parallel_text, - # ) using_action_prompt = prompt_manager.get_prompt("action_prompt") using_action_prompt.add_context("action_name", action_name) using_action_prompt.add_context("action_description", action_info.description) diff --git a/src/chat/replyer/group_generator.py b/src/chat/replyer/group_generator.py index 75ea80e6..204151e3 100644 --- a/src/chat/replyer/group_generator.py +++ b/src/chat/replyer/group_generator.py @@ -32,13 +32,11 @@ from src.plugin_system.base.component_types import ActionInfo, EventType from src.plugin_system.apis import llm_api from src.chat.logger.plan_reply_logger import PlanReplyLogger -from src.chat.replyer.prompt.lpmm_prompt import init_lpmm_prompt -from src.memory_system.memory_retrieval import init_memory_retrieval_prompt, build_memory_retrieval_prompt +from src.memory_system.memory_retrieval import init_memory_retrieval_sys, build_memory_retrieval_prompt from src.bw_learner.jargon_explainer import explain_jargon_in_context, retrieve_concepts_with_jargon from src.chat.utils.common_utils import TempMethodsExpression -init_lpmm_prompt() -init_memory_retrieval_prompt() +init_memory_retrieval_sys() logger = get_logger("replyer") @@ -977,33 +975,6 @@ class DefaultReplyer: # 兜底:即使 multiple_reply_style 配置异常也不影响正常回复 reply_style = global_config.personality.reply_style - # return ( - # await global_prompt_manager.format_prompt( - # prompt_name, - # expression_habits_block=expression_habits_block, - # tool_info_block=tool_info, - # bot_name=global_config.bot.nickname, - # knowledge_prompt=prompt_info, - # # relation_info_block=relation_info, - # extra_info_block=extra_info_block, - # jargon_explanation=jargon_explanation, - # identity=personality_prompt, - # action_descriptions=actions_info, - # sender_name=sender, - # dialogue_prompt=dialogue_prompt, - # time_block=time_block, - # reply_target_block=reply_target_block, - # reply_style=reply_style, - # keywords_reaction_prompt=keywords_reaction_prompt, - # moderation_prompt=moderation_prompt_block, - # memory_retrieval=memory_retrieval, - # chat_prompt=chat_prompt_block, - # planner_reasoning=planner_reasoning, - # ), - # selected_expressions, - # timing_logs, - # almost_zero_str, - # ) prompt = prompt_manager.get_prompt(prompt_name) prompt.add_context("expression_habits_block", expression_habits_block) prompt.add_context("tool_info_block", tool_info) @@ -1111,22 +1082,6 @@ class DefaultReplyer: except Exception: reply_style = global_config.personality.reply_style - # return await global_prompt_manager.format_prompt( - # template_name, - # expression_habits_block=expression_habits_block, - # # relation_info_block=relation_info, - # chat_target=chat_target_1, - # time_block=time_block, - # chat_info=chat_talking_prompt_half, - # identity=personality_prompt, - # chat_target_2=chat_target_2, - # reply_target_block=reply_target_block, - # raw_reply=raw_reply, - # reason=reason, - # reply_style=reply_style, - # keywords_reaction_prompt=keywords_reaction_prompt, - # moderation_prompt=moderation_prompt_block, - # ) prompt_template = prompt_manager.get_prompt("default_expressor_prompt") prompt_template.add_context("expression_habits_block", expression_habits_block) # prompt_template.add_context("relation_info_block", relation_info) @@ -1220,14 +1175,6 @@ class DefaultReplyer: template_prompt.add_context("chat_history", message) template_prompt.add_context("sender", sender) template_prompt.add_context("target_message", target) - # prompt = await global_prompt_manager.format_prompt( - # "lpmm_get_knowledge_prompt", - # bot_name=bot_name, - # time_now=time_now, - # chat_history=message, - # sender=sender, - # target_message=target, - # ) prompt = await prompt_manager.render_prompt(template_prompt) _, _, _, _, tool_calls = await llm_api.generate_with_model_with_tools( prompt, diff --git a/src/chat/replyer/private_generator.py b/src/chat/replyer/private_generator.py index 9ba88f00..bc7ba1e1 100644 --- a/src/chat/replyer/private_generator.py +++ b/src/chat/replyer/private_generator.py @@ -15,9 +15,8 @@ from src.llm_models.utils_model import LLMRequest from src.chat.message_receive.message import UserInfo, Seg, MessageRecv, MessageSending from src.chat.message_receive.chat_stream import ChatStream from src.chat.message_receive.uni_message_sender import UniversalMessageSender -from src.chat.utils.timer_calculator import Timer # <--- Import Timer +from src.chat.utils.timer_calculator import Timer from src.chat.utils.utils import get_chat_type_and_target_info, is_bot_self -from src.chat.utils.prompt_builder import global_prompt_manager from src.prompt.prompt_manager import prompt_manager from src.chat.utils.common_utils import TempMethodsExpression from src.chat.utils.chat_message_builder import ( @@ -27,21 +26,14 @@ from src.chat.utils.chat_message_builder import ( ) from src.bw_learner.expression_selector import expression_selector from src.plugin_system.apis.message_api import translate_pid_to_description - # from src.memory_system.memory_activator import MemoryActivator - from src.person_info.person_info import Person, is_person_known from src.plugin_system.base.component_types import ActionInfo, EventType from src.plugin_system.apis import llm_api - -from src.chat.replyer.prompt.lpmm_prompt import init_lpmm_prompt -from src.chat.replyer.prompt.replyer_private_prompt import init_replyer_private_prompt -from src.memory_system.memory_retrieval import init_memory_retrieval_prompt, build_memory_retrieval_prompt +from src.memory_system.memory_retrieval import init_memory_retrieval_sys, build_memory_retrieval_prompt from src.bw_learner.jargon_explainer import explain_jargon_in_context -init_lpmm_prompt() -init_replyer_private_prompt() -init_memory_retrieval_prompt() +init_memory_retrieval_sys() logger = get_logger("replyer") @@ -667,7 +659,7 @@ class PrivateReplyer: timestamp_mode="relative", read_mark=0.0, show_actions=True, - long_time_notice=True + long_time_notice=True, ) message_list_before_short = get_raw_msg_before_timestamp_with_chat( @@ -724,7 +716,12 @@ class PrivateReplyer: self._time_and_run_task(self.build_personality_prompt(), "personality_prompt"), self._time_and_run_task( build_memory_retrieval_prompt( - chat_talking_prompt_short, sender, target, self.chat_stream, think_level=1, unknown_words=unknown_words + chat_talking_prompt_short, + sender, + target, + self.chat_stream, + think_level=1, + unknown_words=unknown_words, ), "memory_retrieval", ), @@ -800,7 +797,7 @@ class PrivateReplyer: # 根据配置构建最终的 reply_style:支持 multiple_reply_style 按概率随机替换 reply_style = global_config.personality.reply_style - multi_styles =global_config.personality.multiple_reply_style + multi_styles = global_config.personality.multiple_reply_style multi_prob = global_config.personality.multiple_probability or 0.0 if multi_styles and multi_prob > 0 and random.random() < multi_prob: try: @@ -810,50 +807,33 @@ class PrivateReplyer: reply_style = global_config.personality.reply_style # 使用统一的 is_bot_self 函数判断是否是机器人自己(支持多平台,包括 WebUI) + if is_bot_self(platform, user_id): - return await global_prompt_manager.format_prompt( - "private_replyer_self_prompt", - expression_habits_block=expression_habits_block, - tool_info_block=tool_info, - knowledge_prompt=prompt_info, - relation_info_block=relation_info, - extra_info_block=extra_info_block, - identity=personality_prompt, - action_descriptions=actions_info, - dialogue_prompt=dialogue_prompt, - jargon_explanation=jargon_explanation, - time_block=time_block, - target=target, - reason=reply_reason, - sender_name=sender, - reply_style=reply_style, - keywords_reaction_prompt=keywords_reaction_prompt, - moderation_prompt=moderation_prompt_block, - memory_retrieval=memory_retrieval, - chat_prompt=chat_prompt_block, - ), selected_expressions + prompt_template = prompt_manager.get_prompt("private_replyer_self_prompt") + prompt_template.add_context("target", target) + prompt_template.add_context("reason", reply_reason) else: - return await global_prompt_manager.format_prompt( - "private_replyer_prompt", - expression_habits_block=expression_habits_block, - tool_info_block=tool_info, - knowledge_prompt=prompt_info, - relation_info_block=relation_info, - extra_info_block=extra_info_block, - identity=personality_prompt, - action_descriptions=actions_info, - dialogue_prompt=dialogue_prompt, - jargon_explanation=jargon_explanation, - time_block=time_block, - reply_target_block=reply_target_block, - reply_style=reply_style, - keywords_reaction_prompt=keywords_reaction_prompt, - moderation_prompt=moderation_prompt_block, - sender_name=sender, - memory_retrieval=memory_retrieval, - chat_prompt=chat_prompt_block, - planner_reasoning=planner_reasoning, - ), selected_expressions + prompt_template = prompt_manager.get_prompt("private_replyer_prompt") + prompt_template.add_context("reply_target_block", reply_target_block) + prompt_template.add_context("planner_reasoning", planner_reasoning) + prompt_template.add_context("expression_habits_block", expression_habits_block) + prompt_template.add_context("tool_info_block", tool_info) + prompt_template.add_context("knowledge_prompt", prompt_info) + prompt_template.add_context("relation_info_block", relation_info) + prompt_template.add_context("extra_info_block", extra_info_block) + prompt_template.add_context("identity", personality_prompt) + prompt_template.add_context("action_descriptions", actions_info) + prompt_template.add_context("dialogue_prompt", dialogue_prompt) + prompt_template.add_context("jargon_explanation", jargon_explanation) + prompt_template.add_context("time_block", time_block) + prompt_template.add_context("sender_name", sender) + prompt_template.add_context("keywords_reaction_prompt", keywords_reaction_prompt) + prompt_template.add_context("reply_style", reply_style) + prompt_template.add_context("memory_retrieval", memory_retrieval) + prompt_template.add_context("chat_prompt", chat_prompt_block) + prompt_template.add_context("moderation_prompt", moderation_prompt_block) + prompt = await prompt_manager.render_prompt(prompt_template) + return prompt, selected_expressions async def build_prompt_rewrite_context( self, @@ -943,22 +923,6 @@ class PrivateReplyer: # 兜底:即使 multiple_reply_style 配置异常也不影响正常回复 reply_style = global_config.personality.reply_style - # return await global_prompt_manager.format_prompt( - # template_name, - # expression_habits_block=expression_habits_block, - # # relation_info_block=relation_info, - # chat_target=chat_target_1, - # time_block=time_block, - # chat_info=chat_talking_prompt_half, - # identity=personality_prompt, - # chat_target_2=chat_target_2, - # reply_target_block=reply_target_block, - # raw_reply=raw_reply, - # reason=reason, - # reply_style=reply_style, - # keywords_reaction_prompt=keywords_reaction_prompt, - # moderation_prompt=moderation_prompt_block, - # ) prompt_template = prompt_manager.get_prompt("default_expressor_prompt") prompt_template.add_context("expression_habits_block", expression_habits_block) # prompt_template.add_context("relation_info_block", relation_info) @@ -1046,18 +1010,14 @@ class PrivateReplyer: if global_config.lpmm_knowledge.lpmm_mode == "agent": return "" - time_now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + prompt_template = prompt_manager.get_prompt("lpmm_get_knowledge_prompt") + prompt_template.add_context("bot_name", global_config.bot.nickname) + prompt_template.add_context("time_now", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + prompt_template.add_context("chat_history", message) + prompt_template.add_context("sender", sender) + prompt_template.add_context("target_message", target) + prompt = await prompt_manager.render_prompt(prompt_template) - bot_name = global_config.bot.nickname - - prompt = await global_prompt_manager.format_prompt( - "lpmm_get_knowledge_prompt", - bot_name=bot_name, - time_now=time_now, - chat_history=message, - sender=sender, - target_message=target, - ) _, _, _, _, tool_calls = await llm_api.generate_with_model_with_tools( prompt, model_config=model_config.model_task_config.tool_use, diff --git a/src/chat/replyer/prompt/lpmm_prompt.py b/src/chat/replyer/prompt/lpmm_prompt.py deleted file mode 100644 index 43eb41f2..00000000 --- a/src/chat/replyer/prompt/lpmm_prompt.py +++ /dev/null @@ -1,20 +0,0 @@ -from src.chat.utils.prompt_builder import Prompt -# from src.memory_system.memory_activator import MemoryActivator - - -def init_lpmm_prompt(): - Prompt( - """ -你是一个专门获取知识的助手。你的名字是{bot_name}。现在是{time_now}。 -群里正在进行的聊天内容: -{chat_history} - -现在,{sender}发送了内容:{target_message},你想要回复ta。 -请仔细分析聊天内容,考虑以下几点: -1. 内容中是否包含需要查询信息的问题 -2. 是否有明确的知识获取指令 - -If you need to use the search tool, please directly call the function "lpmm_search_knowledge". If you do not need to use any tool, simply output "No tool needed". -""", - name="lpmm_get_knowledge_prompt", - ) diff --git a/src/chat/replyer/prompt/replyer_private_prompt.py b/src/chat/replyer/prompt/replyer_private_prompt.py deleted file mode 100644 index 7dfd54e7..00000000 --- a/src/chat/replyer/prompt/replyer_private_prompt.py +++ /dev/null @@ -1,41 +0,0 @@ -from src.chat.utils.prompt_builder import Prompt - - -def init_replyer_private_prompt(): - Prompt( - """{knowledge_prompt}{tool_info_block}{extra_info_block} - {expression_habits_block}{memory_retrieval}{jargon_explanation} - - 你正在和{sender_name}聊天,这是你们之前聊的内容: - {time_block} - {dialogue_prompt} - - {reply_target_block}。 - {planner_reasoning} - {identity} - {chat_prompt}你正在和{sender_name}聊天,现在请你读读之前的聊天记录,然后给出日常且口语化的回复,平淡一些, - 尽量简短一些。{keywords_reaction_prompt}请注意把握聊天内容,不要回复的太有条理。 - {reply_style} - 请注意不要输出多余内容(包括前后缀,冒号和引号,括号,表情等),只输出回复内容。 - {moderation_prompt}不要输出多余内容(包括前后缀,冒号和引号,括号,表情包,at或 @等 )。""", - "private_replyer_prompt", - ) - - Prompt( - """{knowledge_prompt}{tool_info_block}{extra_info_block} -{expression_habits_block}{memory_retrieval}{jargon_explanation} - -你正在和{sender_name}聊天,这是你们之前聊的内容: -{time_block} -{dialogue_prompt} - -你现在想补充说明你刚刚自己的发言内容:{target},原因是{reason} -请你根据聊天内容,组织一条新回复。注意,{target} 是刚刚你自己的发言,你要在这基础上进一步发言,请按照你自己的角度来继续进行回复。注意保持上下文的连贯性。 -{identity} -{chat_prompt}尽量简短一些。{keywords_reaction_prompt}请注意把握聊天内容,不要回复的太有条理,可以有个性。 -{reply_style} -请注意不要输出多余内容(包括前后缀,冒号和引号,括号,表情等),只输出回复内容。 -{moderation_prompt}不要输出多余内容(包括冒号和引号,括号,表情包,at或 @等 )。 -""", - "private_replyer_self_prompt", - ) diff --git a/src/dream/dream_agent.py b/src/dream/dream_agent.py index 8770b24d..55ea25bb 100644 --- a/src/dream/dream_agent.py +++ b/src/dream/dream_agent.py @@ -8,7 +8,7 @@ from peewee import fn from src.common.logger import get_logger from src.config.config import global_config, model_config from src.common.database.database_model import ChatHistory -from src.chat.utils.prompt_builder import Prompt, global_prompt_manager +from src.prompt.prompt_manager import prompt_manager from src.llm_models.payload_content.message import MessageBuilder, RoleType, Message from src.plugin_system.apis import llm_api from src.dream.dream_generator import generate_dream_summary @@ -27,60 +27,6 @@ from src.dream.tools.update_jargon_tool import make_update_jargon logger = get_logger("dream_agent") -def init_dream_prompts() -> None: - """初始化 dream agent 的提示词""" - Prompt( - """ -你的名字是{bot_name},你现在处于"梦境维护模式(dream agent)"。 -你可以自由地在 ChatHistory 库中探索、整理、创建和删改记录,以帮助自己在未来更好地回忆和理解对话历史。 - -本轮要维护的聊天ID:{chat_id} -本轮随机选中的起始记忆 ID:{start_memory_id} -请优先以这条起始记忆为切入点,先理解它的内容与上下文,再决定如何在其附近进行创建新概括、重写或删除等整理操作;如果起始记忆为空,则由你自行选择合适的切入点。 - -你可以使用的工具包括: -**ChatHistory 维护工具:** -- search_chat_history:根据关键词或参与人搜索该 chat_id 下的历史记忆概括列表 -- get_chat_history_detail:查看某条概括的详细内容 -- create_chat_history:根据整理后的理解创建一条新的 ChatHistory 概括记录(主题、概括、关键词、关键信息等) -- update_chat_history:在不改变事实的前提下重写或精炼主题、概括、关键词、关键信息 -- delete_chat_history:删除明显冗余、噪声、错误或无意义的记录,或者非常有时效性的信息,或者无太多有用信息的日常互动。 -你也可以先用 create_chat_history 创建一条新的综合概括,再对旧的冗余记录执行多次 delete_chat_history 来完成“合并”效果。 - -**Jargon(黑话)维护工具(只读,禁止修改):** -- search_jargon:根据一个或多个关键词搜索Jargon 记录,通常是含义不明确的词条或者特殊的缩写 - -**通用工具:** -- finish_maintenance:当你认为当前维护工作已经完成,没有更多需要整理的内容时,调用此工具来结束本次运行 - -**工作目标**: -- 发现冗余、重复或高度相似的记录,并进行合并或删除; -- 发现主题/概括过于含糊、啰嗦或缺少关键信息的记录,进行重写和精简; -- summary要尽可能保持有用的信息; -- 尽量保持信息的真实与可用性,不要凭空捏造事实。 - -**合并准则** -- 你可以新建一个记录,然后删除旧记录来实现合并。 -- 如果两个或多个记录的主题相似,内容是对主题不同方面的信息或讨论,且信息量较少,则可以合并为一条记录。 -- 如果两个记录冲突,可以根据逻辑保留一个或者进行整合,也可以采取更新的记录,删除旧的记录 - -**轮次信息**: -- 本次维护最多执行 {max_iterations} 轮 -- 每轮开始时,系统会告知你当前是第几轮,还剩多少轮 -- 如果提前完成维护工作,可以调用 finish_maintenance 工具主动结束 - -**每一轮的执行方式(必须遵守):** -- 第一步:先用一小段中文自然语言,写出你的「思考」和本轮计划(例如要查什么、准备怎么合并/修改); -- 第二步:在这段思考之后,再通过工具调用来执行你的计划(可以调用 0~N 个工具); -- 第三步:收到工具结果后,在下一轮继续先写出新的思考,再视情况继续调用工具。 - -请不要在没有先写出思考的情况下直接调用工具。 -只输出你的思考内容或工具调用结果,由系统负责真正执行工具调用。 -""", - name="dream_react_head_prompt", - ) - - class DreamTool: """dream 模块内部使用的简易工具封装""" @@ -278,17 +224,17 @@ async def run_dream_agent_once( tool_registry = get_dream_tool_registry() tool_defs = tool_registry.get_tool_definitions() - bot_name = global_config.bot.nickname - time_now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) - - head_prompt = await global_prompt_manager.format_prompt( - "dream_react_head_prompt", - bot_name=bot_name, - time_now=time_now, - chat_id=chat_id, - start_memory_id=start_memory_id if start_memory_id is not None else "无(本轮由你自由选择切入点)", - max_iterations=max_iterations, + head_prompt_template = prompt_manager.get_prompt("dream_react_head_prompt") + head_prompt_template.add_context("bot_name", global_config.bot.nickname) + head_prompt_template.add_context("time_now", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + head_prompt_template.add_context("chat_id", chat_id) + head_prompt_template.add_context( + "start_memory_id", + str(start_memory_id) if start_memory_id is not None else "无(本轮由你自由选择切入点)", ) + head_prompt_template.add_context("max_iterations", str(max_iterations)) + + head_prompt = await prompt_manager.render_prompt(head_prompt_template) conversation_messages: List[Message] = [] @@ -575,10 +521,6 @@ async def start_dream_scheduler( raise -# 初始化提示词 -init_dream_prompts() - - class TempMethodsDream: @staticmethod def is_in_dream_time() -> bool: diff --git a/src/dream/dream_generator.py b/src/dream/dream_generator.py index c17c9b12..1526170d 100644 --- a/src/dream/dream_generator.py +++ b/src/dream/dream_generator.py @@ -1,10 +1,11 @@ -import random from typing import List, Optional +import random +import json from src.common.logger import get_logger from src.config.config import global_config, model_config -from src.chat.utils.prompt_builder import Prompt from src.llm_models.payload_content.message import RoleType, Message +from src.prompt.prompt_manager import prompt_manager from src.llm_models.utils_model import LLMRequest from src.chat.message_receive.chat_stream import get_chat_manager from src.plugin_system.apis import send_api @@ -44,27 +45,6 @@ def get_random_dream_styles(count: int = 2) -> List[str]: """从梦境风格列表中随机选择指定数量的风格""" return random.sample(DREAM_STYLES, min(count, len(DREAM_STYLES))) -def init_dream_summary_prompt() -> None: - """初始化梦境总结的提示词""" - Prompt( - """ -你刚刚完成了一次对聊天记录的记忆整理工作。以下是整理过程的摘要: -整理过程: -{conversation_text} - -请将这次整理涉及的相关信息改写为一个富有诗意和想象力的"梦境",请你仅使用具体的记忆的内容,而不是整理过程编写。 -要求: -1. 使用第一人称视角 -2. 叙述直白,不要复杂修辞,口语化 -3. 长度控制在200-800字 -4. 用中文输出 -梦境风格: -{dream_styles} -请直接输出梦境内容,不要添加其他说明: -""", - name="dream_summary_prompt", - ) - async def generate_dream_summary( chat_id: str, @@ -74,8 +54,7 @@ async def generate_dream_summary( ) -> None: """生成梦境总结,输出到日志,并根据配置可选地推送给指定用户""" try: - import json - from src.chat.utils.prompt_builder import global_prompt_manager + # 第一步:建立工具调用结果映射 (call_id -> result) tool_results_map: dict[str, str] = {} @@ -164,14 +143,13 @@ async def generate_dream_summary( dream_styles_text = "\n".join([f"{i + 1}. {style}" for i, style in enumerate(selected_styles)]) # 使用 Prompt 管理器格式化梦境生成 prompt - dream_prompt = await global_prompt_manager.format_prompt( - "dream_summary_prompt", - chat_id=chat_id, - total_iterations=total_iterations, - time_cost=time_cost, - conversation_text=conversation_text, - dream_styles=dream_styles_text, - ) + dream_prompt_template = prompt_manager.get_prompt("dream_summary_prompt") + dream_prompt_template.add_context("chat_id", chat_id) + dream_prompt_template.add_context("total_iterations", str(total_iterations)) + dream_prompt_template.add_context("time_cost", str(time_cost)) + dream_prompt_template.add_context("conversation_text", conversation_text) + dream_prompt_template.add_context("dream_styles", dream_styles_text) + dream_prompt = await prompt_manager.render_prompt(dream_prompt_template) # 调用 utils 模型生成梦境 summary_model = LLMRequest( @@ -199,9 +177,7 @@ async def generate_dream_summary( else: platform, user_id = parts[0].strip(), parts[1].strip() if not platform or not user_id: - logger.warning( - f"[dream][梦境总结] dream_send 平台或用户ID为空,当前值: {dream_send_raw!r}" - ) + logger.warning(f"[dream][梦境总结] dream_send 平台或用户ID为空,当前值: {dream_send_raw!r}") else: # 默认为私聊会话 stream_id = get_chat_manager().get_stream_id( @@ -227,9 +203,7 @@ async def generate_dream_summary( f"[dream][梦境总结] 已将梦境结果发送给配置的目标用户: {platform}:{user_id}" ) else: - logger.error( - f"[dream][梦境总结] 向 {platform}:{user_id} 发送梦境结果失败" - ) + logger.error(f"[dream][梦境总结] 向 {platform}:{user_id} 发送梦境结果失败") except Exception as send_exc: logger.error(f"[dream][梦境总结] 发送梦境结果到配置用户时出错: {send_exc}", exc_info=True) else: @@ -237,6 +211,3 @@ async def generate_dream_summary( except Exception as e: logger.error(f"[dream][梦境总结] 生成梦境总结失败: {e}", exc_info=True) - - -init_dream_summary_prompt() diff --git a/src/memory_system/chat_history_summarizer.py b/src/memory_system/chat_history_summarizer.py index 61ce6f79..83546f8c 100644 --- a/src/memory_system/chat_history_summarizer.py +++ b/src/memory_system/chat_history_summarizer.py @@ -22,73 +22,13 @@ from src.chat.utils.chat_message_builder import build_readable_messages from src.chat.utils.utils import is_bot_self from src.person_info.person_info import Person from src.chat.message_receive.chat_stream import get_chat_manager -from src.chat.utils.prompt_builder import Prompt, global_prompt_manager +from src.prompt.prompt_manager import prompt_manager logger = get_logger("chat_history_summarizer") HIPPO_CACHE_DIR = Path(__file__).resolve().parents[2] / "data" / "hippo_memorizer" -def init_prompt(): - """初始化提示词模板""" - - topic_analysis_prompt = """【历史话题标题列表】(仅标题,不含具体内容): -{history_topics_block} -【历史话题标题列表结束】 - -【本次聊天记录】(每条消息前有编号,用于后续引用): -{messages_block} -【本次聊天记录结束】 - -请完成以下任务: -**识别话题** -1. 识别【本次聊天记录】中正在进行的一个或多个话题; -2. 【本次聊天记录】的中的消息可能与历史话题有关,也可能毫无关联。 -2. 判断【历史话题标题列表】中的话题是否在【本次聊天记录】中出现,如果出现,则直接使用该历史话题标题字符串; - -**选取消息** -1. 对于每个话题(新话题或历史话题),从上述带编号的消息中选出与该话题强相关的消息编号列表; -2. 每个话题用一句话清晰地描述正在发生的事件,必须包含时间(大致即可)、人物、主要事件和主题,保证精准且有区分度; - -请先输出一段简短思考,说明有什么话题,哪些是不包含在历史话题中的,哪些是包含在历史话题中的,并说明为什么; -然后严格以 JSON 格式输出【本次聊天记录】中涉及的话题,格式如下: -[ - {{ - "topic": "话题", - "message_indices": [1, 2, 5] - }}, - ... -] -""" - Prompt(topic_analysis_prompt, "hippo_topic_analysis_prompt") - - topic_summary_prompt = """ -请基于以下话题,对聊天记录片段进行概括,提取以下信息: - -**话题**:{topic} - -**要求**: -1. 关键词:提取与话题相关的关键词,用列表形式返回(3-10个关键词) -2. 概括:对这段话的平文本概括(50-200字),要求: - - 仔细地转述发生的事件和聊天内容; - - 重点突出事件的发展过程和结果; - - 围绕话题这个中心进行概括。 - - 提取话题中的关键信息点,关键信息点应该简洁明了。 - -请以JSON格式返回,格式如下: -{{ - "keywords": ["关键词1", "关键词2", ...], - "summary": "概括内容" -}} - -聊天记录: -{original_text} - -请直接返回JSON,不要包含其他内容。 -""" - Prompt(topic_summary_prompt, "hippo_topic_summary_prompt") - - @dataclass class MessageBatch: """消息批次(用于触发话题检查的原始消息累积)""" @@ -380,12 +320,16 @@ class ChatHistorySummarizer: # 条件1: 消息数量达到阈值,触发一次检查 if message_count >= message_threshold: should_check = True - logger.info(f"{self.log_prefix} 触发检查条件: 消息数量达到 {message_count} 条(阈值: {message_threshold}条)") + logger.info( + f"{self.log_prefix} 触发检查条件: 消息数量达到 {message_count} 条(阈值: {message_threshold}条)" + ) # 条件2: 距离上一次检查超过时间阈值且消息数量达到最小阈值,触发一次检查 elif time_since_last_check > time_threshold_seconds and message_count >= min_messages: should_check = True - logger.info(f"{self.log_prefix} 触发检查条件: 距上次检查 {time_str}(阈值: {time_threshold_hours}小时)且消息数量达到 {message_count} 条(阈值: {min_messages}条)") + logger.info( + f"{self.log_prefix} 触发检查条件: 距上次检查 {time_str}(阈值: {time_threshold_hours}小时)且消息数量达到 {message_count} 条(阈值: {min_messages}条)" + ) if should_check: await self._run_topic_check_and_update_cache(messages) @@ -539,7 +483,9 @@ class ChatHistorySummarizer: topics_to_finalize: List[str] = [] for topic, item in self.topic_cache.items(): if item.no_update_checks >= no_update_checks_threshold: - logger.info(f"{self.log_prefix} 话题[{topic}] 连续 {no_update_checks_threshold} 次检查无新增内容,触发打包存储") + logger.info( + f"{self.log_prefix} 话题[{topic}] 连续 {no_update_checks_threshold} 次检查无新增内容,触发打包存储" + ) topics_to_finalize.append(topic) continue if len(item.messages) > message_count_threshold: @@ -712,11 +658,10 @@ class ChatHistorySummarizer: history_topics_block = "\n".join(f"- {t}" for t in existing_topics) if existing_topics else "(当前无历史话题)" messages_block = "\n".join(numbered_lines) - prompt = await global_prompt_manager.format_prompt( - "hippo_topic_analysis_prompt", - history_topics_block=history_topics_block, - messages_block=messages_block, - ) + prompt_template = prompt_manager.get_prompt("hippo_topic_analysis_prompt") + prompt_template.add_context("history_topics_block", history_topics_block) + prompt_template.add_context("messages_block", messages_block) + prompt = await prompt_manager.render_prompt(prompt_template) try: response, _ = await self.summarizer_llm.generate_response_async( @@ -826,23 +771,17 @@ class ChatHistorySummarizer: while attempt < max_retries: attempt += 1 success, keywords, summary = await self._compress_with_llm(original_text, topic) - + if success and keywords and summary: # 成功获取到有效的 keywords 和 summary if attempt > 1: - logger.info( - f"{self.log_prefix} 话题[{topic}] LLM 概括在第 {attempt} 次重试后成功" - ) + logger.info(f"{self.log_prefix} 话题[{topic}] LLM 概括在第 {attempt} 次重试后成功") break - + if attempt < max_retries: - logger.warning( - f"{self.log_prefix} 话题[{topic}] LLM 概括失败(第 {attempt} 次尝试),准备重试" - ) + logger.warning(f"{self.log_prefix} 话题[{topic}] LLM 概括失败(第 {attempt} 次尝试),准备重试") else: - logger.error( - f"{self.log_prefix} 话题[{topic}] LLM 概括连续 {max_retries} 次失败,放弃存储" - ) + logger.error(f"{self.log_prefix} 话题[{topic}] LLM 概括连续 {max_retries} 次失败,放弃存储") if not success or not keywords or not summary: logger.warning(f"{self.log_prefix} 话题[{topic}] LLM 概括失败,不写入数据库") @@ -875,11 +814,10 @@ class ChatHistorySummarizer: Returns: tuple[bool, List[str], str]: (是否成功, 关键词列表, 概括) """ - prompt = await global_prompt_manager.format_prompt( - "hippo_topic_summary_prompt", - topic=topic, - original_text=original_text, - ) + prompt_template = prompt_manager.get_prompt("hippo_topic_summary_prompt") + prompt_template.add_context("topic", topic) + prompt_template.add_context("original_text", original_text) + prompt = await prompt_manager.render_prompt(prompt_template) try: response, _ = await self.summarizer_llm.generate_response_async(prompt=prompt) @@ -943,7 +881,7 @@ class ChatHistorySummarizer: keywords = result.get("keywords", []) summary = result.get("summary", "") - + # 检查必需字段是否为空 if not keywords or not summary: logger.warning(f"{self.log_prefix} LLM返回的JSON中缺少必需字段,原文\n{response}") @@ -1046,7 +984,7 @@ class ChatHistorySummarizer: # 1. 话题主题 # if theme: - # content_parts.append(f"话题:{theme}") + # content_parts.append(f"话题:{theme}") # 2. 概括内容 if summary: @@ -1134,6 +1072,3 @@ class ChatHistorySummarizer: traceback.print_exc() self._running = False - - -init_prompt() diff --git a/src/memory_system/memory_retrieval.py b/src/memory_system/memory_retrieval.py index bb2ed981..ff181829 100644 --- a/src/memory_system/memory_retrieval.py +++ b/src/memory_system/memory_retrieval.py @@ -4,7 +4,7 @@ import asyncio from typing import List, Dict, Any, Optional, Tuple from src.common.logger import get_logger from src.config.config import global_config, model_config -from src.chat.utils.prompt_builder import Prompt, global_prompt_manager +from src.prompt.prompt_manager import prompt_manager from src.plugin_system.apis import llm_api from src.common.database.database_model import ThinkingBack from src.memory_system.retrieval_tools import get_tool_registry, init_all_tools @@ -41,84 +41,11 @@ def _cleanup_stale_not_found_thinking_back() -> None: logger.error(f"清理未找到答案的thinking_back记录失败: {e}") -def init_memory_retrieval_prompt(): - """初始化记忆检索相关的 prompt 模板和工具""" - # 首先注册所有工具 +def init_memory_retrieval_sys(): + """初始化记忆检索相关工具""" + # 注册所有工具 init_all_tools() - # 第二步:ReAct Agent prompt(使用function calling,要求先思考再行动) - Prompt( - """你的名字是{bot_name}。现在是{time_now}。 -你正在参与聊天,你需要搜集信息来帮助你进行回复。 -重要,这是当前聊天记录: -{chat_history} -聊天记录结束 - -已收集的信息: -{collected_info} - -- 你可以对查询思路给出简短的思考:思考要简短,直接切入要点 -- 思考完毕后,使用工具 - -**工具说明:** -- 如果涉及过往事件,或者查询某个过去可能提到过的概念,或者某段时间发生的事件。可以使用lpmm知识库查询 -- 如果遇到不熟悉的词语、缩写、黑话或网络用语,可以使用query_words工具查询其含义 -- 你必须使用tool,如果需要查询你必须给出使用什么工具进行查询 -- 当你决定结束查询时,必须调用return_information工具返回总结信息并结束查询 -""", - name="memory_retrieval_react_prompt_head_lpmm", - ) - - # 第二步:ReAct Agent prompt(使用function calling,要求先思考再行动) - Prompt( - """你的名字是{bot_name}。现在是{time_now}。 -你正在参与聊天,你需要搜集信息来帮助你进行回复。 -当前聊天记录: -{chat_history} - -已收集的信息: -{collected_info} - -**工具说明:** -- 如果涉及过往事件,或者查询某个过去可能提到过的概念,或者某段时间发生的事件。可以使用聊天记录查询工具查询过往事件 -- 如果涉及人物,可以使用人物信息查询工具查询人物信息 -- 如果遇到不熟悉的词语、缩写、黑话或网络用语,可以使用query_words工具查询其含义 -- 如果没有可靠信息,且查询时间充足,或者不确定查询类别,也可以使用lpmm知识库查询,作为辅助信息 - -**思考** -- 你可以对查询思路给出简短的思考:思考要简短,直接切入要点 -- 先思考当前信息是否足够回答问题 -- 如果信息不足,则需要使用tool查询信息,你必须给出使用什么工具进行查询 -- 当你决定结束查询时,必须调用return_information工具返回总结信息并结束查询 -""", - name="memory_retrieval_react_prompt_head", - ) - - # 额外,如果最后一轮迭代:ReAct Agent prompt(使用function calling,要求先思考再行动) - Prompt( - """你的名字是{bot_name}。现在是{time_now}。 -你正在参与聊天,你需要根据搜集到的信息总结信息。 -如果搜集到的信息对于参与聊天,回答问题有帮助,请加入总结,如果无关,请不要加入到总结。 - -当前聊天记录: -{chat_history} - -已收集的信息: -{collected_info} - - -分析: -- 基于已收集的信息,总结出对当前聊天有帮助的相关信息 -- **如果收集的信息对当前聊天有帮助**,在思考中直接给出总结信息,格式为:return_information(information="你的总结信息") -- **如果信息无关或没有帮助**,在思考中给出:return_information(information="") - -**重要规则:** -- 必须严格使用检索到的信息回答问题,不要编造信息 -- 答案必须精简,不要过多解释 -""", - name="memory_retrieval_react_final_prompt", - ) - def _log_conversation_messages( conversation_messages: List[Message], @@ -304,17 +231,20 @@ async def _react_agent_solve_question( # 第一次构建,使用初始的collected_info(即initial_info) initial_collected_info = initial_info if initial_info else "" # 根据配置选择使用哪个 prompt - prompt_name = "memory_retrieval_react_prompt_head_lpmm" if global_config.experimental.lpmm_memory else "memory_retrieval_react_prompt_head" - first_head_prompt = await global_prompt_manager.format_prompt( - prompt_name, - bot_name=bot_name, - time_now=time_now, - chat_history=chat_history, - collected_info=initial_collected_info, - current_iteration=current_iteration, - remaining_iterations=remaining_iterations, - max_iterations=max_iterations, + prompt_name = ( + "memory_retrieval_react_prompt_head_lpmm" + if global_config.experimental.lpmm_memory + else "memory_retrieval_react_prompt_head" ) + first_head_prompt_template = prompt_manager.get_prompt(prompt_name) + first_head_prompt_template.add_context("bot_name", bot_name) + first_head_prompt_template.add_context("time_now", time_now) + first_head_prompt_template.add_context("chat_history", chat_history) + first_head_prompt_template.add_context("collected_info", initial_collected_info) + first_head_prompt_template.add_context("current_iteration", str(current_iteration)) + first_head_prompt_template.add_context("remaining_iterations", str(remaining_iterations)) + first_head_prompt_template.add_context("max_iterations", str(max_iterations)) + first_head_prompt = await prompt_manager.render_prompt(first_head_prompt_template) # 后续迭代都复用第一次构建的head_prompt head_prompt = first_head_prompt @@ -394,11 +324,11 @@ async def _react_agent_solve_question( """从文本中解析JSON格式的return_information,返回information字符串,如果未找到则返回None""" if not text: return None, None - + try: # 尝试提取JSON对象(可能包含在代码块中或直接是JSON) json_text = text.strip() - + # 如果包含代码块标记,提取JSON部分 if "```json" in json_text: start = json_text.find("```json") + 7 @@ -410,10 +340,10 @@ async def _react_agent_solve_question( end = json_text.find("```", start) if end != -1: json_text = json_text[start:end].strip() - + # 尝试解析JSON data = json.loads(json_text) - + # 检查是否包含return_information字段 if isinstance(data, dict) and "return_information" in data: information = data.get("information", "") @@ -422,20 +352,20 @@ async def _react_agent_solve_question( # 如果JSON解析失败,尝试在文本中查找JSON对象 try: # 查找第一个 { 和最后一个 } 之间的内容(更健壮的JSON提取) - first_brace = text.find('{') + first_brace = text.find("{") if first_brace != -1: # 从第一个 { 开始,找到匹配的 } brace_count = 0 json_end = -1 for i in range(first_brace, len(text)): - if text[i] == '{': + if text[i] == "{": brace_count += 1 - elif text[i] == '}': + elif text[i] == "}": brace_count -= 1 if brace_count == 0: json_end = i + 1 break - + if json_end != -1: json_text = text[first_brace:json_end] data = json.loads(json_text) @@ -444,9 +374,9 @@ async def _react_agent_solve_question( return information except (json.JSONDecodeError, ValueError, TypeError): pass - + return None - + # 尝试从文本中解析return_information函数调用 def parse_return_information_from_text(text: str): """从文本中解析return_information函数调用,返回information字符串,如果未找到则返回None""" @@ -462,14 +392,14 @@ async def _react_agent_solve_question( # 解析information参数(字符串,使用extract_quoted_content) information = extract_quoted_content(text, "return_information", "information") - + # 如果information存在(即使是空字符串),也返回它 return information # 首先尝试解析JSON格式 parsed_information_json = parse_json_return_information(response) is_json_format = parsed_information_json is not None - + # 如果JSON解析成功,使用JSON结果 if is_json_format: parsed_information = parsed_information_json @@ -519,9 +449,7 @@ async def _react_agent_solve_question( # 如果没有检测到return_information格式,记录思考过程,继续下一轮迭代 step["observations"] = [f"思考完成,但未调用工具。响应: {response}"] - logger.info( - f"{react_log_prefix}第 {iteration + 1} 次迭代 思考完成但未调用工具: {response}" - ) + logger.info(f"{react_log_prefix}第 {iteration + 1} 次迭代 思考完成但未调用工具: {response}") collected_info += f"思考: {response}" else: logger.warning(f"{react_log_prefix}第 {iteration + 1} 次迭代 无工具调用且无响应") @@ -566,9 +494,7 @@ async def _react_agent_solve_question( # 信息为空,直接退出查询 step["observations"] = ["检测到return_information工具调用,信息为空"] thinking_steps.append(step) - logger.info( - f"{react_log_prefix}第 {iteration + 1} 次迭代 通过return_information工具判断信息为空" - ) + logger.info(f"{react_log_prefix}第 {iteration + 1} 次迭代 通过return_information工具判断信息为空") _log_conversation_messages( conversation_messages, @@ -614,9 +540,7 @@ async def _react_agent_solve_question( return f"查询{tool_name_str}({param_str})的结果:{observation}" except Exception as e: error_msg = f"工具执行失败: {str(e)}" - logger.error( - f"{react_log_prefix}第 {iter_num + 1} 次迭代 工具 {tool_name_str} {error_msg}" - ) + logger.error(f"{react_log_prefix}第 {iter_num + 1} 次迭代 工具 {tool_name_str} {error_msg}") return f"查询{tool_name_str}失败: {error_msg}" tool_tasks.append(execute_single_tool(tool, tool_params, tool_name, iteration)) @@ -636,9 +560,7 @@ async def _react_agent_solve_question( for i, (tool_call_item, observation) in enumerate(zip(tool_calls, observations, strict=False)): if isinstance(observation, Exception): observation = f"工具执行异常: {str(observation)}" - logger.error( - f"{react_log_prefix}第 {iteration + 1} 次迭代 工具 {i + 1} 执行异常: {observation}" - ) + logger.error(f"{react_log_prefix}第 {iteration + 1} 次迭代 工具 {i + 1} 执行异常: {observation}") observation_text = observation if isinstance(observation, str) else str(observation) stripped_observation = observation_text.strip() @@ -740,16 +662,15 @@ async def _react_agent_solve_question( return None # 执行最终评估 - evaluation_prompt = await global_prompt_manager.format_prompt( - "memory_retrieval_react_final_prompt", - bot_name=bot_name, - time_now=time_now, - chat_history=chat_history, - collected_info=collected_info if collected_info else "暂无信息", - current_iteration=current_iteration, - remaining_iterations=remaining_iterations, - max_iterations=max_iterations, - ) + evaluation_prompt_template = prompt_manager.get_prompt("memory_retrieval_react_final_prompt") + evaluation_prompt_template.add_context("bot_name", bot_name) + evaluation_prompt_template.add_context("time_now", time_now) + evaluation_prompt_template.add_context("chat_history", chat_history) + evaluation_prompt_template.add_context("collected_info", collected_info if collected_info else "暂无信息") + evaluation_prompt_template.add_context("current_iteration", str(current_iteration)) + evaluation_prompt_template.add_context("remaining_iterations", str(remaining_iterations)) + evaluation_prompt_template.add_context("max_iterations", str(max_iterations)) + evaluation_prompt = await prompt_manager.render_prompt(evaluation_prompt_template) ( eval_success, @@ -788,7 +709,9 @@ async def _react_agent_solve_question( eval_step = { "iteration": current_iteration, "thought": f"[最终评估] {eval_response}", - "actions": [{"action_type": "return_information", "action_params": {"information": return_information_content}}], + "actions": [ + {"action_type": "return_information", "action_params": {"information": return_information_content}} + ], "observations": ["最终评估阶段检测到return_information"], } thinking_steps.append(eval_step) @@ -813,9 +736,7 @@ async def _react_agent_solve_question( eval_step = { "iteration": current_iteration, "thought": f"[最终评估] {eval_response}", - "actions": [ - {"action_type": "return_information", "action_params": {"information": ""}} - ], + "actions": [{"action_type": "return_information", "action_params": {"information": ""}}], "observations": ["已到达最大迭代次数,信息为空"], } thinking_steps.append(eval_step) @@ -1124,9 +1045,7 @@ async def build_memory_retrieval_prompt( end_time = time.time() if result: - logger.info( - f"{log_prefix}记忆检索成功,耗时: {(end_time - start_time):.3f}秒" - ) + logger.info(f"{log_prefix}记忆检索成功,耗时: {(end_time - start_time):.3f}秒") return f"你回忆起了以下信息:\n{result}\n如果与回复内容相关,可以参考这些回忆的信息。\n" else: logger.debug(f"{log_prefix}记忆检索未找到相关信息") diff --git a/src/plugin_system/core/tool_use.py b/src/plugin_system/core/tool_use.py index 6d60ee77..937dde2d 100644 --- a/src/plugin_system/core/tool_use.py +++ b/src/plugin_system/core/tool_use.py @@ -6,35 +6,13 @@ from src.plugin_system.core.global_announcement_manager import global_announceme from src.llm_models.utils_model import LLMRequest from src.llm_models.payload_content import ToolCall from src.config.config import global_config, model_config -from src.chat.utils.prompt_builder import Prompt, global_prompt_manager +from src.prompt.prompt_manager import prompt_manager from src.chat.message_receive.chat_stream import get_chat_manager from src.common.logger import get_logger logger = get_logger("tool_use") -def init_tool_executor_prompt(): - """初始化工具执行器的提示词""" - tool_executor_prompt = """ -你是一个专门执行工具的助手。你的名字是{bot_name}。现在是{time_now}。 -群里正在进行的聊天内容: -{chat_history} - -现在,{sender}发送了内容:{target_message},你想要回复ta。 -请仔细分析聊天内容,考虑以下几点: -1. 内容中是否包含需要查询信息的问题 -2. 是否有明确的工具使用指令 -你可以选择多个动作 - -If you need to use tools, please directly call the corresponding tool function. If you do not need to use any tool, simply output "No tool needed". -""" - Prompt(tool_executor_prompt, "tool_executor_prompt") - - -# 初始化提示词 -init_tool_executor_prompt() - - class ToolExecutor: """独立的工具执行器组件 @@ -101,22 +79,14 @@ class ToolExecutor: else: return [], [], "" - # print(f"tools: {tools}") - - # 获取当前时间 - time_now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) - - bot_name = global_config.bot.nickname - # 构建工具调用提示词 - prompt = await global_prompt_manager.format_prompt( - "tool_executor_prompt", - target_message=target_message, - chat_history=chat_history, - sender=sender, - bot_name=bot_name, - time_now=time_now, - ) + prompt_template = prompt_manager.get_prompt("tool_executor_prompt") + prompt_template.add_context("target_message", target_message) + prompt_template.add_context("chat_history", chat_history) + prompt_template.add_context("sender", sender) + prompt_template.add_context("bot_name", global_config.bot.nickname) + prompt_template.add_context("time_now", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + prompt = await prompt_manager.render_prompt(prompt_template) logger.debug(f"{self.log_prefix}开始LLM工具调用分析")