Merge branch 'dev' into patch-2

This commit is contained in:
foxplaying
2025-09-28 07:33:15 +08:00
committed by GitHub
40 changed files with 867 additions and 254 deletions

2
.gitignore vendored
View File

@@ -323,6 +323,8 @@ run_pet.bat
!/plugins/hello_world_plugin !/plugins/hello_world_plugin
!/plugins/emoji_manage_plugin !/plugins/emoji_manage_plugin
!/plugins/take_picture_plugin !/plugins/take_picture_plugin
!/plugins/deep_think
!/plugins/__init__.py
config.toml config.toml

0
plugins/__init__.py Normal file
View File

View File

@@ -0,0 +1,34 @@
{
"manifest_version": 1,
"name": "Deep Think插件 (Deep Think Actions)",
"version": "1.0.0",
"description": "可以深度思考",
"author": {
"name": "SengokuCola",
"url": "https://github.com/MaiM-with-u"
},
"license": "GPL-v3.0-or-later",
"host_application": {
"min_version": "0.11.0"
},
"homepage_url": "https://github.com/MaiM-with-u/maibot",
"repository_url": "https://github.com/MaiM-with-u/maibot",
"keywords": ["deep", "think", "action", "built-in"],
"categories": ["Deep Think"],
"default_locale": "zh-CN",
"locales_path": "_locales",
"plugin_info": {
"is_built_in": true,
"plugin_type": "action_provider",
"components": [
{
"type": "action",
"name": "deep_think",
"description": "发送深度思考"
}
]
}
}

View File

@@ -0,0 +1,102 @@
from typing import List, Tuple, Type, Any
# 导入新插件系统
from src.plugin_system import BasePlugin, register_plugin, ComponentInfo
from src.plugin_system.base.config_types import ConfigField
from src.person_info.person_info import Person
from src.plugin_system.base.base_tool import BaseTool, ToolParamType
# 导入依赖的系统组件
from src.common.logger import get_logger
from src.plugins.built_in.relation.relation import BuildRelationAction
from src.plugin_system.apis import llm_api
logger = get_logger("relation_actions")
class DeepThinkTool(BaseTool):
"""获取用户信息"""
name = "deep_think"
description = "深度思考,对某个问题进行全面且深入的思考,当面临复杂环境或重要问题时,使用此获得更好的解决方案"
parameters = [
("question", ToolParamType.STRING, "需要思考的问题,越具体越好", True, None),
]
available_for_llm = True
async def execute(self, function_args: dict[str, Any]) -> dict[str, Any]:
"""执行比较两个数的大小
Args:
function_args: 工具参数
Returns:
dict: 工具执行结果
"""
question: str = function_args.get("question") # type: ignore
print(f"question: {question}")
prompt = f"""
请你思考以下问题,以简洁的一段话回答:
{question}
"""
models = llm_api.get_available_models()
chat_model_config = models.get("replyer") # 使用字典访问方式
success, thinking_result, _, _ = await llm_api.generate_with_model(
prompt, model_config=chat_model_config, request_type="deep_think"
)
print(f"thinking_result: {thinking_result}")
thinking_result =f"思考结果:{thinking_result}\n**注意** 因为你进行了深度思考,最后的回复内容可以回复的长一些,更加详细一些,不用太简洁。\n"
return {"content": thinking_result}
@register_plugin
class DeepThinkPlugin(BasePlugin):
"""关系动作插件
系统内置插件,提供基础的聊天交互功能:
- Reply: 回复动作
- NoReply: 不回复动作
- Emoji: 表情动作
注意插件基本信息优先从_manifest.json文件中读取
"""
# 插件基本信息
plugin_name: str = "deep_think" # 内部标识符
enable_plugin: bool = True
dependencies: list[str] = [] # 插件依赖列表
python_dependencies: list[str] = [] # Python包依赖列表
config_file_name: str = "config.toml"
# 配置节描述
config_section_descriptions = {
"plugin": "插件启用配置",
"components": "核心组件启用配置",
}
# 配置Schema定义
config_schema: dict = {
"plugin": {
"enabled": ConfigField(type=bool, default=False, description="是否启用插件"),
"config_version": ConfigField(type=str, default="2.0.0", description="配置文件版本"),
}
}
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
"""返回插件包含的组件列表"""
# --- 根据配置注册组件 ---
components = []
components.append((DeepThinkTool.get_tool_info(), DeepThinkTool))
return components

View File

@@ -1,26 +1,19 @@
import random from typing import List, Tuple, Type
from typing import List, Tuple, Type, Any
from src.plugin_system import ( from src.plugin_system import (
BasePlugin, BasePlugin,
register_plugin, register_plugin,
BaseAction,
BaseCommand, BaseCommand,
BaseTool,
ComponentInfo, ComponentInfo,
ActionActivationType,
ConfigField, ConfigField,
BaseEventHandler,
EventType,
MaiMessages,
ToolParamType,
ReplyContentType, ReplyContentType,
emoji_api, emoji_api,
) )
from maim_message import Seg from maim_message import Seg
from src.config.config import global_config
from src.common.logger import get_logger from src.common.logger import get_logger
logger = get_logger("emoji_manage_plugin") logger = get_logger("emoji_manage_plugin")
class AddEmojiCommand(BaseCommand): class AddEmojiCommand(BaseCommand):
command_name = "add_emoji" command_name = "add_emoji"
command_description = "添加表情包" command_description = "添加表情包"
@@ -29,7 +22,7 @@ class AddEmojiCommand(BaseCommand):
async def execute(self) -> Tuple[bool, str, bool]: async def execute(self) -> Tuple[bool, str, bool]:
# 查找消息中的表情包 # 查找消息中的表情包
# logger.info(f"查找消息中的表情包: {self.message.message_segment}") # logger.info(f"查找消息中的表情包: {self.message.message_segment}")
emoji_base64_list = self.find_and_return_emoji_in_message(self.message.message_segment) emoji_base64_list = self.find_and_return_emoji_in_message(self.message.message_segment)
if not emoji_base64_list: if not emoji_base64_list:
@@ -51,7 +44,7 @@ class AddEmojiCommand(BaseCommand):
emotions = result.get("emotions", []) emotions = result.get("emotions", [])
replaced = result.get("replaced", False) replaced = result.get("replaced", False)
result_msg = f"表情包 {i+1} 注册成功{'(替换旧表情包)' if replaced else '(新增表情包)'}" result_msg = f"表情包 {i + 1} 注册成功{'(替换旧表情包)' if replaced else '(新增表情包)'}"
if description: if description:
result_msg += f"\n描述: {description}" result_msg += f"\n描述: {description}"
if emotions: if emotions:
@@ -61,11 +54,11 @@ class AddEmojiCommand(BaseCommand):
else: else:
fail_count += 1 fail_count += 1
error_msg = result.get("message", "注册失败") error_msg = result.get("message", "注册失败")
results.append(f"表情包 {i+1} 注册失败: {error_msg}") results.append(f"表情包 {i + 1} 注册失败: {error_msg}")
except Exception as e: except Exception as e:
fail_count += 1 fail_count += 1
results.append(f"表情包 {i+1} 注册时发生错误: {str(e)}") results.append(f"表情包 {i + 1} 注册时发生错误: {str(e)}")
# 构建返回消息 # 构建返回消息
total_count = success_count + fail_count total_count = success_count + fail_count
@@ -140,6 +133,7 @@ class AddEmojiCommand(BaseCommand):
emoji_base64_list.extend(self.find_and_return_emoji_in_message(seg.data)) emoji_base64_list.extend(self.find_and_return_emoji_in_message(seg.data))
return emoji_base64_list return emoji_base64_list
class ListEmojiCommand(BaseCommand): class ListEmojiCommand(BaseCommand):
"""列表表情包Command - 响应/emoji list命令""" """列表表情包Command - 响应/emoji list命令"""
@@ -156,6 +150,7 @@ class ListEmojiCommand(BaseCommand):
# 解析命令参数 # 解析命令参数
import re import re
match = re.match(r"^/emoji list(?:\s+(\d+))?$", self.message.raw_message) match = re.match(r"^/emoji list(?:\s+(\d+))?$", self.message.raw_message)
max_count = 10 # 默认显示10个 max_count = 10 # 默认显示10个
if match and match.group(1): if match and match.group(1):
@@ -195,7 +190,7 @@ class ListEmojiCommand(BaseCommand):
display_emojis = all_emojis[:max_count] display_emojis = all_emojis[:max_count]
message_lines.append(f"\n📋 显示前 {len(display_emojis)} 个表情包:") message_lines.append(f"\n📋 显示前 {len(display_emojis)} 个表情包:")
for i, (emoji_base64, description, emotion) in enumerate(display_emojis, 1): for i, (_, description, emotion) in enumerate(display_emojis, 1):
# 截断过长的描述 # 截断过长的描述
short_desc = description[:50] + "..." if len(description) > 50 else description short_desc = description[:50] + "..." if len(description) > 50 else description
message_lines.append(f"{i}. {short_desc} [{emotion}]") message_lines.append(f"{i}. {short_desc} [{emotion}]")
@@ -257,7 +252,7 @@ class DeleteEmojiCommand(BaseCommand):
count_after = result.get("count_after", 0) count_after = result.get("count_after", 0)
emotions = result.get("emotions", []) emotions = result.get("emotions", [])
result_msg = f"表情包 {i+1} 删除成功" result_msg = f"表情包 {i + 1} 删除成功"
if description: if description:
result_msg += f"\n描述: {description}" result_msg += f"\n描述: {description}"
if emotions: if emotions:
@@ -268,11 +263,11 @@ class DeleteEmojiCommand(BaseCommand):
else: else:
fail_count += 1 fail_count += 1
error_msg = result.get("message", "删除失败") error_msg = result.get("message", "删除失败")
results.append(f"表情包 {i+1} 删除失败: {error_msg}") results.append(f"表情包 {i + 1} 删除失败: {error_msg}")
except Exception as e: except Exception as e:
fail_count += 1 fail_count += 1
results.append(f"表情包 {i+1} 删除时发生错误: {str(e)}") results.append(f"表情包 {i + 1} 删除时发生错误: {str(e)}")
# 构建返回消息 # 构建返回消息
total_count = success_count + fail_count total_count = success_count + fail_count
@@ -401,4 +396,4 @@ class EmojiManagePlugin(BasePlugin):
(AddEmojiCommand.get_command_info(), AddEmojiCommand), (AddEmojiCommand.get_command_info(), AddEmojiCommand),
(ListEmojiCommand.get_command_info(), ListEmojiCommand), (ListEmojiCommand.get_command_info(), ListEmojiCommand),
(DeleteEmojiCommand.get_command_info(), DeleteEmojiCommand), (DeleteEmojiCommand.get_command_info(), DeleteEmojiCommand),
] ]

View File

