better:优化表达方式和侧面人格
This commit is contained in:
16
bot.py
16
bot.py
@@ -1,18 +1,21 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import hashlib
|
import hashlib
|
||||||
import os
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
if os.path.exists(".env"):
|
||||||
|
load_dotenv(".env", override=True)
|
||||||
|
print("成功加载环境变量配置")
|
||||||
|
else:
|
||||||
|
print("未找到.env文件,请确保程序所需的环境变量被正确设置")
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import platform
|
import platform
|
||||||
import traceback
|
import traceback
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from dotenv import load_dotenv
|
|
||||||
from rich.traceback import install
|
from rich.traceback import install
|
||||||
|
|
||||||
# maim_message imports for console input
|
# maim_message imports for console input
|
||||||
from maim_message import Seg, UserInfo, BaseMessageInfo, MessageBase
|
from maim_message import Seg, UserInfo, BaseMessageInfo, MessageBase
|
||||||
from src.chat.message_receive.bot import chat_bot
|
from src.chat.message_receive.bot import chat_bot
|
||||||
|
|
||||||
# 最早期初始化日志系统,确保所有后续模块都使用正确的日志格式
|
# 最早期初始化日志系统,确保所有后续模块都使用正确的日志格式
|
||||||
from src.common.logger import initialize_logging, get_logger, shutdown_logging
|
from src.common.logger import initialize_logging, get_logger, shutdown_logging
|
||||||
from src.main import MainSystem
|
from src.main import MainSystem
|
||||||
@@ -22,12 +25,7 @@ initialize_logging()
|
|||||||
|
|
||||||
logger = get_logger("main")
|
logger = get_logger("main")
|
||||||
|
|
||||||
# 直接加载生产环境变量配置
|
|
||||||
if os.path.exists(".env"):
|
|
||||||
load_dotenv(".env", override=True)
|
|
||||||
logger.info("成功加载环境变量配置")
|
|
||||||
else:
|
|
||||||
logger.warning("未找到.env文件,请确保程序所需的环境变量被正确设置")
|
|
||||||
|
|
||||||
install(extra_lines=3)
|
install(extra_lines=3)
|
||||||
|
|
||||||
|
|||||||
243
src/chat/express/expression_selector.py
Normal file
243
src/chat/express/expression_selector.py
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
from .exprssion_learner import get_expression_learner
|
||||||
|
import random
|
||||||
|
from typing import List, Dict, Tuple
|
||||||
|
from json_repair import repair_json
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
from src.llm_models.utils_model import LLMRequest
|
||||||
|
from src.config.config import global_config
|
||||||
|
from src.common.logger import get_logger
|
||||||
|
from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
|
||||||
|
|
||||||
|
logger = get_logger("expression_selector")
|
||||||
|
|
||||||
|
def init_prompt():
|
||||||
|
expression_evaluation_prompt = """
|
||||||
|
你的名字是{bot_name}
|
||||||
|
|
||||||
|
以下是正在进行的聊天内容:
|
||||||
|
{chat_observe_info}
|
||||||
|
|
||||||
|
以下是可选的表达情境:
|
||||||
|
{all_situations}
|
||||||
|
|
||||||
|
请你分析聊天内容的语境、情绪、话题类型,从上述情境中选择最适合当前聊天情境的5-10个情境。
|
||||||
|
考虑因素包括:
|
||||||
|
1. 聊天的情绪氛围(轻松、严肃、幽默等)
|
||||||
|
2. 话题类型(日常、技术、游戏、情感等)
|
||||||
|
3. 情境与当前语境的匹配度
|
||||||
|
|
||||||
|
请以JSON格式输出,只需要输出选中的情境编号:
|
||||||
|
例如:
|
||||||
|
{{
|
||||||
|
"selected_situations": [2, 3, 5, 7, 9, 12, 15, 18, 21, 25]
|
||||||
|
}}
|
||||||
|
例如:
|
||||||
|
{{
|
||||||
|
"selected_situations": [1, 4, 7, 9, 13, 18, 24]
|
||||||
|
}}
|
||||||
|
|
||||||
|
请严格按照JSON格式输出,不要包含其他内容:
|
||||||
|
"""
|
||||||
|
Prompt(expression_evaluation_prompt, "expression_evaluation_prompt")
|
||||||
|
|
||||||
|
def weighted_sample(population: List[Dict], weights: List[float], k: int) -> List[Dict]:
|
||||||
|
"""按权重随机抽样"""
|
||||||
|
if not population or not weights or k <= 0:
|
||||||
|
return []
|
||||||
|
|
||||||
|
if len(population) <= k:
|
||||||
|
return population.copy()
|
||||||
|
|
||||||
|
# 使用累积权重的方法进行加权抽样
|
||||||
|
selected = []
|
||||||
|
population_copy = population.copy()
|
||||||
|
weights_copy = weights.copy()
|
||||||
|
|
||||||
|
for _ in range(k):
|
||||||
|
if not population_copy:
|
||||||
|
break
|
||||||
|
|
||||||
|
# 选择一个元素
|
||||||
|
chosen_idx = random.choices(range(len(population_copy)), weights=weights_copy)[0]
|
||||||
|
selected.append(population_copy.pop(chosen_idx))
|
||||||
|
weights_copy.pop(chosen_idx)
|
||||||
|
|
||||||
|
return selected
|
||||||
|
|
||||||
|
class ExpressionSelector:
|
||||||
|
def __init__(self):
|
||||||
|
self.expression_learner = get_expression_learner()
|
||||||
|
# TODO: API-Adapter修改标记
|
||||||
|
self.llm_model = LLMRequest(
|
||||||
|
model=global_config.model.utils_small,
|
||||||
|
request_type="expression.selector",
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_random_expressions(self, chat_id: str, style_num: int, grammar_num: int, personality_num: int) -> Tuple[List[Dict[str, str]], List[Dict[str, str]]]:
|
||||||
|
(
|
||||||
|
learnt_style_expressions,
|
||||||
|
learnt_grammar_expressions,
|
||||||
|
personality_expressions,
|
||||||
|
) = self.expression_learner.get_expression_by_chat_id(chat_id)
|
||||||
|
|
||||||
|
# 按权重抽样(使用count作为权重)
|
||||||
|
if learnt_style_expressions:
|
||||||
|
style_weights = [expr.get("count", 1) for expr in learnt_style_expressions]
|
||||||
|
selected_style = weighted_sample(learnt_style_expressions, style_weights, style_num)
|
||||||
|
else:
|
||||||
|
selected_style = []
|
||||||
|
|
||||||
|
if learnt_grammar_expressions:
|
||||||
|
grammar_weights = [expr.get("count", 1) for expr in learnt_grammar_expressions]
|
||||||
|
selected_grammar = weighted_sample(learnt_grammar_expressions, grammar_weights, grammar_num)
|
||||||
|
else:
|
||||||
|
selected_grammar = []
|
||||||
|
|
||||||
|
if personality_expressions:
|
||||||
|
personality_weights = [expr.get("count", 1) for expr in personality_expressions]
|
||||||
|
selected_personality = weighted_sample(personality_expressions, personality_weights, personality_num)
|
||||||
|
else:
|
||||||
|
selected_personality = []
|
||||||
|
|
||||||
|
return selected_style, selected_grammar, selected_personality
|
||||||
|
|
||||||
|
def update_expression_count(self, chat_id: str, expression: Dict[str, str], multiplier: float = 1.5):
|
||||||
|
"""更新表达方式的count值"""
|
||||||
|
if expression.get("type") == "style_personality":
|
||||||
|
# personality表达方式存储在全局文件中
|
||||||
|
file_path = os.path.join("data", "expression", "personality", "expressions.json")
|
||||||
|
else:
|
||||||
|
# style和grammar表达方式存储在对应chat_id目录中
|
||||||
|
expr_type = expression.get("type", "style")
|
||||||
|
if expr_type == "style":
|
||||||
|
file_path = os.path.join("data", "expression", "learnt_style", str(chat_id), "expressions.json")
|
||||||
|
elif expr_type == "grammar":
|
||||||
|
file_path = os.path.join("data", "expression", "learnt_grammar", str(chat_id), "expressions.json")
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
if not os.path.exists(file_path):
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(file_path, "r", encoding="utf-8") as f:
|
||||||
|
expressions = json.load(f)
|
||||||
|
|
||||||
|
# 找到匹配的表达方式并更新count
|
||||||
|
for expr in expressions:
|
||||||
|
if (expr.get("situation") == expression.get("situation") and
|
||||||
|
expr.get("style") == expression.get("style")):
|
||||||
|
expr["count"] = expr.get("count", 1) * multiplier
|
||||||
|
expr["last_active_time"] = time.time()
|
||||||
|
break
|
||||||
|
|
||||||
|
# 保存更新后的文件
|
||||||
|
with open(file_path, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(expressions, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"更新表达方式count失败: {e}")
|
||||||
|
|
||||||
|
async def select_suitable_expressions_llm(self, chat_id: str, chat_info: str) -> List[Dict[str, str]]:
|
||||||
|
"""使用LLM选择适合的表达方式"""
|
||||||
|
|
||||||
|
# 1. 获取35个随机表达方式(现在按权重抽取)
|
||||||
|
style_exprs, grammar_exprs, personality_exprs = self.get_random_expressions(chat_id, 25, 25, 10)
|
||||||
|
|
||||||
|
# 2. 构建所有表达方式的索引和情境列表
|
||||||
|
all_expressions = []
|
||||||
|
all_situations = []
|
||||||
|
|
||||||
|
# 添加style表达方式
|
||||||
|
for expr in style_exprs:
|
||||||
|
if isinstance(expr, dict) and "situation" in expr and "style" in expr:
|
||||||
|
expr_with_type = expr.copy()
|
||||||
|
expr_with_type["type"] = "style"
|
||||||
|
all_expressions.append(expr_with_type)
|
||||||
|
all_situations.append(f"{len(all_expressions)}.{expr['situation']}")
|
||||||
|
|
||||||
|
# 添加grammar表达方式
|
||||||
|
for expr in grammar_exprs:
|
||||||
|
if isinstance(expr, dict) and "situation" in expr and "style" in expr:
|
||||||
|
expr_with_type = expr.copy()
|
||||||
|
expr_with_type["type"] = "grammar"
|
||||||
|
all_expressions.append(expr_with_type)
|
||||||
|
all_situations.append(f"{len(all_expressions)}.{expr['situation']}")
|
||||||
|
|
||||||
|
# 添加personality表达方式
|
||||||
|
for expr in personality_exprs:
|
||||||
|
if isinstance(expr, dict) and "situation" in expr and "style" in expr:
|
||||||
|
expr_with_type = expr.copy()
|
||||||
|
expr_with_type["type"] = "style_personality"
|
||||||
|
all_expressions.append(expr_with_type)
|
||||||
|
all_situations.append(f"{len(all_expressions)}.{expr['situation']}")
|
||||||
|
|
||||||
|
if not all_expressions:
|
||||||
|
logger.warning("没有找到可用的表达方式")
|
||||||
|
return []
|
||||||
|
|
||||||
|
all_situations_str = "\n".join(all_situations)
|
||||||
|
|
||||||
|
# 3. 构建prompt(只包含情境,不包含完整的表达方式)
|
||||||
|
prompt = (await global_prompt_manager.get_prompt_async("expression_evaluation_prompt")).format(
|
||||||
|
bot_name=global_config.bot.nickname,
|
||||||
|
chat_observe_info=chat_info,
|
||||||
|
all_situations=all_situations_str,
|
||||||
|
)
|
||||||
|
|
||||||
|
print(prompt)
|
||||||
|
|
||||||
|
# 4. 调用LLM
|
||||||
|
try:
|
||||||
|
content, (_, _) = await self.llm_model.generate_response_async(prompt=prompt)
|
||||||
|
|
||||||
|
# logger.info(f"{self.log_prefix} LLM返回结果: {content}")
|
||||||
|
|
||||||
|
if not content:
|
||||||
|
logger.warning("LLM返回空结果")
|
||||||
|
return []
|
||||||
|
|
||||||
|
# 5. 解析结果
|
||||||
|
result = repair_json(content)
|
||||||
|
if isinstance(result, str):
|
||||||
|
result = json.loads(result)
|
||||||
|
|
||||||
|
if not isinstance(result, dict) or "selected_situations" not in result:
|
||||||
|
logger.error("LLM返回格式错误")
|
||||||
|
return []
|
||||||
|
|
||||||
|
selected_indices = result["selected_situations"]
|
||||||
|
|
||||||
|
# 根据索引获取完整的表达方式
|
||||||
|
valid_expressions = []
|
||||||
|
for idx in selected_indices:
|
||||||
|
if isinstance(idx, int) and 1 <= idx <= len(all_expressions):
|
||||||
|
expression = all_expressions[idx - 1] # 索引从1开始
|
||||||
|
valid_expressions.append(expression)
|
||||||
|
|
||||||
|
# 对选中的表达方式count数*1.5
|
||||||
|
self.update_expression_count(chat_id, expression, 1.5)
|
||||||
|
|
||||||
|
# logger.info(f"LLM从{len(all_expressions)}个情境中选择了{len(valid_expressions)}个")
|
||||||
|
return valid_expressions
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"LLM处理表达方式选择时出错: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
init_prompt()
|
||||||
|
|
||||||
|
try:
|
||||||
|
expression_selector = ExpressionSelector()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"ExpressionSelector初始化失败: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ def init_prompt() -> None:
|
|||||||
4. 思考有没有特殊的梗,一并总结成语言风格
|
4. 思考有没有特殊的梗,一并总结成语言风格
|
||||||
5. 例子仅供参考,请严格根据群聊内容总结!!!
|
5. 例子仅供参考,请严格根据群聊内容总结!!!
|
||||||
注意:总结成如下格式的规律,总结的内容要详细,但具有概括性:
|
注意:总结成如下格式的规律,总结的内容要详细,但具有概括性:
|
||||||
当"xxxxxx"时,可以"xxxxxx", xxxxxx不超过20个字
|
当"xxxxxx"时,可以"xxxxxx", xxxxxx不超过20个字,为特定句式或表达
|
||||||
|
|
||||||
例如:
|
例如:
|
||||||
当"对某件事表示十分惊叹,有些意外"时,使用"我嘞个xxxx"
|
当"对某件事表示十分惊叹,有些意外"时,使用"我嘞个xxxx"
|
||||||
@@ -73,7 +73,7 @@ class ExpressionLearner:
|
|||||||
request_type="expressor.learner",
|
request_type="expressor.learner",
|
||||||
)
|
)
|
||||||
|
|
||||||
async def get_expression_by_chat_id(self, chat_id: str) -> Tuple[List[Dict[str, str]], List[Dict[str, str]]]:
|
def get_expression_by_chat_id(self, chat_id: str) -> Tuple[List[Dict[str, str]], List[Dict[str, str]]]:
|
||||||
"""
|
"""
|
||||||
读取/data/expression/learnt/{chat_id}/expressions.json和/data/expression/personality/expressions.json
|
读取/data/expression/learnt/{chat_id}/expressions.json和/data/expression/personality/expressions.json
|
||||||
返回(learnt_expressions, personality_expressions)
|
返回(learnt_expressions, personality_expressions)
|
||||||
|
|||||||
@@ -1,98 +1,18 @@
|
|||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
from typing import List, Dict
|
from typing import List
|
||||||
from src.chat.heart_flow.observation.chatting_observation import ChattingObservation
|
from src.chat.heart_flow.observation.chatting_observation import ChattingObservation
|
||||||
from src.chat.heart_flow.observation.observation import Observation
|
from src.chat.heart_flow.observation.observation import Observation
|
||||||
from src.llm_models.utils_model import LLMRequest
|
|
||||||
from src.config.config import global_config
|
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
|
|
||||||
from src.chat.message_receive.chat_stream import get_chat_manager
|
from src.chat.message_receive.chat_stream import get_chat_manager
|
||||||
from .base_processor import BaseProcessor
|
from .base_processor import BaseProcessor
|
||||||
from src.chat.focus_chat.info.info_base import InfoBase
|
from src.chat.focus_chat.info.info_base import InfoBase
|
||||||
from src.chat.focus_chat.info.expression_selection_info import ExpressionSelectionInfo
|
from src.chat.focus_chat.info.expression_selection_info import ExpressionSelectionInfo
|
||||||
from src.chat.express.exprssion_learner import get_expression_learner
|
from src.chat.express.expression_selector import expression_selector
|
||||||
from json_repair import repair_json
|
|
||||||
import json
|
|
||||||
|
|
||||||
logger = get_logger("processor")
|
logger = get_logger("processor")
|
||||||
|
|
||||||
|
|
||||||
def weighted_sample_no_replacement(items, weights, k) -> list:
|
|
||||||
"""
|
|
||||||
加权随机抽样,不允许重复
|
|
||||||
|
|
||||||
Args:
|
|
||||||
items: 待抽样的项目列表
|
|
||||||
weights: 对应项目的权重列表
|
|
||||||
k: 抽样数量
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
抽样结果列表
|
|
||||||
"""
|
|
||||||
if not items or k <= 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
k = min(k, len(items))
|
|
||||||
selected = []
|
|
||||||
remaining_items = list(items)
|
|
||||||
remaining_weights = list(weights)
|
|
||||||
|
|
||||||
for _ in range(k):
|
|
||||||
if not remaining_items:
|
|
||||||
break
|
|
||||||
|
|
||||||
# 计算累积权重
|
|
||||||
total_weight = sum(remaining_weights)
|
|
||||||
if total_weight <= 0:
|
|
||||||
# 如果权重都为0或负数,则随机选择
|
|
||||||
selected_index = random.randint(0, len(remaining_items) - 1)
|
|
||||||
else:
|
|
||||||
# 加权随机选择
|
|
||||||
rand_val = random.uniform(0, total_weight)
|
|
||||||
cumulative_weight = 0
|
|
||||||
selected_index = 0
|
|
||||||
for i, weight in enumerate(remaining_weights):
|
|
||||||
cumulative_weight += weight
|
|
||||||
if rand_val <= cumulative_weight:
|
|
||||||
selected_index = i
|
|
||||||
break
|
|
||||||
|
|
||||||
# 添加选中的项目
|
|
||||||
selected.append(remaining_items[selected_index])
|
|
||||||
# 移除已选中的项目
|
|
||||||
remaining_items.pop(selected_index)
|
|
||||||
remaining_weights.pop(selected_index)
|
|
||||||
|
|
||||||
return selected
|
|
||||||
|
|
||||||
|
|
||||||
def init_prompt():
|
|
||||||
expression_evaluation_prompt = """
|
|
||||||
你的名字是{bot_name}
|
|
||||||
|
|
||||||
以下是正在进行的聊天内容:
|
|
||||||
{chat_observe_info}
|
|
||||||
|
|
||||||
以下是可选的表达情境:
|
|
||||||
{all_situations}
|
|
||||||
|
|
||||||
请你分析聊天内容的语境、情绪、话题类型,从上述情境中选择最适合当前聊天情境的10个情境。
|
|
||||||
考虑因素包括:
|
|
||||||
1. 聊天的情绪氛围(轻松、严肃、幽默等)
|
|
||||||
2. 话题类型(日常、技术、游戏、情感等)
|
|
||||||
3. 情境与当前语境的匹配度
|
|
||||||
|
|
||||||
请以JSON格式输出,只需要输出选中的情境编号:
|
|
||||||
{{
|
|
||||||
"selected_situations": [1, 3, 5, 7, 9, 12, 15, 18, 21, 25]
|
|
||||||
}}
|
|
||||||
|
|
||||||
请严格按照JSON格式输出,不要包含其他内容:
|
|
||||||
"""
|
|
||||||
Prompt(expression_evaluation_prompt, "expression_evaluation_prompt")
|
|
||||||
|
|
||||||
|
|
||||||
class ExpressionSelectorProcessor(BaseProcessor):
|
class ExpressionSelectorProcessor(BaseProcessor):
|
||||||
log_prefix = "表达选择器"
|
log_prefix = "表达选择器"
|
||||||
|
|
||||||
@@ -101,16 +21,9 @@ class ExpressionSelectorProcessor(BaseProcessor):
|
|||||||
|
|
||||||
self.subheartflow_id = subheartflow_id
|
self.subheartflow_id = subheartflow_id
|
||||||
self.last_selection_time = 0
|
self.last_selection_time = 0
|
||||||
self.selection_interval = 40 # 1分钟间隔
|
self.selection_interval = 10 # 40秒间隔
|
||||||
self.cached_expressions = [] # 缓存上一次选择的表达方式
|
self.cached_expressions = [] # 缓存上一次选择的表达方式
|
||||||
|
|
||||||
# 表达方式选择模式
|
|
||||||
self.selection_mode = getattr(global_config.expression, "selection_mode", "llm") # "llm" 或 "random"
|
|
||||||
|
|
||||||
self.llm_model = LLMRequest(
|
|
||||||
model=global_config.model.utils_small,
|
|
||||||
request_type="focus.processor.expression_selector",
|
|
||||||
)
|
|
||||||
|
|
||||||
name = get_chat_manager().get_stream_name(self.subheartflow_id)
|
name = get_chat_manager().get_stream_name(self.subheartflow_id)
|
||||||
self.log_prefix = f"[{name}] 表达选择器"
|
self.log_prefix = f"[{name}] 表达选择器"
|
||||||
@@ -158,26 +71,20 @@ class ExpressionSelectorProcessor(BaseProcessor):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 根据模式选择表达方式
|
# LLM模式:调用LLM选择5-10个,然后随机选5个
|
||||||
# LLM模式:调用LLM选择15个,然后随机选5个
|
selected_expressions = await expression_selector.select_suitable_expressions_llm(self.subheartflow_id, chat_info)
|
||||||
selected_expressions = await self._select_suitable_expressions_llm(chat_info)
|
|
||||||
cache_size = len(selected_expressions) if selected_expressions else 0
|
cache_size = len(selected_expressions) if selected_expressions else 0
|
||||||
mode_desc = f"LLM模式(已缓存{cache_size}个)"
|
mode_desc = f"LLM模式(已缓存{cache_size}个)"
|
||||||
|
|
||||||
if selected_expressions:
|
if selected_expressions:
|
||||||
# 缓存选择的表达方式
|
|
||||||
self.cached_expressions = selected_expressions
|
self.cached_expressions = selected_expressions
|
||||||
# 更新最后选择时间
|
|
||||||
self.last_selection_time = current_time
|
self.last_selection_time = current_time
|
||||||
|
|
||||||
# 从选择的表达方式中随机选5个
|
|
||||||
final_expressions = random.sample(selected_expressions, min(4, len(selected_expressions)))
|
|
||||||
|
|
||||||
# 创建表达选择信息
|
# 创建表达选择信息
|
||||||
expression_info = ExpressionSelectionInfo()
|
expression_info = ExpressionSelectionInfo()
|
||||||
expression_info.set_selected_expressions(final_expressions)
|
expression_info.set_selected_expressions(selected_expressions)
|
||||||
|
|
||||||
logger.info(f"{self.log_prefix} 为当前聊天选择了{len(final_expressions)}个表达方式({mode_desc})")
|
logger.info(f"{self.log_prefix} 为当前聊天选择了{len(selected_expressions)}个表达方式({mode_desc})")
|
||||||
return [expression_info]
|
return [expression_info]
|
||||||
else:
|
else:
|
||||||
logger.debug(f"{self.log_prefix} 未选择任何表达方式")
|
logger.debug(f"{self.log_prefix} 未选择任何表达方式")
|
||||||
@@ -187,104 +94,3 @@ class ExpressionSelectorProcessor(BaseProcessor):
|
|||||||
logger.error(f"{self.log_prefix} 处理表达方式选择时出错: {e}")
|
logger.error(f"{self.log_prefix} 处理表达方式选择时出错: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
async def _get_random_expressions(self) -> tuple[List[Dict], List[Dict], List[Dict]]:
|
|
||||||
"""随机获取表达方式:20个style,20个grammar,20个personality"""
|
|
||||||
expression_learner = get_expression_learner()
|
|
||||||
|
|
||||||
# 获取所有表达方式
|
|
||||||
(
|
|
||||||
learnt_style_expressions,
|
|
||||||
learnt_grammar_expressions,
|
|
||||||
personality_expressions,
|
|
||||||
) = await expression_learner.get_expression_by_chat_id(self.subheartflow_id)
|
|
||||||
|
|
||||||
# 随机选择
|
|
||||||
selected_style = random.sample(learnt_style_expressions, min(15, len(learnt_style_expressions)))
|
|
||||||
selected_grammar = random.sample(learnt_grammar_expressions, min(15, len(learnt_grammar_expressions)))
|
|
||||||
selected_personality = random.sample(personality_expressions, min(5, len(personality_expressions)))
|
|
||||||
|
|
||||||
return selected_style, selected_grammar, selected_personality
|
|
||||||
|
|
||||||
async def _select_suitable_expressions_llm(self, chat_info: str) -> List[Dict[str, str]]:
|
|
||||||
"""使用LLM选择适合的表达方式"""
|
|
||||||
|
|
||||||
# 1. 获取35个随机表达方式
|
|
||||||
style_exprs, grammar_exprs, personality_exprs = await self._get_random_expressions()
|
|
||||||
|
|
||||||
# 2. 构建所有表达方式的索引和情境列表
|
|
||||||
all_expressions = []
|
|
||||||
all_situations = []
|
|
||||||
|
|
||||||
# 添加style表达方式
|
|
||||||
for expr in style_exprs:
|
|
||||||
if isinstance(expr, dict) and "situation" in expr and "style" in expr:
|
|
||||||
expr_with_type = expr.copy()
|
|
||||||
expr_with_type["type"] = "style"
|
|
||||||
all_expressions.append(expr_with_type)
|
|
||||||
all_situations.append(f"{len(all_expressions)}. [语言风格] {expr['situation']}")
|
|
||||||
|
|
||||||
# 添加grammar表达方式
|
|
||||||
for expr in grammar_exprs:
|
|
||||||
if isinstance(expr, dict) and "situation" in expr and "style" in expr:
|
|
||||||
expr_with_type = expr.copy()
|
|
||||||
expr_with_type["type"] = "grammar"
|
|
||||||
all_expressions.append(expr_with_type)
|
|
||||||
all_situations.append(f"{len(all_expressions)}. [句法语法] {expr['situation']}")
|
|
||||||
|
|
||||||
# 添加personality表达方式
|
|
||||||
for expr in personality_exprs:
|
|
||||||
if isinstance(expr, dict) and "situation" in expr and "style" in expr:
|
|
||||||
expr_with_type = expr.copy()
|
|
||||||
expr_with_type["type"] = "personality"
|
|
||||||
all_expressions.append(expr_with_type)
|
|
||||||
all_situations.append(f"{len(all_expressions)}. [个性表达] {expr['situation']}")
|
|
||||||
|
|
||||||
if not all_expressions:
|
|
||||||
logger.warning(f"{self.log_prefix} 没有找到可用的表达方式")
|
|
||||||
return []
|
|
||||||
|
|
||||||
all_situations_str = "\n".join(all_situations)
|
|
||||||
|
|
||||||
# 3. 构建prompt(只包含情境,不包含完整的表达方式)
|
|
||||||
prompt = (await global_prompt_manager.get_prompt_async("expression_evaluation_prompt")).format(
|
|
||||||
bot_name=global_config.bot.nickname,
|
|
||||||
chat_observe_info=chat_info,
|
|
||||||
all_situations=all_situations_str,
|
|
||||||
)
|
|
||||||
|
|
||||||
# 4. 调用LLM
|
|
||||||
try:
|
|
||||||
content, _ = await self.llm_model.generate_response_async(prompt=prompt)
|
|
||||||
|
|
||||||
# logger.info(f"{self.log_prefix} LLM返回结果: {content}")
|
|
||||||
|
|
||||||
if not content:
|
|
||||||
logger.warning(f"{self.log_prefix} LLM返回空结果")
|
|
||||||
return []
|
|
||||||
|
|
||||||
# 5. 解析结果
|
|
||||||
result = repair_json(content)
|
|
||||||
if isinstance(result, str):
|
|
||||||
result = json.loads(result)
|
|
||||||
|
|
||||||
if not isinstance(result, dict) or "selected_situations" not in result:
|
|
||||||
logger.error(f"{self.log_prefix} LLM返回格式错误")
|
|
||||||
return []
|
|
||||||
|
|
||||||
selected_indices = result["selected_situations"]
|
|
||||||
|
|
||||||
# 根据索引获取完整的表达方式
|
|
||||||
valid_expressions = []
|
|
||||||
for idx in selected_indices:
|
|
||||||
if isinstance(idx, int) and 1 <= idx <= len(all_expressions):
|
|
||||||
valid_expressions.append(all_expressions[idx - 1]) # 索引从1开始
|
|
||||||
|
|
||||||
logger.info(f"{self.log_prefix} LLM从{len(all_expressions)}个情境中选择了{len(valid_expressions)}个")
|
|
||||||
return valid_expressions
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"{self.log_prefix} LLM处理表达方式选择时出错: {e}")
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
init_prompt()
|
|
||||||
|
|||||||
@@ -490,7 +490,7 @@ class DefaultReplyer:
|
|||||||
learnt_style_expressions,
|
learnt_style_expressions,
|
||||||
learnt_grammar_expressions,
|
learnt_grammar_expressions,
|
||||||
personality_expressions,
|
personality_expressions,
|
||||||
) = await expression_learner.get_expression_by_chat_id(chat_stream.stream_id)
|
) = expression_learner.get_expression_by_chat_id(chat_stream.stream_id)
|
||||||
|
|
||||||
style_habbits = []
|
style_habbits = []
|
||||||
grammar_habbits = []
|
grammar_habbits = []
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
from src.chat.express.exprssion_learner import get_expression_learner
|
|
||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
from src.common.logger import get_logger
|
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.utils.prompt_builder import Prompt, global_prompt_manager
|
||||||
from src.chat.utils.chat_message_builder import build_readable_messages, get_raw_msg_before_timestamp_with_chat
|
from src.chat.utils.chat_message_builder import build_readable_messages, get_raw_msg_before_timestamp_with_chat
|
||||||
import time
|
import time
|
||||||
@@ -10,6 +8,8 @@ from src.manager.mood_manager import mood_manager
|
|||||||
from src.chat.memory_system.Hippocampus import hippocampus_manager
|
from src.chat.memory_system.Hippocampus import hippocampus_manager
|
||||||
from src.chat.knowledge.knowledge_lib import qa_manager
|
from src.chat.knowledge.knowledge_lib import qa_manager
|
||||||
import random
|
import random
|
||||||
|
from src.person_info.person_info import get_person_info_manager
|
||||||
|
from src.chat.express.expression_selector import expression_selector
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from src.person_info.relationship_manager import get_relationship_manager
|
from src.person_info.relationship_manager import get_relationship_manager
|
||||||
@@ -27,7 +27,7 @@ def init_prompt():
|
|||||||
"""
|
"""
|
||||||
你可以参考以下的语言习惯,如果情景合适就使用,不要盲目使用,不要生硬使用,而是结合到表达中:
|
你可以参考以下的语言习惯,如果情景合适就使用,不要盲目使用,不要生硬使用,而是结合到表达中:
|
||||||
{style_habbits}
|
{style_habbits}
|
||||||
请你根据情景使用以下句法,不要盲目使用,不要生硬使用,而是结合到表达中:
|
请你根据情景使用以下,不要盲目使用,不要生硬使用,而是结合到表达中:
|
||||||
{grammar_habbits}
|
{grammar_habbits}
|
||||||
|
|
||||||
{memory_prompt}
|
{memory_prompt}
|
||||||
@@ -91,7 +91,14 @@ class PromptBuilder:
|
|||||||
enable_planner: bool = False,
|
enable_planner: bool = False,
|
||||||
available_actions=None,
|
available_actions=None,
|
||||||
) -> str:
|
) -> str:
|
||||||
prompt_personality = get_individuality().get_prompt(x_person=2, level=2)
|
core_personality = global_config.personality.personality_core
|
||||||
|
person_info_manager = get_person_info_manager()
|
||||||
|
bot_person_id = person_info_manager.get_person_id("system", "bot_id")
|
||||||
|
short_impression = await person_info_manager.get_value(bot_person_id, "short_impression")
|
||||||
|
prompt_personality = core_personality
|
||||||
|
if short_impression:
|
||||||
|
prompt_personality += short_impression
|
||||||
|
|
||||||
is_group_chat = bool(chat_stream.group_info)
|
is_group_chat = bool(chat_stream.group_info)
|
||||||
|
|
||||||
who_chat_in_group = []
|
who_chat_in_group = []
|
||||||
@@ -113,40 +120,8 @@ class PromptBuilder:
|
|||||||
relation_prompt += await relationship_manager.build_relationship_info(person)
|
relation_prompt += await relationship_manager.build_relationship_info(person)
|
||||||
|
|
||||||
mood_prompt = mood_manager.get_mood_prompt()
|
mood_prompt = mood_manager.get_mood_prompt()
|
||||||
expression_learner = get_expression_learner()
|
|
||||||
(
|
|
||||||
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)
|
|
||||||
|
|
||||||
memory_prompt = ""
|
memory_prompt = ""
|
||||||
|
|
||||||
if global_config.memory.enable_memory:
|
if global_config.memory.enable_memory:
|
||||||
related_memory = await hippocampus_manager.get_memory_from_text(
|
related_memory = await hippocampus_manager.get_memory_from_text(
|
||||||
text=message_txt, max_memory_num=2, max_memory_length=2, max_depth=3, fast_retrieval=False
|
text=message_txt, max_memory_num=2, max_memory_length=2, max_depth=3, fast_retrieval=False
|
||||||
@@ -174,6 +149,37 @@ class PromptBuilder:
|
|||||||
show_actions=True,
|
show_actions=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
message_list_before_now_half = get_raw_msg_before_timestamp_with_chat(
|
||||||
|
chat_id=chat_stream.stream_id,
|
||||||
|
timestamp=time.time(),
|
||||||
|
limit=global_config.focus_chat.observation_context_size * 0.5,
|
||||||
|
)
|
||||||
|
chat_talking_prompt_half = build_readable_messages(
|
||||||
|
message_list_before_now_half,
|
||||||
|
replace_bot_name=True,
|
||||||
|
merge_messages=False,
|
||||||
|
timestamp_mode="relative",
|
||||||
|
read_mark=0.0,
|
||||||
|
show_actions=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
expressions = expression_selector.select_suitable_expressions_llm(chat_stream.stream_id, chat_talking_prompt_half)
|
||||||
|
style_habbits = []
|
||||||
|
grammar_habbits = []
|
||||||
|
if expressions:
|
||||||
|
for expr in expressions:
|
||||||
|
if isinstance(expr, dict) and "situation" in expr and "style" in expr:
|
||||||
|
expr_type = expr.get("type", "style")
|
||||||
|
if expr_type == "grammar":
|
||||||
|
grammar_habbits.append(f"当{expr['situation']}时,使用 {expr['style']}")
|
||||||
|
else:
|
||||||
|
style_habbits.append(f"当{expr['situation']}时,使用 {expr['style']}")
|
||||||
|
else:
|
||||||
|
logger.debug("没有从处理器获得表达方式,将使用空的表达方式")
|
||||||
|
|
||||||
|
style_habbits_str = "\n".join(style_habbits)
|
||||||
|
grammar_habbits_str = "\n".join(grammar_habbits)
|
||||||
|
|
||||||
# 关键词检测与反应
|
# 关键词检测与反应
|
||||||
keywords_reaction_prompt = ""
|
keywords_reaction_prompt = ""
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -599,7 +599,6 @@ def build_readable_messages(
|
|||||||
copy_messages.sort(key=lambda x: x.get("time", 0))
|
copy_messages.sort(key=lambda x: x.get("time", 0))
|
||||||
|
|
||||||
if read_mark <= 0:
|
if read_mark <= 0:
|
||||||
print(f"read_mark: {read_mark}")
|
|
||||||
# 没有有效的 read_mark,直接格式化所有消息
|
# 没有有效的 read_mark,直接格式化所有消息
|
||||||
formatted_string, _, pic_id_mapping, _ = _build_readable_messages_internal(
|
formatted_string, _, pic_id_mapping, _ = _build_readable_messages_internal(
|
||||||
copy_messages, replace_bot_name, merge_messages, timestamp_mode, truncate
|
copy_messages, replace_bot_name, merge_messages, timestamp_mode, truncate
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
|
from src.llm_models.utils_model import LLMRequest
|
||||||
from .personality import Personality
|
from .personality import Personality
|
||||||
from .identity import Identity
|
from .identity import Identity
|
||||||
from .expression_style import PersonalityExpression
|
from .expression_style import PersonalityExpression
|
||||||
@@ -10,6 +12,7 @@ import hashlib
|
|||||||
from rich.traceback import install
|
from rich.traceback import install
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
from src.person_info.person_info import get_person_info_manager
|
from src.person_info.person_info import get_person_info_manager
|
||||||
|
from src.config.config import global_config
|
||||||
|
|
||||||
install(extra_lines=3)
|
install(extra_lines=3)
|
||||||
|
|
||||||
@@ -29,6 +32,11 @@ class Individuality:
|
|||||||
self.bot_person_id = ""
|
self.bot_person_id = ""
|
||||||
self.meta_info_file_path = "data/personality/meta.json"
|
self.meta_info_file_path = "data/personality/meta.json"
|
||||||
|
|
||||||
|
self.model = LLMRequest(
|
||||||
|
model=global_config.model.utils,
|
||||||
|
request_type="individuality.compress",
|
||||||
|
)
|
||||||
|
|
||||||
async def initialize(
|
async def initialize(
|
||||||
self,
|
self,
|
||||||
bot_nickname: str,
|
bot_nickname: str,
|
||||||
@@ -90,6 +98,11 @@ class Individuality:
|
|||||||
)
|
)
|
||||||
logger.info("已将完整人设更新到bot的impression中")
|
logger.info("已将完整人设更新到bot的impression中")
|
||||||
|
|
||||||
|
# 创建压缩版本的short_impression
|
||||||
|
asyncio.create_task(self._create_compressed_impression(
|
||||||
|
personality_core, personality_sides, identity_detail
|
||||||
|
))
|
||||||
|
|
||||||
asyncio.create_task(self.express_style.extract_and_store_personality_expressions())
|
asyncio.create_task(self.express_style.extract_and_store_personality_expressions())
|
||||||
|
|
||||||
def to_dict(self) -> dict:
|
def to_dict(self) -> dict:
|
||||||
@@ -357,6 +370,71 @@ class Individuality:
|
|||||||
logger.error(f"解析info_list失败: {info_list_json}")
|
logger.error(f"解析info_list失败: {info_list_json}")
|
||||||
return keywords
|
return keywords
|
||||||
|
|
||||||
|
async def _create_compressed_impression(
|
||||||
|
self, personality_core: str, personality_sides: list, identity_detail: list
|
||||||
|
) -> str:
|
||||||
|
"""使用LLM创建压缩版本的impression
|
||||||
|
|
||||||
|
Args:
|
||||||
|
personality_core: 核心人格
|
||||||
|
personality_sides: 人格侧面列表
|
||||||
|
identity_detail: 身份细节列表
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 压缩后的impression文本
|
||||||
|
"""
|
||||||
|
# 核心人格保持不变
|
||||||
|
compressed_parts = []
|
||||||
|
if personality_core:
|
||||||
|
compressed_parts.append(f"{personality_core}")
|
||||||
|
|
||||||
|
# 准备需要压缩的内容
|
||||||
|
content_to_compress = []
|
||||||
|
if personality_sides:
|
||||||
|
content_to_compress.append(f"人格特质: {'、'.join(personality_sides)}")
|
||||||
|
if identity_detail:
|
||||||
|
content_to_compress.append(f"身份背景: {'、'.join(identity_detail)}")
|
||||||
|
|
||||||
|
if not content_to_compress:
|
||||||
|
# 如果没有需要压缩的内容,直接返回核心人格
|
||||||
|
result = "。".join(compressed_parts)
|
||||||
|
return result + "。" if result else ""
|
||||||
|
|
||||||
|
# 使用LLM压缩其他内容
|
||||||
|
try:
|
||||||
|
compress_content = "、".join(content_to_compress)
|
||||||
|
|
||||||
|
prompt = f"""请将以下人设信息进行简洁压缩,保留主要内容,用简练的中文表达:
|
||||||
|
|
||||||
|
{compress_content}
|
||||||
|
|
||||||
|
要求:
|
||||||
|
1. 保持原意不变,尽量使用原文
|
||||||
|
2. 尽量简洁,不超过30字
|
||||||
|
3. 直接输出压缩后的内容,不要解释"""
|
||||||
|
|
||||||
|
response,(_,_) = await self.model.generate_response_async(
|
||||||
|
prompt=prompt,
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.strip():
|
||||||
|
compressed_parts.append(response.strip())
|
||||||
|
logger.info(f"精简人格侧面: {response.strip()}")
|
||||||
|
else:
|
||||||
|
logger.error(f"使用LLM压缩人设时出错: {response}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"使用LLM压缩人设时出错: {e}")
|
||||||
|
|
||||||
|
result = "。".join(compressed_parts)
|
||||||
|
|
||||||
|
# 更新short_impression字段
|
||||||
|
if result:
|
||||||
|
person_info_manager = get_person_info_manager()
|
||||||
|
await person_info_manager.update_one_field(
|
||||||
|
self.bot_person_id, "short_impression", result
|
||||||
|
)
|
||||||
|
logger.info("已将压缩人设更新到bot的short_impression中")
|
||||||
|
|
||||||
|
|
||||||
individuality = None
|
individuality = None
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ class LLMRequest:
|
|||||||
def __init__(self, model: dict, **kwargs):
|
def __init__(self, model: dict, **kwargs):
|
||||||
# 将大写的配置键转换为小写并从config中获取实际值
|
# 将大写的配置键转换为小写并从config中获取实际值
|
||||||
try:
|
try:
|
||||||
|
# print(f"model['provider']: {model['provider']}")
|
||||||
self.api_key = os.environ[f"{model['provider']}_KEY"]
|
self.api_key = os.environ[f"{model['provider']}_KEY"]
|
||||||
self.base_url = os.environ[f"{model['provider']}_BASE_URL"]
|
self.base_url = os.environ[f"{model['provider']}_BASE_URL"]
|
||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
|
|||||||
Reference in New Issue
Block a user