feat:优化工具调用的得到的信息调用和缓存

This commit is contained in:
SengokuCola
2025-05-02 00:58:55 +08:00
parent f4616afb3a
commit f30d472c0b
14 changed files with 154 additions and 113 deletions

View File

@@ -9,7 +9,7 @@ class CompareNumbersTool(BaseTool):
"""比较两个数大小的工具"""
name = "compare_numbers"
description = "比较两个数的大小,返回较大的数"
description = "使用工具 比较两个数的大小,返回较大的数"
parameters = {
"type": "object",
"properties": {
@@ -39,10 +39,10 @@ class CompareNumbersTool(BaseTool):
else:
result = f"{num1} 等于 {num2}"
return {"name": self.name, "content": result}
return {"type": "comparison_result", "id": f"{num1}_vs_{num2}", "content": result}
except Exception as e:
logger.error(f"比较数字失败: {str(e)}")
return {"name": self.name, "content": f"比较数字失败: {str(e)}"}
return {"type": "info", "id": f"{num1}_vs_{num2}", "content": f"比较数字失败,炸了: {str(e)}"}
# 注册工具

View File

@@ -11,7 +11,7 @@ class SearchKnowledgeTool(BaseTool):
"""从知识库中搜索相关信息的工具"""
name = "search_knowledge"
description = "从知识库中搜索相关信息"
description = "使用工具从知识库中搜索相关信息"
parameters = {
"type": "object",
"properties": {
@@ -42,11 +42,11 @@ class SearchKnowledgeTool(BaseTool):
content = f"你知道这些知识: {knowledge_info}"
else:
content = f"你不太了解有关{query}的知识"
return {"name": "search_knowledge", "content": content}
return {"name": "search_knowledge", "content": f"无法获取关于'{query}'的嵌入向量"}
return {"type": "knowledge", "id": query, "content": content}
return {"type": "info", "id": query, "content": f"无法获取关于'{query}'的嵌入向量,你知识库炸了"}
except Exception as e:
logger.error(f"知识库搜索工具执行失败: {str(e)}")
return {"name": "search_knowledge", "content": f"知识库搜索失败: {str(e)}"}
return {"type": "info", "id": query, "content": f"知识库搜索失败,炸了: {str(e)}"}
@staticmethod
def get_info_from_db(

View File

@@ -10,7 +10,7 @@ class GetMemoryTool(BaseTool):
"""从记忆系统中获取相关记忆的工具"""
name = "get_memory"
description = "从记忆系统中获取相关记忆"
description = "使用工具从记忆系统中获取相关记忆"
parameters = {
"type": "object",
"properties": {
@@ -53,10 +53,11 @@ class GetMemoryTool(BaseTool):
else:
content = f"{topic}的记忆,你记不太清"
return {"name": "get_memory", "content": content}
return {"type": "memory", "id": topic_list, "content": content}
except Exception as e:
logger.error(f"记忆获取工具执行失败: {str(e)}")
return {"name": "get_memory", "content": f"记忆获取失败: {str(e)}"}
# 在失败时也保持格式一致但id可能不适用或设为None/Error
return {"type": "memory_error", "id": topic_list, "content": f"记忆获取失败: {str(e)}"}
# 注册工具

View File

@@ -2,6 +2,7 @@ from src.do_tool.tool_can_use.base_tool import BaseTool
from src.common.logger_manager import get_logger
from typing import Dict, Any
from datetime import datetime
import time
logger = get_logger("get_time_date")
@@ -32,6 +33,7 @@ class GetCurrentDateTimeTool(BaseTool):
current_weekday = datetime.now().strftime("%A")
return {
"name": "get_current_date_time",
"type": "time_info",
"id": f"time_info_{time.time()}",
"content": f"当前时间: {current_time}, 日期: {current_date}, 年份: {current_year}, 星期: {current_weekday}",
}

View File

@@ -46,11 +46,14 @@ class SearchKnowledgeFromLPMMTool(BaseTool):
content = f"你知道这些知识: {knowledge_info}"
else:
content = f"你不太了解有关{query}的知识"
return {"name": "search_knowledge", "content": content}
return {"name": "search_knowledge", "content": f"无法获取关于'{query}'的嵌入向量"}
return {"type": "lpmm_knowledge", "id": query, "content": content}
# 如果获取嵌入失败
return {"type": "info", "id": query, "content": f"无法获取关于'{query}'的嵌入向量你lpmm知识库炸了"}
except Exception as e:
logger.error(f"知识库搜索工具执行失败: {str(e)}")
return {"name": "search_knowledge", "content": f"知识库搜索失败: {str(e)}"}
# 在其他异常情况下,确保 id 仍然是 query (如果它被定义了)
query_id = query if 'query' in locals() else 'unknown_query'
return {"type": "info", "id": query_id, "content": f"lpmm知识库搜索失败炸了: {str(e)}"}
# def get_info_from_db(
# self, query_embedding: list, limit: int = 1, threshold: float = 0.5, return_raw: bool = False
@@ -133,6 +136,25 @@ class SearchKnowledgeFromLPMMTool(BaseTool):
# # 返回所有找到的内容,用换行分隔
# return "\n".join(str(result["content"]) for result in results)
def _format_results(self, results: list) -> str:
"""格式化结果"""
if not results:
return "未找到相关知识。"
formatted_string = "我找到了一些相关知识:\n"
for i, result in enumerate(results):
chunk_id = result.get("chunk_id")
text = result.get("text", "")
source = result.get("source", "未知来源")
source_type = result.get("source_type", "未知类型")
similarity = result.get("similarity", 0.0)
formatted_string += f"{i + 1}. (相似度: {similarity:.2f}) 类型: {source_type}, 来源: {source} \n内容片段: {text}\n\n"
# 暂时去掉chunk_id
# formatted_string += f"{i + 1}. (相似度: {similarity:.2f}) 类型: {source_type}, 来源: {source}, Chunk ID: {chunk_id} \n内容片段: {text}\n\n"
return formatted_string
# 注册工具
# register_tool(SearchKnowledgeTool)

View File

@@ -1,7 +1,9 @@
from src.do_tool.tool_can_use.base_tool import BaseTool, register_tool
from src.plugins.person_info.person_info import person_info_manager
from src.common.logger_manager import get_logger
from src.plugins.person_info.relationship_manager import relationship_manager
import json
import time
logger = get_logger("rename_person_tool")
@@ -82,10 +84,10 @@ class RenamePersonTool(BaseTool):
new_name = result["nickname"]
reason = result.get("reason", "未提供理由")
logger.info(f"成功为用户 {person_id} 取了新昵称: {new_name}")
return {
"name": self.name,
"content": f"好的,我已经给 '{person_name_to_find}' 取了新昵称:'{new_name}'。因为:{reason}"
}
content = f"已成功将用户 {person_name_to_find} 的备注名更新为 {new_name}"
logger.info(content)
return {"type": "info", "id": f"rename_success_{time.time()}", "content": content}
else:
logger.warning(f"为用户 {person_id} 调用 qv_person_name 后未能成功获取新昵称。")
# 尝试从内存中获取可能已经更新的名字
@@ -102,11 +104,9 @@ class RenamePersonTool(BaseTool):
}
except Exception as e:
logger.error(f"执行 rename_person 工具时出错: {e}", exc_info=True)
return {
"name": self.name,
"content": f"执行重命名操作时遇到内部错误: {e}"
}
error_msg = f"重命名失败: {str(e)}"
logger.error(error_msg, exc_info=True)
return {"type": "info_error", "id": f"rename_error_{time.time()}", "content": error_msg}
# 注册工具
register_tool(RenamePersonTool)

View File

@@ -79,9 +79,8 @@ class ChattingObservation(Observation):
async def initialize(self):
# --- Use utility function to determine chat type and fetch info ---
self.is_group_chat, self.chat_target_info = await get_chat_type_and_target_info(self.chat_id)
logger.debug(
f"ChattingObservation {self.chat_id} initialized: is_group={self.is_group_chat}, target_info={self.chat_target_info}"
)
# logger.debug(f"is_group_chat: {self.is_group_chat}")
# logger.debug(f"chat_target_info: {self.chat_target_info}")
# --- End using utility function ---
# Fetch initial messages (existing logic)

View File

@@ -1,4 +1,4 @@
from .observation import Observation
from .observation import Observation, ChattingObservation
from src.plugins.models.utils_model import LLMRequest
from src.config.config import global_config
import time
@@ -114,7 +114,7 @@ def calculate_replacement_probability(similarity: float) -> float:
class SubMind:
def __init__(self, subheartflow_id: str, chat_state: ChatStateInfo, observations: Observation):
def __init__(self, subheartflow_id: str, chat_state: ChatStateInfo, observations: ChattingObservation):
self.last_active_time = None
self.subheartflow_id = subheartflow_id
@@ -130,10 +130,41 @@ class SubMind:
self.current_mind = ""
self.past_mind = []
self.structured_info = {}
self.structured_info = []
self.structured_info_str = ""
name = chat_manager.get_stream_name(self.subheartflow_id)
self.log_prefix = f"[{name}] "
self._update_structured_info_str()
def _update_structured_info_str(self):
"""根据 structured_info 更新 structured_info_str"""
if not self.structured_info:
self.structured_info_str = ""
return
lines = ["【信息】"]
for item in self.structured_info:
# 简化展示突出内容和类型包含TTL供调试
type_str = item.get('type', '未知类型')
content_str = item.get('content', '')
ttl = item.get('ttl', '?')
if type_str == "info":
lines.append(f"刚刚: {content_str}")
elif type_str == "memory":
lines.append(f"{content_str}")
elif type_str == "comparison_result":
lines.append(f"数字大小比较结果: {content_str}")
elif type_str == "time_info":
lines.append(f"{content_str}")
elif type_str == "lpmm_knowledge":
lines.append(f"你知道:{content_str}")
else:
lines.append(f"{type_str}的信息: {content_str}")
self.structured_info_str = "\n".join(lines)
logger.debug(f"{self.log_prefix} 更新 structured_info_str: \n{self.structured_info_str}")
async def do_thinking_before_reply(self, history_cycle: list[CycleInfo] = None):
"""
@@ -145,19 +176,36 @@ class SubMind:
# 更新活跃时间
self.last_active_time = time.time()
# ---------- 0. 更新和清理 structured_info ----------
if self.structured_info:
logger.debug(f"{self.log_prefix} 更新前的 structured_info: {safe_json_dumps(self.structured_info, ensure_ascii=False)}")
updated_info = []
for item in self.structured_info:
item['ttl'] -= 1
if item['ttl'] > 0:
updated_info.append(item)
else:
logger.debug(f"{self.log_prefix} 移除过期的 structured_info 项: {item['id']}")
self.structured_info = updated_info
logger.debug(f"{self.log_prefix} 更新后的 structured_info: {safe_json_dumps(self.structured_info, ensure_ascii=False)}")
self._update_structured_info_str()
logger.debug(f"{self.log_prefix} 当前完整的 structured_info: {safe_json_dumps(self.structured_info, ensure_ascii=False)}")
# ---------- 1. 准备基础数据 ----------
# 获取现有想法和情绪状态
previous_mind = self.current_mind if self.current_mind else ""
mood_info = self.chat_state.mood
# 获取观察对象
observation = self.observations[0] if self.observations else None
observation: ChattingObservation = self.observations[0] if self.observations else None
if not observation or not hasattr(observation, "is_group_chat"): # Ensure it's ChattingObservation or similar
logger.error(f"{self.log_prefix} 无法获取有效的观察对象或缺少聊天类型信息")
self.update_current_mind("(观察出错了...)")
return self.current_mind, self.past_mind
is_group_chat = observation.is_group_chat
# logger.debug(f"is_group_chat: {is_group_chat}")
chat_target_info = observation.chat_target_info
chat_target_name = "对方" # Default for private
if not is_group_chat and chat_target_info:
@@ -277,10 +325,11 @@ class SubMind:
# ---------- 4. 构建最终提示词 ----------
# --- Choose template based on chat type ---
logger.debug(f"is_group_chat: {is_group_chat}")
if is_group_chat:
template_name = "sub_heartflow_prompt_before"
prompt = (await global_prompt_manager.get_prompt_async(template_name)).format(
extra_info="",
extra_info=self.structured_info_str,
prompt_personality=prompt_personality,
relation_prompt=relation_prompt,
bot_name=individuality.name,
@@ -295,7 +344,7 @@ class SubMind:
else: # Private chat
template_name = "sub_heartflow_prompt_private_before"
prompt = (await global_prompt_manager.get_prompt_async(template_name)).format(
extra_info="",
extra_info=self.structured_info_str,
prompt_personality=prompt_personality,
relation_prompt=relation_prompt, # Might need adjustment for private context
bot_name=individuality.name,
@@ -447,7 +496,7 @@ class SubMind:
tool_instance: 工具使用器实例
"""
tool_results = []
structured_info = {} # 动态生成键
new_structured_items = [] # 收集新产生的结构化信息
# 执行所有工具调用
for tool_call in tool_calls:
@@ -455,23 +504,34 @@ class SubMind:
result = await tool_instance._execute_tool_call(tool_call)
if result:
tool_results.append(result)
# 创建新的结构化信息项
new_item = {
"type": result.get("type", "unknown_type"), # 使用 'type' 键
"id": result.get("id", f"fallback_id_{time.time()}"), # 使用 'id' 键
"content": result.get("content", ""), # 'content' 键保持不变
"ttl": 3
}
new_structured_items.append(new_item)
# 使用工具名称作为键
tool_name = result["name"]
if tool_name not in structured_info:
structured_info[tool_name] = []
structured_info[tool_name].append({"name": result["name"], "content": result["content"]})
except Exception as tool_e:
logger.error(f"[{self.subheartflow_id}] 工具执行失败: {tool_e}")
logger.error(traceback.format_exc()) # 添加 traceback 记录
# 如果有工具结果,记录并更新结构化信息
if structured_info:
logger.debug(f"工具调用收集到结构化信息: {safe_json_dumps(structured_info, ensure_ascii=False)}")
self.structured_info = structured_info
# 如果有新的工具结果,记录并更新结构化信息
if new_structured_items:
self.structured_info.extend(new_structured_items) # 添加到现有列表
logger.debug(f"工具调用收集到新的结构化信息: {safe_json_dumps(new_structured_items, ensure_ascii=False)}")
# logger.debug(f"当前完整的 structured_info: {safe_json_dumps(self.structured_info, ensure_ascii=False)}") # 可以取消注释以查看完整列表
self._update_structured_info_str() # 添加新信息后,更新字符串表示
def update_current_mind(self, response):
self.past_mind.append(self.current_mind)
if self.current_mind: # 只有当 current_mind 非空时才添加到 past_mind
self.past_mind.append(self.current_mind)
# 可以考虑限制 past_mind 的大小,例如:
# max_past_mind_size = 10
# if len(self.past_mind) > max_past_mind_size:
# self.past_mind.pop(0) # 移除最旧的
self.current_mind = response

View File

@@ -127,6 +127,8 @@ class SubHeartflowManager:
# 添加聊天观察者
observation = ChattingObservation(chat_id=subheartflow_id)
await observation.initialize()
new_subflow.add_observation(observation)
# 注册子心流

View File

@@ -18,7 +18,7 @@ async def get_chat_type_and_target_info(chat_id: str) -> Tuple[bool, Optional[Di
Tuple[bool, Optional[Dict]]:
- bool: 是否为群聊 (True 是群聊, False 是私聊或未知)
- Optional[Dict]: 如果是私聊,包含对方信息的字典;否则为 None。
字典包含: platform, user_id, user_nickname, person_id, person_name
字典包含: platform, user_id, user_nickname, person_id, person_name
"""
is_group_chat = False # Default to private/unknown
chat_target_info = None

View File

@@ -31,19 +31,21 @@ class Message(MessageBase):
def __init__(
self,
message_id: str,
timestamp: float,
chat_stream: ChatStream,
user_info: UserInfo,
message_segment: Optional[Seg] = None,
timestamp: Optional[float] = None,
reply: Optional["MessageRecv"] = None,
detailed_plain_text: str = "",
processed_plain_text: str = "",
):
# 使用传入的时间戳或当前时间
current_timestamp = timestamp if timestamp is not None else round(time.time(), 3)
# 构造基础消息信息
message_info = BaseMessageInfo(
platform=chat_stream.platform,
message_id=message_id,
time=timestamp,
time=current_timestamp,
group_info=chat_stream.group_info,
user_info=user_info,
)
@@ -164,11 +166,12 @@ class MessageProcessBase(Message):
message_segment: Optional[Seg] = None,
reply: Optional["MessageRecv"] = None,
thinking_start_time: float = 0,
timestamp: Optional[float] = None,
):
# 调用父类初始化
# 调用父类初始化,传递时间戳
super().__init__(
message_id=message_id,
timestamp=round(time.time(), 3), # 保留3位小数
timestamp=timestamp,
chat_stream=chat_stream,
user_info=bot_user_info,
message_segment=message_segment,
@@ -238,8 +241,9 @@ class MessageThinking(MessageProcessBase):
bot_user_info: UserInfo,
reply: Optional["MessageRecv"] = None,
thinking_start_time: float = 0,
timestamp: Optional[float] = None,
):
# 调用父类初始化
# 调用父类初始化,传递时间戳
super().__init__(
message_id=message_id,
chat_stream=chat_stream,
@@ -247,6 +251,7 @@ class MessageThinking(MessageProcessBase):
message_segment=None, # 思考状态不需要消息段
reply=reply,
thinking_start_time=thinking_start_time,
timestamp=timestamp,
)
# 思考状态特有属性

View File

@@ -107,53 +107,6 @@ async def get_embedding(text, request_type="embedding"):
return embedding
async def get_recent_group_messages(chat_id: str, limit: int = 12) -> list:
"""从数据库获取群组最近的消息记录
Args:
chat_id: 群组ID
limit: 获取消息数量默认12条
Returns:
list: Message对象列表按时间正序排列
"""
# 从数据库获取最近消息
recent_messages = list(
db.messages.find(
{"chat_id": chat_id},
)
.sort("time", -1)
.limit(limit)
)
if not recent_messages:
return []
# 转换为 Message对象列表
message_objects = []
for msg_data in recent_messages:
try:
chat_info = msg_data.get("chat_info", {})
chat_stream = ChatStream.from_dict(chat_info)
user_info = msg_data.get("user_info", {})
user_info = UserInfo.from_dict(user_info)
msg = Message(
message_id=msg_data["message_id"],
chat_stream=chat_stream,
timestamp=msg_data["time"],
user_info=user_info,
processed_plain_text=msg_data.get("processed_text", ""),
detailed_plain_text=msg_data.get("detailed_plain_text", ""),
)
message_objects.append(msg)
except KeyError:
logger.warning("数据库中存在无效的消息")
continue
# 按时间正序排列
message_objects.reverse()
return message_objects
def get_recent_group_detailed_plain_text(chat_stream_id: str, limit: int = 12, combine=False):

View File

@@ -264,9 +264,6 @@ class HeartFChatting:
logger.error(f"[HFC:{self.stream_id}] 获取ChatStream时出错 in _initialize: {e}")
return False
logger.debug(
f"{self.log_prefix} HeartFChatting initialized: is_group={self.is_group_chat}, target_info={self.chat_target_info}"
)
# --- End using utility function ---
self._initialized = True
@@ -861,7 +858,7 @@ class HeartFChatting:
cycle_history=self._cycle_history, # <-- Pass HFC state
observed_messages_str=observed_messages_str, # <-- Pass local variable
current_mind=current_mind, # <-- Pass argument
structured_info=self.sub_mind.structured_info, # <-- Pass SubMind info
structured_info=self.sub_mind.structured_info_str, # <-- Pass SubMind info
current_available_actions=current_available_actions, # <-- Pass determined actions
)
@@ -1301,7 +1298,7 @@ class HeartFChatting:
# Focus specific args:
reason=reason,
current_mind_info=self.sub_mind.current_mind,
structured_info=self.sub_mind.structured_info,
structured_info=self.sub_mind.structured_info_str,
sender_name=sender_name_for_prompt, # Pass determined name
# Normal specific args (not used in focus mode):
# message_txt="",

View File

@@ -63,15 +63,12 @@ class NormalChat:
self.is_group_chat, self.chat_target_info = await get_chat_type_and_target_info(self.stream_id)
# Update stream_name again after potential async call in util func
self.stream_name = chat_manager.get_stream_name(self.stream_id) or self.stream_id
logger.debug(
f"[{self.stream_name}] NormalChat initialized: is_group={self.is_group_chat}, target_info={self.chat_target_info}"
)
# --- End using utility function ---
self._initialized = True
logger.info(f"[{self.stream_name}] NormalChat 实例 initialize 完成 (异步部分)。")
# 改为实例方法
async def _create_thinking_message(self, message: MessageRecv) -> str:
async def _create_thinking_message(self, message: MessageRecv, timestamp: Optional[float] = None) -> str:
"""创建思考消息"""
messageinfo = message.message_info
@@ -85,15 +82,15 @@ class NormalChat:
thinking_id = "mt" + str(thinking_time_point)
thinking_message = MessageThinking(
message_id=thinking_id,
chat_stream=self.chat_stream, # 使用 self.chat_stream
chat_stream=self.chat_stream,
bot_user_info=bot_user_info,
reply=message,
thinking_start_time=thinking_time_point,
timestamp=timestamp if timestamp is not None else None
)
await message_manager.add_message(thinking_message)
return thinking_id
# 改为实例方法
async def _add_messages_to_manager(
self, message: MessageRecv, response_set: List[str], thinking_id
@@ -209,7 +206,7 @@ class NormalChat:
try:
# 处理消息
await self.normal_response(
message=message, is_mentioned=is_mentioned, interested_rate=interest_value
message=message, is_mentioned=is_mentioned, interested_rate=interest_value, rewind_response = False
)
except Exception as e:
logger.error(f"[{self.stream_name}] 处理兴趣消息{msg_id}时出错: {e}\n{traceback.format_exc()}")
@@ -217,7 +214,7 @@ class NormalChat:
self.interest_dict.pop(msg_id, None)
# 改为实例方法, 移除 chat 参数
async def normal_response(self, message: MessageRecv, is_mentioned: bool, interested_rate: float) -> None:
async def normal_response(self, message: MessageRecv, is_mentioned: bool, interested_rate: float, rewind_response: bool = False) -> None:
# 检查收到的消息是否属于当前实例处理的 chat stream
if message.chat_stream.stream_id != self.stream_id:
logger.error(
@@ -264,7 +261,10 @@ class NormalChat:
await willing_manager.before_generate_reply_handle(message.message_info.message_id)
with Timer("创建思考消息", timing_results):
thinking_id = await self._create_thinking_message(message)
if rewind_response:
thinking_id = await self._create_thinking_message(message, message.message_info.time)
else:
thinking_id = await self._create_thinking_message(message)
logger.debug(f"[{self.stream_name}] 创建捕捉器thinking_id:{thinking_id}")
@@ -393,7 +393,7 @@ class NormalChat:
try:
logger.info(f"[{self.stream_name}] 处理初始高兴趣消息 {msg_id} (兴趣值: {interest_value:.2f})")
await self.normal_response(message=message, is_mentioned=is_mentioned, interested_rate=interest_value)
await self.normal_response(message=message, is_mentioned=is_mentioned, interested_rate=interest_value, rewind_response = True)
processed_count += 1
except Exception as e:
logger.error(f"[{self.stream_name}] 处理初始兴趣消息 {msg_id} 时出错: {e}\\n{traceback.format_exc()}")