@@ -16,7 +16,6 @@ from src.chat.brain_chat.brain_planner import BrainPlanner
from src.chat.planner_actions.action_modifier import ActionModifier from src.chat.planner_actions.action_modifier import ActionModifier
from src.chat.planner_actions.action_manager import ActionManager from src.chat.planner_actions.action_manager import ActionManager
from src.chat.heart_flow.hfc_utils import CycleDetail from src.chat.heart_flow.hfc_utils import CycleDetail
from src.chat.heart_flow.hfc_utils import send_typing, stop_typing
from src.chat.express.expression_learner import expression_learner_manager from src.chat.express.expression_learner import expression_learner_manager
from src.person_info.person_info import Person from src.person_info.person_info import Person
from src.plugin_system.base.component_types import EventType, ActionInfo from src.plugin_system.base.component_types import EventType, ActionInfo
@@ -96,7 +95,6 @@ class BrainChatting:
self.last_read_time = time.time() - 2 self.last_read_time = time.time() - 2
self.more_plan = False self.more_plan = False
async def start(self): async def start(self):
"""检查是否需要启动主循环,如果未激活则启动。""" """检查是否需要启动主循环,如果未激活则启动。"""
@@ -171,10 +169,8 @@ class BrainChatting:
if len(recent_messages_list) >= 1: if len(recent_messages_list) >= 1:
self.last_read_time = time.time() self.last_read_time = time.time()
await self._observe( await self._observe(recent_messages_list=recent_messages_list)
recent_messages_list=recent_messages_list
)
else: else:
# Normal模式消息数量不足等待 # Normal模式消息数量不足等待
await asyncio.sleep(0.2) await asyncio.sleep(0.2)
@@ -233,11 +229,11 @@ class BrainChatting:
async def _observe( async def _observe(
self, # interest_value: float = 0.0, self, # interest_value: float = 0.0,
recent_messages_list: Optional[List["DatabaseMessages"]] = None recent_messages_list: Optional[List["DatabaseMessages"]] = None,
) -> bool: # sourcery skip: merge-else-if-into-elif, remove-redundant-if ) -> bool: # sourcery skip: merge-else-if-into-elif, remove-redundant-if
if recent_messages_list is None: if recent_messages_list is None:
recent_messages_list = [] recent_messages_list = []
reply_text = "" # 初始化reply_text变量避免UnboundLocalError _reply_text = "" # 初始化reply_text变量避免UnboundLocalError
async with global_prompt_manager.async_message_scope(self.chat_stream.context.get_template_name()): async with global_prompt_manager.async_message_scope(self.chat_stream.context.get_template_name()):
await self.expression_learner.trigger_learning_for_chat() await self.expression_learner.trigger_learning_for_chat()
@@ -334,7 +330,7 @@ class BrainChatting:
"taken_time": time.time(), "taken_time": time.time(),
} }
) )
reply_text = reply_text_from_reply _reply_text = reply_text_from_reply
else: else:
# 没有回复信息构建纯动作的loop_info # 没有回复信息构建纯动作的loop_info
loop_info = { loop_info = {
@@ -347,7 +343,7 @@ class BrainChatting:
"taken_time": time.time(), "taken_time": time.time(),
}, },
} }
reply_text = action_reply_text _reply_text = action_reply_text
self.end_cycle(loop_info, cycle_timers) self.end_cycle(loop_info, cycle_timers)
self.print_cycle_info(cycle_timers) self.print_cycle_info(cycle_timers)
@@ -484,7 +480,6 @@ class BrainChatting:
"""执行单个动作的通用函数""" """执行单个动作的通用函数"""
try: try:
with Timer(f"动作{action_planner_info.action_type}", cycle_timers): with Timer(f"动作{action_planner_info.action_type}", cycle_timers):
if action_planner_info.action_type == "no_reply": if action_planner_info.action_type == "no_reply":
# 直接处理no_action逻辑不再通过动作系统 # 直接处理no_action逻辑不再通过动作系统
reason = action_planner_info.reasoning or "选择不回复" reason = action_planner_info.reasoning or "选择不回复"
@@ -517,7 +512,9 @@ class BrainChatting:
if not success or not llm_response or not llm_response.reply_set: if not success or not llm_response or not llm_response.reply_set:
if action_planner_info.action_message: if action_planner_info.action_message:
logger.info(f"{action_planner_info.action_message.processed_plain_text} 的回复生成失败") logger.info(
f"{action_planner_info.action_message.processed_plain_text} 的回复生成失败"
)
else: else:
logger.info("回复生成失败") logger.info("回复生成失败")
return {"action_type": "reply", "success": False, "reply_text": "", "loop_info": None} return {"action_type": "reply", "success": False, "reply_text": "", "loop_info": None}

View File

@@ -307,7 +307,9 @@ class BrainPlanner:
if chat_target_info: if chat_target_info:
# 构建聊天上下文描述 # 构建聊天上下文描述
chat_context_description = f"你正在和 {chat_target_info.person_name or chat_target_info.user_nickname or '对方'} 聊天中" chat_context_description = (
f"你正在和 {chat_target_info.person_name or chat_target_info.user_nickname or '对方'} 聊天中"
)
# 构建动作选项块 # 构建动作选项块
action_options_block = await self._build_action_options_block(current_available_actions) action_options_block = await self._build_action_options_block(current_available_actions)

View File

@@ -10,11 +10,14 @@ from src.common.logger import get_logger
from src.common.database.database_model import Expression from src.common.database.database_model import Expression
from src.llm_models.utils_model import LLMRequest from src.llm_models.utils_model import LLMRequest
from src.config.config import model_config, global_config from src.config.config import model_config, global_config
from src.chat.utils.chat_message_builder import get_raw_msg_by_timestamp_with_chat_inclusive, build_anonymous_messages, build_bare_messages from src.chat.utils.chat_message_builder import (
get_raw_msg_by_timestamp_with_chat_inclusive,
build_anonymous_messages,
build_bare_messages,
)
from src.chat.utils.prompt_builder import Prompt, global_prompt_manager from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
from src.chat.message_receive.chat_stream import get_chat_manager from src.chat.message_receive.chat_stream import get_chat_manager
from json_repair import repair_json from json_repair import repair_json
from src.chat.utils.utils import get_embedding
MAX_EXPRESSION_COUNT = 300 MAX_EXPRESSION_COUNT = 300
@@ -99,7 +102,9 @@ class ExpressionLearner:
self.last_learning_time: float = time.time() self.last_learning_time: float = time.time()
# 学习参数 # 学习参数
_, self.enable_learning, self.learning_intensity = global_config.expression.get_expression_config_for_chat(self.chat_id) _, self.enable_learning, self.learning_intensity = global_config.expression.get_expression_config_for_chat(
self.chat_id
)
self.min_messages_for_learning = 15 / self.learning_intensity # 触发学习所需的最少消息数 self.min_messages_for_learning = 15 / self.learning_intensity # 触发学习所需的最少消息数
self.min_learning_interval = 150 / self.learning_intensity self.min_learning_interval = 150 / self.learning_intensity
@@ -237,17 +242,42 @@ class ExpressionLearner:
return [] return []
learnt_expressions = res learnt_expressions = res
learnt_expressions_str = "" learnt_expressions_str = ""
for _chat_id, situation, style, context, context_words, full_context, full_context_embedding in learnt_expressions: for (
_chat_id,
situation,
style,
_context,
_context_words,
_full_context,
_full_context_embedding,
) in learnt_expressions:
learnt_expressions_str += f"{situation}->{style}\n" learnt_expressions_str += f"{situation}->{style}\n"
logger.info(f"{self.chat_name} 学习到表达风格:\n{learnt_expressions_str}") logger.info(f"{self.chat_name} 学习到表达风格:\n{learnt_expressions_str}")
# 按chat_id分组 # 按chat_id分组
chat_dict: Dict[str, List[Dict[str, Any]]] = {} chat_dict: Dict[str, List[Dict[str, Any]]] = {}
for chat_id, situation, style, context, context_words, full_context, full_context_embedding in learnt_expressions: for (
chat_id,
situation,
style,
context,
context_words,
full_context,
full_context_embedding,
) in learnt_expressions:
if chat_id not in chat_dict: if chat_id not in chat_dict:
chat_dict[chat_id] = [] chat_dict[chat_id] = []
chat_dict[chat_id].append({"situation": situation, "style": style, "context": context, "context_words": context_words, "full_context": full_context, "full_context_embedding": full_context_embedding}) chat_dict[chat_id].append(
{
"situation": situation,
"style": style,
"context": context,
"context_words": context_words,
"full_context": full_context,
"full_context_embedding": full_context_embedding,
}
)
current_time = time.time() current_time = time.time()
@@ -300,11 +330,13 @@ class ExpressionLearner:
expr.delete_instance() expr.delete_instance()
return learnt_expressions return learnt_expressions
async def match_expression_context(self, expression_pairs: List[Tuple[str, str]], random_msg_match_str: str) -> List[Tuple[str, str, str]]: async def match_expression_context(
self, expression_pairs: List[Tuple[str, str]], random_msg_match_str: str
) -> List[Tuple[str, str, str]]:
# 为expression_pairs逐个条目赋予编号并构建成字符串 # 为expression_pairs逐个条目赋予编号并构建成字符串
numbered_pairs = [] numbered_pairs = []
for i, (situation, style) in enumerate(expression_pairs, 1): for i, (situation, style) in enumerate(expression_pairs, 1):
numbered_pairs.append(f"{i}. 当\"{situation}\"时,使用\"{style}\"") numbered_pairs.append(f'{i}. 当"{situation}"时,使用"{style}"')
expression_pairs_str = "\n".join(numbered_pairs) expression_pairs_str = "\n".join(numbered_pairs)
@@ -319,20 +351,20 @@ class ExpressionLearner:
print(f"match_expression_context_prompt: {prompt}") print(f"match_expression_context_prompt: {prompt}")
print(f"random_msg_match_str: {response}") print(f"random_msg_match_str: {response}")
# 解析JSON响应 # 解析JSON响应
match_responses = [] match_responses = []
try: try:
response = response.strip() response = response.strip()
# 检查是否已经是标准JSON数组格式 # 检查是否已经是标准JSON数组格式
if response.startswith('[') and response.endswith(']'): if response.startswith("[") and response.endswith("]"):
match_responses = json.loads(response) match_responses = json.loads(response)
else: else:
# 尝试直接解析多个JSON对象 # 尝试直接解析多个JSON对象
try: try:
# 如果是多个JSON对象用逗号分隔包装成数组 # 如果是多个JSON对象用逗号分隔包装成数组
if response.startswith('{') and not response.startswith('['): if response.startswith("{") and not response.startswith("["):
response = '[' + response + ']' response = "[" + response + "]"
match_responses = json.loads(response) match_responses = json.loads(response)
else: else:
# 使用repair_json处理响应 # 使用repair_json处理响应
@@ -394,7 +426,9 @@ class ExpressionLearner:
return matched_expressions return matched_expressions
async def learn_expression(self, num: int = 10) -> Optional[List[Tuple[str, str, str, List[str], str, List[float]]]]: async def learn_expression(
self, num: int = 10
) -> Optional[List[Tuple[str, str, str, List[str], str, List[float]]]]:
"""从指定聊天流学习表达方式 """从指定聊天流学习表达方式
Args: Args:
@@ -416,18 +450,17 @@ class ExpressionLearner:
if not random_msg or random_msg == []: if not random_msg or random_msg == []:
return None return None
# 转化成str # 转化成str
chat_id: str = random_msg[0].chat_id _chat_id: str = random_msg[0].chat_id
# random_msg_str: str = build_readable_messages(random_msg, timestamp_mode="normal") # random_msg_str: str = build_readable_messages(random_msg, timestamp_mode="normal")
random_msg_str: str = await build_anonymous_messages(random_msg) random_msg_str: str = await build_anonymous_messages(random_msg)
random_msg_match_str: str = await build_bare_messages(random_msg) random_msg_match_str: str = await build_bare_messages(random_msg)
prompt: str = await global_prompt_manager.format_prompt( prompt: str = await global_prompt_manager.format_prompt(
prompt, prompt,
chat_str=random_msg_str, chat_str=random_msg_str,
) )
print(f"random_msg_str:{random_msg_str}") # print(f"random_msg_str:{random_msg_str}")
logger.info(f"学习{type_str}的prompt: {prompt}") logger.info(f"学习{type_str}的prompt: {prompt}")
try: try:
@@ -440,24 +473,31 @@ class ExpressionLearner:
expressions: List[Tuple[str, str]] = self.parse_expression_response(response) expressions: List[Tuple[str, str]] = self.parse_expression_response(response)
matched_expressions: List[Tuple[str, str, str]] = await self.match_expression_context(expressions, random_msg_match_str) matched_expressions: List[Tuple[str, str, str]] = await self.match_expression_context(
expressions, random_msg_match_str
)
split_matched_expressions: List[Tuple[str, str, str, List[str]]] = self.split_expression_context(
matched_expressions
)
split_matched_expressions: List[Tuple[str, str, str, List[str]]] = self.split_expression_context(matched_expressions)
split_matched_expressions_w_emb = [] split_matched_expressions_w_emb = []
full_context_embedding: List[float] = await self.get_full_context_embedding(random_msg_match_str) full_context_embedding: List[float] = await self.get_full_context_embedding(random_msg_match_str)
for situation, style, context, context_words in split_matched_expressions:
split_matched_expressions_w_emb.append((self.chat_id, situation, style, context, context_words, random_msg_match_str,full_context_embedding))
for situation, style, context, context_words in split_matched_expressions:
split_matched_expressions_w_emb.append(
(self.chat_id, situation, style, context, context_words, random_msg_match_str, full_context_embedding)
)
return split_matched_expressions_w_emb return split_matched_expressions_w_emb
async def get_full_context_embedding(self, context: str) -> List[float]: async def get_full_context_embedding(self, context: str) -> List[float]:
embedding, _ = await self.embedding_model.get_embedding(context) embedding, _ = await self.embedding_model.get_embedding(context)
return embedding return embedding
def split_expression_context(self, matched_expressions: List[Tuple[str, str, str]]) -> List[Tuple[str, str, str, List[str]]]: def split_expression_context(
self, matched_expressions: List[Tuple[str, str, str]]
) -> List[Tuple[str, str, str, List[str]]]:
""" """
对matched_expressions中的context部分进行jieba分词 对matched_expressions中的context部分进行jieba分词

