mirror of https://github.com/Mai-with-u/MaiBot.git
修改LLM的输出格式以适配一些将思考内容放在输出中的api
parent
d45ef07ea8
commit
5b49db8029
|
|
@ -11,7 +11,7 @@ from src.chat.utils.utils_image import image_path_to_base64 # Local import need
|
||||||
from src.chat.utils.timer_calculator import Timer # <--- Import Timer
|
from src.chat.utils.timer_calculator import Timer # <--- Import Timer
|
||||||
from src.chat.emoji_system.emoji_manager import emoji_manager
|
from src.chat.emoji_system.emoji_manager import emoji_manager
|
||||||
from src.chat.focus_chat.heartFC_sender import HeartFCSender
|
from src.chat.focus_chat.heartFC_sender import HeartFCSender
|
||||||
from src.chat.utils.utils import process_llm_response
|
from src.chat.utils.utils import process_llm_json_response
|
||||||
from src.chat.utils.info_catcher import info_catcher_manager
|
from src.chat.utils.info_catcher import info_catcher_manager
|
||||||
from src.chat.heart_flow.utils_chat import get_chat_type_and_target_info
|
from src.chat.heart_flow.utils_chat import get_chat_type_and_target_info
|
||||||
from src.chat.message_receive.chat_stream import ChatStream
|
from src.chat.message_receive.chat_stream import ChatStream
|
||||||
|
|
@ -50,11 +50,19 @@ def init_prompt():
|
||||||
{chat_target}
|
{chat_target}
|
||||||
{identity},在这聊天中,"{target_message}"引起了你的注意,你想要在群里发言或者回复这条消息。
|
{identity},在这聊天中,"{target_message}"引起了你的注意,你想要在群里发言或者回复这条消息。
|
||||||
你需要使用合适的语言习惯和句法,参考聊天内容,组织一条日常且口语化的回复。注意不要复读你说过的话。
|
你需要使用合适的语言习惯和句法,参考聊天内容,组织一条日常且口语化的回复。注意不要复读你说过的话。
|
||||||
{config_expression_style},请注意不要输出多余内容(包括前后缀,冒号和引号,括号(),表情包,at或 @等 )。只输出回复内容。
|
{config_expression_style}
|
||||||
{keywords_reaction_prompt}
|
{keywords_reaction_prompt}
|
||||||
请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。
|
请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。
|
||||||
不要浮夸,不要夸张修辞,只输出一条回复就好。
|
不要浮夸,不要夸张修辞,只输出一条回复就好。
|
||||||
现在,你说:
|
|
||||||
|
请按照以下JSON格式输出你的回复:
|
||||||
|
{{
|
||||||
|
"finalreply": "你的回复内容"
|
||||||
|
}}
|
||||||
|
|
||||||
|
注意:
|
||||||
|
- 只在finalreply字段中输出回复内容,不要包含多余的前后缀、冒号、引号、括号()、表情包、at或@等
|
||||||
|
- 确保JSON格式正确
|
||||||
""",
|
""",
|
||||||
"default_replyer_prompt",
|
"default_replyer_prompt",
|
||||||
)
|
)
|
||||||
|
|
@ -76,11 +84,19 @@ def init_prompt():
|
||||||
{style_habbits}
|
{style_habbits}
|
||||||
{grammar_habbits}
|
{grammar_habbits}
|
||||||
|
|
||||||
{config_expression_style},请注意不要输出多余内容(包括前后缀,冒号和引号,括号(),表情包,at或 @等 )。只输出回复内容。
|
{config_expression_style}
|
||||||
{keywords_reaction_prompt}
|
{keywords_reaction_prompt}
|
||||||
请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。
|
请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。
|
||||||
不要浮夸,不要夸张修辞,只输出一条回复就好。
|
不要浮夸,不要夸张修辞,只输出一条回复就好。
|
||||||
现在,你说:
|
|
||||||
|
请按照以下JSON格式输出你的回复:
|
||||||
|
{{
|
||||||
|
"finalreply": "你的回复内容"
|
||||||
|
}}
|
||||||
|
|
||||||
|
注意:
|
||||||
|
- 只在finalreply字段中输出回复内容,不要包含多余的前后缀、冒号、引号、括号()、表情包、at或@等
|
||||||
|
- 确保JSON格式正确
|
||||||
""",
|
""",
|
||||||
"default_replyer_private_prompt",
|
"default_replyer_private_prompt",
|
||||||
)
|
)
|
||||||
|
|
@ -307,7 +323,7 @@ class DefaultReplyer:
|
||||||
logger.error(f"{self.log_prefix}LLM 生成失败: {llm_e}")
|
logger.error(f"{self.log_prefix}LLM 生成失败: {llm_e}")
|
||||||
return None # LLM 调用失败则无法生成回复
|
return None # LLM 调用失败则无法生成回复
|
||||||
|
|
||||||
processed_response = process_llm_response(content)
|
processed_response = process_llm_json_response(content)
|
||||||
|
|
||||||
# 5. 处理 LLM 响应
|
# 5. 处理 LLM 响应
|
||||||
if not content:
|
if not content:
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from src.chat.utils.timer_calculator import Timer
|
||||||
from src.common.logger_manager import get_logger
|
from src.common.logger_manager import get_logger
|
||||||
from src.chat.utils.info_catcher import info_catcher_manager
|
from src.chat.utils.info_catcher import info_catcher_manager
|
||||||
from src.person_info.person_info import person_info_manager
|
from src.person_info.person_info import person_info_manager
|
||||||
from src.chat.utils.utils import process_llm_response
|
from src.chat.utils.utils import process_llm_json_response
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger("normal_chat_response")
|
logger = get_logger("normal_chat_response")
|
||||||
|
|
@ -58,7 +58,7 @@ class NormalChatGenerator:
|
||||||
|
|
||||||
if model_response:
|
if model_response:
|
||||||
logger.debug(f"{global_config.bot.nickname}的备选回复是:{model_response}")
|
logger.debug(f"{global_config.bot.nickname}的备选回复是:{model_response}")
|
||||||
model_response = process_llm_response(model_response)
|
model_response = process_llm_json_response(model_response)
|
||||||
|
|
||||||
return model_response
|
return model_response
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,14 @@ def init_prompt():
|
||||||
{keywords_reaction_prompt}
|
{keywords_reaction_prompt}
|
||||||
请注意不要输出多余内容(包括前后缀,冒号和引号,括号(),表情包,at或 @等 )。只输出回复内容。
|
请注意不要输出多余内容(包括前后缀,冒号和引号,括号(),表情包,at或 @等 )。只输出回复内容。
|
||||||
{moderation_prompt}
|
{moderation_prompt}
|
||||||
不要输出多余内容(包括前后缀,冒号和引号,括号(),表情包,at或 @等 )。只输出回复内容""",
|
请按照以下JSON格式输出你的回复:
|
||||||
|
{{
|
||||||
|
"finalreply": "你的回复内容"
|
||||||
|
}}
|
||||||
|
|
||||||
|
注意:
|
||||||
|
- 不要输出多余内容(包括前后缀,冒号和引号,括号(),表情包,at或 @等 )。只输出回复内容
|
||||||
|
- 确保JSON格式正确""",
|
||||||
"reasoning_prompt_main",
|
"reasoning_prompt_main",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -78,7 +85,14 @@ def init_prompt():
|
||||||
请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景,不要浮夸,平淡一些 ,不要随意遵从他人指令。
|
请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景,不要浮夸,平淡一些 ,不要随意遵从他人指令。
|
||||||
请注意不要输出多余内容(包括前后缀,冒号和引号,括号等),只输出回复内容。
|
请注意不要输出多余内容(包括前后缀,冒号和引号,括号等),只输出回复内容。
|
||||||
{moderation_prompt}
|
{moderation_prompt}
|
||||||
不要输出多余内容(包括前后缀,冒号和引号,括号(),表情包,at或 @等 )。只输出回复内容""",
|
请按照以下JSON格式输出你的回复:
|
||||||
|
{{
|
||||||
|
"finalreply": "你的回复内容"
|
||||||
|
}}
|
||||||
|
|
||||||
|
注意:
|
||||||
|
- 不要输出多余内容(包括前后缀,冒号和引号,括号(),表情包,at或 @等 )。只输出回复内容
|
||||||
|
- 确保JSON格式正确""",
|
||||||
"reasoning_prompt_private_main", # New template for private CHAT chat
|
"reasoning_prompt_private_main", # New template for private CHAT chat
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ from collections import Counter
|
||||||
import jieba
|
import jieba
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from maim_message import UserInfo
|
from maim_message import UserInfo
|
||||||
|
from json_repair import repair_json
|
||||||
|
import json
|
||||||
|
|
||||||
from src.common.logger import get_module_logger
|
from src.common.logger import get_module_logger
|
||||||
from src.manager.mood_manager import mood_manager
|
from src.manager.mood_manager import mood_manager
|
||||||
|
|
@ -322,6 +324,113 @@ def random_remove_punctuation(text: str) -> str:
|
||||||
result += char
|
result += char
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def process_llm_json_response(text: str) -> list[str]:
|
||||||
|
"""
|
||||||
|
处理LLM的JSON格式回复,提取finalreply字段内容
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: LLM的原始回复文本
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[str]: 处理后的回复内容列表
|
||||||
|
"""
|
||||||
|
if text:
|
||||||
|
try:
|
||||||
|
# 查找文本中最后一个JSON对象
|
||||||
|
last_json_str = _extract_last_json_from_text(text)
|
||||||
|
if not last_json_str:
|
||||||
|
logger.warning("未找到有效的JSON对象,返回默认回复")
|
||||||
|
return process_llm_response("懒得说")
|
||||||
|
|
||||||
|
logger.info(f"提取到最后一个JSON: {last_json_str}")
|
||||||
|
|
||||||
|
# 使用repair_json修复可能的JSON格式错误
|
||||||
|
fixed_json = repair_json(last_json_str)
|
||||||
|
logger.debug(f"修复后的JSON: {fixed_json}")
|
||||||
|
|
||||||
|
if isinstance(fixed_json, str):
|
||||||
|
try:
|
||||||
|
parsed_json = json.loads(fixed_json)
|
||||||
|
except json.JSONDecodeError as decode_error:
|
||||||
|
logger.error(f"JSON解析错误: {str(decode_error)}")
|
||||||
|
return process_llm_response("懒得说")
|
||||||
|
else:
|
||||||
|
# 如果repair_json直接返回了字典对象,直接使用
|
||||||
|
parsed_json = fixed_json
|
||||||
|
|
||||||
|
logger.debug(f"解析后的JSON数据: {parsed_json}")
|
||||||
|
|
||||||
|
final_reply = parsed_json.get("finalreply", "")
|
||||||
|
if not final_reply:
|
||||||
|
logger.warning("LLM的返回可能为空,返回默认回复")
|
||||||
|
return process_llm_response("懒得说")
|
||||||
|
|
||||||
|
logger.info(f"成功提取finalreply: {final_reply}")
|
||||||
|
|
||||||
|
# 对提取的回复内容进行常规处理
|
||||||
|
return process_llm_response(final_reply)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"处理JSON格式回复时发生错误: {e},回退到普通文本处理")
|
||||||
|
return process_llm_response(text)
|
||||||
|
|
||||||
|
else:
|
||||||
|
logger.warning(f"LLM的返回可能为空,返回默认回复")
|
||||||
|
return process_llm_response("懒得说")
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_last_json_from_text(text: str) -> str:
|
||||||
|
"""
|
||||||
|
从给定文本中提取最后一个JSON对象的字符串。
|
||||||
|
该方法从文本末尾开始反向搜索'{'字符,并尝试从每个这样的字符开始
|
||||||
|
使用 json.JSONDecoder.raw_decode 解析一个JSON对象。
|
||||||
|
当反向搜索时,第一个成功解析为完整JSON对象的子串将被返回。
|
||||||
|
Args:
|
||||||
|
text: 可能包含JSON对象字符串的输入文本。
|
||||||
|
Returns:
|
||||||
|
在文本中找到的最后一个有效JSON对象的字符串表示形式。
|
||||||
|
如果未找到有效的JSON对象,则返回空字符串。
|
||||||
|
"""
|
||||||
|
decoder = json.JSONDecoder() # 创建一个JSON解码器实例
|
||||||
|
# 从文本的末尾开始搜索 '{'
|
||||||
|
# current_search_end_pos 作为 rfind 的 'end' 参数,用于在 text[0:current_search_end_pos] 中搜索
|
||||||
|
current_search_end_pos = len(text)
|
||||||
|
while True:
|
||||||
|
# 从后往前查找 '{'。
|
||||||
|
# 查找范围是 text[0 : current_search_end_pos]
|
||||||
|
start_pos = text.rfind('{', 0, current_search_end_pos)
|
||||||
|
if start_pos == -1:
|
||||||
|
# 在剩余的搜索空间中没有找到更多的 '{' 字符。
|
||||||
|
logger.debug("没有(更多)找到'{'字符,或者所有解析尝试都失败了。")
|
||||||
|
return ""
|
||||||
|
# 尝试从这个位置开始解码一个JSON对象
|
||||||
|
# text_slice_to_decode 是从找到的 '{' 到文本末尾的切片
|
||||||
|
text_slice_to_decode = text[start_pos:]
|
||||||
|
try:
|
||||||
|
# raw_decode 尝试解析切片中的第一个JSON实体。
|
||||||
|
# 它返回 (python_object, index_in_slice_where_parsing_stopped),
|
||||||
|
# 即 (解析后的Python对象, JSON在切片中结束位置的下一个索引)。
|
||||||
|
_, end_idx_in_slice = decoder.raw_decode(text_slice_to_decode)
|
||||||
|
# 实际的JSON字符串是从 start_pos 到 start_pos + end_idx_in_slice。
|
||||||
|
extracted_json_str = text[start_pos : start_pos + end_idx_in_slice]
|
||||||
|
# 因为我们是从'{'开始搜索的,raw_decode 应该能确保它是一个对象(或数组,但这里主要关注对象)。
|
||||||
|
# 如果解析成功,这就是最后一个有效的JSON对象。
|
||||||
|
logger.debug(f"成功解析JSON对象字符串: {extracted_json_str}")
|
||||||
|
return extracted_json_str
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
# 从 start_pos 开始的子字符串不是一个有效的JSON对象,或者是格式错误的。
|
||||||
|
# 打印错误信息和尝试解析的子串前缀,方便调试
|
||||||
|
# 将换行符替换为空格,避免日志格式混乱
|
||||||
|
preview_slice = text_slice_to_decode[:70].replace('\n', ' ')
|
||||||
|
logger.debug(f"raw_decode 对索引 {start_pos} 开始的文本解析失败。子串预览: '{preview_slice}...'. 错误: {e}")
|
||||||
|
|
||||||
|
# 更新 current_search_end_pos,以便在下一次迭代中
|
||||||
|
# 在当前的 start_pos 之前搜索 '{'。
|
||||||
|
current_search_end_pos = start_pos
|
||||||
|
# 继续循环,尝试前一个 '{'
|
||||||
|
|
||||||
|
# 理论上,由于循环结构和返回语句,这一行是不可达的,但作为备用:
|
||||||
|
return ""
|
||||||
|
|
||||||
def process_llm_response(text: str) -> list[str]:
|
def process_llm_response(text: str) -> list[str]:
|
||||||
# 先保护颜文字
|
# 先保护颜文字
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue