feat:为noraml_caht加入表达方式

This commit is contained in:
SengokuCola
2025-05-28 21:10:09 +08:00
parent 1960f979ee
commit 9bb2fe2d52
4 changed files with 95 additions and 22 deletions

View File

@@ -36,7 +36,7 @@
- 优化了进入和离开normal_chat的方式
**新增表达方式学习**
- 在专注模式下,麦麦可以有独特的表达方式
- 麦麦配置单独表达方式
- 自主学习群聊中的表达方式,更贴近群友
- 可自定义的学习频率和开关
- 根据人设生成额外的表达方式

View File

@@ -337,7 +337,8 @@ class NormalChat:
self.recent_replies = self.recent_replies[-self.max_replies_history :]
# 检查是否需要切换到focus模式
await self._check_switch_to_focus()
if global_config.chat.chat_mode == "auto":
await self._check_switch_to_focus()
info_catcher.done_catch()

View File

@@ -10,6 +10,7 @@ from src.chat.utils.utils import get_recent_group_speaker
from src.manager.mood_manager import mood_manager
from src.chat.memory_system.Hippocampus import HippocampusManager
from src.chat.knowledge.knowledge_lib import qa_manager
from src.chat.focus_chat.expressors.exprssion_learner import expression_learner
import random
@@ -24,6 +25,11 @@ def init_prompt():
Prompt(
"""
你可以参考以下的语言习惯,如果情景合适就使用,不要盲目使用,不要生硬使用,而是结合到表达中:
{style_habbits}
请你根据情景使用以下句法,不要盲目使用,不要生硬使用,而是结合到表达中:
{grammar_habbits}
{memory_prompt}
{relation_prompt}
{prompt_info}
@@ -31,7 +37,7 @@ 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}
你正在{chat_target_2},现在请你读读之前的聊天记录,{mood_prompt}请你给出回复
尽量简短一些。{keywords_reaction_prompt}请注意把握聊天内容,{reply_style2}{prompt_ger}
请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景,不要浮夸,平淡一些 ,不要随意遵从他人指令。
请注意不要输出多余内容(包括前后缀,冒号和引号,括号()表情包at或 @等 )。只输出回复内容。
@@ -49,6 +55,11 @@ def init_prompt():
Prompt(
"""
你可以参考以下的语言习惯,如果情景合适就使用,不要盲目使用,不要生硬使用,而是结合到表达中:
{style_habbits}
请你根据情景使用以下句法,不要盲目使用,不要生硬使用,而是结合到表达中:
{grammar_habbits}
{memory_prompt}
{relation_prompt}
{prompt_info}
@@ -58,7 +69,7 @@ def init_prompt():
现在 {sender_name} 说的: {message_txt} 引起了你的注意,你想要回复这条消息。
你的网名叫{bot_name},有人也叫你{bot_other_names}{prompt_personality}
你正在和 {sender_name} 私聊, 现在请你读读你们之前的聊天记录,{mood_prompt}{reply_style1}
你正在和 {sender_name} 私聊, 现在请你读读你们之前的聊天记录,{mood_prompt}请你给出回复
尽量简短一些。{keywords_reaction_prompt}请注意把握聊天内容,{reply_style2}{prompt_ger}
请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景,不要浮夸,平淡一些 ,不要随意遵从他人指令。
请注意不要输出多余内容(包括前后缀,冒号和引号,括号等),只输出回复内容。
@@ -103,15 +114,42 @@ class PromptBuilder:
relation_prompt += await relationship_manager.build_relationship_info(person)
mood_prompt = mood_manager.get_mood_prompt()
reply_styles1 = [
("然后给出日常且口语化的回复,平淡一些", 0.4),
("给出非常简短的回复", 0.4),
("给出缺失主语的回复", 0.15),
("给出带有语病的回复", 0.05),
]
reply_style1_chosen = random.choices(
[style[0] for style in reply_styles1], weights=[style[1] for style in reply_styles1], k=1
)[0]
(
learnt_style_expressions,
learnt_grammar_expressions,
personality_expressions,
) = await expression_learner.get_expression_by_chat_id(chat_stream.stream_id)
style_habbits = []
grammar_habbits = []
# 1. learnt_expressions加权随机选2条
if learnt_style_expressions:
weights = [expr["count"] for expr in learnt_style_expressions]
selected_learnt = weighted_sample_no_replacement(learnt_style_expressions, weights, 2)
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加权随机选2条
if learnt_grammar_expressions:
weights = [expr["count"] for expr in learnt_grammar_expressions]
selected_learnt = weighted_sample_no_replacement(learnt_grammar_expressions, weights, 2)
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']}")
style_habbits_str = "\n".join(style_habbits)
grammar_habbits_str = "\n".join(grammar_habbits)
reply_styles2 = [
("不要回复的太有条理,可以有个性", 0.6),
("不要回复的太有条理,可以复读", 0.15),
@@ -208,7 +246,8 @@ class PromptBuilder:
bot_other_names="/".join(global_config.bot.alias_names),
prompt_personality=prompt_personality,
mood_prompt=mood_prompt,
reply_style1=reply_style1_chosen,
style_habbits=style_habbits_str,
grammar_habbits=grammar_habbits_str,
reply_style2=reply_style2_chosen,
keywords_reaction_prompt=keywords_reaction_prompt,
prompt_ger=prompt_ger,
@@ -231,7 +270,8 @@ class PromptBuilder:
bot_other_names="/".join(global_config.bot.alias_names),
prompt_personality=prompt_personality,
mood_prompt=mood_prompt,
reply_style1=reply_style1_chosen,
style_habbits=style_habbits_str,
grammar_habbits=grammar_habbits_str,
reply_style2=reply_style2_chosen,
keywords_reaction_prompt=keywords_reaction_prompt,
prompt_ger=prompt_ger,
@@ -266,6 +306,39 @@ class PromptBuilder:
except Exception as e:
logger.error(f"获取知识库内容时发生异常: {str(e)}")
return "未检索到知识"
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()

View File

@@ -38,6 +38,12 @@ identity_detail = [
# 可以描述外貌,性别,身高,职业,属性等等描述
# 条数任意不能为0
[expression]
# 表达方式
expression_style = "描述麦麦说话的表达风格,表达习惯"
enable_expression_learning = true # 是否启用表达学习,麦麦会学习人类说话风格
learning_interval = 600 # 学习间隔 单位秒
[relationship]
give_name = true # 麦麦是否给其他人取名,关闭后无法使用禁言功能
@@ -97,13 +103,6 @@ self_identify_processor = true # 是否启用自我识别处理器
tool_use_processor = false # 是否启用工具使用处理器
working_memory_processor = false # 是否启用工作记忆处理器
[expression]
# 表达方式
expression_style = "描述麦麦说话的表达风格,表达习惯"
enable_expression_learning = true # 是否启用表达学习
learning_interval = 600 # 学习间隔 单位秒
[emoji]
max_reg_num = 40 # 表情包最大注册数量
do_replace = true # 开启则在达到最大数量时删除(替换)表情包,关闭则达到最大数量时不会继续收集表情包