View File

@@ -114,10 +114,10 @@ class ExpressionSelector:
def get_related_chat_ids(self, chat_id: str) -> List[str]: def get_related_chat_ids(self, chat_id: str) -> List[str]:
"""根据expression_groups配置获取与当前chat_id相关的所有chat_id包括自身""" """根据expression_groups配置获取与当前chat_id相关的所有chat_id包括自身"""
groups = global_config.expression.expression_groups groups = global_config.expression.expression_groups
# 检查是否存在全局共享组(包含"*"的组) # 检查是否存在全局共享组(包含"*"的组)
global_group_exists = any("*" in group for group in groups) global_group_exists = any("*" in group for group in groups)
if global_group_exists: if global_group_exists:
# 如果存在全局共享组则返回所有可用的chat_id # 如果存在全局共享组则返回所有可用的chat_id
all_chat_ids = set() all_chat_ids = set()
@@ -126,7 +126,7 @@ class ExpressionSelector:
if chat_id_candidate := self._parse_stream_config_to_chat_id(stream_config_str): if chat_id_candidate := self._parse_stream_config_to_chat_id(stream_config_str):
all_chat_ids.add(chat_id_candidate) all_chat_ids.add(chat_id_candidate)
return list(all_chat_ids) if all_chat_ids else [chat_id] return list(all_chat_ids) if all_chat_ids else [chat_id]
# 否则使用现有的组逻辑 # 否则使用现有的组逻辑
for group in groups: for group in groups:
group_chat_ids = [] group_chat_ids = []

View File

@@ -43,4 +43,4 @@ class FrequencyControlManager:
# 创建全局实例 # 创建全局实例
frequency_control_manager = FrequencyControlManager() frequency_control_manager = FrequencyControlManager()

View File

@@ -25,6 +25,7 @@ from src.plugin_system.core import events_manager
from src.plugin_system.apis import generator_api, send_api, message_api, database_api from src.plugin_system.apis import generator_api, send_api, message_api, database_api
from src.mais4u.mai_think import mai_thinking_manager from src.mais4u.mai_think import mai_thinking_manager
from src.mais4u.s4u_config import s4u_config from src.mais4u.s4u_config import s4u_config
from src.chat.memory_system.Memory_chest import global_memory_chest
from src.chat.utils.chat_message_builder import ( from src.chat.utils.chat_message_builder import (
build_readable_messages_with_id, build_readable_messages_with_id,
get_raw_msg_before_timestamp_with_chat, get_raw_msg_before_timestamp_with_chat,
@@ -102,6 +103,7 @@ class HeartFChatting:
self.talk_threshold = global_config.chat.talk_value self.talk_threshold = global_config.chat.talk_value
self.no_reply_until_call = False self.no_reply_until_call = False
async def start(self): async def start(self):
"""检查是否需要启动主循环,如果未激活则启动。""" """检查是否需要启动主循环,如果未激活则启动。"""
@@ -206,7 +208,11 @@ class HeartFChatting:
# *控制频率用 # *控制频率用
if mentioned_message: if mentioned_message:
await self._observe(recent_messages_list=recent_messages_list, force_reply_message=mentioned_message) await self._observe(recent_messages_list=recent_messages_list, force_reply_message=mentioned_message)
elif random.random() < global_config.chat.talk_value * frequency_control_manager.get_or_create_frequency_control(self.stream_id).get_talk_frequency_adjust(): elif (
random.random()
< global_config.chat.talk_value
* frequency_control_manager.get_or_create_frequency_control(self.stream_id).get_talk_frequency_adjust()
):
await self._observe(recent_messages_list=recent_messages_list) await self._observe(recent_messages_list=recent_messages_list)
else: else:
# 没有提到继续保持沉默等待5秒防止频繁触发 # 没有提到继续保持沉默等待5秒防止频繁触发
@@ -276,14 +282,17 @@ class HeartFChatting:
recent_messages_list = [] recent_messages_list = []
reply_text = "" # 初始化reply_text变量避免UnboundLocalError reply_text = "" # 初始化reply_text变量避免UnboundLocalError
start_time = time.time() start_time = time.time()
if s4u_config.enable_s4u: if s4u_config.enable_s4u:
await send_typing() await send_typing()
async with global_prompt_manager.async_message_scope(self.chat_stream.context.get_template_name()): async with global_prompt_manager.async_message_scope(self.chat_stream.context.get_template_name()):
await self.expression_learner.trigger_learning_for_chat() await self.expression_learner.trigger_learning_for_chat()
await global_memory_chest.build_running_content(chat_id=self.stream_id)
cycle_timers, thinking_id = self.start_cycle() cycle_timers, thinking_id = self.start_cycle()
logger.info(f"{self.log_prefix} 开始第{self._cycle_counter}次思考") logger.info(f"{self.log_prefix} 开始第{self._cycle_counter}次思考")
@@ -350,7 +359,7 @@ class HeartFChatting:
available_actions=available_actions, available_actions=available_actions,
) )
) )
logger.info( logger.info(
f"{self.log_prefix} 决定执行{len(action_to_use_info)}个动作: {' '.join([a.action_type for a in action_to_use_info])}" f"{self.log_prefix} 决定执行{len(action_to_use_info)}个动作: {' '.join([a.action_type for a in action_to_use_info])}"
) )
@@ -412,7 +421,7 @@ class HeartFChatting:
}, },
} }
reply_text = action_reply_text reply_text = action_reply_text
self.end_cycle(loop_info, cycle_timers) self.end_cycle(loop_info, cycle_timers)
self.print_cycle_info(cycle_timers) self.print_cycle_info(cycle_timers)
@@ -423,11 +432,6 @@ class HeartFChatting:
else: else:
await asyncio.sleep(0.1) await asyncio.sleep(0.1)
"""S4U内容暂时保留""" """S4U内容暂时保留"""
if s4u_config.enable_s4u: if s4u_config.enable_s4u:
await stop_typing() await stop_typing()

View File

@@ -1,10 +1,8 @@
import asyncio
import re import re
import traceback import traceback
from typing import Tuple, TYPE_CHECKING from typing import Tuple, TYPE_CHECKING
from src.config.config import global_config
from src.chat.message_receive.message import MessageRecv from src.chat.message_receive.message import MessageRecv
from src.chat.message_receive.storage import MessageStorage from src.chat.message_receive.storage import MessageStorage
from src.chat.heart_flow.heartflow import heartflow from src.chat.heart_flow.heartflow import heartflow
@@ -74,7 +72,7 @@ class HeartFCMessageReceiver:
await self.storage.store_message(message, chat) await self.storage.store_message(message, chat)
heartflow_chat: HeartFChatting = await heartflow.get_or_create_heartflow_chat(chat.stream_id) # type: ignore _heartflow_chat: HeartFChatting = await heartflow.get_or_create_heartflow_chat(chat.stream_id) # type: ignore
# 3. 日志记录 # 3. 日志记录
mes_name = chat.group_info.group_name if chat.group_info else "私聊" mes_name = chat.group_info.group_name if chat.group_info else "私聊"
@@ -102,7 +100,7 @@ class HeartFCMessageReceiver:
replace_bot_name=True, replace_bot_name=True,
) )
# if not processed_plain_text: # if not processed_plain_text:
# print(message) # print(message)
logger.info(f"[{mes_name}]{userinfo.user_nickname}:{processed_plain_text}") # type: ignore logger.info(f"[{mes_name}]{userinfo.user_nickname}:{processed_plain_text}") # type: ignore

View File

@@ -0,0 +1,321 @@
from src.llm_models.utils_model import LLMRequest
from src.config.config import model_config
from src.common.database.database_model import MemoryChest as MemoryChestModel
from src.common.logger import get_logger
from src.config.config import global_config
from src.plugin_system.apis.message_api import build_readable_messages
import time
from src.plugin_system.apis.message_api import get_raw_msg_by_timestamp_with_chat
logger = get_logger("memory_chest")
class MemoryChest:
def __init__(self):
self.LLMRequest = LLMRequest(
model_set=model_config.model_task_config.utils_small,
request_type="memory_chest",
)
self.memory_build_threshold = 20
self.memory_size_limit = 300
self.running_content_list = {} # {chat_id: {"content": running_content, "last_update_time": timestamp}}
self.fetched_memory_list = [] # [(chat_id, (question, answer, timestamp)), ...]
async def build_running_content(self, chat_id: str = None) -> str:
"""
构建记忆仓库的运行内容
Args:
message_str: 消息内容
chat_id: 聊天ID用于提取对应的运行内容
Returns:
str: 构建后的运行内容
"""
# 检查是否需要更新上次更新时间和现在时间的消息数量大于30
if chat_id not in self.running_content_list:
self.running_content_list[chat_id] = {
"content": "",
"last_update_time": time.time()
}
should_update = True
if chat_id and chat_id in self.running_content_list:
last_update_time = self.running_content_list[chat_id]["last_update_time"]
current_time = time.time()
# 使用message_api获取消息数量
message_list = get_raw_msg_by_timestamp_with_chat(
timestamp_start=last_update_time,
timestamp_end=current_time,
chat_id=chat_id,
limit=global_config.chat.max_context_size * 2,
)
new_messages_count = len(message_list)
should_update = new_messages_count > self.memory_build_threshold
logger.info(f"chat_id {chat_id} 自上次更新后有 {new_messages_count} 条新消息,{'需要' if should_update else '不需要'}更新")
if should_update:
# 如果有chat_id先提取对应的running_content
message_str = build_readable_messages(
message_list,
replace_bot_name=True,
timestamp_mode="relative",
read_mark=0.0,
show_actions=True,
)
current_running_content = ""
if chat_id and chat_id in self.running_content_list:
current_running_content = self.running_content_list[chat_id]["content"]
prompt = f"""
以下是你的记忆内容:
{current_running_content}
请将下面的新聊天记录内的有用的信息,添加到你的记忆中
请主要关注概念和知识,而不是聊天的琐事
记忆为一段纯文本,逻辑清晰,指出事件,概念的含义,并说明关系
请输出添加后的记忆内容,不要输出其他内容:
{message_str}
"""
if global_config.debug.show_prompt:
logger.info(f"记忆仓库构建运行内容 prompt: {prompt}")
else:
logger.debug(f"记忆仓库构建运行内容 prompt: {prompt}")
running_content, (reasoning_content, model_name, tool_calls) = await self.LLMRequest.generate_response_async(prompt)
print(f"记忆仓库构建运行内容: {running_content}")
# 如果有chat_id更新对应的running_content
if chat_id and running_content:
self.running_content_list[chat_id] = {
"content": running_content,
"last_update_time": time.time()
}
# 检查running_content长度是否大于500
if len(running_content) > self.memory_size_limit:
await self._save_to_database_and_clear(chat_id, running_content)
return running_content
def get_all_titles(self) -> list[str]:
"""
获取记忆仓库中的所有标题
Returns:
list: 包含所有标题的列表
"""
try:
# 查询所有记忆记录的标题
titles = []
for memory in MemoryChestModel.select():
if memory.title:
titles.append(memory.title)
return titles
except Exception as e:
print(f"获取记忆标题时出错: {e}")
return []
async def get_answer_by_question(self, chat_id: str = "", question: str = "") -> str:
"""
根据问题获取答案
"""
title = await self.select_title_by_question(question)
if not title:
return ""
for memory in MemoryChestModel.select():
if memory.title == title:
content = memory.content
prompt = f"""
{content}
请根据问题:{question}
在上方内容中,提取相关信息的原文并输出,请务必提取上面原文,不要输出其他内容:
"""
if global_config.debug.show_prompt:
logger.info(f"记忆仓库获取答案 prompt: {prompt}")
else:
logger.debug(f"记忆仓库获取答案 prompt: {prompt}")
answer, (reasoning_content, model_name, tool_calls) = await self.LLMRequest.generate_response_async(prompt)
logger.info(f"记忆仓库获取答案: {answer}")
# 将问题和答案存到fetched_memory_list
if chat_id and answer:
self.fetched_memory_list.append((chat_id, (question, answer, time.time())))
# 清理fetched_memory_list
self._cleanup_fetched_memory_list()
return answer
def get_chat_memories_as_string(self, chat_id: str) -> str:
"""
获取某个chat_id的所有记忆并构建成字符串
Args:
chat_id: 聊天ID
Returns:
str: 格式化的记忆字符串格式问题xxx,答案:xxxxx\n问题xxx,答案:xxxxx\n...
"""
try:
memories = []
# 从fetched_memory_list中获取该chat_id的所有记忆
for cid, (question, answer, timestamp) in self.fetched_memory_list:
if cid == chat_id:
memories.append(f"问题:{question},答案:{answer}")
# 按时间戳排序(最新的在后面)
memories.sort()
# 用换行符连接所有记忆
result = "\n".join(memories)
logger.info(f"chat_id {chat_id} 共有 {len(memories)} 条记忆")
return result
except Exception as e:
logger.error(f"获取chat_id {chat_id} 的记忆时出错: {e}")
return ""
async def select_title_by_question(self, question: str) -> str:
"""
根据消息内容选择最匹配的标题
Args:
question: 问题
Returns:
str: 选择的标题
"""
# 获取所有标题并构建格式化字符串
titles = self.get_all_titles()
formatted_titles = ""
for title in titles:
formatted_titles += f"{title}\n"
prompt = f"""
所有主题:
{formatted_titles}
请根据以下问题,选择一个能够回答问题的主题:
问题:{question}
请你输出主题,不要输出其他内容,完整输出主题名:
"""
if global_config.debug.show_prompt:
logger.info(f"记忆仓库选择标题 prompt: {prompt}")
else:
logger.debug(f"记忆仓库选择标题 prompt: {prompt}")
title, (reasoning_content, model_name, tool_calls) = await self.LLMRequest.generate_response_async(prompt)
# 根据 title 获取 titles 里的对应项
titles = self.get_all_titles()
selected_title = None
# 查找完全匹配的标题
for t in titles:
if t == title:
selected_title = t
break
logger.info(f"记忆仓库选择标题: {selected_title}")
return selected_title
def _cleanup_fetched_memory_list(self):
"""
清理fetched_memory_list移除超过10分钟的记忆和超过10条的最旧记忆
"""
try:
current_time = time.time()
ten_minutes_ago = current_time - 600 # 10分钟 = 600秒
# 移除超过10分钟的记忆
self.fetched_memory_list = [
(chat_id, (question, answer, timestamp))
for chat_id, (question, answer, timestamp) in self.fetched_memory_list
if timestamp > ten_minutes_ago
]
# 如果记忆条数超过10条移除最旧的5条
if len(self.fetched_memory_list) > 10:
# 按时间戳排序移除最旧的5条
self.fetched_memory_list.sort(key=lambda x: x[1][2]) # 按timestamp排序
self.fetched_memory_list = self.fetched_memory_list[5:] # 保留最新的5条
logger.debug(f"fetched_memory_list清理后当前有 {len(self.fetched_memory_list)} 条记忆")
except Exception as e:
logger.error(f"清理fetched_memory_list时出错: {e}")
async def _save_to_database_and_clear(self, chat_id: str, content: str):
"""
生成标题保存到数据库并清空对应chat_id的running_content
Args:
chat_id: 聊天ID
content: 要保存的内容
"""
try:
# 生成标题
title_prompt = f"""
请为以下内容生成一个描述全面的标题,要求描述内容的主要概念和事件:
{content}
请只输出标题,不要输出其他内容:
"""
if global_config.debug.show_prompt:
logger.info(f"记忆仓库生成标题 prompt: {title_prompt}")
else:
logger.debug(f"记忆仓库生成标题 prompt: {title_prompt}")
title, (reasoning_content, model_name, tool_calls) = await self.LLMRequest.generate_response_async(title_prompt)
if title:
# 保存到数据库
MemoryChestModel.create(
title=title.strip(),
content=content
)
logger.info(f"已保存记忆仓库内容,标题: {title.strip()}, chat_id: {chat_id}")
# 清空对应chat_id的running_content
if chat_id in self.running_content_list:
del self.running_content_list[chat_id]
logger.info(f"已清空chat_id {chat_id} 的running_content")
else:
logger.warning(f"生成标题失败chat_id: {chat_id}")
except Exception as e:
logger.error(f"保存记忆仓库内容时出错: {e}")
global_memory_chest = MemoryChest()

