From a9179856ebb1fbe77b63d8b542ebb035fdad31b6 Mon Sep 17 00:00:00 2001
From: ChensenCHX <2087826155@qq.com>
Date: Thu, 27 Mar 2025 13:21:39 +0800
Subject: [PATCH] =?UTF-8?q?=E7=88=86=E6=94=B9=E4=B8=80=E4=B8=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitignore | 2 ++
config/auto_update.py | 7 +++++++
.../action_executer/action_executer.py | 16 +++++++++++---
src/plugins/chat/bot.py | 15 ++++++++++++-
src/plugins/chat/llm_generator.py | 12 +++++++++++
src/plugins/chat/prompt_builder.py | 4 ++++
src/plugins/config/config.py | 2 +-
template/actions_template.py | 21 +++++++++++++++++++
8 files changed, 74 insertions(+), 5 deletions(-)
create mode 100644 template/actions_template.py
diff --git a/.gitignore b/.gitignore
index 22e2612d..0cd14149 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,8 @@ memory_graph.gml
config/bot_config_dev.toml
config/bot_config.toml
config/bot_config.toml.bak
+config/actions.py
+config/__init__.py
src/plugins/remote/client_uuid.json
# Byte-compiled / optimized / DLL files
__pycache__/
diff --git a/config/auto_update.py b/config/auto_update.py
index a0d87852..241e97b2 100644
--- a/config/auto_update.py
+++ b/config/auto_update.py
@@ -14,6 +14,13 @@ def update_config():
template_path = template_dir / "bot_config_template.toml"
old_config_path = config_dir / "bot_config.toml"
new_config_path = config_dir / "bot_config.toml"
+ action_template_path = template_dir / "actions_template.py"
+ action_config_path = config_dir / "actions.py"
+
+ # 如果config里没有actions.py就复制一份过来
+ if not action_config_path.exists():
+ shutil.copy(action_template_path, action_config_path)
+ Path('config/__init__.py').touch()
# 读取旧配置文件
old_config = {}
diff --git a/src/plugins/action_executer/action_executer.py b/src/plugins/action_executer/action_executer.py
index 8edb7340..30b5a55d 100644
--- a/src/plugins/action_executer/action_executer.py
+++ b/src/plugins/action_executer/action_executer.py
@@ -1,6 +1,7 @@
class ResponseAction:
def __init__(self):
self.tags = []
+ self.msgs = []
def parse_action(self, msg: str, action: str) -> str:
if action in msg:
@@ -18,6 +19,15 @@ class ResponseAction:
# 非str输入直接抛异常
raise TypeError
- def __bool__(self):
- # 这是为了直接嵌入到llm_generator.py的处理流里 便于跳过消息处理流程
- return False
\ No newline at end of file
+
+
+
+from ....config.actions import usable_action_description
+
+extern_prompt = f"""
+``
+{'\n'.join(usable_action_description)}
+``
+你可以使用**``**中给出的标签来执行特定动作,请参考对应部分的描述。
+注意,标签一定是以“[内容]”形式输出的,你可以在一次响应中执行多个动作。
+"""
\ No newline at end of file
diff --git a/src/plugins/chat/bot.py b/src/plugins/chat/bot.py
index ed387c06..5a56d347 100644
--- a/src/plugins/chat/bot.py
+++ b/src/plugins/chat/bot.py
@@ -191,9 +191,17 @@ class ChatBot:
willing_manager.change_reply_willing_not_sent(chat)
# print(f"response: {response}")
+ response_action = None
+ if global_config.enable_action_execute:
+ from ..action_executer.action_executer import ResponseAction
+ from ....config.actions import usable_action
+ if isinstance(response, ResponseAction):
+ response_actions = response.tags
+ response = response.msgs
+
if response:
stream_id = message.chat_stream.stream_id
-
+
if global_config.enable_think_flow:
chat_talking_prompt = ""
if stream_id:
@@ -220,6 +228,11 @@ class ChatBot:
logger.warning("未找到对应的思考消息,可能已超时被移除")
return
+ # 清理掉思考消息后开始做发送前处理
+ if global_config.enable_action_execute:
+ for action in response_action:
+ await response = usable_action[action](response)
+
# 记录开始思考的时间,避免从思考到回复的时间太久
thinking_start_time = thinking_message.thinking_start_time
message_set = MessageSet(chat, think_id)
diff --git a/src/plugins/chat/llm_generator.py b/src/plugins/chat/llm_generator.py
index 95052ee7..85ea49fb 100644
--- a/src/plugins/chat/llm_generator.py
+++ b/src/plugins/chat/llm_generator.py
@@ -68,6 +68,18 @@ class ResponseGenerator:
# print(f"raw_content: {raw_content}")
# print(f"model_response: {model_response}")
+ if global_config.enable_action_execute:
+ from ..action_executer.action_executer import ResponseAction
+ from ....config.actions import usable_action
+ actions = ResponseAction()
+ for action in usable_action:
+ model_response = actions.parse_action(model_response, action)
+ if not actions.empty():
+ actions.msgs = await self._process_response(model_response)
+ if actions.msgs:
+ return actions, raw_content
+ return None, raw_content
+
if model_response:
logger.info(f"{global_config.BOT_NICKNAME}的回复是:{model_response}")
model_response = await self._process_response(model_response)
diff --git a/src/plugins/chat/prompt_builder.py b/src/plugins/chat/prompt_builder.py
index 6b33e988..b3ec27e1 100644
--- a/src/plugins/chat/prompt_builder.py
+++ b/src/plugins/chat/prompt_builder.py
@@ -174,6 +174,10 @@ class PromptBuilder:
请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景,
请注意不要输出多余内容(包括前后缀,冒号和引号,括号,表情等),只输出回复内容。
{moderation_prompt}不要输出多余内容(包括前后缀,冒号和引号,括号,表情包,at或 @等 )。"""
+
+ if global_config.enable_action_execute:
+ from ..action_executer.action_executer import extern_prompt
+ prompt = prompt + extern_prompt
prompt_check_if_response = ""
diff --git a/src/plugins/config/config.py b/src/plugins/config/config.py
index 37daf580..bc5a162e 100644
--- a/src/plugins/config/config.py
+++ b/src/plugins/config/config.py
@@ -115,7 +115,7 @@ class BotConfig:
# experimental
enable_friend_chat: bool = False # 是否启用好友聊天
enable_think_flow: bool = False # 是否启用思考流程
- enable_think_flow: bool = False # 是否允许执行动作
+ enable_action_execute: bool = False # 是否允许执行动作
# 模型配置
diff --git a/template/actions_template.py b/template/actions_template.py
new file mode 100644
index 00000000..b9f373ba
--- /dev/null
+++ b/template/actions_template.py
@@ -0,0 +1,21 @@
+from typing import Callable
+import time
+
+# 示例函数
+async def refuse_response(response: list[str]) -> list[str]:
+ return []
+async def ping_response(response: list[str]) -> list[str]:
+ return [f"Pong! at {time.asctime()}."]
+
+# 可用函数表 注意每个函数都应该接收一个list[str](输入的响应), 输出一个list[str](输出的响应)
+# 显然 你可以在函数里做各种操作来修改响应,做出其他动作,etc
+# 注意到MaiMBot基于Python 3.9, 所以这里的注册顺序实际上决定了tag的执行顺序,越上方的越靠前
+usable_action: dict[str, Callable[[list[str]], list[str]]] = {
+ "[ping]" : ping_response,
+ "[refuse]" : refuse_response,
+}
+
+usable_action_description = [
+ "[ping]: 此标签**仅**用于用户确认系统是否在线,输出此标签会**导致消息被替换为**`Pong! at %当前时间%`"
+ "[refuse]: 此标签用于标识认为无需回复的情况,输出此标签会**使得消息不被发送**。",
+]
\ No newline at end of file