[dev] del prompt & modify default human_profile_setting

This commit is contained in:
jinli.yl 2024-07-03 14:50:07 +08:00
parent d7dab0ca7d
commit d0e112fd6e
5 changed files with 1 additions and 519 deletions

View file

@ -1,11 +0,0 @@
system_prompt:
cn:
few_shot_prompt:
cn:
user_query_prompt:
cn:
content_format:
cn: "用户的{insight_key}{insight_value}"

View file

@ -1,151 +0,0 @@
from datetime import datetime
from typing import List
from memory_scope.utils.tool_functions import time_to_formatted_str, get_datetime_info_dict, prompt_to_msg
from memory_scope.constants.common_constants import (
NEW_INSIGHT_NODES,
DT,
NOT_REFLECTED_MERGE_NODES,
NEW_INSIGHT_KEYS
)
from memory_scope.enumeration.memory_status_enum import MemoryNodeStatus
from memory_scope.enumeration.memory_type_enum import MemoryTypeEnum
from memory_scope.scheme.memory_node import MemoryNode
from memory_scope.memory.worker.memory_base_worker import MemoryBaseWorker
from memory_scope.constants.language_constants import NONE_WORD
class GetInsightWorker(MemoryBaseWorker):
def new_insight_node(self, insight_key: str, insight_value: str) -> MemoryNode:
created_dt = datetime.now()
obs_dt = time_to_formatted_str(time=created_dt)
meta_data = {k: str(v) for k, v in get_datetime_info_dict(created_dt).items()}
content = self.prompt_handler.content_format.format(insight_key=insight_key, insight_value=insight_value)
return MemoryNode(
content=content,
user_name=self.user_name,
target_name=self.target_name,
memory_type=MemoryTypeEnum.INSIGHT.value,
meta_data=meta_data,
status=MemoryNodeStatus.ACTIVE.value,
obs_dt=obs_dt,
obs_updated=True,
insight_key=insight_key,
insight_value=insight_value,
)
def reflect_new_insight_key(
self, insight_key: str, not_reflected_merge_nodes: List[MemoryNode]
) -> MemoryNode | None:
# 检索历史memory
related_nodes = self.vector_store.similar_search(
text=insight_key,
size=self.es_insight_similar_top_k,
exact_filters={
"user_name": self.user_name,
"target_name": self.target_name,
"status": MemoryNodeStatus.ACTIVE.value,
"memory_type": [
MemoryTypeEnum.OBSERVATION.value,
MemoryTypeEnum.OBS_CUSTOMIZED.value,
],
},
)
# 合并新增nodes
related_nodes.extend(not_reflected_merge_nodes)
# content去重
related_node_dict = {n.memory_node.content: n for n in related_nodes}
related_nodes = sorted(
list(related_node_dict.values()), key=lambda x: x.memory_node.memory_id # memory_id or use_id?
)
documents = [n.memory_node.content for n in related_nodes]
# 重排所有记忆
result = self.rank_model.call(query=insight_key, documents=documents)
if not result:
self.add_run_info(
f"reflect insight_key={insight_key} call rerank client failed!"
)
return
# 根据打分过滤
for index, score in result.rank_scores.items():
related_nodes[index].score_rank = score
related_nodes_sorted = sorted(
related_nodes, key=lambda x: x.score_rank, reverse=True
)[: self.insight_obs_max_cnt]
# prepare prompt
user_query_list = [x.memory_node.content for x in related_nodes_sorted]
get_insight_message = prompt_to_msg(
system_prompt=self.prompt_handler.system_prompt,
few_shot=self.prompt_handler.few_shot_prompt,
user_query=self.prompt_handler.user_query_prompt.format(
insight_key=insight_key, user_query="\n".join(user_query_list)
)
)
self.logger.info(f"get_insight_message={get_insight_message}")
# call LLM, 提取insight
response = self.generation_model.call(
messages=get_insight_message,
model_name=self.get_insight_model,
max_token=self.get_insight_max_token,
temperature=self.get_insight_temperature,
top_k=self.get_insight_top_k,
)
# return if empty
if not response:
self.add_run_info("reflect_upon_user_attr call llm failed!")
return
response_text = response.message.content.strip()
if response_text in [self.get_language_value(NONE_WORD)]:
return
return self.new_insight_node(
insight_key=insight_key, insight_value=response_text
)
def _run(self):
new_insight_keys: List[MemoryNode] = self.get_context(NEW_INSIGHT_KEYS)
if not new_insight_keys:
self.add_run_info("new_insight_keys is empty! stop insight.")
return
not_reflected_merge_nodes: List[MemoryNode] = self.get_context(
NOT_REFLECTED_MERGE_NODES
)
if not not_reflected_merge_nodes:
self.add_run_info("not_reflected_merge_nodes is empty! stop get insight.")
return
# submit insight task
for insight_key in new_insight_keys:
self.submit_thread(
self.reflect_new_insight_key,
sleep_time=1,
insight_key=insight_key,
not_reflected_merge_nodes=not_reflected_merge_nodes,
)
# save output
new_insight_nodes: List[MemoryNode] = []
for result in self.join_threads():
if result:
new_insight_nodes.append(result)
assert isinstance(result, MemoryNode)
insight_key = result.insight_key
insight_value = result.insight_value
self.logger.info(
f"after_get_insight insight_key={insight_key} insight_value={insight_value}"
)
self.set_context(NEW_INSIGHT_NODES, new_insight_nodes)
# set REFLECTED
for node in not_reflected_merge_nodes:
node.obs_reflected = True