View File

@@ -8,7 +8,7 @@ from maim_message import UserInfo, Seg, GroupInfo
from src.common.logger import get_logger from src.common.logger import get_logger
from src.config.config import global_config from src.config.config import global_config
from src.mood.mood_manager import mood_manager # 导入情绪管理器 from src.mood.mood_manager import mood_manager # 导入情绪管理器
from src.chat.message_receive.chat_stream import get_chat_manager, ChatStream from src.chat.message_receive.chat_stream import get_chat_manager
from src.chat.message_receive.message import MessageRecv, MessageRecvS4U from src.chat.message_receive.message import MessageRecv, MessageRecvS4U
from src.chat.message_receive.storage import MessageStorage from src.chat.message_receive.storage import MessageStorage
from src.chat.heart_flow.heartflow_message_processor import HeartFCMessageReceiver from src.chat.heart_flow.heartflow_message_processor import HeartFCMessageReceiver

View File

@@ -109,7 +109,7 @@ no_reply_until_call
""" """
{action_name} {action_name}
动作描述:{action_description} 动作描述:{action_description}
使用条件: 使用条件{parallel_text}
{action_require} {action_require}
{{ {{
"action": "{action_name}",{action_parameters}, "action": "{action_name}",{action_parameters},
@@ -343,7 +343,6 @@ class ActionPlanner:
interest=interest, interest=interest,
plan_style=global_config.personality.plan_style, plan_style=global_config.personality.plan_style,
) )
return prompt, message_id_list return prompt, message_id_list
except Exception as e: except Exception as e:
@@ -421,6 +420,11 @@ class ActionPlanner:
for require_item in action_info.action_require: for require_item in action_info.action_require:
require_text += f"- {require_item}\n" require_text += f"- {require_item}\n"
require_text = require_text.rstrip("\n") require_text = require_text.rstrip("\n")
if not action_info.parallel_action:
parallel_text = "(当选择这个动作时,请不要选择其他动作)"
else:
parallel_text = ""
# 获取动作提示模板并填充 # 获取动作提示模板并填充
using_action_prompt = await global_prompt_manager.get_prompt_async("action_prompt") using_action_prompt = await global_prompt_manager.get_prompt_async("action_prompt")
@@ -429,6 +433,7 @@ class ActionPlanner:
action_description=action_info.description, action_description=action_info.description,
action_parameters=param_text, action_parameters=param_text,
action_require=require_text, action_require=require_text,
parallel_text=parallel_text,
) )
action_options_block += using_action_prompt action_options_block += using_action_prompt
@@ -502,9 +507,7 @@ class ActionPlanner:
action.action_data = action.action_data or {} action.action_data = action.action_data or {}
action.action_data["loop_start_time"] = loop_start_time action.action_data["loop_start_time"] = loop_start_time
logger.debug( logger.debug(f"{self.log_prefix}规划器选择了{len(actions)}个动作: {' '.join([a.action_type for a in actions])}")
f"{self.log_prefix}规划器选择了{len(actions)}个动作: {' '.join([a.action_type for a in actions])}"
)
return actions return actions

View File

