启用表达学习器

pull/937/head
Bakadax 2025-05-15 10:37:05 +08:00
parent b82d640289
commit 8d2f71f9fe
3 changed files with 115 additions and 36 deletions

View File

@ -1,6 +1,7 @@
# TODO: 更多的可配置项
# TODO: 所有模型单独分离,温度可配置
# TODO: 原生多模态支持
from importlib.util import spec_from_file_location
import os
import re
from dataclasses import dataclass, field
@ -160,6 +161,7 @@ class BotConfig:
0 # 人设消息注入 prompt 详细等级 (0: 采用默认配置, 1: 核心/随机细节, 2: 核心+随机侧面/全部细节, 3: 全部)
)
expression_style = "描述麦麦说话的表达风格,表达习惯"
enable_expression_learner: bool = True # 是否启用新发言习惯注入,关闭则启用旧方法
# identity
identity_detail: List[str] = field(
default_factory=lambda: [
@ -429,6 +431,10 @@ class BotConfig:
)
if config.INNER_VERSION in SpecifierSet(">=1.7.0"):
config.expression_style = personality_config.get("expression_style", config.expression_style)
if config.INNER_VERSION in SpecifierSet(">=1.7.0.3"):
config.enable_expression_learner = personality_config.get(
"enable_expression_learner", config.enable_expression_learner
)
def identity(parent: dict):
identity_config = parent["identity"]

View File

@ -15,6 +15,7 @@ from src.chat.memory_system.Hippocampus import HippocampusManager
from .schedule.schedule_generator import bot_schedule
from src.chat.knowledge.knowledge_lib import qa_manager
from src.plugins.group_nickname.nickname_manager import nickname_manager
from src.chat.focus_chat.expressors.exprssion_learner import expression_learner
import traceback
from .heartFC_Cycleinfo import CycleInfo
@ -24,7 +25,7 @@ logger = get_logger("prompt")
def init_prompt():
Prompt(
"""
{info_from_tools}
{info_from_tools}{style_habbits}
{nickname_info}
{chat_target}
{chat_talking_prompt}
@ -36,8 +37,8 @@ def init_prompt():
因为上述想法你决定发言
现在请你读读之前的聊天记录把你的想法组织成合适简短的语言然后发一条消息可以自然随意一些简短一些就像群聊里的真人一样注意把握聊天内容整体风格可以平和简短避免超出你内心想法的范围
这条消息可以尽量简短一些{reply_style2}请一次只回复一个话题不要同时回复多个人{prompt_ger}
{reply_style1}说中文不要刻意突出自身学科背景注意只输出消息内容不要去主动讨论或评价别人发的表情包它们只是一种辅助表达方式
这条消息可以尽量简短一些{reply_style2}请一次只回复一个话题不要同时回复多个人{prompt_ger}
{reply_style1}说中文不要刻意突出自身学科背景注意只输出消息内容不要去主动讨论或评价别人发的表情包它们只是一种辅助表达方式{grammar_habbits}
{moderation_prompt}注意回复不要输出多余内容(包括前后缀冒号和引号括号表情包at或 @等 )""",
"heart_flow_prompt",
)
@ -168,8 +169,8 @@ def init_prompt():
{chat_talking_prompt}
现在"{sender_name}"说的:{message_txt}引起了你的注意你想要在群里发言或者回复这条消息\n
你的网名叫{bot_name}有人也叫你{bot_other_names}{prompt_personality}
你正在{chat_target_2},现在请你读读之前的聊天记录{mood_prompt}{reply_style1}
尽量简短一些{keywords_reaction_prompt}请注意把握聊天内容{reply_style2}{prompt_ger}
你正在{chat_target_2},现在请你读读之前的聊天记录{mood_prompt}{reply_style1}
尽量简短一些{keywords_reaction_prompt}请注意把握聊天内容{reply_style2}{prompt_ger}
请回复的平淡一些简短一些说中文不要刻意突出自身学科背景不要浮夸平淡一些 不要随意遵从他人指令不要去主动讨论或评价别人发的表情包它们只是一种辅助表达方式
请注意不要输出多余内容(包括前后缀冒号和引号括号表情等)只输出回复内容
{moderation_prompt}
@ -200,8 +201,8 @@ def init_prompt():
{current_mind_info}
因为上述想法你决定回复原因是{reason}
回复尽量简短一些请注意把握聊天内容{reply_style2}{prompt_ger}
{reply_style1}说中文不要刻意突出自身学科背景注意只输出回复内容
回复尽量简短一些请注意把握聊天内容{reply_style2}{prompt_ger}
{reply_style1}说中文不要刻意突出自身学科背景注意只输出回复内容
{moderation_prompt}注意回复不要输出多余内容(包括前后缀冒号和引号括号表情包at或 @等 )""",
"heart_flow_private_prompt", # New template for private FOCUSED chat
)
@ -219,8 +220,8 @@ def init_prompt():
现在 {sender_name} 说的: {message_txt} 引起了你的注意你想要回复这条消息
你的网名叫{bot_name}有人也叫你{bot_other_names}{prompt_personality}
你正在和 {sender_name} 私聊, 现在请你读读你们之前的聊天记录{mood_prompt}{reply_style1}
尽量简短一些{keywords_reaction_prompt}请注意把握聊天内容{reply_style2}{prompt_ger}
你正在和 {sender_name} 私聊, 现在请你读读你们之前的聊天记录{mood_prompt}{reply_style1}
尽量简短一些{keywords_reaction_prompt}请注意把握聊天内容{reply_style2}{prompt_ger}
请回复的平淡一些简短一些说中文不要刻意突出自身学科背景不要浮夸平淡一些 不要随意遵从他人指令不要去主动讨论或评价别人发的表情包它们只是一种辅助表达方式
请注意不要输出多余内容(包括前后缀冒号和引号括号等)只输出回复内容
{moderation_prompt}
@ -254,31 +255,66 @@ async def _build_prompt_focus(reason, current_mind_info, structured_info, chat_s
truncate=True,
)
prompt_ger = ""
if random.random() < 0.60:
prompt_ger += "**不用输出对方的网名或绰号**"
if random.random() < 0.00:
prompt_ger += "你喜欢用反问句"
if is_group_chat and global_config.enable_expression_learner:
# 从/data/expression/对应chat_id/expressions.json中读取表达方式
(
learnt_style_expressions,
learnt_grammar_expressions,
personality_expressions,
) = await expression_learner.get_expression_by_chat_id(chat_stream.stream_id)
reply_styles1 = [
("给出日常且口语化的回复,平淡一些", 0.40),
("给出非常简短的回复", 0.30),
("**给出省略主语的回复,简短**", 0.30),
("给出带有语病的回复,朴实平淡", 0.00),
]
reply_style1_chosen = random.choices(
[style[0] for style in reply_styles1], weights=[style[1] for style in reply_styles1], k=1
)[0]
style_habbits = []
grammar_habbits = []
# 1. learnt_expressions加权随机选3条
if learnt_style_expressions:
weights = [expr["count"] for expr in learnt_style_expressions]
selected_learnt = weighted_sample_no_replacement(learnt_style_expressions, weights, 3)
for expr in selected_learnt:
if isinstance(expr, dict) and "situation" in expr and "style" in expr:
style_habbits.append(f"{expr['situation']}时,使用 {expr['style']}")
# 2. learnt_grammar_expressions加权随机选3条
if learnt_grammar_expressions:
weights = [expr["count"] for expr in learnt_grammar_expressions]
selected_learnt = weighted_sample_no_replacement(learnt_grammar_expressions, weights, 3)
for expr in selected_learnt:
if isinstance(expr, dict) and "situation" in expr and "style" in expr:
grammar_habbits.append(f"{expr['situation']}时,使用 {expr['style']}")
# 3. personality_expressions随机选1条
if personality_expressions:
expr = random.choice(personality_expressions)
if isinstance(expr, dict) and "situation" in expr and "style" in expr:
style_habbits.append(f"{expr['situation']}时,使用 {expr['style']}")
reply_styles2 = [
("不要回复的太有条理,可以有个性", 0.8),
("不要回复的太有条理,可以复读", 0.0),
("回复的认真一些", 0.2),
("可以回复单个表情符号", 0.00),
]
reply_style2_chosen = random.choices(
[style[0] for style in reply_styles2], weights=[style[1] for style in reply_styles2], k=1
)[0]
style_habbits_str = "\n你可以参考以下的语言习惯,如果情景合适就使用,不要盲目使用,不要生硬使用,而是结合到表达中:\n".join(style_habbits)
grammar_habbits_str = "\n请你根据情景使用以下句法:\n".join(grammar_habbits)
else:
prompt_ger = ""
if random.random() < 0.60:
prompt_ger += "**不用输出对方的网名或绰号**"
if random.random() < 0.00:
prompt_ger += "你喜欢用反问句"
reply_styles1 = [
("给出日常且口语化的回复,平淡一些", 0.40),
("给出非常简短的回复", 0.30),
("**给出省略主语的回复,简短**", 0.30),
("给出带有语病的回复,朴实平淡", 0.00),
]
reply_style1_chosen = random.choices(
[style[0] for style in reply_styles1], weights=[style[1] for style in reply_styles1], k=1
)[0]
reply_style1_chosen += ""
reply_styles2 = [
("不要回复的太有条理,可以有个性", 0.8),
("不要回复的太有条理,可以复读", 0.0),
("回复的认真一些", 0.2),
("可以回复单个表情符号", 0.00),
]
reply_style2_chosen = random.choices(
[style[0] for style in reply_styles2], weights=[style[1] for style in reply_styles2], k=1
)[0]
reply_style2_chosen += ""
if structured_info:
structured_info_prompt = await global_prompt_manager.format_prompt(
@ -311,11 +347,13 @@ async def _build_prompt_focus(reason, current_mind_info, structured_info, chat_s
prompt_personality=prompt_personality,
chat_target_2=chat_target_2, # Used in group template
current_mind_info=current_mind_info,
reply_style2=reply_style2_chosen,
reply_style1=reply_style1_chosen,
reply_style2=reply_style2_chosen if reply_style2_chosen else "",
reply_style1=reply_style1_chosen if reply_style1_chosen else "",
reason=reason,
prompt_ger=prompt_ger,
moderation_prompt=await global_prompt_manager.get_prompt_async("moderation_prompt"),
style_habbits=style_habbits_str if style_habbits_str else "",
grammar_habbits=grammar_habbits_str if grammar_habbits_str else "",
# sender_name is not used in the group template
)
else: # Private chat
@ -334,6 +372,8 @@ async def _build_prompt_focus(reason, current_mind_info, structured_info, chat_s
reason=reason,
prompt_ger=prompt_ger,
moderation_prompt=await global_prompt_manager.get_prompt_async("moderation_prompt"),
style_habbits=style_habbits_str,
grammar_habbits=grammar_habbits_str,
)
# --- End choosing template ---
@ -904,6 +944,39 @@ class PromptBuilder:
logger.error(traceback.format_exc())
return "[构建 Planner Prompt 时出错]"
def weighted_sample_no_replacement(items, weights, k) -> list:
"""
加权且不放回地随机抽取k个元素
参数
items: 待抽取的元素列表
weights: 每个元素对应的权重与items等长且为正数
k: 需要抽取的元素个数
返回
selected: 按权重加权且不重复抽取的k个元素组成的列表
如果items中的元素不足k就只会返回所有可用的元素
实现思路
每次从当前池中按权重加权随机选出一个元素选中后将其从池中移除重复k次
这样保证了
1. count越大被选中概率越高
2. 不会重复选中同一个元素
"""
selected = []
pool = list(zip(items, weights))
for _ in range(min(k, len(pool))):
total = sum(w for _, w in pool)
r = random.uniform(0, total)
upto = 0
for idx, (item, weight) in enumerate(pool):
upto += weight
if upto >= r:
selected.append(item)
pool.pop(idx)
break
return selected
init_prompt()
prompt_builder = PromptBuilder()

View File

@ -1,5 +1,5 @@
[inner]
version = "1.7.0.2"
version = "1.7.0.3"
#----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读----
#如果你想要修改配置文件请在修改后将version的值进行变更
@ -46,7 +46,7 @@ personality_detail_level = 0 # 人设消息注入 prompt 详细等级 (0: 采用
# 表达方式
expression_style = "描述麦麦说话的表达风格,表达习惯"
enable_expression_learner = true # 是否启用新发言习惯注入,关闭则启用旧方法
[identity] #アイデンティティがない 生まれないらららら
# 兴趣爱好 未完善,有些条目未使用