better:重构no_reply,不再使用小模型,而是采用激活度决定是否结束等待
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -316,4 +316,6 @@ run_pet.bat
|
||||
!/plugins/hello_world_plugin
|
||||
!/plugins/take_picture_plugin
|
||||
|
||||
config.toml
|
||||
config.toml
|
||||
|
||||
interested_rates.txt
|
||||
@@ -618,3 +618,333 @@
|
||||
6.234776695167793
|
||||
6.327799950981746
|
||||
6.234224872543608
|
||||
0.12555594323694738
|
||||
4.691362169321614
|
||||
0.760960373429408
|
||||
0.03190953220831115
|
||||
0.21221850656149352
|
||||
0.21221850656149352
|
||||
0.21740663700282584
|
||||
0.2209989240456402
|
||||
0.20691340738722538
|
||||
6.01401596040071
|
||||
6.01401596040071
|
||||
6.012067868465945
|
||||
1.0339877364806525
|
||||
1.033607944262916
|
||||
0.02126634371149695
|
||||
6.013123465676255
|
||||
6.013123465676255
|
||||
0.014013152602464482
|
||||
0.03203964454588806
|
||||
0.014013152602464482
|
||||
0.03203964454588806
|
||||
0.03203964454588806
|
||||
1.7309854097635113
|
||||
0.03203964454588806
|
||||
1.121214185121496
|
||||
1.121214185121496
|
||||
6.013123465676255
|
||||
0.019318251776732617
|
||||
6.0176000459743335
|
||||
6.013123465676255
|
||||
6.012067868465945
|
||||
1.3442614225195928
|
||||
0.018026305204928966
|
||||
6.013123465676255
|
||||
6.01880222709907
|
||||
6.012067868465945
|
||||
0.21221850656149352
|
||||
6.021149770881184
|
||||
1.3442614225195928
|
||||
0.03203964454588806
|
||||
6.013123465676255
|
||||
0.020373848987042205
|
||||
0.21475589539598097
|
||||
0.016360696384577725
|
||||
0.02203945780739345
|
||||
6.001110655549735
|
||||
0.21129104527597597
|
||||
5.989741835616894
|
||||
0.21129104527597597
|
||||
0.022721392769155448
|
||||
5.987020629586234
|
||||
0.019318251776732617
|
||||
0.019318251776732617
|
||||
5.989741835616894
|
||||
0.022721392769155448
|
||||
0.21066124009593168
|
||||
1.4665149210247743
|
||||
0.03203964454588806
|
||||
0.5645996978699889
|
||||
0.20681943402193914
|
||||
0.014013152602464482
|
||||
0.5638265837740923
|
||||
0.02203945780739345
|
||||
0.019318251776732617
|
||||
1.0300657630123224
|
||||
5.982811300748257
|
||||
0.5638265837740923
|
||||
5.982811300748257
|
||||
1.3509125289080943
|
||||
0.022721392769155448
|
||||
5.977132539325442
|
||||
5.994180120681098
|
||||
0.03677746112588288
|
||||
5.990837605953186
|
||||
1.7039225527703115
|
||||
1.7053776018279698
|
||||
0.02388322700338219
|
||||
0.014013152602464482
|
||||
0.02126634371149695
|
||||
5.993867084697062
|
||||
0.016360696384577725
|
||||
5.994666941359478
|
||||
0.2131553677924724
|
||||
7.678798914186269
|
||||
6.1788740664574044
|
||||
0.02567894816131034
|
||||
5.994572796921638
|
||||
5.985158844530371
|
||||
5.988398883036939
|
||||
5.994180120681098
|
||||
5.994759579421516
|
||||
5.994940524002717
|
||||
1.13603122500849
|
||||
1.021266343711497
|
||||
0.020373848987042205
|
||||
5.985158844530371
|
||||
5.982038186652361
|
||||
0.014013152602464482
|
||||
0.016360696384577725
|
||||
0.016360696384577725
|
||||
0.02203945780739345
|
||||
5.984655069944246
|
||||
1.7046956668662079
|
||||
1.7046956668662079
|
||||
1.7030300580458566
|
||||
1.1226176950628484
|
||||
6.0881758047020735
|
||||
6.0881758047020735
|
||||
0.037161756536937846
|
||||
5.994077644459755
|
||||
0.037161756536937846
|
||||
0.3189385906841562
|
||||
0.02203945780739345
|
||||
5.980090094717597
|
||||
0.019318251776732617
|
||||
7.664694395711176
|
||||
0.014013152602464482
|
||||
5.986051339254826
|
||||
0.02126634371149695
|
||||
0.014013152602464482
|
||||
0.016360696384577725
|
||||
1.6990169054433921
|
||||
1.701974460835547
|
||||
0.21066124009593168
|
||||
0.3189385906841562
|
||||
7.666759456378876
|
||||
5.982038186652361
|
||||
1.0399358374132401
|
||||
5.994940524002717
|
||||
0.03927442624240585
|
||||
0.016360696384577725
|
||||
0.0233314043791971
|
||||
0.0233314043791971
|
||||
6.088857739663836
|
||||
6.088857739663836
|
||||
6.088857739663836
|
||||
0.014013152602464482
|
||||
1.5083907510110965
|
||||
0.02126634371149695
|
||||
0.014013152602464482
|
||||
2.819591566494889
|
||||
1.7046956668662079
|
||||
0.03203964454588806
|
||||
0.02388322700338219
|
||||
0.03203964454588806
|
||||
6.047071409613139
|
||||
0.014013152602464482
|
||||
0.016360696384577725
|
||||
6.053135130371711
|
||||
6.040993196342974
|
||||
0.21159916334682072
|
||||
1.0405293774464313
|
||||
0.03203964454588806
|
||||
6.224053874545717
|
||||
5.94049068378016
|
||||
5.944903288229973
|
||||
5.943107567072045
|
||||
1.6898299547044922
|
||||
0.21859874798662643
|
||||
5.948916440832438
|
||||
5.946568897050325
|
||||
0.020373848987042205
|
||||
0.12176885627431103
|
||||
0.12176885627431103
|
||||
0.036889887092514305
|
||||
0.022721392769155448
|
||||
5.941263797876056
|
||||
5.952632617808897
|
||||
8.287843209657629
|
||||
5.954808346153387
|
||||
5.941263797876056
|
||||
8.291482700011546
|
||||
0.016360696384577725
|
||||
5.95231958182486
|
||||
5.953985190645212
|
||||
0.014013152602464482
|
||||
0.3745255599226728
|
||||
0.21159916334682072
|
||||
0.03203964454588806
|
||||
5.9419457328378185
|
||||
5.94255574444786
|
||||
0.21159916334682072
|
||||
0.20910503565028
|
||||
5.935585036453241
|
||||
0.014013152602464482
|
||||
7.610509343830814
|
||||
0.014013152602464482
|
||||
1.7911055813632024
|
||||
0.21159916334682072
|
||||
0.025279496313961432
|
||||
0.014013152602464482
|
||||
0.31291086336433277
|
||||
0.014013152602464482
|
||||
0.035856515457896934
|
||||
5.941263797876056
|
||||
5.941263797876056
|
||||
5.941263797876056
|
||||
5.941263797876056
|
||||
0.014013152602464482
|
||||
1.0227213927691554
|
||||
0.014013152602464482
|
||||
0.21159916334682072
|
||||
0.3150010423159331
|
||||
5.94049068378016
|
||||
5.94361134165817
|
||||
0.2148392018533887
|
||||
1.0363045653081948
|
||||
1.2173498481213567
|
||||
1.216787293788153
|
||||
0.20910503565028
|
||||
0.21745608514527395
|
||||
5.944074769353784
|
||||
5.944074769353784
|
||||
0.014013152602464482
|
||||
5.9395981890557055
|
||||
0.02388322700338219
|
||||
0.31444921969174805
|
||||
5.943107567072045
|
||||
0.02969210076377482
|
||||
0.034919483934155664
|
||||
0.0233314043791971
|
||||
0.02126634371149695
|
||||
0.02126634371149695
|
||||
5.944074769353784
|
||||
1.446424442364109
|
||||
0.027047581355656602
|
||||
5.945627952207503
|
||||
5.9419457328378185
|
||||
1.1168632089473918
|
||||
1.0203738489870422
|
||||
5.935585036453241
|
||||
0.016360696384577725
|
||||
0.014013152602464482
|
||||
0.014013152602464482
|
||||
5.951988648592081
|
||||
5.945627952207503
|
||||
5.94255574444786
|
||||
10.502971083394339
|
||||
5.953393021130516
|
||||
0.21159916334682072
|
||||
5.94049068378016
|
||||
7.417010356982582
|
||||
0.018026305204928966
|
||||
0.0233314043791971
|
||||
0.3096075482130941
|
||||
0.035653345301003635
|
||||
0.13594529810112496
|
||||
0.02789637960584667
|
||||
0.12655512297267202
|
||||
0.03467987365713867
|
||||
0.20629406417255258
|
||||
0.31444921969174805
|
||||
0.014013152602464482
|
||||
5.94255574444786
|
||||
0.21159916334682072
|
||||
0.20910503565028
|
||||
0.020373848987042205
|
||||
0.016360696384577725
|
||||
0.02388322700338219
|
||||
0.02126634371149695
|
||||
0.20910503565028
|
||||
0.014013152602464482
|
||||
0.014013152602464482
|
||||
0.21394670712893396
|
||||
0.02203945780739345
|
||||
0.02388322700338219
|
||||
0.12578200887677551
|
||||
0.21159916334682072
|
||||
0.024387001589506692
|
||||
0.014013152602464482
|
||||
0.018026305204928966
|
||||
0.019318251776732617
|
||||
0.02388322700338219
|
||||
0.03150067377017187
|
||||
0.018026305204928966
|
||||
0.21159916334682072
|
||||
0.026734545371619928
|
||||
0.020373848987042205
|
||||
0.21159916334682072
|
||||
0.20629406417255258
|
||||
0.019318251776732617
|
||||
0.03434414162146728
|
||||
0.025279496313961432
|
||||
0.21159916334682072
|
||||
0.12383391694201118
|
||||
0.016360696384577725
|
||||
0.13019461332658888
|
||||
0.02388322700338219
|
||||
0.018026305204928966
|
||||
0.02126634371149695
|
||||
0.022721392769155448
|
||||
0.20629406417255258
|
||||
0.13019461332658888
|
||||
0.11982076433954669
|
||||
0.02388322700338219
|
||||
0.0233314043791971
|
||||
1.4942766388975819
|
||||
0.13019461332658888
|
||||
2.369300804550725
|
||||
0.0233314043791971
|
||||
5.938542591845396
|
||||
0.02126634371149695
|
||||
5.94049068378016
|
||||
5.938542591845396
|
||||
0.02388322700338219
|
||||
0.02388322700338219
|
||||
0.03264965615592971
|
||||
0.1322789410848081
|
||||
1.018026305204929
|
||||
0.13019461332658888
|
||||
0.03818183366431797
|
||||
0.21159916334682072
|
||||
0.03642645939690014
|
||||
0.0233314043791971
|
||||
0.0233314043791971
|
||||
0.02203945780739345
|
||||
5.9527333117444465
|
||||
0.21159916334682072
|
||||
0.21159916334682072
|
||||
5.897685246828215
|
||||
1.6780365136197586
|
||||
5.897685246828215
|
||||
5.9010883878206375
|
||||
0.13532111938738237
|
||||
1.2293321950146374
|
||||
0.2075746817768152
|
||||
0.018026305204928966
|
||||
0.21006880947335593
|
||||
0.016360696384577725
|
||||
0.041803481922888616
|
||||
|
||||
@@ -10,26 +10,27 @@ from src.plugin_system import BaseAction, ActionActivationType, ChatMode
|
||||
from src.common.logger import get_logger
|
||||
|
||||
# 导入API模块 - 标准Python包方式
|
||||
from src.plugin_system.apis import message_api, llm_api
|
||||
from src.plugin_system.apis import message_api
|
||||
from src.config.config import global_config
|
||||
from json_repair import repair_json
|
||||
from src.chat.message_receive.message import MessageRecv
|
||||
from src.chat.message_receive.chat_stream import get_chat_manager
|
||||
from src.chat.memory_system.Hippocampus import hippocampus_manager
|
||||
import math
|
||||
from src.chat.utils.utils import is_mentioned_bot_in_message
|
||||
|
||||
|
||||
logger = get_logger("core_actions")
|
||||
|
||||
|
||||
class NoReplyAction(BaseAction):
|
||||
"""不回复动作,使用智能判断机制决定何时结束等待
|
||||
"""不回复动作,根据新消息的兴趣值或数量决定何时结束等待.
|
||||
|
||||
新的等待逻辑:
|
||||
- 每0.2秒检查是否有新消息(提高响应性)
|
||||
- 如果累计消息数量达到阈值(默认20条),直接结束等待
|
||||
- 有新消息时进行LLM判断,但最快1秒一次(防止过于频繁)
|
||||
- 如果判断需要回复,则结束等待;否则继续等待
|
||||
- 达到最大超时时间后强制结束
|
||||
新的等待逻辑:
|
||||
1. 新消息累计兴趣值超过阈值 (默认10) 则结束等待
|
||||
2. 累计新消息数量达到随机阈值 (默认5-10条) 则结束等待
|
||||
"""
|
||||
|
||||
focus_activation_type = ActionActivationType.ALWAYS
|
||||
# focus_activation_type = ActionActivationType.RANDOM
|
||||
normal_activation_type = ActionActivationType.NEVER
|
||||
mode_enable = ChatMode.FOCUS
|
||||
parallel_action = False
|
||||
@@ -41,21 +42,11 @@ class NoReplyAction(BaseAction):
|
||||
# 连续no_reply计数器
|
||||
_consecutive_count = 0
|
||||
|
||||
# LLM判断的最小间隔时间
|
||||
_min_judge_interval = 1.0 # 最快1秒一次LLM判断
|
||||
|
||||
# 自动结束的消息数量阈值
|
||||
_auto_exit_message_count = 20 # 累计20条消息自动结束
|
||||
|
||||
# 最大等待超时时间
|
||||
_max_timeout = 600 # 1200秒
|
||||
|
||||
# 跳过LLM判断的配置
|
||||
_skip_judge_when_tired = True
|
||||
_skip_probability = 0.5
|
||||
|
||||
# 新增:回复频率退出专注模式的配置
|
||||
_frequency_check_window = 600 # 频率检查窗口时间(秒)
|
||||
# 新增:兴趣值退出阈值
|
||||
_interest_exit_threshold = 3.0
|
||||
# 新增:消息数量退出阈值
|
||||
_min_exit_message_count = 5
|
||||
_max_exit_message_count = 10
|
||||
|
||||
# 动作参数定义
|
||||
action_parameters = {"reason": "不回复的原因"}
|
||||
@@ -67,7 +58,7 @@ class NoReplyAction(BaseAction):
|
||||
associated_types = []
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
"""执行不回复动作,有新消息时进行判断,但最快1秒一次"""
|
||||
"""执行不回复动作"""
|
||||
import asyncio
|
||||
|
||||
try:
|
||||
@@ -77,30 +68,14 @@ class NoReplyAction(BaseAction):
|
||||
|
||||
reason = self.action_data.get("reason", "")
|
||||
start_time = time.time()
|
||||
last_judge_time = start_time # 上次进行LLM判断的时间
|
||||
min_judge_interval = self._min_judge_interval # 最小判断间隔,从配置获取
|
||||
check_interval = 0.2 # 检查新消息的间隔,设为0.2秒提高响应性
|
||||
check_interval = 1.0 # 每秒检查一次
|
||||
|
||||
# 累积判断历史
|
||||
judge_history = [] # 存储每次判断的结果和理由
|
||||
|
||||
# 获取no_reply开始时的上下文消息(10条),用于后续记录
|
||||
context_messages = message_api.get_messages_by_time_in_chat(
|
||||
chat_id=self.chat_id,
|
||||
start_time=start_time - 600, # 获取开始前10分钟内的消息
|
||||
end_time=start_time,
|
||||
limit=10,
|
||||
limit_mode="latest",
|
||||
# 随机生成本次等待需要的新消息数量阈值
|
||||
exit_message_count_threshold = random.randint(self._min_exit_message_count, self._max_exit_message_count)
|
||||
logger.info(
|
||||
f"{self.log_prefix} 本次no_reply需要 {exit_message_count_threshold} 条新消息或累计兴趣值超过 {self._interest_exit_threshold} 才能打断"
|
||||
)
|
||||
|
||||
# 构建上下文字符串
|
||||
context_str = ""
|
||||
if context_messages:
|
||||
context_str = message_api.build_readable_messages(
|
||||
messages=context_messages, timestamp_mode="normal_no_YMD", truncate=False, show_actions=True
|
||||
)
|
||||
context_str = f"当时选择no_reply前的聊天上下文:\n{context_str}\n"
|
||||
|
||||
logger.info(f"{self.log_prefix} 选择不回复(第{count}次),开始摸鱼,原因: {reason}")
|
||||
|
||||
# 进入等待状态
|
||||
@@ -108,196 +83,52 @@ class NoReplyAction(BaseAction):
|
||||
current_time = time.time()
|
||||
elapsed_time = current_time - start_time
|
||||
|
||||
if global_config.chat.chat_mode == "auto" and self.is_group:
|
||||
# 检查是否超时
|
||||
if elapsed_time >= self._max_timeout or self._check_no_activity_and_exit_focus(current_time):
|
||||
logger.info(
|
||||
f"{self.log_prefix} 等待时间过久({self._max_timeout}秒)或过去10分钟完全没有发言,退出专注模式"
|
||||
)
|
||||
# 标记退出专注模式
|
||||
self.action_data["_system_command"] = "stop_focus_chat"
|
||||
exit_reason = f"{global_config.bot.nickname}(你)等待了{self._max_timeout}秒,或完全没有说话,感觉群里没有新内容,决定退出专注模式,稍作休息"
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=True,
|
||||
action_prompt_display=exit_reason,
|
||||
action_done=True,
|
||||
)
|
||||
return True, exit_reason
|
||||
|
||||
# 检查是否有新消息
|
||||
new_message_count = message_api.count_new_messages(
|
||||
# 1. 检查新消息
|
||||
recent_messages_dict = message_api.get_messages_by_time_in_chat(
|
||||
chat_id=self.chat_id, start_time=start_time, end_time=current_time
|
||||
)
|
||||
new_message_count = len(recent_messages_dict)
|
||||
|
||||
# 如果累计消息数量达到阈值,直接结束等待
|
||||
if new_message_count >= self._auto_exit_message_count:
|
||||
logger.info(f"{self.log_prefix} 累计消息数量达到{new_message_count}条,直接结束等待")
|
||||
# 2. 检查消息数量是否达到阈值
|
||||
if new_message_count >= exit_message_count_threshold:
|
||||
logger.info(f"{self.log_prefix} 累计消息数量达到{new_message_count}条(>{exit_message_count_threshold}),结束等待")
|
||||
exit_reason = f"{global_config.bot.nickname}(你)看到了{new_message_count}条新消息,可以考虑一下是否要进行回复"
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=True,
|
||||
action_build_into_prompt=False,
|
||||
action_prompt_display=exit_reason,
|
||||
action_done=True,
|
||||
)
|
||||
return True, f"累计消息数量达到{new_message_count}条,直接结束等待 (等待时间: {elapsed_time:.1f}秒)"
|
||||
return True, f"累计消息数量达到{new_message_count}条,结束等待 (等待时间: {elapsed_time:.1f}秒)"
|
||||
|
||||
# 判定条件:累计3条消息或等待超过5秒且有新消息
|
||||
time_since_last_judge = current_time - last_judge_time
|
||||
should_judge, trigger_reason = self._should_trigger_judge(new_message_count, time_since_last_judge)
|
||||
|
||||
if should_judge and time_since_last_judge >= min_judge_interval:
|
||||
logger.info(f"{self.log_prefix} 触发判定({trigger_reason}),进行智能判断...")
|
||||
|
||||
# 获取最近的消息内容用于判断
|
||||
recent_messages = message_api.get_messages_by_time_in_chat(
|
||||
chat_id=self.chat_id,
|
||||
start_time=start_time,
|
||||
end_time=current_time,
|
||||
)
|
||||
|
||||
if recent_messages:
|
||||
# 使用message_api构建可读的消息字符串
|
||||
messages_text = message_api.build_readable_messages(
|
||||
messages=recent_messages,
|
||||
timestamp_mode="normal_no_YMD",
|
||||
truncate=False,
|
||||
show_actions=False,
|
||||
# 3. 检查累计兴趣值
|
||||
if new_message_count > 0:
|
||||
accumulated_interest = await self._calculate_accumulated_interest(recent_messages_dict)
|
||||
logger.info(f"{self.log_prefix} 当前累计兴趣值: {accumulated_interest:.2f}")
|
||||
if accumulated_interest >= self._interest_exit_threshold:
|
||||
logger.info(
|
||||
f"{self.log_prefix} 累计兴趣值达到{accumulated_interest:.2f}(>{self._interest_exit_threshold}),结束等待"
|
||||
)
|
||||
|
||||
# 获取身份信息
|
||||
bot_name = global_config.bot.nickname
|
||||
bot_nickname = ""
|
||||
if global_config.bot.alias_names:
|
||||
bot_nickname = f",也有人叫你{','.join(global_config.bot.alias_names)}"
|
||||
bot_core_personality = global_config.personality.personality_core
|
||||
identity_block = f"你的名字是{bot_name}{bot_nickname},你{bot_core_personality}"
|
||||
|
||||
# 构建判断历史字符串(最多显示3条)
|
||||
history_block = ""
|
||||
if judge_history:
|
||||
history_block = "之前的判断历史:\n"
|
||||
# 只取最近的3条历史记录
|
||||
recent_history = judge_history[-3:] if len(judge_history) > 3 else judge_history
|
||||
for i, (timestamp, judge_result, reason) in enumerate(recent_history, 1):
|
||||
elapsed_seconds = int(timestamp - start_time)
|
||||
history_block += f"{i}. 等待{elapsed_seconds}秒时判断:{judge_result},理由:{reason}\n"
|
||||
history_block += "\n"
|
||||
|
||||
# 检查过去10分钟的发言频率
|
||||
frequency_block, should_skip_llm_judge = self._get_fatigue_status(current_time)
|
||||
|
||||
# 如果决定跳过LLM判断,直接更新时间并继续等待
|
||||
if should_skip_llm_judge:
|
||||
logger.info(f"{self.log_prefix} 疲劳,继续等待。")
|
||||
last_judge_time = time.time() # 更新判断时间,避免立即重新判断
|
||||
start_time = current_time # 更新消息检查的起始时间,以避免重复判断
|
||||
continue # 跳过本次LLM判断,继续循环等待
|
||||
|
||||
# 构建判断上下文
|
||||
chat_context = "QQ群" if self.is_group else "私聊"
|
||||
judge_prompt = f"""
|
||||
{identity_block}
|
||||
|
||||
你现在正在{chat_context}参与聊天,以下是聊天内容:
|
||||
{context_str}
|
||||
在以上的聊天中,你选择了暂时不回复,现在,你看到了新的聊天消息如下:
|
||||
{messages_text}
|
||||
|
||||
{history_block}
|
||||
请注意:{frequency_block}
|
||||
请你判断,是否要结束不回复的状态,重新加入聊天讨论。
|
||||
|
||||
判断标准:
|
||||
1. 如果有人直接@你、提到你的名字或明确向你询问,应该回复
|
||||
2. 如果话题发生重要变化,需要你参与讨论,应该回复
|
||||
3. 如果只是普通闲聊、重复内容或与你无关的讨论,不需要回复
|
||||
4. 如果消息内容过于简单(如单纯的表情、"哈哈"等),不需要回复
|
||||
5. 参考之前的判断历史,如果情况有明显变化或持续等待时间过长,考虑调整判断
|
||||
|
||||
请用JSON格式回复你的判断,严格按照以下格式:
|
||||
{{
|
||||
"should_reply": true/false,
|
||||
"reason": "详细说明你的判断理由"
|
||||
}}
|
||||
"""
|
||||
|
||||
try:
|
||||
# 获取可用的模型配置
|
||||
available_models = llm_api.get_available_models()
|
||||
|
||||
# 使用 utils_small 模型
|
||||
small_model = getattr(available_models, "utils_small", None)
|
||||
|
||||
logger.debug(judge_prompt)
|
||||
|
||||
if small_model:
|
||||
# 使用小模型进行判断
|
||||
success, response, reasoning, model_name = await llm_api.generate_with_model(
|
||||
prompt=judge_prompt,
|
||||
model_config=small_model,
|
||||
request_type="plugin.no_reply_judge",
|
||||
temperature=0.7, # 进一步降低温度,提高JSON输出的一致性和准确性
|
||||
)
|
||||
|
||||
# 更新上次判断时间
|
||||
last_judge_time = time.time()
|
||||
|
||||
if success and response:
|
||||
response = response.strip()
|
||||
logger.debug(f"{self.log_prefix} 模型({model_name})原始JSON响应: {response}")
|
||||
|
||||
# 解析LLM的JSON响应,提取判断结果和理由
|
||||
judge_result, reason = self._parse_llm_judge_response(response)
|
||||
|
||||
if judge_result:
|
||||
logger.info(f"{self.log_prefix} 决定继续参与讨论,结束等待,原因: {reason}")
|
||||
else:
|
||||
logger.info(f"{self.log_prefix} 决定不参与讨论,继续等待,原因: {reason}")
|
||||
|
||||
# 将判断结果保存到历史中
|
||||
judge_history.append((current_time, judge_result, reason))
|
||||
|
||||
if judge_result == "需要回复":
|
||||
# logger.info(f"{self.log_prefix} 模型判断需要回复,结束等待")
|
||||
|
||||
full_prompt = f"{global_config.bot.nickname}(你)的想法是:{reason}"
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=True,
|
||||
action_prompt_display=full_prompt,
|
||||
action_done=True,
|
||||
)
|
||||
return True, f"检测到需要回复的消息,结束等待 (等待时间: {elapsed_time:.1f}秒)"
|
||||
else:
|
||||
logger.info(f"{self.log_prefix} 模型判断不需要回复,理由: {reason},继续等待")
|
||||
# 更新开始时间,避免重复判断同样的消息
|
||||
start_time = current_time
|
||||
else:
|
||||
logger.warning(f"{self.log_prefix} 模型判断失败,继续等待")
|
||||
else:
|
||||
logger.warning(f"{self.log_prefix} 未找到可用的模型配置,继续等待")
|
||||
last_judge_time = time.time() # 即使失败也更新时间,避免频繁重试
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} 模型判断异常: {e},继续等待")
|
||||
last_judge_time = time.time() # 异常时也更新时间,避免频繁重试
|
||||
exit_reason = f"{global_config.bot.nickname}(你)感觉到了大家浓厚的兴趣(兴趣值{accumulated_interest:.1f}),决定重新加入讨论"
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=False,
|
||||
action_prompt_display=exit_reason,
|
||||
action_done=True,
|
||||
)
|
||||
return True, f"累计兴趣值达到{accumulated_interest:.2f},结束等待 (等待时间: {elapsed_time:.1f}秒)"
|
||||
|
||||
# 每10秒输出一次等待状态
|
||||
if elapsed_time < 60:
|
||||
if int(elapsed_time) % 10 == 0 and int(elapsed_time) > 0:
|
||||
logger.debug(f"{self.log_prefix} 已等待{elapsed_time:.0f}秒,等待新消息...")
|
||||
await asyncio.sleep(1)
|
||||
else:
|
||||
if int(elapsed_time) % 180 == 0 and int(elapsed_time) > 0:
|
||||
logger.info(f"{self.log_prefix} 已等待{elapsed_time / 60:.0f}分钟,等待新消息...")
|
||||
await asyncio.sleep(1)
|
||||
if int(elapsed_time) > 0 and int(elapsed_time) % 10 == 0:
|
||||
logger.debug(f"{self.log_prefix} 已等待{elapsed_time:.0f}秒,累计{new_message_count}条消息,继续等待...")
|
||||
# 使用 asyncio.sleep(1) 来避免在同一秒内重复打印日志
|
||||
await asyncio.sleep(1)
|
||||
|
||||
# 短暂等待后继续检查
|
||||
await asyncio.sleep(check_interval)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} 不回复动作执行失败: {e}")
|
||||
# 即使执行失败也要记录
|
||||
exit_reason = f"执行异常: {str(e)}"
|
||||
full_prompt = f"{context_str}{exit_reason},你思考是否要进行回复"
|
||||
full_prompt = f"no_reply执行异常: {exit_reason},你思考是否要进行回复"
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=True,
|
||||
action_prompt_display=full_prompt,
|
||||
@@ -305,214 +136,53 @@ class NoReplyAction(BaseAction):
|
||||
)
|
||||
return False, f"不回复动作执行失败: {e}"
|
||||
|
||||
def _should_trigger_judge(self, new_message_count: int, time_since_last_judge: float) -> Tuple[bool, str]:
|
||||
"""判断是否应该触发智能判断,并返回触发原因。
|
||||
async def _calculate_accumulated_interest(self, messages_dicts: list[dict]) -> float:
|
||||
"""将所有新消息文本合并,然后一次性计算兴趣值"""
|
||||
if not messages_dicts:
|
||||
return 0.0
|
||||
|
||||
Args:
|
||||
new_message_count: 新消息的数量。
|
||||
time_since_last_judge: 距离上次判断的时间。
|
||||
|
||||
Returns:
|
||||
一个元组 (should_judge, reason)。
|
||||
- should_judge: 一个布尔值,指示是否应该触发判断。
|
||||
- reason: 触发判断的原因字符串。
|
||||
"""
|
||||
# 判定条件:累计3条消息或等待超过15秒且有新消息
|
||||
should_judge_flag = new_message_count >= 3 or (new_message_count > 0 and time_since_last_judge >= 15.0)
|
||||
|
||||
if not should_judge_flag:
|
||||
return False, ""
|
||||
combined_text_parts = []
|
||||
is_any_mentioned = False
|
||||
|
||||
# 判断触发原因
|
||||
if new_message_count >= 3:
|
||||
return True, f"累计{new_message_count}条消息"
|
||||
elif new_message_count > 0 and time_since_last_judge >= 15.0:
|
||||
return True, f"等待{time_since_last_judge:.1f}秒且有新消息"
|
||||
|
||||
return False, ""
|
||||
|
||||
def _get_fatigue_status(self, current_time: float) -> Tuple[str, bool]:
|
||||
"""
|
||||
根据最近的发言频率生成疲劳提示,并决定是否跳过判断。
|
||||
|
||||
Args:
|
||||
current_time: 当前时间戳。
|
||||
|
||||
Returns:
|
||||
一个元组 (frequency_block, should_skip_judge)。
|
||||
- frequency_block: 疲劳度相关的提示字符串。
|
||||
- should_skip_judge: 是否应该跳过LLM判断的布尔值。
|
||||
"""
|
||||
try:
|
||||
# 获取过去10分钟的所有消息
|
||||
past_10min_time = current_time - 600 # 10分钟前
|
||||
all_messages_10min = message_api.get_messages_by_time_in_chat(
|
||||
chat_id=self.chat_id,
|
||||
start_time=past_10min_time,
|
||||
end_time=current_time,
|
||||
)
|
||||
|
||||
# 手动过滤bot自己的消息
|
||||
bot_message_count = 0
|
||||
if all_messages_10min:
|
||||
user_id = global_config.bot.qq_account
|
||||
for message in all_messages_10min:
|
||||
sender_id = message.get("user_id", "")
|
||||
if sender_id == user_id:
|
||||
bot_message_count += 1
|
||||
|
||||
talk_frequency_threshold = global_config.chat.get_current_talk_frequency(self.chat_id) * 10
|
||||
|
||||
if bot_message_count > talk_frequency_threshold:
|
||||
over_count = bot_message_count - talk_frequency_threshold
|
||||
skip_probability = 0
|
||||
frequency_block = ""
|
||||
|
||||
if over_count <= 3:
|
||||
frequency_block = "你感觉稍微有些累,回复的有点多了。\n"
|
||||
elif over_count <= 5:
|
||||
frequency_block = "你今天说话比较多,感觉有点疲惫,想要稍微休息一下。\n"
|
||||
elif over_count <= 8:
|
||||
frequency_block = "你发现自己说话太多了,感觉很累,想要安静一会儿,除非有重要的事情否则不想回复。\n"
|
||||
skip_probability = self._skip_probability
|
||||
else:
|
||||
frequency_block = "你感觉非常累,想要安静一会儿。\n"
|
||||
skip_probability = 1
|
||||
|
||||
should_skip_judge = self._skip_judge_when_tired and random.random() < skip_probability
|
||||
|
||||
if should_skip_judge:
|
||||
logger.info(
|
||||
f"{self.log_prefix} 发言过多(超过{over_count}条),随机决定跳过此次LLM判断(概率{skip_probability * 100:.0f}%)"
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"{self.log_prefix} 过去10分钟发言{bot_message_count}条,超过阈值{talk_frequency_threshold},添加疲惫提示"
|
||||
)
|
||||
return frequency_block, should_skip_judge
|
||||
else:
|
||||
# 回复次数少时的正向提示
|
||||
under_count = talk_frequency_threshold - bot_message_count
|
||||
frequency_block = ""
|
||||
if under_count >= talk_frequency_threshold * 0.8:
|
||||
frequency_block = "你感觉精力充沛,状态很好,积极参与聊天。\n"
|
||||
elif under_count >= talk_frequency_threshold * 0.5:
|
||||
frequency_block = "你感觉状态不错。\n"
|
||||
|
||||
logger.info(
|
||||
f"{self.log_prefix} 过去10分钟发言{bot_message_count}条,未超过阈值{talk_frequency_threshold},添加正向提示"
|
||||
)
|
||||
return frequency_block, False
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"{self.log_prefix} 检查发言频率时出错: {e}")
|
||||
return "", False
|
||||
|
||||
def _check_no_activity_and_exit_focus(self, current_time: float) -> bool:
|
||||
"""检查过去10分钟是否完全没有发言,决定是否退出专注模式
|
||||
|
||||
Args:
|
||||
current_time: 当前时间戳
|
||||
|
||||
Returns:
|
||||
bool: 是否应该退出专注模式
|
||||
"""
|
||||
try:
|
||||
# 只在auto模式下进行检查
|
||||
if global_config.chat.chat_mode != "auto":
|
||||
return False
|
||||
|
||||
# 获取过去10分钟的所有消息
|
||||
past_10min_time = current_time - 600 # 10分钟前
|
||||
all_messages = message_api.get_messages_by_time_in_chat(
|
||||
chat_id=self.chat_id,
|
||||
start_time=past_10min_time,
|
||||
end_time=current_time,
|
||||
)
|
||||
|
||||
if not all_messages:
|
||||
# 如果完全没有消息,也不需要退出专注模式
|
||||
return False
|
||||
|
||||
# 统计bot自己的回复数量
|
||||
bot_message_count = 0
|
||||
user_id = global_config.bot.qq_account
|
||||
|
||||
for message in all_messages:
|
||||
sender_id = message.get("user_id", "")
|
||||
if sender_id == user_id:
|
||||
bot_message_count += 1
|
||||
|
||||
# 如果过去10分钟bot一条消息也没有发送,退出专注模式
|
||||
if bot_message_count == 0:
|
||||
logger.info(f"{self.log_prefix} 过去10分钟bot完全没有发言,准备退出专注模式")
|
||||
return True
|
||||
else:
|
||||
logger.debug(f"{self.log_prefix} 过去10分钟bot发言{bot_message_count}条,继续保持专注模式")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} 检查无活动状态时出错: {e}")
|
||||
return False
|
||||
|
||||
def _parse_llm_judge_response(self, response: str) -> tuple[str, str]:
|
||||
"""解析LLM判断响应,使用JSON格式提取判断结果和理由
|
||||
|
||||
Args:
|
||||
response: LLM的原始JSON响应
|
||||
|
||||
Returns:
|
||||
tuple: (判断结果, 理由)
|
||||
"""
|
||||
try:
|
||||
# 使用repair_json修复可能有问题的JSON格式
|
||||
fixed_json_string = repair_json(response)
|
||||
logger.debug(f"{self.log_prefix} repair_json修复后的响应: {fixed_json_string}")
|
||||
|
||||
# 如果repair_json返回的是字符串,需要解析为Python对象
|
||||
if isinstance(fixed_json_string, str):
|
||||
result_json = json.loads(fixed_json_string)
|
||||
else:
|
||||
# 如果repair_json直接返回了字典对象,直接使用
|
||||
result_json = fixed_json_string
|
||||
|
||||
# 从JSON中提取判断结果和理由
|
||||
should_reply = result_json.get("should_reply", False)
|
||||
reason = result_json.get("reason", "无法获取判断理由")
|
||||
|
||||
# 转换布尔值为中文字符串
|
||||
judge_result = "需要回复" if should_reply else "不需要回复"
|
||||
|
||||
logger.debug(f"{self.log_prefix} JSON解析成功 - 判断: {judge_result}, 理由: {reason}")
|
||||
return judge_result, reason
|
||||
|
||||
except (json.JSONDecodeError, KeyError, TypeError) as e:
|
||||
logger.warning(f"{self.log_prefix} JSON解析失败,尝试文本解析: {e}")
|
||||
|
||||
# 如果JSON解析失败,回退到简单的关键词匹配
|
||||
for msg_dict in messages_dicts:
|
||||
try:
|
||||
response_lower = response.lower()
|
||||
text = msg_dict.get("processed_plain_text", "")
|
||||
if text:
|
||||
combined_text_parts.append(text)
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} 处理单条消息以计算兴趣值时出错: {e}")
|
||||
|
||||
if "true" in response_lower or "需要回复" in response:
|
||||
judge_result = "需要回复"
|
||||
reason = "从响应文本中检测到需要回复的指示"
|
||||
elif "false" in response_lower or "不需要回复" in response:
|
||||
judge_result = "不需要回复"
|
||||
reason = "从响应文本中检测到不需要回复的指示"
|
||||
else:
|
||||
judge_result = "不需要回复" # 默认值
|
||||
reason = f"无法解析响应格式,使用默认判断。原始响应: {response[:100]}..."
|
||||
full_text = " ".join(combined_text_parts).strip()
|
||||
if not full_text:
|
||||
return 0.0
|
||||
|
||||
logger.debug(f"{self.log_prefix} 文本解析结果 - 判断: {judge_result}, 理由: {reason}")
|
||||
return judge_result, reason
|
||||
# --- 使用合并后的文本计算兴趣值 ---
|
||||
|
||||
if global_config.bot.nickname in full_text:
|
||||
is_any_mentioned = True
|
||||
|
||||
except Exception as fallback_e:
|
||||
logger.error(f"{self.log_prefix} 文本解析也失败: {fallback_e}")
|
||||
return "不需要回复", f"解析异常: {str(e)}, 回退解析也失败: {str(fallback_e)}"
|
||||
interested_rate = 0.0
|
||||
if global_config.memory.enable_memory:
|
||||
try:
|
||||
interested_rate = await hippocampus_manager.get_activate_from_text(
|
||||
full_text,
|
||||
fast_retrieval=True,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} 记忆激活计算失败: {e}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} 解析LLM响应时出错: {e}")
|
||||
return "不需要回复", f"解析异常: {str(e)}"
|
||||
text_len = len(full_text)
|
||||
# 根据文本长度调整兴趣度
|
||||
base_interest = 0.01 + (0.05 - 0.01) * (math.log10(text_len + 1) / math.log10(1000 + 1))
|
||||
base_interest = min(max(base_interest, 0.01), 0.05)
|
||||
interested_rate += base_interest
|
||||
|
||||
if is_any_mentioned:
|
||||
interested_rate += 1
|
||||
|
||||
return interested_rate
|
||||
|
||||
@classmethod
|
||||
def reset_consecutive_count(cls):
|
||||
|
||||
Reference in New Issue
Block a user