@@ -6,6 +6,7 @@ import re
from typing import List, Optional, Dict, Any, Tuple from typing import List, Optional, Dict, Any, Tuple
from datetime import datetime from datetime import datetime
from src.chat.memory_system.Memory_chest import global_memory_chest
from src.mais4u.mai_think import mai_thinking_manager from src.mais4u.mai_think import mai_thinking_manager
from src.common.logger import get_logger from src.common.logger import get_logger
from src.common.data_models.database_data_model import DatabaseMessages from src.common.data_models.database_data_model import DatabaseMessages
@@ -27,7 +28,7 @@ from src.chat.utils.chat_message_builder import (
from src.chat.express.expression_selector import expression_selector from src.chat.express.expression_selector import expression_selector
# from src.chat.memory_system.memory_activator import MemoryActivator # from src.chat.memory_system.memory_activator import MemoryActivator
from src.person_info.person_info import Person, is_person_known from src.person_info.person_info import Person
from src.plugin_system.base.component_types import ActionInfo, EventType from src.plugin_system.base.component_types import ActionInfo, EventType
from src.plugin_system.apis import llm_api from src.plugin_system.apis import llm_api
@@ -42,6 +43,7 @@ init_rewrite_prompt()
logger = get_logger("replyer") logger = get_logger("replyer")
class DefaultReplyer: class DefaultReplyer:
def __init__( def __init__(
self, self,
@@ -215,7 +217,7 @@ class DefaultReplyer:
traceback.print_exc() traceback.print_exc()
return False, llm_response return False, llm_response
#移动到 relation插件中构建 # 移动到 relation插件中构建
# async def build_relation_info(self, chat_content: str, sender: str, person_list: List[Person]): # async def build_relation_info(self, chat_content: str, sender: str, person_list: List[Person]):
# if not global_config.relationship.enable_relationship: # if not global_config.relationship.enable_relationship:
# return "" # return ""
@@ -277,9 +279,7 @@ class DefaultReplyer:
expression_habits_block = "" expression_habits_block = ""
expression_habits_title = "" expression_habits_title = ""
if style_habits_str.strip(): if style_habits_str.strip():
expression_habits_title = ( expression_habits_title = "在回复时,你可以参考以下的语言习惯,不要生硬使用:"
"在回复时,你可以参考以下的语言习惯,不要生硬使用:"
)
expression_habits_block += f"{style_habits_str}\n" expression_habits_block += f"{style_habits_str}\n"
return f"{expression_habits_title}\n{expression_habits_block}", selected_ids return f"{expression_habits_title}\n{expression_habits_block}", selected_ids
@@ -315,6 +315,17 @@ class DefaultReplyer:
# memory_str += f"- {instant_memory}\n" # memory_str += f"- {instant_memory}\n"
# return memory_str # return memory_str
async def build_memory_block(self) -> str:
"""构建记忆块
"""
# if not global_config.memory.enable_memory:
# return ""
if global_memory_chest.get_chat_memories_as_string(self.chat_stream.stream_id):
return f"你有以下记忆:\n{global_memory_chest.get_chat_memories_as_string(self.chat_stream.stream_id)}"
else:
return ""
async def build_tool_info(self, chat_history: str, sender: str, target: str, enable_tool: bool = True) -> str: async def build_tool_info(self, chat_history: str, sender: str, target: str, enable_tool: bool = True) -> str:
"""构建工具信息块 """构建工具信息块
@@ -498,7 +509,6 @@ class DefaultReplyer:
-------------------------------- --------------------------------
""" """
# 构建背景对话 prompt # 构建背景对话 prompt
all_dialogue_prompt = "" all_dialogue_prompt = ""
if message_list_before_now: if message_list_before_now:
@@ -524,7 +534,6 @@ class DefaultReplyer:
time_block: str, time_block: str,
chat_target_1: str, chat_target_1: str,
chat_target_2: str, chat_target_2: str,
identity_block: str, identity_block: str,
sender: str, sender: str,
target: str, target: str,
@@ -701,6 +710,7 @@ class DefaultReplyer:
# self.build_relation_info(chat_talking_prompt_short, sender, person_list_short), "relation_info" # self.build_relation_info(chat_talking_prompt_short, sender, person_list_short), "relation_info"
# ), # ),
# self._time_and_run_task(self.build_memory_block(message_list_before_short, target), "memory_block"), # self._time_and_run_task(self.build_memory_block(message_list_before_short, target), "memory_block"),
self._time_and_run_task(self.build_memory_block(), "memory_block"),
self._time_and_run_task( self._time_and_run_task(
self.build_tool_info(chat_talking_prompt_short, sender, target, enable_tool=enable_tool), "tool_info" self.build_tool_info(chat_talking_prompt_short, sender, target, enable_tool=enable_tool), "tool_info"
), ),
@@ -714,6 +724,7 @@ class DefaultReplyer:
"expression_habits": "选取表达方式", "expression_habits": "选取表达方式",
"relation_info": "感受关系", "relation_info": "感受关系",
# "memory_block": "回忆", # "memory_block": "回忆",
"memory_block": "记忆",
"tool_info": "使用工具", "tool_info": "使用工具",
"prompt_info": "获取知识", "prompt_info": "获取知识",
"actions_info": "动作信息", "actions_info": "动作信息",
@@ -742,6 +753,7 @@ class DefaultReplyer:
selected_expressions: List[int] selected_expressions: List[int]
# relation_info: str = results_dict["relation_info"] # relation_info: str = results_dict["relation_info"]
# memory_block: str = results_dict["memory_block"] # memory_block: str = results_dict["memory_block"]
memory_block: str = results_dict["memory_block"]
tool_info: str = results_dict["tool_info"] tool_info: str = results_dict["tool_info"]
prompt_info: str = results_dict["prompt_info"] # 直接使用格式化后的结果 prompt_info: str = results_dict["prompt_info"] # 直接使用格式化后的结果
actions_info: str = results_dict["actions_info"] actions_info: str = results_dict["actions_info"]
@@ -759,13 +771,9 @@ class DefaultReplyer:
if sender: if sender:
if is_group_chat: if is_group_chat:
reply_target_block = ( reply_target_block = f"现在{sender}说的:{target}。引起了你的注意"
f"现在{sender}说的:{target}。引起了你的注意"
)
else: # private chat else: # private chat
reply_target_block = ( reply_target_block = f"现在{sender}说的:{target}。引起了你的注意"
f"现在{sender}说的:{target}。引起了你的注意"
)
else: else:
reply_target_block = "" reply_target_block = ""
@@ -779,6 +787,7 @@ class DefaultReplyer:
"replyer_self_prompt", "replyer_self_prompt",
expression_habits_block=expression_habits_block, expression_habits_block=expression_habits_block,
tool_info_block=tool_info, tool_info_block=tool_info,
memory_block=memory_block,
knowledge_prompt=prompt_info, knowledge_prompt=prompt_info,
# memory_block=memory_block, # memory_block=memory_block,
# relation_info_block=relation_info, # relation_info_block=relation_info,
@@ -798,6 +807,7 @@ class DefaultReplyer:
"replyer_prompt", "replyer_prompt",
expression_habits_block=expression_habits_block, expression_habits_block=expression_habits_block,
tool_info_block=tool_info, tool_info_block=tool_info,
memory_block=memory_block,
knowledge_prompt=prompt_info, knowledge_prompt=prompt_info,
# memory_block=memory_block, # memory_block=memory_block,
# relation_info_block=relation_info, # relation_info_block=relation_info,
@@ -946,7 +956,7 @@ class DefaultReplyer:
async def llm_generate_content(self, prompt: str): async def llm_generate_content(self, prompt: str):
with Timer("LLM生成", {}): # 内部计时器,可选保留 with Timer("LLM生成", {}): # 内部计时器,可选保留
# 直接使用已初始化的模型实例 # 直接使用已初始化的模型实例
# logger.info(f"\n{prompt}\n") logger.info(f"\n{prompt}\n")
if global_config.debug.show_prompt: if global_config.debug.show_prompt:
logger.info(f"\n{prompt}\n") logger.info(f"\n{prompt}\n")
@@ -1044,6 +1054,3 @@ def weighted_sample_no_replacement(items, weights, k) -> list:
pool.pop(idx) pool.pop(idx)
break break
return selected return selected

View File

@@ -6,6 +6,7 @@ import re
from typing import List, Optional, Dict, Any, Tuple from typing import List, Optional, Dict, Any, Tuple
from datetime import datetime from datetime import datetime
from src.chat.memory_system.Memory_chest import global_memory_chest
from src.mais4u.mai_think import mai_thinking_manager from src.mais4u.mai_think import mai_thinking_manager
from src.common.logger import get_logger from src.common.logger import get_logger
from src.common.data_models.database_data_model import DatabaseMessages from src.common.data_models.database_data_model import DatabaseMessages
@@ -312,6 +313,15 @@ class PrivateReplyer:
# return memory_str # return memory_str
async def build_memory_block(self) -> str:
"""构建记忆块
"""
if global_memory_chest.get_chat_memories_as_string(self.chat_stream.stream_id):
return f"你有以下记忆:\n{global_memory_chest.get_chat_memories_as_string(self.chat_stream.stream_id)}"
else:
return ""
async def build_tool_info(self, chat_history: str, sender: str, target: str, enable_tool: bool = True) -> str: async def build_tool_info(self, chat_history: str, sender: str, target: str, enable_tool: bool = True) -> str:
"""构建工具信息块 """构建工具信息块
@@ -582,6 +592,7 @@ class PrivateReplyer:
self._time_and_run_task( self._time_and_run_task(
self.build_relation_info(chat_talking_prompt_short, sender), "relation_info" self.build_relation_info(chat_talking_prompt_short, sender), "relation_info"
), ),
self._time_and_run_task(self.build_memory_block(), "memory_block"),
# self._time_and_run_task(self.build_memory_block(message_list_before_short, target), "memory_block"), # self._time_and_run_task(self.build_memory_block(message_list_before_short, target), "memory_block"),
self._time_and_run_task( self._time_and_run_task(
self.build_tool_info(chat_talking_prompt_short, sender, target, enable_tool=enable_tool), "tool_info" self.build_tool_info(chat_talking_prompt_short, sender, target, enable_tool=enable_tool), "tool_info"
@@ -595,7 +606,7 @@ class PrivateReplyer:
task_name_mapping = { task_name_mapping = {
"expression_habits": "选取表达方式", "expression_habits": "选取表达方式",
"relation_info": "感受关系", "relation_info": "感受关系",
# "memory_block": "回忆", "memory_block": "回忆",
"tool_info": "使用工具", "tool_info": "使用工具",
"prompt_info": "获取知识", "prompt_info": "获取知识",
"actions_info": "动作信息", "actions_info": "动作信息",
@@ -623,7 +634,7 @@ class PrivateReplyer:
expression_habits_block: str expression_habits_block: str
selected_expressions: List[int] selected_expressions: List[int]
relation_info: str = results_dict["relation_info"] relation_info: str = results_dict["relation_info"]
# memory_block: str = results_dict["memory_block"] memory_block: str = results_dict["memory_block"]
tool_info: str = results_dict["tool_info"] tool_info: str = results_dict["tool_info"]
prompt_info: str = results_dict["prompt_info"] # 直接使用格式化后的结果 prompt_info: str = results_dict["prompt_info"] # 直接使用格式化后的结果
actions_info: str = results_dict["actions_info"] actions_info: str = results_dict["actions_info"]
@@ -649,7 +660,7 @@ class PrivateReplyer:
expression_habits_block=expression_habits_block, expression_habits_block=expression_habits_block,
tool_info_block=tool_info, tool_info_block=tool_info,
knowledge_prompt=prompt_info, knowledge_prompt=prompt_info,
# memory_block=memory_block, memory_block=memory_block,
relation_info_block=relation_info, relation_info_block=relation_info,
extra_info_block=extra_info_block, extra_info_block=extra_info_block,
identity=personality_prompt, identity=personality_prompt,
@@ -670,7 +681,7 @@ class PrivateReplyer:
expression_habits_block=expression_habits_block, expression_habits_block=expression_habits_block,
tool_info_block=tool_info, tool_info_block=tool_info,
knowledge_prompt=prompt_info, knowledge_prompt=prompt_info,
# memory_block=memory_block, memory_block=memory_block,
relation_info_block=relation_info, relation_info_block=relation_info,
extra_info_block=extra_info_block, extra_info_block=extra_info_block,
identity=personality_prompt, identity=personality_prompt,

View File

@@ -1,9 +1,7 @@
from src.chat.utils.prompt_builder import Prompt from src.chat.utils.prompt_builder import Prompt
# from src.chat.memory_system.memory_activator import MemoryActivator # from src.chat.memory_system.memory_activator import MemoryActivator
def init_lpmm_prompt(): def init_lpmm_prompt():
Prompt( Prompt(
""" """
@@ -20,5 +18,3 @@ If you need to use the search tool, please directly call the function "lpmm_sear
""", """,
name="lpmm_get_knowledge_prompt", name="lpmm_get_knowledge_prompt",
) )

View File

@@ -13,7 +13,7 @@ def init_replyer_prompt():
Prompt( Prompt(
"""{knowledge_prompt}{tool_info_block}{extra_info_block} """{knowledge_prompt}{tool_info_block}{extra_info_block}
{expression_habits_block} {expression_habits_block}{memory_block}
你正在qq群里聊天下面是群里正在聊的内容: 你正在qq群里聊天下面是群里正在聊的内容:
{time_block} {time_block}
@@ -34,7 +34,7 @@ def init_replyer_prompt():
Prompt( Prompt(
"""{knowledge_prompt}{tool_info_block}{extra_info_block} """{knowledge_prompt}{tool_info_block}{extra_info_block}
{expression_habits_block} {expression_habits_block}{memory_block}
你正在qq群里聊天下面是群里正在聊的内容: 你正在qq群里聊天下面是群里正在聊的内容:
{time_block} {time_block}
@@ -55,7 +55,7 @@ def init_replyer_prompt():
Prompt( Prompt(
"""{knowledge_prompt}{tool_info_block}{extra_info_block} """{knowledge_prompt}{tool_info_block}{extra_info_block}
{expression_habits_block} {expression_habits_block}{memory_block}
你正在和{sender_name}聊天,这是你们之前聊的内容: 你正在和{sender_name}聊天,这是你们之前聊的内容:
{time_block} {time_block}
@@ -74,7 +74,7 @@ def init_replyer_prompt():
Prompt( Prompt(
"""{knowledge_prompt}{tool_info_block}{extra_info_block} """{knowledge_prompt}{tool_info_block}{extra_info_block}
{expression_habits_block} {expression_habits_block}{memory_block}
你正在和{sender_name}聊天,这是你们之前聊的内容: 你正在和{sender_name}聊天,这是你们之前聊的内容:
{time_block} {time_block}

View File

@@ -1,9 +1,7 @@
from src.chat.utils.prompt_builder import Prompt from src.chat.utils.prompt_builder import Prompt
# from src.chat.memory_system.memory_activator import MemoryActivator # from src.chat.memory_system.memory_activator import MemoryActivator
def init_rewrite_prompt(): def init_rewrite_prompt():
Prompt("你正在qq群里聊天下面是群里正在聊的内容:", "chat_target_group1") Prompt("你正在qq群里聊天下面是群里正在聊的内容:", "chat_target_group1")
Prompt("你正在和{sender_name}聊天,这是你们之前聊的内容:", "chat_target_private1") Prompt("你正在和{sender_name}聊天,这是你们之前聊的内容:", "chat_target_private1")
@@ -31,4 +29,4 @@ def init_rewrite_prompt():
现在,你说: 现在,你说:
""", """,
"default_expressor_prompt", "default_expressor_prompt",
) )

View File

@@ -859,7 +859,6 @@ async def build_anonymous_messages(messages: List[DatabaseMessages]) -> str:
# 处理图片ID # 处理图片ID
content = process_pic_ids(content) content = process_pic_ids(content)
anon_name = get_anon_name(platform, user_id) anon_name = get_anon_name(platform, user_id)
# print(f"anon_name:{anon_name}") # print(f"anon_name:{anon_name}")
@@ -945,11 +944,12 @@ async def build_bare_messages(messages: List[DatabaseMessages]) -> str:
# 获取纯文本内容 # 获取纯文本内容
content = msg.processed_plain_text or "" content = msg.processed_plain_text or ""
# 处理图片ID # 处理图片ID
pic_pattern = r"\[picid:[^\]]+\]" pic_pattern = r"\[picid:[^\]]+\]"
def replace_pic_id(match): def replace_pic_id(match):
return "[图片]" return "[图片]"
content = re.sub(pic_pattern, replace_pic_id, content) content = re.sub(pic_pattern, replace_pic_id, content)
# 处理用户引用格式,移除回复和@标记 # 处理用户引用格式,移除回复和@标记

View File

@@ -16,4 +16,4 @@ class LLMGenerationDataModel(BaseDataModel):
tool_calls: Optional[List["ToolCall"]] = None tool_calls: Optional[List["ToolCall"]] = None
prompt: Optional[str] = None prompt: Optional[str] = None
selected_expressions: Optional[List[int]] = None selected_expressions: Optional[List[int]] = None
reply_set: Optional["ReplySetModel"] = None reply_set: Optional["ReplySetModel"] = None

View File

@@ -317,6 +317,19 @@ class Expression(BaseModel):
class Meta: class Meta:
table_name = "expression" table_name = "expression"
class MemoryChest(BaseModel):
"""
用于存储记忆仓库的模型
"""
title = TextField() # 标题
content = TextField() # 内容
class Meta:
table_name = "memory_chest"
class GraphNodes(BaseModel): class GraphNodes(BaseModel):
""" """
@@ -369,6 +382,7 @@ def create_tables():
GraphNodes, # 添加图节点表 GraphNodes, # 添加图节点表
GraphEdges, # 添加图边表 GraphEdges, # 添加图边表
ActionRecords, # 添加 ActionRecords 到初始化列表 ActionRecords, # 添加 ActionRecords 到初始化列表
MemoryChest,
] ]
) )
@@ -396,6 +410,7 @@ def initialize_database(sync_constraints=False):
GraphNodes, GraphNodes,
GraphEdges, GraphEdges,
ActionRecords, # 添加 ActionRecords 到初始化列表 ActionRecords, # 添加 ActionRecords 到初始化列表
MemoryChest,
] ]
try: try:
@@ -493,6 +508,7 @@ def sync_field_constraints():
GraphNodes, GraphNodes,
GraphEdges, GraphEdges,
ActionRecords, ActionRecords,
MemoryChest,
] ]
try: try:
@@ -732,11 +748,14 @@ def check_field_constraints():
logger.exception(f"检查字段约束时出错: {e}") logger.exception(f"检查字段约束时出错: {e}")
return inconsistencies return inconsistencies
def fix_image_id(): def fix_image_id():
""" """
修复表情包的 image_id 字段 修复表情包的 image_id 字段
""" """
import uuid import uuid
try: try:
with db: with db:
for img in Images.select(): for img in Images.select():
@@ -747,6 +766,7 @@ def fix_image_id():
except Exception as e: except Exception as e:
logger.exception(f"修复 image_id 时出错: {e}") logger.exception(f"修复 image_id 时出错: {e}")
# 模块加载时调用初始化函数 # 模块加载时调用初始化函数
initialize_database(sync_constraints=True) initialize_database(sync_constraints=True)
fix_image_id() fix_image_id()

View File

@@ -53,7 +53,7 @@ TEMPLATE_DIR = os.path.join(PROJECT_ROOT, "template")
# 考虑到实际上配置文件中的mai_version是不会自动更新的,所以采用硬编码 # 考虑到实际上配置文件中的mai_version是不会自动更新的,所以采用硬编码
# 对该字段的更新请严格参照语义化版本规范https://semver.org/lang/zh-CN/ # 对该字段的更新请严格参照语义化版本规范https://semver.org/lang/zh-CN/
MMC_VERSION = "0.10.4-snapshot.1" MMC_VERSION = "0.11.0-snapshot.1"
def get_key_comment(toml_table, key): def get_key_comment(toml_table, key):

View File

@@ -46,13 +46,13 @@ class PersonalityConfig(ConfigBase):
interest: str = "" interest: str = ""
"""兴趣""" """兴趣"""
plan_style: str = "" plan_style: str = ""
"""说话规则,行为风格""" """说话规则,行为风格"""
visual_style: str = "" visual_style: str = ""
"""图片提示词""" """图片提示词"""
private_plan_style: str = "" private_plan_style: str = ""
"""私聊说话规则,行为风格""" """私聊说话规则,行为风格"""
@@ -86,7 +86,7 @@ class ChatConfig(ConfigBase):
planner_smooth: float = 3 planner_smooth: float = 3
"""规划器平滑增大数值会减小planner负荷略微降低反应速度推荐2-50为关闭必须大于等于0""" """规划器平滑增大数值会减小planner负荷略微降低反应速度推荐2-50为关闭必须大于等于0"""
talk_value: float = 1 talk_value: float = 1
"""思考频率""" """思考频率"""
@@ -302,6 +302,7 @@ class EmojiConfig(ConfigBase):
filtration_prompt: str = "符合公序良俗" filtration_prompt: str = "符合公序良俗"
"""表情包过滤要求""" """表情包过滤要求"""
@dataclass @dataclass
class KeywordRuleConfig(ConfigBase): class KeywordRuleConfig(ConfigBase):
"""关键词规则配置类""" """关键词规则配置类"""

View File

@@ -85,4 +85,4 @@ class ModelAttemptFailed(Exception):
self.original_exception = original_exception self.original_exception = original_exception
def __str__(self): def __str__(self):
return self.message return self.message

View File

@@ -192,7 +192,7 @@ def _process_delta(
elif getattr(p, "text", None): elif getattr(p, "text", None):
# 正常输出写入 buffer # 正常输出写入 buffer
fc_delta_buffer.write(p.text) fc_delta_buffer.write(p.text)
if delta.function_calls: # 为什么不用hasattr呢是因为这个属性一定有即使是个空的 if delta.function_calls: # 为什么不用hasattr呢是因为这个属性一定有即使是个空的
for call in delta.function_calls: for call in delta.function_calls:
try: try:
@@ -396,10 +396,7 @@ def _default_normal_response_parser(
" 可能会对回复内容造成影响,建议修改模型 max_tokens 配置!" " 可能会对回复内容造成影响,建议修改模型 max_tokens 配置!"
) )
else: else:
logger.warning( logger.warning("⚠ Gemini 响应因达到 max_tokens 限制被截断,\n 请修改模型 max_tokens 配置!")
"⚠ Gemini 响应因达到 max_tokens 限制被截断,\n"
" 请修改模型 max_tokens 配置!"
)
return api_response, _usage_record return api_response, _usage_record
except Exception as e: except Exception as e:
@@ -456,7 +453,7 @@ class GeminiClient(BaseClient):
logger.warning( logger.warning(
f"无效的 thinking_budget 值 {extra_params['thinking_budget']},将使用模型自动预算模式 {tb}" f"无效的 thinking_budget 值 {extra_params['thinking_budget']},将使用模型自动预算模式 {tb}"
) )
# 优先尝试精确匹配 # 优先尝试精确匹配
if model_id in THINKING_BUDGET_LIMITS: if model_id in THINKING_BUDGET_LIMITS:
limits = THINKING_BUDGET_LIMITS[model_id] limits = THINKING_BUDGET_LIMITS[model_id]
@@ -541,7 +538,7 @@ class GeminiClient(BaseClient):
tools = _convert_tool_options(tool_options) if tool_options else None tools = _convert_tool_options(tool_options) if tool_options else None
# 解析并裁剪 thinking_budget # 解析并裁剪 thinking_budget
tb = self.clamp_thinking_budget(extra_params, model_info.model_identifier) tb = self.clamp_thinking_budget(extra_params, model_info.model_identifier)
# 将response_format转换为Gemini API所需的格式 # 将response_format转换为Gemini API所需的格式
generation_config_dict = { generation_config_dict = {
"max_output_tokens": max_tokens, "max_output_tokens": max_tokens,

View File

@@ -487,7 +487,7 @@ class OpenaiClient(BaseClient):
req_task.cancel() req_task.cancel()
raise ReqAbortException("请求被外部信号中断") raise ReqAbortException("请求被外部信号中断")
await asyncio.sleep(0.1) # 等待0.5秒后再次检查任务&中断信号量状态 await asyncio.sleep(0.1) # 等待0.5秒后再次检查任务&中断信号量状态
# logger. # logger.
logger.debug(f"OpenAI API响应(非流式): {req_task.result()}") logger.debug(f"OpenAI API响应(非流式): {req_task.result()}")
@@ -511,7 +511,7 @@ class OpenaiClient(BaseClient):
) )
# logger.debug(f"OpenAI API响应: {resp}") # logger.debug(f"OpenAI API响应: {resp}")
return resp return resp
async def get_embedding( async def get_embedding(

View File

@@ -149,7 +149,7 @@ class LLMRequest:
logger.debug(f"LLM请求总耗时: {time.time() - start_time}") logger.debug(f"LLM请求总耗时: {time.time() - start_time}")
logger.debug(f"LLM生成内容: {response}") logger.debug(f"LLM生成内容: {response}")
content = response.content content = response.content
reasoning_content = response.reasoning_content or "" reasoning_content = response.reasoning_content or ""
tool_calls = response.tool_calls tool_calls = response.tool_calls

View File

@@ -44,7 +44,7 @@ def init_prompt():
""", """,
"get_mood_prompt", "get_mood_prompt",
) )
Prompt( Prompt(
""" """
{chat_talking_prompt} {chat_talking_prompt}
@@ -103,9 +103,7 @@ class ChatMood:
if random.random() > update_probability: if random.random() > update_probability:
return return
logger.debug( logger.debug(f"{self.log_prefix} 更新情绪状态,更新概率: {update_probability:.2f}")
f"{self.log_prefix} 更新情绪状态,更新概率: {update_probability:.2f}"
)
message_time: float = message.message_info.time # type: ignore message_time: float = message.message_info.time # type: ignore
message_list_before_now = get_raw_msg_by_timestamp_with_chat_inclusive( message_list_before_now = get_raw_msg_by_timestamp_with_chat_inclusive(
@@ -154,12 +152,12 @@ class ChatMood:
self.mood_state = response self.mood_state = response
self.last_change_time = message_time self.last_change_time = message_time
async def get_mood(self) -> str: async def get_mood(self) -> str:
self.regression_count = 0 self.regression_count = 0
current_time = time.time() current_time = time.time()
logger.info(f"{self.log_prefix} 获取情绪状态") logger.info(f"{self.log_prefix} 获取情绪状态")
message_list_before_now = get_raw_msg_by_timestamp_with_chat_inclusive( message_list_before_now = get_raw_msg_by_timestamp_with_chat_inclusive(
chat_id=self.chat_id, chat_id=self.chat_id,
@@ -207,7 +205,7 @@ class ChatMood:
self.mood_state = response self.mood_state = response
self.last_change_time = current_time self.last_change_time = current_time
return response return response
async def regress_mood(self): async def regress_mood(self):

View File

@@ -17,7 +17,9 @@ from src.config.config import global_config, model_config
logger = get_logger("person_info") logger = get_logger("person_info")
relation_selection_model = LLMRequest(model_set=model_config.model_task_config.utils_small, request_type="relation_selection") relation_selection_model = LLMRequest(
model_set=model_config.model_task_config.utils_small, request_type="relation_selection"
)
def get_person_id(platform: str, user_id: Union[int, str]) -> str: def get_person_id(platform: str, user_id: Union[int, str]) -> str:
@@ -91,9 +93,10 @@ def extract_categories_from_response(response: str) -> list[str]:
"""从response中提取所有<>包裹的内容""" """从response中提取所有<>包裹的内容"""
if not isinstance(response, str): if not isinstance(response, str):
return [] return []
import re import re
pattern = r'<([^<>]+)>'
pattern = r"<([^<>]+)>"
matches = re.findall(pattern, response) matches = re.findall(pattern, response)
return matches return matches
@@ -420,7 +423,7 @@ class Person:
except Exception as e: except Exception as e:
logger.error(f"同步用户 {self.person_id} 信息到数据库时出错: {e}") logger.error(f"同步用户 {self.person_id} 信息到数据库时出错: {e}")
async def build_relationship(self,chat_content:str = "",info_type = ""): async def build_relationship(self, chat_content: str = "", info_type=""):
if not self.is_known: if not self.is_known:
return "" return ""
# 构建points文本 # 构建points文本
@@ -433,7 +436,7 @@ class Person:
points_text = "" points_text = ""
category_list = self.get_all_category() category_list = self.get_all_category()
if chat_content: if chat_content:
prompt = f"""当前聊天内容: prompt = f"""当前聊天内容:
{chat_content} {chat_content}
@@ -449,11 +452,13 @@ class Person:
# print(prompt) # print(prompt)
# print(response) # print(response)
category_list = extract_categories_from_response(response) category_list = extract_categories_from_response(response)
if "none" not in category_list: if "none" not in category_list:
for category in category_list: for category in category_list:
random_memory = self.get_random_memory_by_category(category, 2) random_memory = self.get_random_memory_by_category(category, 2)
if random_memory: if random_memory:
random_memory_str = "\n".join([get_memory_content_from_memory(memory) for memory in random_memory]) random_memory_str = "\n".join(
[get_memory_content_from_memory(memory) for memory in random_memory]
)
points_text = f"有关 {category} 的内容:{random_memory_str}" points_text = f"有关 {category} 的内容:{random_memory_str}"
break break
elif info_type: elif info_type:
@@ -469,15 +474,16 @@ class Person:
# print(prompt) # print(prompt)
# print(response) # print(response)
category_list = extract_categories_from_response(response) category_list = extract_categories_from_response(response)
if "none" not in category_list: if "none" not in category_list:
for category in category_list: for category in category_list:
random_memory = self.get_random_memory_by_category(category, 3) random_memory = self.get_random_memory_by_category(category, 3)
if random_memory: if random_memory:
random_memory_str = "\n".join([get_memory_content_from_memory(memory) for memory in random_memory]) random_memory_str = "\n".join(
[get_memory_content_from_memory(memory) for memory in random_memory]
)
points_text = f"有关 {category} 的内容:{random_memory_str}" points_text = f"有关 {category} 的内容:{random_memory_str}"
break break
else: else:
for category in category_list: for category in category_list:
random_memory = self.get_random_memory_by_category(category, 1)[0] random_memory = self.get_random_memory_by_category(category, 1)[0]
if random_memory: if random_memory:

View File

@@ -12,7 +12,6 @@ import random
import base64 import base64
import os import os
import uuid import uuid
import time
from typing import Optional, Tuple, List, Dict, Any from typing import Optional, Tuple, List, Dict, Any
from src.common.logger import get_logger from src.common.logger import get_logger
@@ -358,7 +357,7 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
"description": None, "description": None,
"emotions": None, "emotions": None,
"replaced": None, "replaced": None,
"hash": None "hash": None,
} }
# 3. 确保emoji目录存在 # 3. 确保emoji目录存在
@@ -368,19 +367,21 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
if not filename: if not filename:
# 基于时间戳、微秒和短base64生成唯一文件名 # 基于时间戳、微秒和短base64生成唯一文件名
import time import time
timestamp = int(time.time()) timestamp = int(time.time())
microseconds = int(time.time() * 1000000) % 1000000 # 添加微秒级精度 microseconds = int(time.time() * 1000000) % 1000000 # 添加微秒级精度
# 生成12位随机标识符使用base64编码增加随机性 # 生成12位随机标识符使用base64编码增加随机性
import random import random
random_bytes = random.getrandbits(72).to_bytes(9, 'big') # 72位 = 9字节 = 12位base64
short_id = base64.b64encode(random_bytes).decode('ascii')[:12].rstrip('=') random_bytes = random.getrandbits(72).to_bytes(9, "big") # 72位 = 9字节 = 12位base64
short_id = base64.b64encode(random_bytes).decode("ascii")[:12].rstrip("=")
# 确保base64编码适合文件名替换/和- # 确保base64编码适合文件名替换/和-
short_id = short_id.replace('/', '_').replace('+', '-') short_id = short_id.replace("/", "_").replace("+", "-")
filename = f"emoji_{timestamp}_{microseconds}_{short_id}" filename = f"emoji_{timestamp}_{microseconds}_{short_id}"
# 确保文件名有扩展名 # 确保文件名有扩展名
if not filename.lower().endswith(('.jpg', '.jpeg', '.png', '.gif')): if not filename.lower().endswith((".jpg", ".jpeg", ".png", ".gif")):
filename = f"{filename}.png" # 默认使用png格式 filename = f"{filename}.png" # 默认使用png格式
# 检查文件名是否已存在,如果存在则重新生成短标识符 # 检查文件名是否已存在,如果存在则重新生成短标识符
@@ -390,14 +391,15 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
while os.path.exists(temp_file_path) and attempts < max_attempts: while os.path.exists(temp_file_path) and attempts < max_attempts:
# 重新生成短标识符 # 重新生成短标识符
import random import random
random_bytes = random.getrandbits(48).to_bytes(6, 'big')
short_id = base64.b64encode(random_bytes).decode('ascii')[:8].rstrip('=') random_bytes = random.getrandbits(48).to_bytes(6, "big")
short_id = short_id.replace('/', '_').replace('+', '-') short_id = base64.b64encode(random_bytes).decode("ascii")[:8].rstrip("=")
short_id = short_id.replace("/", "_").replace("+", "-")
# 分离文件名和扩展名,重新生成文件名 # 分离文件名和扩展名,重新生成文件名
name_part, ext = os.path.splitext(filename) name_part, ext = os.path.splitext(filename)
# 去掉原来的标识符,添加新的 # 去掉原来的标识符,添加新的
base_name = name_part.rsplit('_', 1)[0] # 移除最后一个_后的部分 base_name = name_part.rsplit("_", 1)[0] # 移除最后一个_后的部分
filename = f"{base_name}_{short_id}{ext}" filename = f"{base_name}_{short_id}{ext}"
temp_file_path = os.path.join(EMOJI_DIR, filename) temp_file_path = os.path.join(EMOJI_DIR, filename)
attempts += 1 attempts += 1
@@ -406,7 +408,7 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
if os.path.exists(temp_file_path): if os.path.exists(temp_file_path):
uuid_short = str(uuid.uuid4())[:8] uuid_short = str(uuid.uuid4())[:8]
name_part, ext = os.path.splitext(filename) name_part, ext = os.path.splitext(filename)
base_name = name_part.rsplit('_', 1)[0] base_name = name_part.rsplit("_", 1)[0]
filename = f"{base_name}_{uuid_short}{ext}" filename = f"{base_name}_{uuid_short}{ext}"
temp_file_path = os.path.join(EMOJI_DIR, filename) temp_file_path = os.path.join(EMOJI_DIR, filename)
@@ -428,7 +430,7 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
"description": None, "description": None,
"emotions": None, "emotions": None,
"replaced": None, "replaced": None,
"hash": None "hash": None,
} }
# 5. 保存base64图片到emoji目录 # 5. 保存base64图片到emoji目录
@@ -443,7 +445,7 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
"description": None, "description": None,
"emotions": None, "emotions": None,
"replaced": None, "replaced": None,
"hash": None "hash": None,
} }
logger.debug(f"[EmojiAPI] 图片已保存到临时文件: {temp_file_path}") logger.debug(f"[EmojiAPI] 图片已保存到临时文件: {temp_file_path}")
@@ -456,7 +458,7 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
"description": None, "description": None,
"emotions": None, "emotions": None,
"replaced": None, "replaced": None,
"hash": None "hash": None,
} }
# 6. 调用注册方法 # 6. 调用注册方法
@@ -483,8 +485,8 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
# 通过文件名查找新注册的表情包(注意:文件名在注册后可能已经改变) # 通过文件名查找新注册的表情包(注意:文件名在注册后可能已经改变)
for emoji_obj in reversed(emoji_manager.emoji_objects): for emoji_obj in reversed(emoji_manager.emoji_objects):
if not emoji_obj.is_deleted and ( if not emoji_obj.is_deleted and (
emoji_obj.filename == filename or # 直接匹配 emoji_obj.filename == filename # 直接匹配
(hasattr(emoji_obj, 'full_path') and filename in emoji_obj.full_path) # 路径包含匹配 or (hasattr(emoji_obj, "full_path") and filename in emoji_obj.full_path) # 路径包含匹配
): ):
new_emoji_info = emoji_obj new_emoji_info = emoji_obj
break break
@@ -501,7 +503,7 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
"description": description, "description": description,
"emotions": emotions, "emotions": emotions,
"replaced": replaced, "replaced": replaced,
"hash": emoji_hash "hash": emoji_hash,
} }
else: else:
return { return {
@@ -510,7 +512,7 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
"description": None, "description": None,
"emotions": None, "emotions": None,
"replaced": None, "replaced": None,
"hash": None "hash": None,
} }
except Exception as e: except Exception as e:
@@ -521,7 +523,7 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
"description": None, "description": None,
"emotions": None, "emotions": None,
"replaced": None, "replaced": None,
"hash": None "hash": None,
} }
@@ -585,16 +587,16 @@ async def delete_emoji(emoji_hash: str) -> Dict[str, Any]:
"count_before": count_before, "count_before": count_before,
"count_after": count_after, "count_after": count_after,
"description": description, "description": description,
"emotions": emotions "emotions": emotions,
} }
else: else:
return { return {
"success": False, "success": False,
"message": f"表情包删除失败,可能因为哈希值不存在或删除过程出错", "message": "表情包删除失败,可能因为哈希值不存在或删除过程出错",
"count_before": count_before, "count_before": count_before,
"count_after": count_after, "count_after": count_after,
"description": None, "description": None,
"emotions": None "emotions": None,
} }
except Exception as e: except Exception as e:
@@ -605,7 +607,7 @@ async def delete_emoji(emoji_hash: str) -> Dict[str, Any]:
"count_before": None, "count_before": None,
"count_after": None, "count_after": None,
"description": None, "description": None,
"emotions": None "emotions": None,
} }
@@ -659,7 +661,7 @@ async def delete_emoji_by_description(description: str, exact_match: bool = Fals
"message": f"未找到匹配描述 '{description}' 的表情包", "message": f"未找到匹配描述 '{description}' 的表情包",
"deleted_count": 0, "deleted_count": 0,
"deleted_hashes": [], "deleted_hashes": [],
"matched_count": 0 "matched_count": 0,
} }
# 删除匹配的表情包 # 删除匹配的表情包
@@ -681,7 +683,7 @@ async def delete_emoji_by_description(description: str, exact_match: bool = Fals
"message": f"成功删除 {deleted_count} 个表情包 (匹配到 {matched_count} 个)", "message": f"成功删除 {deleted_count} 个表情包 (匹配到 {matched_count} 个)",
"deleted_count": deleted_count, "deleted_count": deleted_count,
"deleted_hashes": deleted_hashes, "deleted_hashes": deleted_hashes,
"matched_count": matched_count "matched_count": matched_count,
} }
else: else:
return { return {
@@ -689,7 +691,7 @@ async def delete_emoji_by_description(description: str, exact_match: bool = Fals
"message": f"匹配到 {matched_count} 个表情包,但删除全部失败", "message": f"匹配到 {matched_count} 个表情包,但删除全部失败",
"deleted_count": 0, "deleted_count": 0,
"deleted_hashes": [], "deleted_hashes": [],
"matched_count": matched_count "matched_count": matched_count,
} }
except Exception as e: except Exception as e:
@@ -699,5 +701,5 @@ async def delete_emoji_by_description(description: str, exact_match: bool = Fals
"message": f"删除过程中发生错误: {str(e)}", "message": f"删除过程中发生错误: {str(e)}",
"deleted_count": 0, "deleted_count": 0,
"deleted_hashes": [], "deleted_hashes": [],
"matched_count": 0 "matched_count": 0,
} }