View file

@ -47,7 +47,7 @@ class LoadMemoryWorker(MemoryBaseWorker):
top_k=self.retrieve_profiles_top_k,
filter_dict=filter_dict)
nodes: List[MemoryNode] = []
human_profile_setting = G_CONTEXT.meta_data.get("human_profile_setting", [])
human_profile_setting = G_CONTEXT.meta_data.get("human_profile_setting", {})
for attr_key in human_profile_setting:

View file

@ -1,120 +0,0 @@
update_plural_profile_system_prompt:
cn: "
从下面的句子中提取出给定类别的用户资料信息,并判断和已有信息是否重复。只输出无重复的新信息。若无法提取该类别的用户资料的新信息则回答无。
请一步步思考,并按如下格式输出:
思考: 思考的依据和过程150字以内。
用户资料: <信息>或<无>, 一定加<>
"
en:
update_plural_profile_few_shot_prompt:
cn: "
示例1:
句子:用户上周去了西溪游泳馆游泳,那个游泳馆人非常多。
句子:用户计划每周六和朋友张三去朝阳体育馆打羽毛球。
类别:运动(用户喜欢的运动)
已有信息:运动(用户喜欢的运动):游泳
思考:从第一句句子可以得出游泳是用户喜欢的运动之一,但与已有信息重复。从第二句句子可以得出羽毛球是用户喜欢的运动之一,是新的信息。
用户资料: <羽毛球>
示例2:
句子:用户对咖啡因过敏。
句子:用户不喜欢吃香菇。
类别:过敏(用户的已知过敏反应)
已有信息:过敏(用户的已知过敏反应): 咖啡因
思考:从第一句句子可以得出咖啡因是用户的已知过敏反应之一,但与已有信息重复。从第二句句子只能得出用户不喜欢香菇而非对香菇过敏,无法得出新的用户已知过敏信息。
用户资料: <无>
示例3:
句子:用户热衷于动作类类游戏如只狼、艾尔登法环。
句子用户在休闲时间经常长时间玩策略类游戏如文明6。
句子:用户是音乐发烧友,关注各个品牌的耳机的音质和性价比。
类别:爱好(用户的业余爱好)
已有信息:爱好(用户的业余爱好):
思考:从第一句句子可以得出动作类游戏是用户的爱好之一,是新的信息。从第二句句子可以得出策略类游戏是用户的爱好之一,是新的信息。从第三句句子可以得出音乐是用户的爱好之一,是新的信息。
用户资料: <动作类游戏, 策略类游戏, 音乐>
示例4:
句子:关于职场沟通你有什么具体的建议吗?最好结合一个实例。我一直听人说要加强沟通,经常和上司沟通,同步项目的进展,但是我总是感觉还有许多事情要做。
句子:项目并没有达到一个充分的可以汇报的状态,然后准备汇报材料又很费时间,导致有时候我没有及时和上司同步项目状态。针对这个情况你有什么建议?
类别:职业(用户的职业)
已有信息:职业(用户的职业):工程师
思考:句子中虽然提及了职场沟通等工作相关内容,但是并不能推断出用户的职位是什么,只能推知与宽泛的项目实施与管理相关。
用户资料: <无>
"
en:
update_plural_profile_user_query_prompt:
cn: "
{user_query}
类别:{update_profile}
已有信息:{update_profile_value}
"
en:
update_unique_profile_system_prompt:
cn: "
从下面的句子中提取出给定类别的用户资料信息,并判断与已有信息是否矛盾。若矛盾则输出更新的信息,若不矛盾则保留已有信息,整合已有信息和新信息并输出。
请一步步思考,并按如下格式输出:
思考: 思考的依据和过程150字以内。
用户资料: <信息>, 一定加<>
"
en:
update_unique_profile_few_shot_prompt:
cn: "
示例1:
句子:因为昨天成都下大雨,用户全身都被淋湿了。
句子:用户关心明天成都的天气预报。
类别:地区(用户所在地区)
已有信息:地区(用户所在地区): 杭州
思考:从第一句句子可以得出用户在成都。第二句句子没有直接透露用户所在地信息,但与第一句句子用户在成都的信息吻合。这与已有信息(用户在杭州)矛盾,输出更新的信息。
用户资料:<成都>
示例2:
句子:用户女朋友下个月过生日。
句子用户生日在7月15日。
类别:生日(用户的生日)。
已有信息生日用户的生日1987年7月15日。
思考第一句句子中提及生日但并不是用户的生日无法得出用户生日信息。从第二句句子可以得出用户生日在7月15日与已有信息不矛盾整合可以得出用户生日是1987年7月15日。
用户资料: <1987年7月15日>
示例3:
句子:用户在招商银行工作。
句子:用户刚刚毕业,第一份工作是银行前台。
句子:用户的理想工作是职业游戏选手。
类别:职业(用户的职业)
已有信息:职业(用户的职业):
思考:整合第一和第二句句子的信息可以得出用户的现在的职业是招商银行前台。第三句句子说明了用户的理想工作但并不是现在的职业。
用户资料:<招商银行前台>
示例4:
句子:用户大学期间接触过优化算法的研究。
类别:学习专业 (用户大学学习的专业)
已有信息:学习专业 (用户大学学习的专业):与人工智能相关
思考:从句子可以得出用户大学学习的专业与优化算法相关,这与已有信息(用户大学学习的专业与人工智能相关)不矛盾,整合可以得出用户大学学习的专业与人工智能和优化算法相关。
用户资料:<与人工智能和优化算法相关>
示例5:
句子:今天和同学去打球了。
句子:明天和女朋友一起去杭州旅游。
类别:学习专业 (用户大学学习的专业)
已有信息:学习专业 (用户大学学习的专业)
思考:两个句子和学习专业都没有关联,没有新提取的信息。
用户资料:<无>
"
en:
update_unique_profile_user_query_prompt:
cn: "
{user_query}
类别:{update_profile}
已有信息:{update_profile_value}
"
en:
user_query:
cn: "句子:{content}"
en:

