better:大大优化自我认知和关系处理器的token消耗
This commit is contained in:
@@ -27,11 +27,6 @@ import os
|
||||
import pickle
|
||||
|
||||
|
||||
# 配置常量:是否启用小模型即时信息提取
|
||||
# 开启时:使用小模型并行即时提取,速度更快,但精度可能略低
|
||||
# 关闭时:使用原来的异步模式,精度更高但速度较慢
|
||||
ENABLE_INSTANT_INFO_EXTRACTION = True
|
||||
|
||||
# 消息段清理配置
|
||||
SEGMENT_CLEANUP_CONFIG = {
|
||||
"enable_cleanup": True, # 是否启用清理
|
||||
@@ -49,32 +44,26 @@ def init_prompt():
|
||||
{chat_observe_info}
|
||||
</聊天记录>
|
||||
|
||||
<调取记录>
|
||||
{info_cache_block}
|
||||
</调取记录>
|
||||
请不要重复调取相同的信息
|
||||
|
||||
{name_block}
|
||||
请你阅读聊天记录,查看是否需要调取某个人的信息,这个人可以是出现在聊天记录中的,也可以是记录中提到的人。
|
||||
你不同程度上认识群聊里的人,以及他们谈论到的人,你可以根据聊天记录,回忆起有关他们的信息,帮助你参与聊天
|
||||
1.你需要提供用户名,以及你想要提取的信息名称类型来进行调取
|
||||
1.你需要提供用户名和你想要提取的信息名称类型来进行调取
|
||||
2.请注意,提取的信息类型一定要和用户有关,不要提取无关的信息
|
||||
3.阅读调取记录,如果已经回忆过某个人的信息,请不要重复调取,除非你忘记了
|
||||
|
||||
请以json格式输出,例如:
|
||||
|
||||
{{
|
||||
"用户A": "ta的昵称",
|
||||
"用户B": "ta对你的态度",
|
||||
"用户C": "你和ta最近做的事",
|
||||
"用户D": "你对ta的印象",
|
||||
"person_name": "其他信息",
|
||||
"person_name": "其他信息",
|
||||
}}
|
||||
|
||||
|
||||
请严格按照以下输出格式,不要输出多余内容,person_name可以有多个:
|
||||
{{
|
||||
"person_name": "信息名称",
|
||||
"person_name": "信息名称",
|
||||
}}
|
||||
请严格按照json输出格式,不要输出多余内容,可以同时查询多个人的信息:
|
||||
|
||||
"""
|
||||
Prompt(relationship_prompt, "relationship_prompt")
|
||||
@@ -83,20 +72,14 @@ def init_prompt():
|
||||
|
||||
{name_block}
|
||||
以下是你对{person_name}的了解,请你从中提取用户的有关"{info_type}"的信息,如果用户没有相关信息,请输出none:
|
||||
<对{person_name}的总体了解>
|
||||
{person_impression}
|
||||
</对{person_name}的总体了解>
|
||||
|
||||
<你记得{person_name}最近的事>
|
||||
{points_text}
|
||||
</你记得{person_name}最近的事>
|
||||
|
||||
{person_impression_block}
|
||||
{points_text_block}
|
||||
请严格按照以下json输出格式,不要输出多余内容:
|
||||
{{
|
||||
{info_json_str}
|
||||
}}
|
||||
"""
|
||||
Prompt(fetch_info_prompt, "fetch_info_prompt")
|
||||
Prompt(fetch_info_prompt, "fetch_person_info_prompt")
|
||||
|
||||
|
||||
class RelationshipProcessor(BaseProcessor):
|
||||
@@ -131,7 +114,6 @@ class RelationshipProcessor(BaseProcessor):
|
||||
)
|
||||
|
||||
# 小模型用于即时信息提取
|
||||
if ENABLE_INSTANT_INFO_EXTRACTION:
|
||||
self.instant_llm_model = LLMRequest(
|
||||
model=global_config.model.utils_small,
|
||||
request_type="focus.relationship.instant",
|
||||
@@ -480,6 +462,7 @@ class RelationshipProcessor(BaseProcessor):
|
||||
for observation in observations:
|
||||
if isinstance(observation, ChattingObservation):
|
||||
chat_observe_info = observation.get_observe_info()
|
||||
chat_observe_info = chat_observe_info[-300:]
|
||||
|
||||
# 从聊天观察中提取用户信息并更新消息段
|
||||
# 获取最新的非bot消息来更新消息段
|
||||
@@ -536,31 +519,10 @@ class RelationshipProcessor(BaseProcessor):
|
||||
del self.person_engaged_cache[person_id]
|
||||
self._save_cache()
|
||||
|
||||
# 2. 减少info_fetched_cache中所有信息的TTL
|
||||
for person_id in list(self.info_fetched_cache.keys()):
|
||||
for info_type in list(self.info_fetched_cache[person_id].keys()):
|
||||
self.info_fetched_cache[person_id][info_type]["ttl"] -= 1
|
||||
if self.info_fetched_cache[person_id][info_type]["ttl"] <= 0:
|
||||
# 在删除前查找匹配的info_fetching_cache记录
|
||||
matched_record = None
|
||||
min_time_diff = float("inf")
|
||||
for record in self.info_fetching_cache:
|
||||
if (
|
||||
record["person_id"] == person_id
|
||||
and record["info_type"] == info_type
|
||||
and not record["forget"]
|
||||
):
|
||||
time_diff = abs(
|
||||
record["start_time"] - self.info_fetched_cache[person_id][info_type]["start_time"]
|
||||
)
|
||||
if time_diff < min_time_diff:
|
||||
min_time_diff = time_diff
|
||||
matched_record = record
|
||||
|
||||
if matched_record:
|
||||
matched_record["forget"] = True
|
||||
logger.info(f"{self.log_prefix} 用户 {person_id} 的 {info_type} 信息已过期,标记为遗忘。")
|
||||
|
||||
del self.info_fetched_cache[person_id][info_type]
|
||||
if not self.info_fetched_cache[person_id]:
|
||||
del self.info_fetched_cache[person_id]
|
||||
@@ -571,11 +533,17 @@ class RelationshipProcessor(BaseProcessor):
|
||||
|
||||
info_cache_block = ""
|
||||
if self.info_fetching_cache:
|
||||
# 对于每个(person_id, info_type)组合,只保留最新的记录
|
||||
latest_records = {}
|
||||
for info_fetching in self.info_fetching_cache:
|
||||
if info_fetching["forget"]:
|
||||
info_cache_block += f"在{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(info_fetching['start_time']))},你回忆了[{info_fetching['person_name']}]的[{info_fetching['info_type']}],但是现在你忘记了\n"
|
||||
else:
|
||||
info_cache_block += f"在{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(info_fetching['start_time']))},你回忆了[{info_fetching['person_name']}]的[{info_fetching['info_type']}],还记着呢\n"
|
||||
key = (info_fetching['person_id'], info_fetching['info_type'])
|
||||
if key not in latest_records or info_fetching['start_time'] > latest_records[key]['start_time']:
|
||||
latest_records[key] = info_fetching
|
||||
|
||||
# 按时间排序并生成显示文本
|
||||
sorted_records = sorted(latest_records.values(), key=lambda x: x['start_time'])
|
||||
for info_fetching in sorted_records:
|
||||
info_cache_block += f"你已经调取了[{info_fetching['person_name']}]的[{info_fetching['info_type']}]信息\n"
|
||||
|
||||
prompt = (await global_prompt_manager.get_prompt_async("relationship_prompt")).format(
|
||||
name_block=name_block,
|
||||
@@ -585,7 +553,7 @@ class RelationshipProcessor(BaseProcessor):
|
||||
)
|
||||
|
||||
try:
|
||||
# logger.debug(f"{self.log_prefix} 人物信息prompt: \n{prompt}\n")
|
||||
logger.info(f"{self.log_prefix} 人物信息prompt: \n{prompt}\n")
|
||||
content, _ = await self.llm_model.generate_response_async(prompt=prompt)
|
||||
if content:
|
||||
# print(f"content: {content}")
|
||||
@@ -616,20 +584,12 @@ class RelationshipProcessor(BaseProcessor):
|
||||
|
||||
logger.info(f"{self.log_prefix} 调取用户 {person_name} 的 {info_type} 信息。")
|
||||
|
||||
# 这里不需要检查person_engaged_cache,因为消息段的管理由_update_message_segments处理
|
||||
# 信息提取和消息段管理是独立的流程
|
||||
|
||||
if ENABLE_INSTANT_INFO_EXTRACTION:
|
||||
# 收集即时提取任务
|
||||
instant_tasks.append((person_id, info_type, time.time()))
|
||||
else:
|
||||
# 使用原来的异步模式
|
||||
async_tasks.append(
|
||||
asyncio.create_task(self.fetch_person_info(person_id, [info_type], start_time=time.time()))
|
||||
)
|
||||
|
||||
# 执行即时提取任务
|
||||
if ENABLE_INSTANT_INFO_EXTRACTION and instant_tasks:
|
||||
if instant_tasks:
|
||||
await self._execute_instant_extraction_batch(instant_tasks)
|
||||
|
||||
# 启动异步任务(如果不是即时模式)
|
||||
@@ -660,31 +620,6 @@ class RelationshipProcessor(BaseProcessor):
|
||||
if person_infos_str:
|
||||
persons_infos_str += f"你对 {person_name} 的了解:{person_infos_str}\n"
|
||||
|
||||
# 处理正在调取但还没有结果的项目(只在非即时提取模式下显示)
|
||||
if not ENABLE_INSTANT_INFO_EXTRACTION:
|
||||
pending_info_dict = {}
|
||||
for record in self.info_fetching_cache:
|
||||
if not record["forget"]:
|
||||
current_time = time.time()
|
||||
# 只处理不超过2分钟的调取请求,避免过期请求一直显示
|
||||
if current_time - record["start_time"] <= 120: # 10分钟内的请求
|
||||
person_id = record["person_id"]
|
||||
person_name = record["person_name"]
|
||||
info_type = record["info_type"]
|
||||
|
||||
# 检查是否已经在info_fetched_cache中有结果
|
||||
if person_id in self.info_fetched_cache and info_type in self.info_fetched_cache[person_id]:
|
||||
continue
|
||||
|
||||
# 按人物组织正在调取的信息
|
||||
if person_name not in pending_info_dict:
|
||||
pending_info_dict[person_name] = []
|
||||
pending_info_dict[person_name].append(info_type)
|
||||
|
||||
# 添加正在调取的信息到返回字符串
|
||||
for person_name, info_types in pending_info_dict.items():
|
||||
info_types_str = "、".join(info_types)
|
||||
persons_infos_str += f"你正在识图回忆有关 {person_name} 的 {info_types_str} 信息,稍等一下再回答...\n"
|
||||
|
||||
return persons_infos_str
|
||||
|
||||
@@ -793,165 +728,163 @@ class RelationshipProcessor(BaseProcessor):
|
||||
使用小模型提取单个信息类型
|
||||
"""
|
||||
person_info_manager = get_person_info_manager()
|
||||
|
||||
# 首先检查 info_list 缓存
|
||||
info_list = await person_info_manager.get_value(person_id, "info_list") or []
|
||||
cached_info = None
|
||||
|
||||
print(f"info_list: {info_list}")
|
||||
|
||||
# 查找对应的 info_type
|
||||
for info_item in info_list:
|
||||
if info_item.get("info_type") == info_type:
|
||||
cached_info = info_item.get("info_content")
|
||||
logger.info(f"{self.log_prefix} [缓存命中] 从 info_list 中找到 {info_type} 信息: {cached_info}")
|
||||
break
|
||||
|
||||
|
||||
# 如果缓存中有信息,直接使用
|
||||
if cached_info :
|
||||
person_name = await person_info_manager.get_value(person_id, "person_name")
|
||||
if person_id not in self.info_fetched_cache:
|
||||
self.info_fetched_cache[person_id] = {}
|
||||
|
||||
if cached_info == "none":
|
||||
unknow = True
|
||||
else:
|
||||
unknow = False
|
||||
|
||||
self.info_fetched_cache[person_id][info_type] = {
|
||||
"info": cached_info,
|
||||
"ttl": 8,
|
||||
"start_time": start_time,
|
||||
"person_name": person_name,
|
||||
"unknow": unknow,
|
||||
}
|
||||
logger.info(f"{self.log_prefix} [缓存使用] 直接使用缓存的 {person_name} 的 {info_type}: {cached_info}")
|
||||
return
|
||||
|
||||
logger.info(f"{self.log_prefix} [缓存命中] 缓存中没有信息")
|
||||
|
||||
try:
|
||||
nickname_str = ",".join(global_config.bot.alias_names)
|
||||
name_block = f"你的名字是{global_config.bot.nickname},你的昵称有{nickname_str},有人也会用这些昵称称呼你。"
|
||||
|
||||
person_name = await person_info_manager.get_value(person_id, "person_name")
|
||||
|
||||
person_impression = await person_info_manager.get_value(person_id, "impression")
|
||||
if not person_impression:
|
||||
impression_block = "你对ta没有什么深刻的印象"
|
||||
if person_impression:
|
||||
person_impression_block = f"<对{person_name}的总体了解>\n{person_impression}\n</对{person_name}的总体了解>"
|
||||
else:
|
||||
impression_block = f"{person_impression}"
|
||||
person_impression_block = ""
|
||||
|
||||
points = await person_info_manager.get_value(person_id, "points")
|
||||
if points:
|
||||
points_text = "\n".join([f"{point[2]}:{point[0]}" for point in points])
|
||||
points_text_block = f"<对{person_name}的近期了解>\n{points_text}\n</对{person_name}的近期了解>"
|
||||
else:
|
||||
points_text = "你不记得ta最近发生了什么"
|
||||
points_text_block = ""
|
||||
|
||||
prompt = (await global_prompt_manager.get_prompt_async("fetch_info_prompt")).format(
|
||||
name_block=name_block,
|
||||
info_type=info_type,
|
||||
person_impression=impression_block,
|
||||
person_name=person_name,
|
||||
info_json_str=f'"{info_type}": "信息内容"',
|
||||
points_text=points_text,
|
||||
)
|
||||
|
||||
try:
|
||||
# 使用小模型进行即时提取
|
||||
content, _ = await self.instant_llm_model.generate_response_async(prompt=prompt)
|
||||
|
||||
logger.info(f"{self.log_prefix} [即时提取] {person_name} 的 {info_type} 结果: {content}")
|
||||
|
||||
if content:
|
||||
content_json = json.loads(repair_json(content))
|
||||
if info_type in content_json:
|
||||
info_content = content_json[info_type]
|
||||
if info_content != "none" and info_content:
|
||||
if person_id not in self.info_fetched_cache:
|
||||
self.info_fetched_cache[person_id] = {}
|
||||
if not points_text_block and not person_impression_block:
|
||||
self.info_fetched_cache[person_id][info_type] = {
|
||||
"info": info_content,
|
||||
"ttl": 8, # 小模型提取的信息TTL稍短
|
||||
"start_time": start_time,
|
||||
"person_name": person_name,
|
||||
"unknow": False,
|
||||
}
|
||||
logger.info(
|
||||
f"{self.log_prefix} [即时提取] 成功获取 {person_name} 的 {info_type}: {info_content}"
|
||||
)
|
||||
else:
|
||||
if person_id not in self.info_fetched_cache:
|
||||
self.info_fetched_cache[person_id] = {}
|
||||
self.info_fetched_cache[person_id][info_type] = {
|
||||
"info": "unknow",
|
||||
"info": "none",
|
||||
"ttl": 8,
|
||||
"start_time": start_time,
|
||||
"person_name": person_name,
|
||||
"unknow": True,
|
||||
}
|
||||
logger.info(f"{self.log_prefix} [即时提取] {person_name} 的 {info_type} 信息不明确")
|
||||
else:
|
||||
logger.warning(
|
||||
f"{self.log_prefix} [即时提取] 小模型返回空结果,获取 {person_name} 的 {info_type} 信息失败。"
|
||||
|
||||
prompt = (await global_prompt_manager.get_prompt_async("fetch_person_info_prompt")).format(
|
||||
name_block=name_block,
|
||||
info_type=info_type,
|
||||
person_impression_block=person_impression_block,
|
||||
person_name=person_name,
|
||||
info_json_str=f'"{info_type}": "有关{info_type}的信息内容"',
|
||||
points_text_block=points_text_block,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} [即时提取] 执行小模型请求获取用户信息时出错: {e}")
|
||||
except Exception:
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
async def fetch_person_info(self, person_id: str, info_types: list[str], start_time: float):
|
||||
"""
|
||||
获取某个人的信息
|
||||
"""
|
||||
# 检查缓存中是否已存在且未过期的信息
|
||||
info_types_to_fetch = []
|
||||
|
||||
for info_type in info_types:
|
||||
if person_id in self.info_fetched_cache and info_type in self.info_fetched_cache[person_id]:
|
||||
logger.info(f"{self.log_prefix} 用户 {person_id} 的 {info_type} 信息已存在且未过期,跳过调取。")
|
||||
continue
|
||||
info_types_to_fetch.append(info_type)
|
||||
|
||||
if not info_types_to_fetch:
|
||||
return
|
||||
|
||||
nickname_str = ",".join(global_config.bot.alias_names)
|
||||
name_block = f"你的名字是{global_config.bot.nickname},你的昵称有{nickname_str},有人也会用这些昵称称呼你。"
|
||||
|
||||
person_info_manager = get_person_info_manager()
|
||||
person_name = await person_info_manager.get_value(person_id, "person_name")
|
||||
|
||||
info_type_str = ""
|
||||
info_json_str = ""
|
||||
for info_type in info_types_to_fetch:
|
||||
info_type_str += f"{info_type},"
|
||||
info_json_str += f'"{info_type}": "信息内容",'
|
||||
info_type_str = info_type_str[:-1]
|
||||
info_json_str = info_json_str[:-1]
|
||||
|
||||
person_impression = await person_info_manager.get_value(person_id, "impression")
|
||||
if not person_impression:
|
||||
impression_block = "你对ta没有什么深刻的印象"
|
||||
else:
|
||||
impression_block = f"{person_impression}"
|
||||
|
||||
points = await person_info_manager.get_value(person_id, "points")
|
||||
|
||||
if points:
|
||||
points_text = "\n".join([f"{point[2]}:{point[0]}" for point in points])
|
||||
else:
|
||||
points_text = "你不记得ta最近发生了什么"
|
||||
|
||||
prompt = (await global_prompt_manager.get_prompt_async("fetch_info_prompt")).format(
|
||||
name_block=name_block,
|
||||
info_type=info_type_str,
|
||||
person_impression=impression_block,
|
||||
person_name=person_name,
|
||||
info_json_str=info_json_str,
|
||||
points_text=points_text,
|
||||
)
|
||||
print(prompt)
|
||||
|
||||
try:
|
||||
content, _ = await self.llm_model.generate_response_async(prompt=prompt)
|
||||
# 使用小模型进行即时提取
|
||||
content, _ = await self.instant_llm_model.generate_response_async(prompt=prompt)
|
||||
|
||||
# logger.info(f"{self.log_prefix} fetch_person_info prompt: \n{prompt}\n")
|
||||
logger.info(f"{self.log_prefix} fetch_person_info 结果: {content}")
|
||||
logger.info(f"{self.log_prefix} [LLM提取] {person_name} 的 {info_type} 结果: {content}")
|
||||
|
||||
if content:
|
||||
try:
|
||||
content_json = json.loads(repair_json(content))
|
||||
for info_type, info_content in content_json.items():
|
||||
if info_content != "none" and info_content:
|
||||
if person_id not in self.info_fetched_cache:
|
||||
self.info_fetched_cache[person_id] = {}
|
||||
self.info_fetched_cache[person_id][info_type] = {
|
||||
"info": info_content,
|
||||
"ttl": 10,
|
||||
"start_time": start_time,
|
||||
"person_name": person_name,
|
||||
"unknow": False,
|
||||
}
|
||||
else:
|
||||
if person_id not in self.info_fetched_cache:
|
||||
self.info_fetched_cache[person_id] = {}
|
||||
if info_type in content_json:
|
||||
info_content = content_json[info_type]
|
||||
is_unknown = info_content == "none" or not info_content
|
||||
|
||||
# 保存到运行时缓存
|
||||
if person_id not in self.info_fetched_cache:
|
||||
self.info_fetched_cache[person_id] = {}
|
||||
self.info_fetched_cache[person_id][info_type] = {
|
||||
"info": "unknow",
|
||||
"ttl": 10,
|
||||
"info": "unknow" if is_unknown else info_content,
|
||||
"ttl": 8,
|
||||
"start_time": start_time,
|
||||
"person_name": person_name,
|
||||
"unknow": True,
|
||||
"unknow": is_unknown,
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} 解析LLM返回的信息时出错: {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
# 保存到持久化缓存 (info_list)
|
||||
await self._save_info_to_cache(person_id, info_type, info_content if not is_unknown else "none")
|
||||
|
||||
if not is_unknown:
|
||||
logger.info(f"{self.log_prefix} [LLM提取] 成功获取并缓存 {person_name} 的 {info_type}: {info_content}")
|
||||
else:
|
||||
logger.warning(f"{self.log_prefix} LLM返回空结果,获取用户 {person_name} 的 {info_type_str} 信息失败。")
|
||||
logger.info(f"{self.log_prefix} [LLM提取] {person_name} 的 {info_type} 信息不明确")
|
||||
else:
|
||||
logger.warning(f"{self.log_prefix} [LLM提取] 小模型返回空结果,获取 {person_name} 的 {info_type} 信息失败。")
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} 执行LLM请求获取用户信息时出错: {e}")
|
||||
logger.error(f"{self.log_prefix} [LLM提取] 执行小模型请求获取用户信息时出错: {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
async def _save_info_to_cache(self, person_id: str, info_type: str, info_content: str):
|
||||
"""
|
||||
将提取到的信息保存到 person_info 的 info_list 字段中
|
||||
|
||||
Args:
|
||||
person_id: 用户ID
|
||||
info_type: 信息类型
|
||||
info_content: 信息内容
|
||||
"""
|
||||
try:
|
||||
person_info_manager = get_person_info_manager()
|
||||
|
||||
# 获取现有的 info_list
|
||||
info_list = await person_info_manager.get_value(person_id, "info_list") or []
|
||||
|
||||
# 查找是否已存在相同 info_type 的记录
|
||||
found_index = -1
|
||||
for i, info_item in enumerate(info_list):
|
||||
if isinstance(info_item, dict) and info_item.get("info_type") == info_type:
|
||||
found_index = i
|
||||
break
|
||||
|
||||
# 创建新的信息记录
|
||||
new_info_item = {
|
||||
"info_type": info_type,
|
||||
"info_content": info_content,
|
||||
}
|
||||
|
||||
if found_index >= 0:
|
||||
# 更新现有记录
|
||||
info_list[found_index] = new_info_item
|
||||
logger.info(f"{self.log_prefix} [缓存更新] 更新 {person_id} 的 {info_type} 信息缓存")
|
||||
else:
|
||||
# 添加新记录
|
||||
info_list.append(new_info_item)
|
||||
logger.info(f"{self.log_prefix} [缓存保存] 新增 {person_id} 的 {info_type} 信息缓存")
|
||||
|
||||
# 保存更新后的 info_list
|
||||
await person_info_manager.update_one_field(person_id, "info_list", info_list)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} [缓存保存] 保存信息到缓存失败: {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
|
||||
init_prompt()
|
||||
|
||||
@@ -4,12 +4,14 @@ from src.llm_models.utils_model import LLMRequest
|
||||
from src.config.config import global_config
|
||||
import time
|
||||
import traceback
|
||||
import json
|
||||
import os
|
||||
import hashlib
|
||||
from src.common.logger import get_logger
|
||||
from src.individuality.individuality import get_individuality
|
||||
from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
|
||||
from src.chat.message_receive.chat_stream import get_chat_manager
|
||||
from .base_processor import BaseProcessor
|
||||
from typing import List
|
||||
from typing import List, Dict
|
||||
from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservation
|
||||
from src.chat.focus_chat.info.info_base import InfoBase
|
||||
from src.chat.focus_chat.info.self_info import SelfInfo
|
||||
@@ -18,33 +20,33 @@ logger = get_logger("processor")
|
||||
|
||||
|
||||
def init_prompt():
|
||||
|
||||
indentify_prompt = """
|
||||
{time_now},以下是正在进行的聊天内容:
|
||||
<聊天记录>
|
||||
{chat_observe_info}
|
||||
</聊天记录>
|
||||
|
||||
<人格>
|
||||
{name_block}
|
||||
你是一个AI,但是你伪装成了一个人类,不要让别人发现这一点,你的性格是:
|
||||
{prompt_personality}
|
||||
{indentify_block}
|
||||
</人格>
|
||||
|
||||
请区分聊天记录的内容和你稳定的人格,聊天记录是现在发生的事情,人格是你稳定的独特的特质。
|
||||
|
||||
{name_block}
|
||||
现在请你提取你人格的关键信息,提取成一串文本:
|
||||
1. 根据聊天记录,输出与聊天记录相关的自我描述,包括人格,形象等等,对人格形象进行精简
|
||||
2. 思考有没有内容与你的描述相关
|
||||
3. 如果没有明显相关内容,请输出十几个字的简短自我描述
|
||||
|
||||
现在请输出你的自我描述,格式是:“你是.....,你.................(描述)”
|
||||
请注意不要输出多余内容(包括前后缀,括号(),表情包,at或 @等 ):
|
||||
|
||||
请你根据以上聊天记录,思考聊天记录中是否有人提到你自己相关的信息,或者有人询问你的相关信息,例如你的性格,身高,喜好,外貌,身份,兴趣,爱好,习惯,等等。
|
||||
然后请你根据你的聊天需要,输出关键词属性在数据库中进行查询,数据库包含了关于你的所有信息,你需要直接输出你要查询的关键词,如果要输出多个,请用逗号隔开
|
||||
如果没有需要查询的内容,请输出none
|
||||
现在请输出关键词,注意只输出关键词就好,不要输出其他内容:
|
||||
"""
|
||||
Prompt(indentify_prompt, "indentify_prompt")
|
||||
|
||||
|
||||
fetch_info_prompt = """
|
||||
|
||||
{name_block},你的性格是:
|
||||
{prompt_personality}
|
||||
{indentify_block}
|
||||
|
||||
请从中提取有关你的有关"{keyword}"信息,请输出原始内容,如果{bot_name}没有涉及"{keyword}"相关信息,请输出none:
|
||||
"""
|
||||
Prompt(fetch_info_prompt, "fetch_info_prompt")
|
||||
|
||||
|
||||
class SelfProcessor(BaseProcessor):
|
||||
log_prefix = "自我认同"
|
||||
|
||||
@@ -53,14 +55,101 @@ class SelfProcessor(BaseProcessor):
|
||||
|
||||
self.subheartflow_id = subheartflow_id
|
||||
|
||||
self.info_fetched_cache: Dict[str, Dict[str, any]] = {}
|
||||
|
||||
self.fetch_info_file_path = "data/personality/fetch_info.json"
|
||||
self.meta_info_file_path = "data/personality/meta_info.json"
|
||||
|
||||
self.llm_model = LLMRequest(
|
||||
model=global_config.model.relation,
|
||||
model=global_config.model.utils_small,
|
||||
request_type="focus.processor.self_identify",
|
||||
)
|
||||
|
||||
|
||||
name = get_chat_manager().get_stream_name(self.subheartflow_id)
|
||||
self.log_prefix = f"[{name}] "
|
||||
|
||||
# 在初始化时检查配置是否发生变化
|
||||
self._check_config_change_and_clear()
|
||||
|
||||
def _get_config_hash(self) -> str:
|
||||
"""获取当前personality和identity配置的哈希值"""
|
||||
personality_sides = list(global_config.personality.personality_sides)
|
||||
identity_detail = list(global_config.identity.identity_detail)
|
||||
|
||||
# 将配置转换为字符串并排序,确保一致性
|
||||
config_str = json.dumps({
|
||||
"personality_sides": sorted(personality_sides),
|
||||
"identity_detail": sorted(identity_detail)
|
||||
}, sort_keys=True)
|
||||
|
||||
return hashlib.md5(config_str.encode('utf-8')).hexdigest()
|
||||
|
||||
def _load_meta_info(self) -> Dict[str, str]:
|
||||
"""从JSON文件中加载元信息"""
|
||||
if os.path.exists(self.meta_info_file_path):
|
||||
try:
|
||||
with open(self.meta_info_file_path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
except Exception as e:
|
||||
logger.warning(f"{self.log_prefix} 读取meta_info文件失败: {e}")
|
||||
return {}
|
||||
return {}
|
||||
|
||||
def _save_meta_info(self, meta_info: Dict[str, str]):
|
||||
"""将元信息保存到JSON文件"""
|
||||
try:
|
||||
# 确保目录存在
|
||||
os.makedirs(os.path.dirname(self.meta_info_file_path), exist_ok=True)
|
||||
with open(self.meta_info_file_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(meta_info, f, ensure_ascii=False, indent=2)
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} 保存meta_info文件失败: {e}")
|
||||
|
||||
def _check_config_change_and_clear(self):
|
||||
"""检查配置是否发生变化,如果变化则清空fetch_info.json"""
|
||||
current_config_hash = self._get_config_hash()
|
||||
meta_info = self._load_meta_info()
|
||||
|
||||
stored_config_hash = meta_info.get("config_hash", "")
|
||||
|
||||
if current_config_hash != stored_config_hash:
|
||||
logger.info(f"{self.log_prefix} 检测到personality或identity配置发生变化,清空fetch_info数据")
|
||||
|
||||
# 清空fetch_info文件
|
||||
if os.path.exists(self.fetch_info_file_path):
|
||||
try:
|
||||
os.remove(self.fetch_info_file_path)
|
||||
logger.info(f"{self.log_prefix} 已清空fetch_info文件")
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} 清空fetch_info文件失败: {e}")
|
||||
|
||||
# 更新元信息
|
||||
meta_info["config_hash"] = current_config_hash
|
||||
self._save_meta_info(meta_info)
|
||||
logger.info(f"{self.log_prefix} 已更新配置哈希值")
|
||||
|
||||
def _load_fetch_info_from_file(self) -> Dict[str, str]:
|
||||
"""从JSON文件中加载已保存的fetch_info数据"""
|
||||
if os.path.exists(self.fetch_info_file_path):
|
||||
try:
|
||||
with open(self.fetch_info_file_path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
except Exception as e:
|
||||
logger.warning(f"{self.log_prefix} 读取fetch_info文件失败: {e}")
|
||||
return {}
|
||||
return {}
|
||||
|
||||
def _save_fetch_info_to_file(self, fetch_info_data: Dict[str, str]):
|
||||
"""将fetch_info数据保存到JSON文件"""
|
||||
try:
|
||||
# 确保目录存在
|
||||
os.makedirs(os.path.dirname(self.fetch_info_file_path), exist_ok=True)
|
||||
with open(self.fetch_info_file_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(fetch_info_data, f, ensure_ascii=False, indent=2)
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} 保存fetch_info文件失败: {e}")
|
||||
|
||||
async def process_info(self, observations: List[Observation] = None, *infos) -> List[InfoBase]:
|
||||
"""处理信息对象
|
||||
|
||||
@@ -121,38 +210,99 @@ class SelfProcessor(BaseProcessor):
|
||||
nickname_str += f"{nicknames},"
|
||||
name_block = f"你的名字是{global_config.bot.nickname},你的昵称有{nickname_str},有人也会用这些昵称称呼你。"
|
||||
|
||||
personality_block = get_individuality().get_personality_prompt(x_person=2, level=2)
|
||||
personality_sides_str = "你"
|
||||
identity_detail_str = "你"
|
||||
for personality_side in global_config.personality.personality_sides:
|
||||
personality_sides_str += f"{personality_side},"
|
||||
|
||||
for identity_detail in global_config.identity.identity_detail:
|
||||
identity_detail_str += f"{identity_detail},"
|
||||
|
||||
identity_block = get_individuality().get_identity_prompt(x_person=2, level=2)
|
||||
|
||||
prompt = (await global_prompt_manager.get_prompt_async("indentify_prompt")).format(
|
||||
name_block=name_block,
|
||||
prompt_personality=personality_block,
|
||||
indentify_block=identity_block,
|
||||
time_now=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
|
||||
chat_observe_info=chat_observe_info,
|
||||
chat_observe_info=chat_observe_info[-200:],
|
||||
bot_name = global_config.bot.nickname
|
||||
)
|
||||
|
||||
# print(prompt)
|
||||
keyword = ""
|
||||
|
||||
content = ""
|
||||
try:
|
||||
content, _ = await self.llm_model.generate_response_async(prompt=prompt)
|
||||
if not content:
|
||||
keyword, _ = await self.llm_model.generate_response_async(prompt=prompt)
|
||||
|
||||
print(f"prompt: {prompt}\nkeyword: {keyword}")
|
||||
|
||||
if not keyword:
|
||||
logger.warning(f"{self.log_prefix} LLM返回空结果,自我识别失败。")
|
||||
except Exception as e:
|
||||
# 处理总体异常
|
||||
logger.error(f"{self.log_prefix} 执行LLM请求或处理响应时出错: {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
content = "自我识别过程中出现错误"
|
||||
keyword = "我是谁,我从哪来,要到哪去"
|
||||
|
||||
if content == "None":
|
||||
content = ""
|
||||
# 记录初步思考结果
|
||||
# logger.debug(f"{self.log_prefix} 自我识别prompt: \n{prompt}\n")
|
||||
logger.info(f"{self.log_prefix} 自我认知: {content}")
|
||||
|
||||
return content
|
||||
# keyword_json = json.loads(repair_json(keyword))
|
||||
# 根据逗号分割为list
|
||||
keyword = keyword.strip()
|
||||
if not keyword or keyword == "none":
|
||||
keyword_set = []
|
||||
else:
|
||||
# 只保留非空关键词,去除多余空格
|
||||
keyword_set = [k.strip() for k in keyword.split(",") if k.strip()]
|
||||
|
||||
for keyword in keyword_set:
|
||||
if keyword not in self.info_fetched_cache:
|
||||
# 首先尝试从文件中读取
|
||||
fetch_info_data = self._load_fetch_info_from_file()
|
||||
|
||||
if keyword in fetch_info_data:
|
||||
# 从文件中获取已保存的信息
|
||||
fetched_info = fetch_info_data[keyword]
|
||||
logger.info(f"{self.log_prefix} 从文件中读取到关键词 '{keyword}' 的信息")
|
||||
else:
|
||||
# 文件中没有,使用LLM生成
|
||||
prompt = (await global_prompt_manager.get_prompt_async("fetch_info_prompt")).format(
|
||||
name_block=name_block,
|
||||
prompt_personality=personality_sides_str,
|
||||
indentify_block=identity_detail_str,
|
||||
keyword=keyword,
|
||||
bot_name = global_config.bot.nickname
|
||||
)
|
||||
|
||||
print(prompt)
|
||||
|
||||
fetched_info, _ = await self.llm_model.generate_response_async(prompt=prompt)
|
||||
if not fetched_info:
|
||||
logger.warning(f"{self.log_prefix} LLM返回空结果,自我识别失败。")
|
||||
fetched_info = ""
|
||||
elif fetched_info == "none":
|
||||
fetched_info = ""
|
||||
else:
|
||||
# 保存新生成的信息到文件
|
||||
fetch_info_data[keyword] = fetched_info
|
||||
self._save_fetch_info_to_file(fetch_info_data)
|
||||
logger.info(f"{self.log_prefix} 新生成的关键词 '{keyword}' 信息已保存到文件")
|
||||
|
||||
if fetched_info:
|
||||
self.info_fetched_cache[keyword] = {
|
||||
"info": fetched_info,
|
||||
"ttl": 5,
|
||||
}
|
||||
|
||||
for fetched_keyword, info in self.info_fetched_cache.items():
|
||||
if info["ttl"] > 0:
|
||||
info["ttl"] -= 1
|
||||
else:
|
||||
del self.info_fetched_cache[fetched_keyword]
|
||||
|
||||
|
||||
fetched_info_str = ""
|
||||
for keyword, info in self.info_fetched_cache.items():
|
||||
fetched_info_str += f"你的:{keyword}信息是: {info['info']}\n"
|
||||
|
||||
return fetched_info_str
|
||||
|
||||
|
||||
|
||||
init_prompt()
|
||||
|
||||
@@ -28,15 +28,12 @@ install(extra_lines=3)
|
||||
def init_prompt():
|
||||
Prompt(
|
||||
"""
|
||||
你的自我认知是:
|
||||
{self_info_block}
|
||||
请记住你的性格,身份和特点。
|
||||
|
||||
{time_block}
|
||||
你是群内的一员,你现在正在参与群内的闲聊,以下是群内的聊天内容:
|
||||
{indentify_block}你现在正在参与以下的聊天,以下是具体的聊天内容:
|
||||
|
||||
{chat_content_block}
|
||||
|
||||
{self_info_block}
|
||||
{relation_info_block}
|
||||
|
||||
{cycle_info_block}
|
||||
@@ -241,7 +238,7 @@ class ActionPlanner(BasePlanner):
|
||||
if key not in ["action", "reasoning"]:
|
||||
action_data[key] = value
|
||||
|
||||
action_data["identity"] = self_info
|
||||
action_data["self_info_block"] = self_info
|
||||
|
||||
extra_info_block = "\n".join(extra_info)
|
||||
extra_info_block += f"\n{structured_info}"
|
||||
@@ -345,21 +342,10 @@ class ActionPlanner(BasePlanner):
|
||||
else:
|
||||
chat_content_block = "你还未开始聊天"
|
||||
|
||||
# mind_info_block = ""
|
||||
# if current_mind:
|
||||
# mind_info_block = f"对聊天的规划:{current_mind}"
|
||||
# else:
|
||||
# mind_info_block = "你刚参与聊天"
|
||||
|
||||
personality_block = get_individuality().get_prompt(x_person=2, level=2)
|
||||
|
||||
action_options_block = ""
|
||||
for using_actions_name, using_actions_info in current_available_actions.items():
|
||||
# print(using_actions_name)
|
||||
# print(using_actions_info)
|
||||
# print(using_actions_info["parameters"])
|
||||
# print(using_actions_info["require"])
|
||||
# print(using_actions_info["description"])
|
||||
|
||||
|
||||
using_action_prompt = await global_prompt_manager.get_prompt_async("action_prompt")
|
||||
|
||||
@@ -398,22 +384,25 @@ class ActionPlanner(BasePlanner):
|
||||
# 获取当前时间
|
||||
time_block = f"当前时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
||||
|
||||
bot_name = global_config.bot.nickname
|
||||
if global_config.bot.alias_names:
|
||||
bot_nickname = f",也有人叫你{','.join(global_config.bot.alias_names)}"
|
||||
else:
|
||||
bot_nickname = ""
|
||||
bot_core_personality = global_config.personality.personality_core
|
||||
indentify_block = f"你的名字是{bot_name}{bot_nickname},你{bot_core_personality}:"
|
||||
|
||||
planner_prompt_template = await global_prompt_manager.get_prompt_async("simple_planner_prompt")
|
||||
prompt = planner_prompt_template.format(
|
||||
relation_info_block=relation_info_block,
|
||||
self_info_block=self_info_block,
|
||||
# memory_str=memory_str,
|
||||
time_block=time_block,
|
||||
# bot_name=global_config.bot.nickname,
|
||||
prompt_personality=personality_block,
|
||||
chat_context_description=chat_context_description,
|
||||
chat_content_block=chat_content_block,
|
||||
# mind_info_block=mind_info_block,
|
||||
cycle_info_block=cycle_info,
|
||||
action_options_text=action_options_block,
|
||||
# action_available_block=action_available_block,
|
||||
# extra_info_block=extra_info_block,
|
||||
moderation_prompt=moderation_prompt_block,
|
||||
indentify_block=indentify_block,
|
||||
)
|
||||
return prompt
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ def init_prompt():
|
||||
{extra_info_block}
|
||||
|
||||
{relation_info_block}
|
||||
{self_info_block}
|
||||
|
||||
{time_block}
|
||||
{chat_target}
|
||||
@@ -280,7 +281,7 @@ class DefaultReplyer:
|
||||
|
||||
is_group_chat = bool(chat_stream.group_info)
|
||||
|
||||
identity = reply_data.get("identity", "")
|
||||
self_info_block = reply_data.get("self_info_block", "")
|
||||
extra_info_block = reply_data.get("extra_info_block", "")
|
||||
relation_info_block = reply_data.get("relation_info_block", "")
|
||||
reply_to = reply_data.get("reply_to", "none")
|
||||
@@ -368,6 +369,13 @@ class DefaultReplyer:
|
||||
time_block = f"当前时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
||||
|
||||
# logger.debug("开始构建 focus prompt")
|
||||
bot_name = global_config.bot.nickname
|
||||
if global_config.bot.alias_names:
|
||||
bot_nickname = f",也有人叫你{','.join(global_config.bot.alias_names)}"
|
||||
else:
|
||||
bot_nickname = ""
|
||||
bot_core_personality = global_config.personality.personality_core
|
||||
indentify_block = f"你的名字是{bot_name}{bot_nickname},你{bot_core_personality}:"
|
||||
|
||||
if sender:
|
||||
reply_target_block = f"现在{sender}说的:{target}。引起了你的注意,你想要在群里发言或者回复这条消息。"
|
||||
@@ -390,10 +398,11 @@ class DefaultReplyer:
|
||||
chat_info=chat_talking_prompt,
|
||||
extra_info_block=extra_info_block,
|
||||
relation_info_block=relation_info_block,
|
||||
self_info_block=self_info_block,
|
||||
time_block=time_block,
|
||||
reply_target_block=reply_target_block,
|
||||
keywords_reaction_prompt=keywords_reaction_prompt,
|
||||
identity=identity,
|
||||
indentify_block=indentify_block,
|
||||
target_message=target,
|
||||
sender_name=sender,
|
||||
config_expression_style=global_config.expression.expression_style,
|
||||
@@ -408,10 +417,11 @@ class DefaultReplyer:
|
||||
chat_info=chat_talking_prompt,
|
||||
extra_info_block=extra_info_block,
|
||||
relation_info_block=relation_info_block,
|
||||
self_info_block=self_info_block,
|
||||
time_block=time_block,
|
||||
reply_target_block=reply_target_block,
|
||||
keywords_reaction_prompt=keywords_reaction_prompt,
|
||||
identity=identity,
|
||||
indentify_block=indentify_block,
|
||||
target_message=target,
|
||||
sender_name=sender,
|
||||
config_expression_style=global_config.expression.expression_style,
|
||||
|
||||
@@ -240,6 +240,10 @@ class RelationshipManager:
|
||||
{{
|
||||
"point": "{person_name}居然搞错了我的名字,生气了",
|
||||
"weight": 8
|
||||
}},
|
||||
{{
|
||||
"point": "{person_name}喜欢吃辣,我和她关系不错",
|
||||
"weight": 8
|
||||
}}
|
||||
}}
|
||||
|
||||
@@ -259,10 +263,10 @@ class RelationshipManager:
|
||||
points = points.replace(mapped_name, original_name)
|
||||
|
||||
logger.info(f"prompt: {prompt}")
|
||||
logger.info(f"points: {points}")
|
||||
# logger.info(f"points: {points}")
|
||||
|
||||
if not points:
|
||||
logger.warning(f"未能从LLM获取 {person_name} 的新印象")
|
||||
logger.info(f"对 {person_name} 没啥新印象")
|
||||
return
|
||||
|
||||
# 解析JSON并转换为元组列表
|
||||
@@ -272,13 +276,20 @@ class RelationshipManager:
|
||||
if points_data == "none" or not points_data or points_data.get("point") == "none":
|
||||
points_list = []
|
||||
else:
|
||||
logger.info(f"points_data: {points_data}")
|
||||
# logger.info(f"points_data: {points_data}")
|
||||
if isinstance(points_data, dict) and "points" in points_data:
|
||||
points_data = points_data["points"]
|
||||
if not isinstance(points_data, list):
|
||||
points_data = [points_data]
|
||||
# 添加可读时间到每个point
|
||||
points_list = [(item["point"], float(item["weight"]), current_time) for item in points_data]
|
||||
|
||||
|
||||
logger_str=f"了解了有关{person_name}的新印象:\n"
|
||||
for point in points_list:
|
||||
logger_str+=f"{point[0]},重要性:{point[1]}\n\n"
|
||||
logger.info("logger_str")
|
||||
|
||||
except json.JSONDecodeError:
|
||||
logger.error(f"解析points JSON失败: {points}")
|
||||
return
|
||||
@@ -456,11 +467,15 @@ class RelationshipManager:
|
||||
await person_info_manager.update_one_field(person_id, "short_impression", compressed_short_summary)
|
||||
|
||||
forgotten_points = []
|
||||
info_list = []
|
||||
|
||||
# 这句代码的作用是:将更新后的 forgotten_points(遗忘的记忆点)列表,序列化为 JSON 字符串后,写回到数据库中的 forgotten_points 字段
|
||||
await person_info_manager.update_one_field(
|
||||
person_id, "forgotten_points", json.dumps(forgotten_points, ensure_ascii=False, indent=None)
|
||||
)
|
||||
await person_info_manager.update_one_field(
|
||||
person_id, "info_list", json.dumps(info_list, ensure_ascii=False, indent=None)
|
||||
)
|
||||
|
||||
# 更新数据库
|
||||
await person_info_manager.update_one_field(
|
||||
@@ -482,7 +497,7 @@ class RelationshipManager:
|
||||
|
||||
# 直接处理所有消息,不进行过滤
|
||||
return build_readable_messages(
|
||||
messages=messages, replace_bot_name=True, timestamp_mode="normal_no_YMD", truncate=False
|
||||
messages=messages, replace_bot_name=True, timestamp_mode="normal_no_YMD", truncate=True
|
||||
)
|
||||
|
||||
def calculate_time_weight(self, point_time: str, current_time: str) -> float:
|
||||
|
||||
Reference in New Issue
Block a user