View File

@@ -3,13 +3,14 @@ from src.chat.frequency_control.frequency_control import frequency_control_manag
logger = get_logger("frequency_api") logger = get_logger("frequency_api")
def get_current_talk_frequency(chat_id: str) -> float: def get_current_talk_frequency(chat_id: str) -> float:
return frequency_control_manager.get_or_create_frequency_control(chat_id).get_talk_frequency_adjust() return frequency_control_manager.get_or_create_frequency_control(chat_id).get_talk_frequency_adjust()
def set_talk_frequency_adjust(chat_id: str, talk_frequency_adjust: float) -> None: def set_talk_frequency_adjust(chat_id: str, talk_frequency_adjust: float) -> None:
frequency_control_manager.get_or_create_frequency_control( frequency_control_manager.get_or_create_frequency_control(chat_id).set_talk_frequency_adjust(talk_frequency_adjust)
chat_id
).set_talk_frequency_adjust(talk_frequency_adjust)
def get_talk_frequency_adjust(chat_id: str) -> float: def get_talk_frequency_adjust(chat_id: str) -> float:
return frequency_control_manager.get_or_create_frequency_control(chat_id).get_talk_frequency_adjust() return frequency_control_manager.get_or_create_frequency_control(chat_id).get_talk_frequency_adjust()