View file

@ -1,236 +0,0 @@
from typing import List
from memory_scope.utils.response_text_parser import ResponseTextParser
from memory_scope.constants.common_constants import NEW_OBS_NODES, NEW_USER_PROFILE
from memory_scope.enumeration.memory_type_enum import MemoryTypeEnum
from memory_scope.scheme.memory_node import MemoryNode
from memory_scope.memory.worker.memory_base_worker import MemoryBaseWorker
from memory_scope.utils.global_context import GlobalContext
from memory_scope.utils.tool_functions import prompt_to_msg
from memory_scope.constants.language_constants import COMMA_WORD, COLON_WORD, NONE_WORD, REPEATED_WORD
class UpdateProfileWorker(MemoryBaseWorker):
@property
def extra_user_attrs(self):
return GlobalContext.global_configs.get("extra_user_attrs", [])
def filter_obs_nodes(
self, user_attr: MemoryNode, new_obs_nodes: List[MemoryNode]
) -> (MemoryNode, List[MemoryNode], float):
max_score: float = 0
filtered_nodes: List[MemoryNode] = []
result = self.rank_model.call(
query=user_attr.meta_data.get("description", ""),
documents=[x.content for x in new_obs_nodes],
)
if not result:
self.add_run_info(
f"update_user_attr={user_attr.meta_data.get("memory_key", "")} call rerank failed!"
)
return user_attr, filtered_nodes, max_score
# 找到大于阈值的obs node
filtered_nodes: List[MemoryNode] = []
for index, score in result.rank_scores.items():
node = new_obs_nodes[index]
keep_flag = "filtered"
if score >= self.update_profile_threshold:
filtered_nodes.append(node)
keep_flag = "keep"
max_score = max(max_score, score)
self.logger.info(
f"key={user_attr.meta_data.get("memory_key", "")} desc={user_attr.meta_data.get("description", "")} "
f"content={node.content} score={score} keep_flag={keep_flag}"
)
if not filtered_nodes:
self.logger.info(f"update_user_attr={user_attr} filtered_nodes is empty!")
return user_attr, filtered_nodes, max_score
def update_user_attr(
self, user_attr: MemoryNode, filtered_nodes: List[MemoryNode]
) -> MemoryNode:
self.logger.info(
f"update_user_attr memory_key={user_attr.meta_data.get("memory_key", "")} desc={user_attr.meta_data.get("description", "")} "
f"value={user_attr.meta_data.get("value", "")} doc.size={len(filtered_nodes)}"
)
# 根据不同的参数类型是否多值分别给出prompt
user_query_list = []
for node in filtered_nodes:
user_query_list.append(self.prompt_handler.user_query.format(content=node.content))
update_profile = f"{user_attr.meta_data.get("memory_key", "")}({user_attr.meta_data.get("description", "")})"
update_profile_value = update_profile + self.get_language_prompt(COLON_WORD) + self.get_language_prompt(COMMA_WORD).join(user_attr.meta_data.get("value", ""))
if user_attr.meta_data.get("is_unique", 0) == 1:
update_profile_message = prompt_to_msg(
system_prompt=self.prompt_handler.update_unique_profile_system_prompt,
few_shot=self.prompt_handler.update_unique_profile_few_shot_prompt,
user_query=self.prompt_handler.update_unique_profile_user_query_prompt.format(
user_query="\n".join(user_query_list),
update_profile=update_profile,
update_profile_value=update_profile_value,
),
)
else:
update_profile_message = prompt_to_msg(
system_prompt=self.prompt_handler.update_plural_profile_system_prompt,
few_shot=self.prompt_handler.update_plural_profile_few_shot_prompt,
user_query=self.prompt_handler.update_plural_profile_user_query_prompt.format(
user_query="\n".join(user_query_list),
update_profile=update_profile,
update_profile_value=update_profile_value,
),
)
self.logger.info(f"update_profile_message={update_profile_message}")
# call LLM
response: str = self.generation_model.call(
messages=update_profile_message,
model_name=self.update_profile_model,
max_token=self.update_profile_max_token,
temperature=self.update_profile_temperature,
top_k=self.update_profile_top_k,
)
# return if empty
if not response:
self.add_run_info(
f"update_one_user_attr key={user_attr.meta_data.get("memory_key", "")} call llm failed!"
)
return user_attr
profile_list = ResponseTextParser(response.message.content).parse_v1(
f"update_attr {user_attr.meta_data.get("memory_key", "")}"
)
if not profile_list:
self.add_run_info(
f"update_one_user_attr key={user_attr.meta_data.get("memory_key", "")} profile_list empty 1!"
)
return user_attr
profile_list = profile_list[0]
if not profile_list:
self.add_run_info(
f"update_one_user_attr key={user_attr.meta_data.get("memory_key", "")} profile_list empty 2"
)
return user_attr
profile = profile_list[0]
if not profile or profile in self.get_language_value([NONE_WORD, REPEATED_WORD]):
self.logger.info(f"profile={profile}, skip.")
return user_attr
# check 英文中午逗号
if user_attr.meta_data.get("is_unique", 0) == 1:
user_attr.meta_data["value"] = [profile.strip()]
else:
attr_value_list = profile.replace("", ",").split(",")
user_attr.meta_data["value"] = [
x.strip() for x in sorted(list(set(user_attr.meta_data.get("value", "") + attr_value_list)))
]
return user_attr
def add_extra_user_attrs(self):
# 解析为空返回
extra_user_attr_list = [x.strip() for x in self.extra_user_attrs if x.strip()]
if not extra_user_attr_list:
return
for user_attr_info in extra_user_attr_list:
user_attr_split = user_attr_info.split(self.get_language_prompt(COLON_WORD))
# 格式不对返回
if len(user_attr_split) < 1:
continue
user_attr_key = user_attr_split[0]
user_attr_desc = ""
if len(user_attr_split) >= 2:
user_attr_desc = user_attr_split[1]
user_attr_unique = 0
if len(user_attr_split) >= 3:
user_attr_unique = int(user_attr_split[2])
# 已经包含返回
if user_attr_key in self.user_profile_dict:
user_attr = self.user_profile_dict[user_attr_key]
# description为空补充description
if not user_attr.meta_data.get("description", ""):
user_attr.meta_data["description"] = user_attr_desc
continue
# 增加新属性
new_attr = MemoryNode(
memory_id=self.memory_id,
meta_data={
"memory_key": user_attr_key,
"is_unique": int(user_attr_unique),
"is_mutable": 1,
"description": user_attr_desc
},
memory_type=MemoryTypeEnum.PROFILE,
status=1,
obs_profile_updated=True,
)
self.user_profile_dict[user_attr_key] = new_attr
def _run(self):
new_obs_nodes: List[MemoryNode] = self.get_context(NEW_OBS_NODES)
if not new_obs_nodes:
self.logger.info("new_obs_nodes is empty, stop user profile!")
self.set_context(NEW_USER_PROFILE, list(self.user_profile_dict.values()))
return
# 增加环境变量配置的属性
if self.extra_user_attrs:
self.add_extra_user_attrs()
new_user_profile: List[MemoryNode] = []
self.set_context(NEW_USER_PROFILE, new_user_profile)
for user_attr_key, user_attr in self.user_profile_dict.items():
# 不可修改直接跳过
if user_attr.meta_data.get("is_mutable", 0) != 1:
new_user_profile.append(user_attr)
self.logger.info(f"{user_attr_key} is not mutable! continue")
continue
self.submit_thread(
self.filter_obs_nodes,
sleep_time=0.1,
user_attr=user_attr,
new_obs_nodes=new_obs_nodes,
)
# 选择topN
result_list = []
for result in self.join_threads():
user_attr, filtered_nodes, max_score = result
if not filtered_nodes:
continue
result_list.append(result)
result_sorted = sorted(result_list, key=lambda x: x[2], reverse=True)
if len(result_sorted) > self.update_profile_max_thread:
result_sorted = result_sorted[: self.update_profile_max_thread]
# 提交LLM update任务
for user_attr, filtered_nodes, _ in result_sorted:
self.submit_thread(
self.update_user_attr,
sleep_time=1,
user_attr=user_attr,
filtered_nodes=filtered_nodes,
)
# collect result & save
for result in self.join_threads():
if result:
user_attribute: MemoryNode = result
self.logger.info(
f"after_update_profile memory_key={user_attribute.meta_data.get("memory_key", "")} "
f"desc={user_attribute.meta_data.get("description", "")} value={user_attribute.meta_data.get("value", "")}"
)
new_user_profile.append(user_attribute)