mirror of https://github.com/Mai-with-u/MaiBot.git
243 lines
11 KiB
Python
243 lines
11 KiB
Python
import random
|
||
from typing import List, Optional
|
||
|
||
from src.common.logger import get_logger
|
||
from src.config.config import global_config, model_config
|
||
from src.chat.utils.prompt_builder import Prompt
|
||
from src.llm_models.payload_content.message import RoleType, Message
|
||
from src.llm_models.utils_model import LLMRequest
|
||
from src.chat.message_receive.chat_stream import get_chat_manager
|
||
from src.plugin_system.apis import send_api
|
||
|
||
logger = get_logger("dream_generator")
|
||
|
||
# 初始化 utils 模型用于生成梦境总结
|
||
_dream_summary_model: Optional[LLMRequest] = None
|
||
|
||
# 梦境风格列表(21种)
|
||
DREAM_STYLES = [
|
||
"保持诗意和想象力,自由编写",
|
||
"诗意朦胧,如薄雾笼罩的清晨",
|
||
"奇幻冒险,充满未知与探索",
|
||
"温暖怀旧,带着时光的痕迹",
|
||
"神秘悬疑,暗藏深意",
|
||
"浪漫唯美,如诗如画",
|
||
"科幻未来,科技与想象交织",
|
||
"自然清新,如山林间的微风",
|
||
"深沉哲思,引人深思",
|
||
"轻松幽默,充满趣味",
|
||
"悲伤忧郁,带着淡淡哀愁",
|
||
"激昂热烈,充满活力",
|
||
"宁静平和,如湖面般平静",
|
||
"荒诞离奇,打破常规",
|
||
"细腻温柔,如春风拂面",
|
||
"壮阔宏大,气势磅礴",
|
||
"简约纯粹,返璞归真",
|
||
"复杂多变,层次丰富",
|
||
"梦幻迷离,虚实难辨",
|
||
"现实写意,贴近生活",
|
||
"抽象概念,超越具象",
|
||
]
|
||
|
||
|
||
def get_random_dream_styles(count: int = 2) -> List[str]:
|
||
"""从梦境风格列表中随机选择指定数量的风格"""
|
||
return random.sample(DREAM_STYLES, min(count, len(DREAM_STYLES)))
|
||
|
||
def init_dream_summary_prompt() -> None:
|
||
"""初始化梦境总结的提示词"""
|
||
Prompt(
|
||
"""
|
||
你刚刚完成了一次对聊天记录的记忆整理工作。以下是整理过程的摘要:
|
||
整理过程:
|
||
{conversation_text}
|
||
|
||
请将这次整理涉及的相关信息改写为一个富有诗意和想象力的"梦境",请你仅使用具体的记忆的内容,而不是整理过程编写。
|
||
要求:
|
||
1. 使用第一人称视角
|
||
2. 叙述直白,不要复杂修辞,口语化
|
||
3. 长度控制在200-800字
|
||
4. 用中文输出
|
||
梦境风格:
|
||
{dream_styles}
|
||
请直接输出梦境内容,不要添加其他说明:
|
||
""",
|
||
name="dream_summary_prompt",
|
||
)
|
||
|
||
|
||
async def generate_dream_summary(
|
||
chat_id: str,
|
||
conversation_messages: List[Message],
|
||
total_iterations: int,
|
||
time_cost: float,
|
||
) -> None:
|
||
"""生成梦境总结,输出到日志,并根据配置可选地推送给指定用户"""
|
||
try:
|
||
import json
|
||
from src.chat.utils.prompt_builder import global_prompt_manager
|
||
|
||
# 第一步:建立工具调用结果映射 (call_id -> result)
|
||
tool_results_map: dict[str, str] = {}
|
||
for msg in conversation_messages:
|
||
if msg.role == RoleType.Tool and msg.tool_call_id:
|
||
content = ""
|
||
if msg.content:
|
||
if isinstance(msg.content, list) and msg.content:
|
||
content = msg.content[0].text if hasattr(msg.content[0], "text") else str(msg.content[0])
|
||
else:
|
||
content = str(msg.content)
|
||
tool_results_map[msg.tool_call_id] = content
|
||
|
||
# 第二步:详细记录所有工具调用操作和结果到日志
|
||
tool_call_count = 0
|
||
logger.info(f"[dream][工具调用详情] 开始记录 chat_id={chat_id} 的所有工具调用操作:")
|
||
|
||
for msg in conversation_messages:
|
||
if msg.role == RoleType.Assistant and msg.tool_calls:
|
||
tool_call_count += 1
|
||
# 提取思考内容
|
||
thought_content = ""
|
||
if msg.content:
|
||
if isinstance(msg.content, list) and msg.content:
|
||
thought_content = (
|
||
msg.content[0].text if hasattr(msg.content[0], "text") else str(msg.content[0])
|
||
)
|
||
else:
|
||
thought_content = str(msg.content)
|
||
|
||
logger.info(f"[dream][工具调用详情] === 第 {tool_call_count} 组工具调用 ===")
|
||
if thought_content:
|
||
logger.info(
|
||
f"[dream][工具调用详情] 思考内容:{thought_content[:500]}{'...' if len(thought_content) > 500 else ''}"
|
||
)
|
||
|
||
# 记录每个工具调用的详细信息
|
||
for idx, tool_call in enumerate(msg.tool_calls, 1):
|
||
tool_name = tool_call.func_name
|
||
tool_args = tool_call.args or {}
|
||
tool_call_id = tool_call.call_id
|
||
tool_result = tool_results_map.get(tool_call_id, "未找到执行结果")
|
||
|
||
# 格式化参数
|
||
try:
|
||
args_str = json.dumps(tool_args, ensure_ascii=False, indent=2) if tool_args else "无参数"
|
||
except Exception:
|
||
args_str = str(tool_args)
|
||
|
||
logger.info(f"[dream][工具调用详情] --- 工具 {idx}: {tool_name} ---")
|
||
logger.info(f"[dream][工具调用详情] 调用参数:\n{args_str}")
|
||
logger.info(f"[dream][工具调用详情] 执行结果:\n{tool_result}")
|
||
logger.info(f"[dream][工具调用详情] {'-' * 60}")
|
||
|
||
logger.info(f"[dream][工具调用详情] 共记录了 {tool_call_count} 组工具调用操作")
|
||
|
||
# 第三步:构建对话历史摘要(用于生成梦境)
|
||
conversation_summary = []
|
||
for msg in conversation_messages:
|
||
role = msg.role.value if hasattr(msg.role, "value") else str(msg.role)
|
||
content = ""
|
||
if msg.content:
|
||
content = msg.content[0].text if isinstance(msg.content, list) and msg.content else str(msg.content)
|
||
|
||
if role == "user" and "轮次信息" in content:
|
||
# 跳过轮次信息消息
|
||
continue
|
||
|
||
if role == "assistant":
|
||
# 只保留思考内容,简化工具调用信息
|
||
if content:
|
||
# 截取前500字符,避免过长
|
||
content_preview = content[:500] + ("..." if len(content) > 500 else "")
|
||
conversation_summary.append(f"[{role}] {content_preview}")
|
||
elif role == "tool":
|
||
# 工具结果,只保留关键信息
|
||
if content:
|
||
# 截取前300字符
|
||
content_preview = content[:300] + ("..." if len(content) > 300 else "")
|
||
conversation_summary.append(f"[工具执行] {content_preview}")
|
||
|
||
conversation_text = "\n".join(conversation_summary[-20:]) # 只保留最后20条消息
|
||
|
||
# 随机选择2个梦境风格
|
||
selected_styles = get_random_dream_styles(2)
|
||
dream_styles_text = "\n".join([f"{i + 1}. {style}" for i, style in enumerate(selected_styles)])
|
||
|
||
# 使用 Prompt 管理器格式化梦境生成 prompt
|
||
dream_prompt = await global_prompt_manager.format_prompt(
|
||
"dream_summary_prompt",
|
||
chat_id=chat_id,
|
||
total_iterations=total_iterations,
|
||
time_cost=time_cost,
|
||
conversation_text=conversation_text,
|
||
dream_styles=dream_styles_text,
|
||
)
|
||
|
||
# 调用 utils 模型生成梦境
|
||
summary_model = LLMRequest(
|
||
model_set=model_config.model_task_config.replyer,
|
||
request_type="dream.summary",
|
||
)
|
||
dream_content, (reasoning, model_name, _) = await summary_model.generate_response_async(
|
||
dream_prompt,
|
||
temperature=0.8,
|
||
)
|
||
|
||
if dream_content:
|
||
logger.info(f"[dream][梦境总结] 对 chat_id={chat_id} 的整理过程梦境:\n{dream_content}")
|
||
|
||
# 第五步:根据配置决定是否将梦境发送给指定用户
|
||
try:
|
||
dream_send_raw = getattr(global_config.dream, "dream_send", "") or ""
|
||
dream_send = dream_send_raw.strip()
|
||
if dream_send:
|
||
parts = dream_send.split(":")
|
||
if len(parts) != 2:
|
||
logger.warning(
|
||
f"[dream][梦境总结] dream_send 配置格式不正确,应为 'platform:user_id',当前值: {dream_send_raw!r}"
|
||
)
|
||
else:
|
||
platform, user_id = parts[0].strip(), parts[1].strip()
|
||
if not platform or not user_id:
|
||
logger.warning(
|
||
f"[dream][梦境总结] dream_send 平台或用户ID为空,当前值: {dream_send_raw!r}"
|
||
)
|
||
else:
|
||
# 默认为私聊会话
|
||
stream_id = get_chat_manager().get_stream_id(
|
||
platform=platform,
|
||
id=str(user_id),
|
||
is_group=False,
|
||
)
|
||
if not stream_id:
|
||
logger.error(
|
||
f"[dream][梦境总结] 无法根据 dream_send 找到有效的聊天流,"
|
||
f"platform={platform!r}, user_id={user_id!r}"
|
||
)
|
||
else:
|
||
dream_visible = global_config.dream.dream_visible
|
||
ok = await send_api.text_to_stream(
|
||
dream_content,
|
||
stream_id=stream_id,
|
||
typing=False,
|
||
storage_message=dream_visible,
|
||
)
|
||
if ok:
|
||
logger.info(
|
||
f"[dream][梦境总结] 已将梦境结果发送给配置的目标用户: {platform}:{user_id}"
|
||
)
|
||
else:
|
||
logger.error(
|
||
f"[dream][梦境总结] 向 {platform}:{user_id} 发送梦境结果失败"
|
||
)
|
||
except Exception as send_exc:
|
||
logger.error(f"[dream][梦境总结] 发送梦境结果到配置用户时出错: {send_exc}", exc_info=True)
|
||
else:
|
||
logger.warning("[dream][梦境总结] 未能生成梦境总结")
|
||
|
||
except Exception as e:
|
||
logger.error(f"[dream][梦境总结] 生成梦境总结失败: {e}", exc_info=True)
|
||
|
||
|
||
init_dream_summary_prompt()
|