View File

@@ -1,9 +1,6 @@
import asyncio import asyncio
import traceback from typing import Optional
import time
from typing import Optional, Union, Dict, List, TYPE_CHECKING, Tuple
from src.chat.message_receive import message
from src.common.logger import get_logger from src.common.logger import get_logger
from src.mood.mood_manager import mood_manager from src.mood.mood_manager import mood_manager
@@ -12,5 +9,5 @@ logger = get_logger("mood_api")
async def get_mood_by_chat_id(chat_id: str) -> Optional[float]: async def get_mood_by_chat_id(chat_id: str) -> Optional[float]:
chat_mood = mood_manager.get_mood_by_chat_id(chat_id) chat_mood = mood_manager.get_mood_by_chat_id(chat_id)
mood = asyncio.create_task(chat_mood.get_mood()) mood = asyncio.create_task(chat_mood.get_mood())
return mood return mood

View File

@@ -363,7 +363,7 @@ async def custom_reply_set_to_stream(
) -> bool: ) -> bool:
""" """
向指定流发送混合型消息集 向指定流发送混合型消息集
Args: Args:
reply_set: ReplySetModel 对象,包含多个 ReplyContent reply_set: ReplySetModel 对象,包含多个 ReplyContent
stream_id: 聊天流ID stream_id: 聊天流ID
@@ -451,7 +451,9 @@ def _parse_content_to_seg(reply_content: "ReplyContent") -> Tuple[Seg, bool]:
single_node_content.append(sub_seg) single_node_content.append(sub_seg)
message_segment = Seg(type="seglist", data=single_node_content) message_segment = Seg(type="seglist", data=single_node_content)
forward_message_list.append( forward_message_list.append(
MessageBase(message_segment=message_segment, message_info=BaseMessageInfo(user_info=user_info)).to_dict() MessageBase(
message_segment=message_segment, message_info=BaseMessageInfo(user_info=user_info)
).to_dict()
) )
return Seg(type="forward", data=forward_message_list), False # type: ignore return Seg(type="forward", data=forward_message_list), False # type: ignore
else: else:

View File

@@ -91,7 +91,7 @@ class ToolExecutor:
# 缓存未命中,执行工具调用 # 缓存未命中,执行工具调用
# 获取可用工具 # 获取可用工具
tools = self._get_tool_definitions() tools = self._get_tool_definitions()
# print(f"tools: {tools}") # print(f"tools: {tools}")
# 获取当前时间 # 获取当前时间

View File

@@ -48,7 +48,7 @@ class EmojiAction(BaseAction):
# 1. 获取发送表情的原因 # 1. 获取发送表情的原因
# reason = self.action_data.get("reason", "表达当前情绪") # reason = self.action_data.get("reason", "表达当前情绪")
reason = self.reasoning reason = self.reasoning
# 2. 随机获取20个表情包 # 2. 随机获取20个表情包
sampled_emojis = await emoji_api.get_random(30) sampled_emojis = await emoji_api.get_random(30)
if not sampled_emojis: if not sampled_emojis:

View File

@@ -3,9 +3,13 @@ from typing import Tuple
from src.common.logger import get_logger from src.common.logger import get_logger
from src.config.config import global_config from src.config.config import global_config
from src.chat.utils.prompt_builder import Prompt from src.chat.utils.prompt_builder import Prompt
from src.llm_models.payload_content.tool_option import ToolParamType
from src.plugin_system import BaseAction, ActionActivationType from src.plugin_system import BaseAction, ActionActivationType
from src.chat.memory_system.Hippocampus import hippocampus_manager from src.chat.memory_system.Hippocampus import hippocampus_manager
from src.chat.utils.utils import cut_key_words from src.chat.utils.utils import cut_key_words
from src.chat.memory_system.Memory_chest import global_memory_chest
from src.plugin_system.base.base_tool import BaseTool
from typing import Any
logger = get_logger("memory") logger = get_logger("memory")
@@ -66,73 +70,153 @@ def init_prompt():
) )
class BuildMemoryAction(BaseAction): # class BuildMemoryAction(BaseAction):
"""关系动作 - 构建关系""" # """关系动作 - 构建关系"""
# activation_type = ActionActivationType.LLM_JUDGE
# parallel_action = True
# # 动作基本信息
# action_name = "build_memory"
# action_description = (
# "了解对于某个概念或者某件事的记忆,并存储下来,在之后的聊天中,你可以根据这条记忆来获取相关信息"
# )
# # 动作参数定义
# action_parameters = {
# "concept_name": "需要了解或记忆的概念或事件的名称",
# "concept_description": "需要了解或记忆的概念或事件的描述,需要具体且明确",
# }
# # 动作使用场景
# action_require = [
# "了解对于某个概念或者某件事的记忆,并存储下来,在之后的聊天中,你可以根据这条记忆来获取相关信息",
# "有你不了解的概念",
# "有人要求你记住某个概念或者事件",
# "你对某件事或概念有新的理解,或产生了兴趣",
# ]
# # 关联类型
# associated_types = ["text"]
# async def execute(self) -> Tuple[bool, str]:
# """执行关系动作"""
# try:
# # 1. 获取构建关系的原因
# concept_description = self.action_data.get("concept_description", "")
# logger.info(f"{self.log_prefix} 添加记忆原因: {self.reasoning}")
# concept_name = self.action_data.get("concept_name", "")
# # 2. 获取目标用户信息
# # 对 concept_name 进行jieba分词
# concept_name_tokens = cut_key_words(concept_name)
# # logger.info(f"{self.log_prefix} 对 concept_name 进行分词结果: {concept_name_tokens}")
# filtered_concept_name_tokens = [
# token
# for token in concept_name_tokens
# if all(keyword not in token for keyword in global_config.memory.memory_ban_words)
# ]
# if not filtered_concept_name_tokens:
# logger.warning(f"{self.log_prefix} 过滤后的概念名称列表为空,跳过添加记忆")
# return False, "过滤后的概念名称列表为空,跳过添加记忆"
# similar_topics_dict = (
# hippocampus_manager.get_hippocampus().parahippocampal_gyrus.get_similar_topics_from_keywords(
# filtered_concept_name_tokens
# )
# )
# await hippocampus_manager.get_hippocampus().parahippocampal_gyrus.add_memory_with_similar(
# concept_description, similar_topics_dict
# )
# return True, f"成功添加记忆: {concept_name}"
# except Exception as e:
# logger.error(f"{self.log_prefix} 构建记忆时出错: {e}")
# return False, f"构建记忆时出错: {e}"
class GetMemoryTool(BaseTool):
"""获取用户信息"""
name = "get_memory"
description = "在记忆中搜索,获取某个问题的答案"
parameters = [
("question", ToolParamType.STRING, "需要获取答案的问题", True, None)
]
available_for_llm = True
async def execute(self, function_args: dict[str, Any]) -> dict[str, Any]:
"""执行比较两个数的大小
Args:
function_args: 工具参数
Returns:
dict: 工具执行结果
"""
question: str = function_args.get("question") # type: ignore
answer = await global_memory_chest.get_answer_by_question(question=question)
if not answer:
return {"content": f"没有找到相关记忆"}
return {"content": f"问题:{question},答案:{answer}"}
class GetMemoryAction(BaseAction):
"""关系动作 - 获取记忆"""
activation_type = ActionActivationType.LLM_JUDGE activation_type = ActionActivationType.LLM_JUDGE
parallel_action = True parallel_action = True
# 动作基本信息 # 动作基本信息
action_name = "build_memory" action_name = "get_memory"
action_description = ( action_description = (
"了解对于某个概念或者某件事的记忆,并存储下来,在之后的聊天中,你可以根据这条记忆来获取相关信息" "在记忆中搜寻某个问题的答案"
) )
# 动作参数定义 # 动作参数定义
action_parameters = { action_parameters = {
"concept_name": "需要了解或记忆的概念或事件的名称", "question": "需要搜寻或回答的问题",
"concept_description": "需要了解或记忆的概念或事件的描述,需要具体且明确",
} }
# 动作使用场景 # 动作使用场景
action_require = [ action_require = [
"了解对于某个概念或者某件事的记忆,并存储下来,在之后的聊天中,你可以根据这条记忆来获取相关信息", "在记忆中搜寻某个问题的答案",
"有你不了解的概念", "有你不了解的概念",
"有人要求你记住某个概念或者事件", "有人提问关于过去的事情"
"对某件事或概念有新的理解,或产生了兴趣", "需要根据记忆回答某个问题",
] ]
# 关联类型 # 关联类型
associated_types = ["text"] associated_types = ["text"]
async def execute(self) -> Tuple[bool, str]: async def execute(self) -> Tuple[bool, str]:
"""执行关系动作""" """执行关系动作"""
try: question = self.action_data.get("question", "")
# 1. 获取构建关系的原因 answer = await global_memory_chest.get_answer_by_question(self.chat_id, question)
concept_description = self.action_data.get("concept_description", "") if not answer:
logger.info(f"{self.log_prefix} 添加记忆原因: {self.reasoning}") await self.store_action_info(
concept_name = self.action_data.get("concept_name", "") action_build_into_prompt=True,
# 2. 获取目标用户信息 action_prompt_display=f"你回忆了有关问题:{question}的记忆,但是没有找到相关记忆",
action_done=True,
# 对 concept_name 进行jieba分词
concept_name_tokens = cut_key_words(concept_name)
# logger.info(f"{self.log_prefix} 对 concept_name 进行分词结果: {concept_name_tokens}")
filtered_concept_name_tokens = [
token
for token in concept_name_tokens
if all(keyword not in token for keyword in global_config.memory.memory_ban_words)
]
if not filtered_concept_name_tokens:
logger.warning(f"{self.log_prefix} 过滤后的概念名称列表为空,跳过添加记忆")
return False, "过滤后的概念名称列表为空,跳过添加记忆"
similar_topics_dict = (
hippocampus_manager.get_hippocampus().parahippocampal_gyrus.get_similar_topics_from_keywords(
filtered_concept_name_tokens
)
) )
await hippocampus_manager.get_hippocampus().parahippocampal_gyrus.add_memory_with_similar(
concept_description, similar_topics_dict return False, f"没有找到相关记忆"
)
await self.store_action_info(
return True, f"成功添加记忆: {concept_name}" action_build_into_prompt=True,
action_prompt_display=f"你回忆了有关问题:{question}的记忆,答案是:{answer}",
except Exception as e: action_done=True,
logger.error(f"{self.log_prefix} 构建记忆时出错: {e}") )
return False, f"构建记忆时出错: {e}"
return True, f"成功获取记忆: {answer}"
# 还缺一个关系的太多遗忘和对应的提取 # 还缺一个关系的太多遗忘和对应的提取

View File

@@ -1,25 +1,23 @@
from typing import List, Tuple, Type from typing import List, Tuple, Type
# 导入新插件系统 # 导入新插件系统
from src.plugin_system import BasePlugin, ComponentInfo from src.plugin_system import BasePlugin, ComponentInfo, register_plugin
from src.plugin_system.base.config_types import ConfigField from src.plugin_system.base.config_types import ConfigField
# 导入依赖的系统组件 # 导入依赖的系统组件
from src.common.logger import get_logger from src.common.logger import get_logger
from src.plugins.built_in.memory.build_memory import BuildMemoryAction from src.plugins.built_in.memory.build_memory import GetMemoryAction, GetMemoryTool
logger = get_logger("relation_actions") logger = get_logger("memory_build")
# @register_plugin @register_plugin
class MemoryBuildPlugin(BasePlugin): class MemoryBuildPlugin(BasePlugin):
"""关系动作插件 """记忆构建插件
系统内置插件,提供基础的聊天交互功能: 系统内置插件,提供基础的聊天交互功能:
- Reply: 回复动作 - GetMemory: 获取记忆
- NoReply: 不回复动作
- Emoji: 表情动作
注意插件基本信息优先从_manifest.json文件中读取 注意插件基本信息优先从_manifest.json文件中读取
""" """
@@ -43,9 +41,6 @@ class MemoryBuildPlugin(BasePlugin):
"enabled": ConfigField(type=bool, default=True, description="是否启用插件"), "enabled": ConfigField(type=bool, default=True, description="是否启用插件"),
"config_version": ConfigField(type=str, default="1.1.0", description="配置文件版本"), "config_version": ConfigField(type=str, default="1.1.0", description="配置文件版本"),
}, },
"components": {
"memory_max_memory_num": ConfigField(type=int, default=10, description="记忆最大数量"),
},
} }
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
@@ -53,6 +48,7 @@ class MemoryBuildPlugin(BasePlugin):
# --- 根据配置注册组件 --- # --- 根据配置注册组件 ---
components = [] components = []
components.append((BuildMemoryAction.get_action_info(), BuildMemoryAction)) components.append((GetMemoryAction.get_action_info(), GetMemoryAction))
components.append((GetMemoryTool.get_tool_info(), GetMemoryTool))
return components return components

View File

@@ -14,7 +14,6 @@ from src.plugins.built_in.relation.relation import BuildRelationAction
logger = get_logger("relation_actions") logger = get_logger("relation_actions")
class GetPersonInfoTool(BaseTool): class GetPersonInfoTool(BaseTool):
"""获取用户信息""" """获取用户信息"""
@@ -24,7 +23,7 @@ class GetPersonInfoTool(BaseTool):
("person_name", ToolParamType.STRING, "需要获取信息的人的名称", True, None), ("person_name", ToolParamType.STRING, "需要获取信息的人的名称", True, None),
("info_type", ToolParamType.STRING, "需要获取信息的类型", True, None), ("info_type", ToolParamType.STRING, "需要获取信息的类型", True, None),
] ]
available_for_llm = True available_for_llm = True
async def execute(self, function_args: dict[str, Any]) -> dict[str, Any]: async def execute(self, function_args: dict[str, Any]) -> dict[str, Any]:
@@ -44,7 +43,7 @@ class GetPersonInfoTool(BaseTool):
return {"content": f"用户 {person_name} 不存在"} return {"content": f"用户 {person_name} 不存在"}
if not person.is_known: if not person.is_known:
return {"content": f"不认识用户 {person_name}"} return {"content": f"不认识用户 {person_name}"}
relation_str = await person.build_relationship(info_type=info_type) relation_str = await person.build_relationship(info_type=info_type)
return {"content": relation_str} return {"content": relation_str}