Merge branch 'dev' of https://github.com/A0000Xz/MaiBot into dev
This commit is contained in:
@@ -4,4 +4,6 @@ __pycache__
|
||||
*.pyd
|
||||
.DS_Store
|
||||
mongodb
|
||||
napcat
|
||||
napcat
|
||||
docs/
|
||||
.github/
|
||||
90
.github/workflows/docker-image.yml
vendored
90
.github/workflows/docker-image.yml
vendored
@@ -12,18 +12,20 @@ on:
|
||||
- "*.*.*"
|
||||
- "*.*.*-*"
|
||||
|
||||
# Workflow's jobs
|
||||
jobs:
|
||||
build-amd64:
|
||||
name: Build AMD64 Image
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
outputs:
|
||||
digest: ${{ steps.build.outputs.digest }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
- name: Check out git repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# Clone required dependencies
|
||||
- name: Clone maim_message
|
||||
run: git clone https://github.com/MaiM-with-u/maim_message maim_message
|
||||
|
||||
@@ -35,106 +37,93 @@ jobs:
|
||||
with:
|
||||
buildkitd-flags: --debug
|
||||
|
||||
- name: Login to Docker Hub
|
||||
# Log in docker hub
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
# Generate metadata for Docker images
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ secrets.DOCKERHUB_USERNAME }}/maibot
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=tag
|
||||
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
|
||||
- name: Build and Push AMD64 Docker Image
|
||||
# Build and push AMD64 image by digest
|
||||
- name: Build and push AMD64
|
||||
id: build
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64
|
||||
tags: ${{ secrets.DOCKERHUB_USERNAME }}/maibot:amd64-${{ github.sha }}
|
||||
push: true
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
file: ./Dockerfile
|
||||
cache-from: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maibot:amd64-buildcache
|
||||
cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maibot:amd64-buildcache,mode=max
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
provenance: true
|
||||
sbom: true
|
||||
outputs: type=image,name=${{ secrets.DOCKERHUB_USERNAME }}/maibot,push-by-digest=true,name-canonical=true,push=true
|
||||
build-args: |
|
||||
BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
VCS_REF=${{ github.sha }}
|
||||
outputs: type=image,push=true
|
||||
|
||||
build-arm64:
|
||||
name: Build ARM64 Image
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
outputs:
|
||||
digest: ${{ steps.build.outputs.digest }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
- name: Check out git repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
# Clone required dependencies
|
||||
- name: Clone maim_message
|
||||
run: git clone https://github.com/MaiM-with-u/maim_message maim_message
|
||||
|
||||
- name: Clone lpmm
|
||||
run: git clone https://github.com/MaiM-with-u/MaiMBot-LPMM.git MaiMBot-LPMM
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
platforms: arm64
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
buildkitd-flags: --debug
|
||||
|
||||
- name: Login to Docker Hub
|
||||
# Log in docker hub
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
# Generate metadata for Docker images
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ secrets.DOCKERHUB_USERNAME }}/maibot
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=tag
|
||||
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
|
||||
- name: Build and Push ARM64 Docker Image
|
||||
# Build and push ARM64 image by digest
|
||||
- name: Build and push ARM64
|
||||
id: build
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/arm64/v8
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
file: ./Dockerfile
|
||||
platforms: linux/arm64
|
||||
tags: ${{ secrets.DOCKERHUB_USERNAME }}/maibot:arm64-${{ github.sha }}
|
||||
push: true
|
||||
cache-from: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maibot:arm64-buildcache
|
||||
cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maibot:arm64-buildcache,mode=max
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
provenance: true
|
||||
sbom: true
|
||||
outputs: type=image,name=${{ secrets.DOCKERHUB_USERNAME }}/maibot,push-by-digest=true,name-canonical=true,push=true
|
||||
build-args: |
|
||||
BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
VCS_REF=${{ github.sha }}
|
||||
outputs: type=image,push=true
|
||||
|
||||
create-manifest:
|
||||
name: Create Multi-Arch Manifest
|
||||
@@ -143,12 +132,17 @@ jobs:
|
||||
- build-amd64
|
||||
- build-arm64
|
||||
steps:
|
||||
- name: Login to Docker Hub
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# Log in docker hub
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
|
||||
# Generate metadata for Docker images
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
@@ -161,7 +155,7 @@ jobs:
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
type=sha,prefix=${{ github.ref_name }}-,enable=${{ github.ref_type == 'branch' }}
|
||||
|
||||
- name: Create and Push Manifest
|
||||
run: |
|
||||
@@ -169,6 +163,6 @@ jobs:
|
||||
for tag in $(echo "${{ steps.meta.outputs.tags }}" | tr '\n' ' '); do
|
||||
echo "Creating manifest for $tag"
|
||||
docker buildx imagetools create -t $tag \
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/maibot:amd64-${{ github.sha }} \
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/maibot:arm64-${{ github.sha }}
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/maibot@${{ needs.build-amd64.outputs.digest }} \
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/maibot@${{ needs.build-arm64.outputs.digest }}
|
||||
done
|
||||
@@ -17,7 +17,6 @@ services:
|
||||
restart: always
|
||||
networks:
|
||||
- maim_bot
|
||||
|
||||
core:
|
||||
container_name: maim-bot-core
|
||||
#### prod ####
|
||||
@@ -40,7 +39,6 @@ services:
|
||||
restart: always
|
||||
networks:
|
||||
- maim_bot
|
||||
|
||||
napcat:
|
||||
environment:
|
||||
- NAPCAT_UID=1000
|
||||
@@ -57,8 +55,8 @@ services:
|
||||
image: mlikiowa/napcat-docker:latest
|
||||
networks:
|
||||
- maim_bot
|
||||
|
||||
sqlite-web:
|
||||
# 注意:coleifer/sqlite-web 镜像不支持arm64
|
||||
image: coleifer/sqlite-web
|
||||
container_name: sqlite-web
|
||||
restart: always
|
||||
@@ -70,7 +68,19 @@ services:
|
||||
- SQLITE_DATABASE=MaiMBot/MaiBot.db # 你的数据库文件
|
||||
networks:
|
||||
- maim_bot
|
||||
|
||||
|
||||
# chat2db占用相对较高但是功能强大
|
||||
# 内存占用约600m,内存充足推荐选此
|
||||
# chat2db:
|
||||
# image: chat2db/chat2db:latest
|
||||
# container_name: maim-bot-chat2db
|
||||
# restart: always
|
||||
# ports:
|
||||
# - "10824:10824"
|
||||
# volumes:
|
||||
# - ./data/MaiMBot:/data/MaiMBot
|
||||
# networks:
|
||||
# - maim_bot
|
||||
networks:
|
||||
maim_bot:
|
||||
driver: bridge
|
||||
|
||||
@@ -324,8 +324,6 @@ async def clear_temp_emoji() -> None:
|
||||
os.remove(file_path)
|
||||
logger.debug(f"[清理] 删除: {filename}")
|
||||
|
||||
logger.info("[清理] 完成")
|
||||
|
||||
|
||||
async def clean_unused_emojis(emoji_dir: str, emoji_objects: List["MaiEmoji"], removed_count: int) -> int:
|
||||
"""清理指定目录中未被 emoji_objects 追踪的表情包文件"""
|
||||
@@ -590,7 +588,7 @@ class EmojiManager:
|
||||
"""定期检查表情包完整性和数量"""
|
||||
await self.get_all_emoji_from_db()
|
||||
while True:
|
||||
logger.info("[扫描] 开始检查表情包完整性...")
|
||||
# logger.info("[扫描] 开始检查表情包完整性...")
|
||||
await self.check_emoji_file_integrity()
|
||||
await clear_temp_emoji()
|
||||
logger.info("[扫描] 开始扫描新表情包...")
|
||||
|
||||
@@ -30,7 +30,7 @@ class Heartflow:
|
||||
# 注册子心流
|
||||
self.subheartflows[subheartflow_id] = new_subflow
|
||||
heartflow_name = get_chat_manager().get_stream_name(subheartflow_id) or subheartflow_id
|
||||
logger.info(f"[{heartflow_name}] 开始接收消息")
|
||||
logger.debug(f"[{heartflow_name}] 开始接收消息")
|
||||
|
||||
return new_subflow
|
||||
except Exception as e:
|
||||
|
||||
@@ -119,15 +119,13 @@ class HeartFCMessageReceiver:
|
||||
# current_time = time.strftime("%H:%M:%S", time.localtime(message.message_info.time))
|
||||
current_talk_frequency = global_config.chat.get_current_talk_frequency(chat.stream_id)
|
||||
|
||||
# 如果消息中包含图片标识,则日志展示为图片
|
||||
# 如果消息中包含图片标识,则将 [picid:...] 替换为 [图片]
|
||||
picid_pattern = r"\[picid:([^\]]+)\]"
|
||||
processed_plain_text = re.sub(picid_pattern, "[图片]", message.processed_plain_text)
|
||||
|
||||
picid_match = re.search(r"\[picid:([^\]]+)\]", message.processed_plain_text)
|
||||
if picid_match:
|
||||
logger.info(f"[{mes_name}]{userinfo.user_nickname}: [图片] [当前回复频率: {current_talk_frequency}]")
|
||||
else:
|
||||
logger.info(
|
||||
f"[{mes_name}]{userinfo.user_nickname}:{message.processed_plain_text}[当前回复频率: {current_talk_frequency}]"
|
||||
)
|
||||
logger.info(f"[{mes_name}]{userinfo.user_nickname}:{processed_plain_text}")
|
||||
|
||||
logger.debug(f"[{mes_name}][当前时段回复频率: {current_talk_frequency}]")
|
||||
|
||||
# 8. 关系处理
|
||||
if global_config.relationship.enable_relationship:
|
||||
|
||||
@@ -119,7 +119,8 @@ class MemoryActivator:
|
||||
valid_keywords=keywords, max_memory_num=3, max_memory_length=2, max_depth=3
|
||||
)
|
||||
|
||||
logger.info(f"当前记忆关键词: {self.cached_keywords} 。获取到的记忆: {related_memory}")
|
||||
logger.debug(f"当前记忆关键词: {self.cached_keywords} ")
|
||||
logger.debug(f"获取到的记忆: {related_memory}")
|
||||
|
||||
# 激活时,所有已有记忆的duration+1,达到3则移除
|
||||
for m in self.running_memory[:]:
|
||||
|
||||
@@ -166,9 +166,10 @@ class ChatBot:
|
||||
message_data["message_info"]["group_info"]["group_id"] = str(
|
||||
message_data["message_info"]["group_info"]["group_id"]
|
||||
)
|
||||
message_data["message_info"]["user_info"]["user_id"] = str(
|
||||
message_data["message_info"]["user_info"]["user_id"]
|
||||
)
|
||||
if message_data["message_info"].get("user_info") is not None:
|
||||
message_data["message_info"]["user_info"]["user_id"] = str(
|
||||
message_data["message_info"]["user_info"]["user_id"]
|
||||
)
|
||||
# print(message_data)
|
||||
# logger.debug(str(message_data))
|
||||
message = MessageRecv(message_data)
|
||||
|
||||
@@ -107,7 +107,9 @@ class MessageRecv(Message):
|
||||
self.processed_plain_text = message_dict.get("processed_plain_text", "")
|
||||
self.detailed_plain_text = message_dict.get("detailed_plain_text", "")
|
||||
self.is_emoji = False
|
||||
self.has_emoji = False
|
||||
self.is_picid = False
|
||||
self.has_picid = False
|
||||
self.is_mentioned = None
|
||||
self.priority_mode = "interest"
|
||||
self.priority_info = None
|
||||
@@ -134,25 +136,35 @@ class MessageRecv(Message):
|
||||
"""
|
||||
try:
|
||||
if segment.type == "text":
|
||||
self.is_picid = False
|
||||
self.is_emoji = False
|
||||
return segment.data
|
||||
elif segment.type == "image":
|
||||
# 如果是base64图片数据
|
||||
if isinstance(segment.data, str):
|
||||
self.has_picid = True
|
||||
self.is_picid = True
|
||||
self.is_emoji = False
|
||||
image_manager = get_image_manager()
|
||||
# print(f"segment.data: {segment.data}")
|
||||
_, processed_text = await image_manager.process_image(segment.data)
|
||||
return processed_text
|
||||
return "[发了一张图片,网卡了加载不出来]"
|
||||
elif segment.type == "emoji":
|
||||
self.has_emoji = True
|
||||
self.is_emoji = True
|
||||
self.is_picid = False
|
||||
if isinstance(segment.data, str):
|
||||
return await get_image_manager().get_emoji_description(segment.data)
|
||||
return "[发了一个表情包,网卡了加载不出来]"
|
||||
elif segment.type == "mention_bot":
|
||||
self.is_picid = False
|
||||
self.is_emoji = False
|
||||
self.is_mentioned = float(segment.data)
|
||||
return ""
|
||||
elif segment.type == "priority_info":
|
||||
self.is_picid = False
|
||||
self.is_emoji = False
|
||||
if isinstance(segment.data, dict):
|
||||
# 处理优先级信息
|
||||
self.priority_mode = "priority"
|
||||
|
||||
@@ -136,7 +136,7 @@ class MessageStorage:
|
||||
if matched_message:
|
||||
# 更新找到的消息记录
|
||||
Messages.update(message_id=qq_message_id).where(Messages.id == matched_message.id).execute()
|
||||
logger.info(f"更新消息ID成功: {matched_message.message_id} -> {qq_message_id}")
|
||||
logger.debug(f"更新消息ID成功: {matched_message.message_id} -> {qq_message_id}")
|
||||
else:
|
||||
logger.debug("未找到匹配的消息")
|
||||
|
||||
|
||||
@@ -302,50 +302,44 @@ class NormalChat:
|
||||
logger.info(f"[{self.stream_name}] 在处理上下文中检测到停止信号,退出")
|
||||
break
|
||||
|
||||
# 并行处理兴趣消息
|
||||
async def process_single_message(msg_id, message, interest_value, is_mentioned):
|
||||
"""处理单个兴趣消息"""
|
||||
try:
|
||||
# 在处理每个消息前检查停止状态
|
||||
if self._disabled:
|
||||
logger.debug(f"[{self.stream_name}] 处理消息时检测到停用,跳过消息 {msg_id}")
|
||||
return
|
||||
semaphore = asyncio.Semaphore(5)
|
||||
|
||||
# 处理消息
|
||||
self.adjust_reply_frequency()
|
||||
async def process_and_acquire(msg_id, message, interest_value, is_mentioned):
|
||||
"""处理单个兴趣消息并管理信号量"""
|
||||
async with semaphore:
|
||||
try:
|
||||
# 在处理每个消息前检查停止状态
|
||||
if self._disabled:
|
||||
logger.debug(
|
||||
f"[{self.stream_name}] 处理消息时检测到停用,跳过消息 {msg_id}"
|
||||
)
|
||||
return
|
||||
|
||||
await self.normal_response(
|
||||
message=message,
|
||||
is_mentioned=is_mentioned,
|
||||
interested_rate=interest_value * self.willing_amplifier,
|
||||
)
|
||||
except asyncio.CancelledError:
|
||||
logger.debug(f"[{self.stream_name}] 处理消息 {msg_id} 时被取消")
|
||||
raise # 重新抛出取消异常
|
||||
except Exception as e:
|
||||
logger.error(f"[{self.stream_name}] 处理兴趣消息{msg_id}时出错: {e}")
|
||||
# 不打印完整traceback,避免日志污染
|
||||
finally:
|
||||
# 无论如何都要清理消息
|
||||
self.interest_dict.pop(msg_id, None)
|
||||
# 处理消息
|
||||
self.adjust_reply_frequency()
|
||||
|
||||
# 创建并行任务列表
|
||||
coroutines = []
|
||||
for msg_id, (message, interest_value, is_mentioned) in items_to_process:
|
||||
coroutine = process_single_message(msg_id, message, interest_value, is_mentioned)
|
||||
coroutines.append(coroutine)
|
||||
await self.normal_response(
|
||||
message=message,
|
||||
is_mentioned=is_mentioned,
|
||||
interested_rate=interest_value * self.willing_amplifier,
|
||||
)
|
||||
except asyncio.CancelledError:
|
||||
logger.debug(f"[{self.stream_name}] 处理消息 {msg_id} 时被取消")
|
||||
raise # 重新抛出取消异常
|
||||
except Exception as e:
|
||||
logger.error(f"[{self.stream_name}] 处理兴趣消息{msg_id}时出错: {e}")
|
||||
# 不打印完整traceback,避免日志污染
|
||||
finally:
|
||||
# 无论如何都要清理消息
|
||||
self.interest_dict.pop(msg_id, None)
|
||||
|
||||
# 并行执行所有任务,限制并发数量避免资源过度消耗
|
||||
if coroutines:
|
||||
# 使用信号量控制并发数,最多同时处理5个消息
|
||||
semaphore = asyncio.Semaphore(5)
|
||||
tasks = [
|
||||
process_and_acquire(msg_id, message, interest_value, is_mentioned)
|
||||
for msg_id, (message, interest_value, is_mentioned) in items_to_process
|
||||
]
|
||||
|
||||
async def limited_process(coroutine, sem):
|
||||
async with sem:
|
||||
await coroutine
|
||||
|
||||
limited_tasks = [limited_process(coroutine, semaphore) for coroutine in coroutines]
|
||||
await asyncio.gather(*limited_tasks, return_exceptions=True)
|
||||
if tasks:
|
||||
await asyncio.gather(*tasks, return_exceptions=True)
|
||||
|
||||
except asyncio.CancelledError:
|
||||
logger.info(f"[{self.stream_name}] 处理上下文时任务被取消")
|
||||
@@ -469,9 +463,6 @@ class NormalChat:
|
||||
) -> Optional[list]:
|
||||
"""生成普通回复"""
|
||||
try:
|
||||
logger.info(
|
||||
f"NormalChat思考:{message.processed_plain_text[:30] + '...' if len(message.processed_plain_text) > 30 else message.processed_plain_text}"
|
||||
)
|
||||
person_info_manager = get_person_info_manager()
|
||||
person_id = person_info_manager.get_person_id(
|
||||
message.chat_stream.user_info.platform, message.chat_stream.user_info.user_id
|
||||
@@ -491,10 +482,6 @@ class NormalChat:
|
||||
logger.info(f"对 {message.processed_plain_text} 的回复生成失败")
|
||||
return None
|
||||
|
||||
content = " ".join([item[1] for item in reply_set if item[0] == "text"])
|
||||
if content:
|
||||
logger.info(f"{global_config.bot.nickname}的备选回复是:{content}")
|
||||
|
||||
return reply_set
|
||||
|
||||
except Exception as e:
|
||||
@@ -532,7 +519,15 @@ class NormalChat:
|
||||
reasoning = plan_result["action_result"]["reasoning"]
|
||||
is_parallel = plan_result["action_result"].get("is_parallel", False)
|
||||
|
||||
logger.info(f"[{self.stream_name}] Planner决策: {action_type}, 理由: {reasoning}, 并行执行: {is_parallel}")
|
||||
if action_type == "no_action":
|
||||
logger.info(f"[{self.stream_name}] {global_config.bot.nickname} 决定进行回复")
|
||||
elif is_parallel:
|
||||
logger.info(
|
||||
f"[{self.stream_name}] {global_config.bot.nickname} 决定进行回复, 同时执行{action_type}动作"
|
||||
)
|
||||
else:
|
||||
logger.info(f"[{self.stream_name}] {global_config.bot.nickname} 决定执行{action_type}动作")
|
||||
|
||||
self.action_type = action_type # 更新实例属性
|
||||
self.is_parallel_action = is_parallel # 新增:保存并行执行标志
|
||||
|
||||
@@ -590,10 +585,21 @@ class NormalChat:
|
||||
)
|
||||
response_set, plan_result = results
|
||||
except asyncio.TimeoutError:
|
||||
gen_timed_out = not gen_task.done()
|
||||
plan_timed_out = not plan_task.done()
|
||||
|
||||
timeout_details = []
|
||||
if gen_timed_out:
|
||||
timeout_details.append("回复生成(gen)")
|
||||
if plan_timed_out:
|
||||
timeout_details.append("动作规划(plan)")
|
||||
|
||||
timeout_source = " 和 ".join(timeout_details)
|
||||
|
||||
logger.warning(
|
||||
f"[{self.stream_name}] 并行执行回复生成和动作规划超时 ({gather_timeout}秒),正在取消相关任务..."
|
||||
f"[{self.stream_name}] {timeout_source} 任务超时 ({global_config.chat.thinking_timeout}秒),正在取消相关任务..."
|
||||
)
|
||||
print(f"111{self.timeout_count}")
|
||||
# print(f"111{self.timeout_count}")
|
||||
self.timeout_count += 1
|
||||
if self.timeout_count > 5:
|
||||
logger.warning(
|
||||
@@ -623,18 +629,23 @@ class NormalChat:
|
||||
elif plan_result:
|
||||
logger.debug(f"[{self.stream_name}] 额外动作处理完成: {self.action_type}")
|
||||
|
||||
if response_set:
|
||||
content = " ".join([item[1] for item in response_set if item[0] == "text"])
|
||||
|
||||
if not response_set or (
|
||||
self.enable_planner and self.action_type not in ["no_action"] and not self.is_parallel_action
|
||||
):
|
||||
if not response_set:
|
||||
logger.info(f"[{self.stream_name}] 模型未生成回复内容")
|
||||
logger.warning(f"[{self.stream_name}] 模型未生成回复内容")
|
||||
elif self.enable_planner and self.action_type not in ["no_action"] and not self.is_parallel_action:
|
||||
logger.info(f"[{self.stream_name}] 模型选择其他动作(非并行动作)")
|
||||
logger.info(
|
||||
f"[{self.stream_name}] {global_config.bot.nickname} 原本想要回复:{content},但选择执行{self.action_type},不发表回复"
|
||||
)
|
||||
# 如果模型未生成回复,移除思考消息
|
||||
await self._cleanup_thinking_message_by_id(thinking_id)
|
||||
return False
|
||||
|
||||
# logger.info(f"[{self.stream_name}] 回复内容: {response_set}")
|
||||
logger.info(f"[{self.stream_name}] {global_config.bot.nickname} 决定的回复内容: {content}")
|
||||
|
||||
if self._disabled:
|
||||
logger.info(f"[{self.stream_name}] 已停用,忽略 normal_response。")
|
||||
|
||||
@@ -96,7 +96,7 @@ class ActionManager:
|
||||
f"从插件系统加载Action组件: {action_name} (插件: {getattr(action_info, 'plugin_name', 'unknown')})"
|
||||
)
|
||||
|
||||
logger.info(f"从插件系统加载了 {len(action_components)} 个Action组件")
|
||||
logger.info(f"加载了 {len(action_components)} 个Action动作")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"从插件系统加载Action组件失败: {e}")
|
||||
|
||||
@@ -124,6 +124,11 @@ class ActionPlanner:
|
||||
logger.info(f"{self.log_prefix}规划器原始响应: {llm_content}")
|
||||
if reasoning_content:
|
||||
logger.info(f"{self.log_prefix}规划器推理: {reasoning_content}")
|
||||
else:
|
||||
logger.debug(f"{self.log_prefix}规划器原始提示词: {prompt}")
|
||||
logger.debug(f"{self.log_prefix}规划器原始响应: {llm_content}")
|
||||
if reasoning_content:
|
||||
logger.debug(f"{self.log_prefix}规划器推理: {reasoning_content}")
|
||||
|
||||
except Exception as req_e:
|
||||
logger.error(f"{self.log_prefix}LLM 请求执行失败: {req_e}")
|
||||
|
||||
@@ -188,7 +188,7 @@ class DefaultReplyer:
|
||||
}
|
||||
for key, value in reply_data.items():
|
||||
if not value:
|
||||
logger.info(f"{self.log_prefix} 回复数据跳过{key},生成回复时将忽略。")
|
||||
logger.debug(f"{self.log_prefix} 回复数据跳过{key},生成回复时将忽略。")
|
||||
|
||||
# 3. 构建 Prompt
|
||||
with Timer("构建Prompt", {}): # 内部计时器,可选保留
|
||||
@@ -218,11 +218,13 @@ class DefaultReplyer:
|
||||
)
|
||||
|
||||
if global_config.debug.show_prompt:
|
||||
logger.info(f"{self.log_prefix}Prompt:\n{prompt}\n")
|
||||
logger.info(f"{self.log_prefix}\n{prompt}\n")
|
||||
else:
|
||||
logger.debug(f"{self.log_prefix}\n{prompt}\n")
|
||||
|
||||
content, (reasoning_content, model_name) = await express_model.generate_response_async(prompt)
|
||||
|
||||
logger.info(f"最终回复: {content}")
|
||||
logger.debug(f"replyer生成内容: {content}")
|
||||
|
||||
except Exception as llm_e:
|
||||
# 精简报错信息
|
||||
@@ -331,7 +333,7 @@ class DefaultReplyer:
|
||||
)
|
||||
|
||||
if selected_expressions:
|
||||
logger.info(f"{self.log_prefix} 使用处理器选中的{len(selected_expressions)}个表达方式")
|
||||
logger.debug(f"{self.log_prefix} 使用处理器选中的{len(selected_expressions)}个表达方式")
|
||||
for expr in selected_expressions:
|
||||
if isinstance(expr, dict) and "situation" in expr and "style" in expr:
|
||||
expr_type = expr.get("type", "style")
|
||||
|
||||
@@ -551,6 +551,9 @@ def build_readable_messages(
|
||||
show_actions: 是否显示动作记录
|
||||
"""
|
||||
# 创建messages的深拷贝,避免修改原始列表
|
||||
if not messages:
|
||||
return ""
|
||||
|
||||
copy_messages = [msg.copy() for msg in messages]
|
||||
|
||||
if show_actions and copy_messages:
|
||||
|
||||
@@ -252,8 +252,7 @@ class PersonInfo(BaseModel):
|
||||
know_times = FloatField(null=True) # 认识时间 (时间戳)
|
||||
know_since = FloatField(null=True) # 首次印象总结时间
|
||||
last_know = FloatField(null=True) # 最后一次印象总结时间
|
||||
familiarity_value = IntegerField(null=True, default=0) # 熟悉度,0-100,从完全陌生到非常熟悉
|
||||
liking_value = IntegerField(null=True, default=50) # 好感度,0-100,从非常厌恶到十分喜欢
|
||||
attitude = IntegerField(null=True, default=50) # 态度,0-100,从非常厌恶到十分喜欢
|
||||
|
||||
class Meta:
|
||||
# database = db # 继承自 BaseModel
|
||||
|
||||
@@ -321,14 +321,13 @@ MODULE_COLORS = {
|
||||
# 核心模块
|
||||
"main": "\033[1;97m", # 亮白色+粗体 (主程序)
|
||||
"api": "\033[92m", # 亮绿色
|
||||
"emoji": "\033[92m", # 亮绿色
|
||||
"chat": "\033[94m", # 亮蓝色
|
||||
"emoji": "\033[33m", # 亮绿色
|
||||
"chat": "\033[92m", # 亮蓝色
|
||||
"config": "\033[93m", # 亮黄色
|
||||
"common": "\033[95m", # 亮紫色
|
||||
"tools": "\033[96m", # 亮青色
|
||||
"lpmm": "\033[96m",
|
||||
"plugin_system": "\033[91m", # 亮红色
|
||||
"experimental": "\033[97m", # 亮白色
|
||||
"person_info": "\033[32m", # 绿色
|
||||
"individuality": "\033[34m", # 蓝色
|
||||
"manager": "\033[35m", # 紫色
|
||||
@@ -339,17 +338,13 @@ MODULE_COLORS = {
|
||||
"planner": "\033[36m",
|
||||
"memory": "\033[34m",
|
||||
"hfc": "\033[96m",
|
||||
"base_action": "\033[96m",
|
||||
"action_manager": "\033[32m",
|
||||
"action_manager": "\033[38;5;166m",
|
||||
# 关系系统
|
||||
"relation": "\033[38;5;201m", # 深粉色
|
||||
# 聊天相关模块
|
||||
"normal_chat": "\033[38;5;81m", # 亮蓝绿色
|
||||
"normal_chat_response": "\033[38;5;123m", # 青绿色
|
||||
"normal_chat_action_modifier": "\033[38;5;111m", # 蓝色
|
||||
"normal_chat_planner": "\033[38;5;75m", # 浅蓝色
|
||||
"heartflow": "\033[38;5;213m", # 粉色
|
||||
"heartflow_utils": "\033[38;5;219m", # 浅粉色
|
||||
"sub_heartflow": "\033[38;5;207m", # 粉紫色
|
||||
"subheartflow_manager": "\033[38;5;201m", # 深粉色
|
||||
"background_tasks": "\033[38;5;240m", # 灰色
|
||||
@@ -359,11 +354,9 @@ MODULE_COLORS = {
|
||||
"message_storage": "\033[38;5;33m", # 深蓝色
|
||||
# 专注聊天模块
|
||||
"replyer": "\033[38;5;166m", # 橙色
|
||||
"expressor": "\033[38;5;172m", # 黄橙色
|
||||
"processor": "\033[38;5;184m", # 黄绿色
|
||||
"base_processor": "\033[38;5;190m", # 绿黄色
|
||||
"working_memory": "\033[38;5;22m", # 深绿色
|
||||
"memory_activator": "\033[38;5;28m", # 绿色
|
||||
"memory_activator": "\033[34m", # 绿色
|
||||
# 插件系统
|
||||
"plugin_manager": "\033[38;5;208m", # 红色
|
||||
"base_plugin": "\033[38;5;202m", # 橙红色
|
||||
@@ -389,11 +382,9 @@ MODULE_COLORS = {
|
||||
"tool_executor": "\033[38;5;64m", # 深绿色
|
||||
"base_tool": "\033[38;5;70m", # 绿色
|
||||
# 工具和实用模块
|
||||
"prompt": "\033[38;5;99m", # 紫色
|
||||
"prompt_build": "\033[38;5;105m", # 紫色
|
||||
"chat_utils": "\033[38;5;111m", # 蓝色
|
||||
"chat_image": "\033[38;5;117m", # 浅蓝色
|
||||
"typo_gen": "\033[38;5;123m", # 青绿色
|
||||
"maibot_statistic": "\033[38;5;129m", # 紫色
|
||||
# 特殊功能插件
|
||||
"mute_plugin": "\033[38;5;240m", # 灰色
|
||||
@@ -405,16 +396,13 @@ MODULE_COLORS = {
|
||||
# 数据库和消息
|
||||
"database_model": "\033[38;5;94m", # 橙褐色
|
||||
"maim_message": "\033[38;5;100m", # 绿褐色
|
||||
# 实验性模块
|
||||
"pfc": "\033[38;5;252m", # 浅灰色
|
||||
# 日志系统
|
||||
"logger": "\033[38;5;8m", # 深灰色
|
||||
"demo": "\033[38;5;15m", # 白色
|
||||
"confirm": "\033[1;93m", # 黄色+粗体
|
||||
# 模型相关
|
||||
"model_utils": "\033[38;5;164m", # 紫红色
|
||||
"relationship_fetcher": "\033[38;5;170m", # 浅紫色
|
||||
"relationship_builder": "\033[38;5;117m", # 浅蓝色
|
||||
"relationship_builder": "\033[38;5;93m", # 浅蓝色
|
||||
}
|
||||
|
||||
RESET_COLOR = "\033[0m"
|
||||
|
||||
@@ -48,6 +48,7 @@ person_info_default = {
|
||||
"points": None,
|
||||
"forgotten_points": None,
|
||||
"relation_value": None,
|
||||
"attitude": 50,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ class RelationshipBuilder:
|
||||
segments.append(new_segment)
|
||||
|
||||
person_name = get_person_info_manager().get_value_sync(person_id, "person_name") or person_id
|
||||
logger.info(
|
||||
logger.debug(
|
||||
f"{self.log_prefix} 眼熟用户 {person_name} 在 {time.strftime('%H:%M:%S', time.localtime(potential_start_time))} - {time.strftime('%H:%M:%S', time.localtime(message_time))} 之间有 {new_segment['message_count']} 条消息"
|
||||
)
|
||||
self._save_cache()
|
||||
@@ -187,7 +187,9 @@ class RelationshipBuilder:
|
||||
segments.append(new_segment)
|
||||
person_info_manager = get_person_info_manager()
|
||||
person_name = person_info_manager.get_value_sync(person_id, "person_name") or person_id
|
||||
logger.info(f"{self.log_prefix} 重新眼熟用户 {person_name} 创建新消息段(超过10条消息间隔): {new_segment}")
|
||||
logger.debug(
|
||||
f"{self.log_prefix} 重新眼熟用户 {person_name} 创建新消息段(超过10条消息间隔): {new_segment}"
|
||||
)
|
||||
|
||||
self._save_cache()
|
||||
|
||||
@@ -384,7 +386,7 @@ class RelationshipBuilder:
|
||||
total_message_count = self._get_total_message_count(person_id)
|
||||
if total_message_count >= 45:
|
||||
users_to_build_relationship.append(person_id)
|
||||
logger.info(
|
||||
logger.debug(
|
||||
f"{self.log_prefix} 用户 {person_id} 满足关系构建条件,总消息数:{total_message_count},消息段数:{len(segments)}"
|
||||
)
|
||||
elif total_message_count > 0:
|
||||
@@ -422,7 +424,7 @@ class RelationshipBuilder:
|
||||
|
||||
# 获取该段的消息(包含边界)
|
||||
segment_messages = get_raw_msg_by_timestamp_with_chat_inclusive(self.chat_id, start_time, end_time)
|
||||
logger.info(
|
||||
logger.debug(
|
||||
f"消息段 {i + 1}: {start_date} - {time.strftime('%Y-%m-%d %H:%M', time.localtime(end_time))}, 消息数: {len(segment_messages)}"
|
||||
)
|
||||
|
||||
@@ -450,7 +452,7 @@ class RelationshipBuilder:
|
||||
# 按时间排序所有消息(包括间隔标识)
|
||||
processed_messages.sort(key=lambda x: x["time"])
|
||||
|
||||
logger.info(f"为 {person_id} 获取到总共 {len(processed_messages)} 条消息(包含间隔标识)用于印象更新")
|
||||
logger.debug(f"为 {person_id} 获取到总共 {len(processed_messages)} 条消息(包含间隔标识)用于印象更新")
|
||||
relationship_manager = get_relationship_manager()
|
||||
|
||||
# 调用原有的更新方法
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
from src.common.logger import get_logger
|
||||
import math
|
||||
from src.person_info.person_info import PersonInfoManager, get_person_info_manager
|
||||
import time
|
||||
import random
|
||||
from src.llm_models.utils_model import LLMRequest
|
||||
from src.config.config import global_config
|
||||
from src.chat.utils.chat_message_builder import build_readable_messages
|
||||
from src.manager.mood_manager import mood_manager
|
||||
import json
|
||||
from json_repair import repair_json
|
||||
from datetime import datetime
|
||||
@@ -21,67 +19,11 @@ logger = get_logger("relation")
|
||||
|
||||
class RelationshipManager:
|
||||
def __init__(self):
|
||||
self.positive_feedback_value = 0 # 正反馈系统
|
||||
self.gain_coefficient = [1.0, 1.0, 1.1, 1.2, 1.4, 1.7, 1.9, 2.0]
|
||||
self._mood_manager = None
|
||||
|
||||
self.relationship_llm = LLMRequest(
|
||||
model=global_config.model.relation,
|
||||
request_type="relationship", # 用于动作规划
|
||||
)
|
||||
|
||||
@property
|
||||
def mood_manager(self):
|
||||
if self._mood_manager is None:
|
||||
self._mood_manager = mood_manager
|
||||
return self._mood_manager
|
||||
|
||||
def positive_feedback_sys(self, label: str, stance: str):
|
||||
"""正反馈系统,通过正反馈系数增益情绪变化,根据情绪再影响关系变更"""
|
||||
|
||||
positive_list = [
|
||||
"开心",
|
||||
"惊讶",
|
||||
"害羞",
|
||||
]
|
||||
|
||||
negative_list = [
|
||||
"愤怒",
|
||||
"悲伤",
|
||||
"恐惧",
|
||||
"厌恶",
|
||||
]
|
||||
|
||||
if label in positive_list:
|
||||
if 7 > self.positive_feedback_value >= 0:
|
||||
self.positive_feedback_value += 1
|
||||
elif self.positive_feedback_value < 0:
|
||||
self.positive_feedback_value = 0
|
||||
elif label in negative_list:
|
||||
if -7 < self.positive_feedback_value <= 0:
|
||||
self.positive_feedback_value -= 1
|
||||
elif self.positive_feedback_value > 0:
|
||||
self.positive_feedback_value = 0
|
||||
|
||||
if abs(self.positive_feedback_value) > 1:
|
||||
logger.debug(f"触发mood变更增益,当前增益系数:{self.gain_coefficient[abs(self.positive_feedback_value)]}")
|
||||
|
||||
def mood_feedback(self, value):
|
||||
"""情绪反馈"""
|
||||
mood_manager = self.mood_manager
|
||||
mood_gain = mood_manager.current_mood.valence**2 * math.copysign(1, value * mood_manager.current_mood.valence)
|
||||
value += value * mood_gain
|
||||
logger.debug(f"当前relationship增益系数:{mood_gain:.3f}")
|
||||
return value
|
||||
|
||||
def feedback_to_mood(self, mood_value):
|
||||
"""对情绪的反馈"""
|
||||
coefficient = self.gain_coefficient[abs(self.positive_feedback_value)]
|
||||
if mood_value > 0 and self.positive_feedback_value > 0 or mood_value < 0 and self.positive_feedback_value < 0:
|
||||
return mood_value * coefficient
|
||||
else:
|
||||
return mood_value / coefficient
|
||||
|
||||
@staticmethod
|
||||
async def is_known_some_one(platform, user_id):
|
||||
"""判断是否认识某人"""
|
||||
@@ -168,19 +110,6 @@ class RelationshipManager:
|
||||
|
||||
return relation_prompt
|
||||
|
||||
async def _update_list_field(self, person_id: str, field_name: str, new_items: list) -> None:
|
||||
"""更新列表类型的字段,将新项目添加到现有列表中
|
||||
|
||||
Args:
|
||||
person_id: 用户ID
|
||||
field_name: 字段名称
|
||||
new_items: 新的项目列表
|
||||
"""
|
||||
person_info_manager = get_person_info_manager()
|
||||
old_items = await person_info_manager.get_value(person_id, field_name) or []
|
||||
updated_items = list(set(old_items + [item for item in new_items if isinstance(item, str) and item]))
|
||||
await person_info_manager.update_one_field(person_id, field_name, updated_items)
|
||||
|
||||
async def update_person_impression(self, person_id, timestamp, bot_engaged_messages=None):
|
||||
"""更新用户印象
|
||||
|
||||
@@ -194,6 +123,7 @@ class RelationshipManager:
|
||||
person_info_manager = get_person_info_manager()
|
||||
person_name = await person_info_manager.get_value(person_id, "person_name")
|
||||
nickname = await person_info_manager.get_value(person_id, "nickname")
|
||||
know_times = await person_info_manager.get_value(person_id, "know_times") or 0
|
||||
|
||||
alias_str = ", ".join(global_config.bot.alias_names)
|
||||
# personality_block =get_individuality().get_personality_prompt(x_person=2, level=2)
|
||||
@@ -240,7 +170,9 @@ class RelationshipManager:
|
||||
name_mapping[replace_person_name] = f"用户{current_user}{user_count if user_count > 1 else ''}"
|
||||
current_user = chr(ord(current_user) + 1)
|
||||
|
||||
readable_messages = self.build_focus_readable_messages(messages=user_messages, target_person_id=person_id)
|
||||
readable_messages = build_readable_messages(
|
||||
messages=user_messages, replace_bot_name=True, timestamp_mode="normal_no_YMD", truncate=True
|
||||
)
|
||||
|
||||
if not readable_messages:
|
||||
return
|
||||
@@ -385,73 +317,118 @@ class RelationshipManager:
|
||||
|
||||
# 如果points超过10条,按权重随机选择多余的条目移动到forgotten_points
|
||||
if len(current_points) > 10:
|
||||
# 获取现有forgotten_points
|
||||
forgotten_points = await person_info_manager.get_value(person_id, "forgotten_points") or []
|
||||
if isinstance(forgotten_points, str):
|
||||
try:
|
||||
forgotten_points = json.loads(forgotten_points)
|
||||
except json.JSONDecodeError:
|
||||
logger.error(f"解析forgotten_points JSON失败: {forgotten_points}")
|
||||
forgotten_points = []
|
||||
elif not isinstance(forgotten_points, list):
|
||||
current_points = await self._update_impression(person_id, current_points, timestamp)
|
||||
|
||||
# 更新数据库
|
||||
await person_info_manager.update_one_field(
|
||||
person_id, "points", json.dumps(current_points, ensure_ascii=False, indent=None)
|
||||
)
|
||||
|
||||
await person_info_manager.update_one_field(person_id, "know_times", know_times + 1)
|
||||
know_since = await person_info_manager.get_value(person_id, "know_since") or 0
|
||||
if know_since == 0:
|
||||
await person_info_manager.update_one_field(person_id, "know_since", timestamp)
|
||||
await person_info_manager.update_one_field(person_id, "last_know", timestamp)
|
||||
|
||||
logger.debug(f"{person_name} 的印象更新完成")
|
||||
|
||||
async def _update_impression(self, person_id, current_points, timestamp):
|
||||
# 获取现有forgotten_points
|
||||
person_info_manager = get_person_info_manager()
|
||||
|
||||
person_name = await person_info_manager.get_value(person_id, "person_name")
|
||||
nickname = await person_info_manager.get_value(person_id, "nickname")
|
||||
know_times = await person_info_manager.get_value(person_id, "know_times") or 0
|
||||
attitude = await person_info_manager.get_value(person_id, "attitude") or 50
|
||||
|
||||
# 根据熟悉度,调整印象和简短印象的最大长度
|
||||
if know_times > 300:
|
||||
max_impression_length = 2000
|
||||
max_short_impression_length = 800
|
||||
elif know_times > 100:
|
||||
max_impression_length = 1000
|
||||
max_short_impression_length = 500
|
||||
elif know_times > 50:
|
||||
max_impression_length = 500
|
||||
max_short_impression_length = 300
|
||||
elif know_times > 10:
|
||||
max_impression_length = 200
|
||||
max_short_impression_length = 100
|
||||
else:
|
||||
max_impression_length = 100
|
||||
max_short_impression_length = 50
|
||||
|
||||
# 根据好感度,调整印象和简短印象的最大长度
|
||||
attitude_multiplier = (abs(100 - attitude) / 100) + 1
|
||||
max_impression_length = max_impression_length * attitude_multiplier
|
||||
max_short_impression_length = max_short_impression_length * attitude_multiplier
|
||||
|
||||
forgotten_points = await person_info_manager.get_value(person_id, "forgotten_points") or []
|
||||
if isinstance(forgotten_points, str):
|
||||
try:
|
||||
forgotten_points = json.loads(forgotten_points)
|
||||
except json.JSONDecodeError:
|
||||
logger.error(f"解析forgotten_points JSON失败: {forgotten_points}")
|
||||
forgotten_points = []
|
||||
elif not isinstance(forgotten_points, list):
|
||||
forgotten_points = []
|
||||
|
||||
# 计算当前时间
|
||||
current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
|
||||
# 计算当前时间
|
||||
current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
# 计算每个点的最终权重(原始权重 * 时间权重)
|
||||
weighted_points = []
|
||||
for point in current_points:
|
||||
time_weight = self.calculate_time_weight(point[2], current_time)
|
||||
final_weight = point[1] * time_weight
|
||||
weighted_points.append((point, final_weight))
|
||||
# 计算每个点的最终权重(原始权重 * 时间权重)
|
||||
weighted_points = []
|
||||
for point in current_points:
|
||||
time_weight = self.calculate_time_weight(point[2], current_time)
|
||||
final_weight = point[1] * time_weight
|
||||
weighted_points.append((point, final_weight))
|
||||
|
||||
# 计算总权重
|
||||
total_weight = sum(w for _, w in weighted_points)
|
||||
# 计算总权重
|
||||
total_weight = sum(w for _, w in weighted_points)
|
||||
|
||||
# 按权重随机选择要保留的点
|
||||
remaining_points = []
|
||||
points_to_move = []
|
||||
# 按权重随机选择要保留的点
|
||||
remaining_points = []
|
||||
points_to_move = []
|
||||
|
||||
# 对每个点进行随机选择
|
||||
for point, weight in weighted_points:
|
||||
# 计算保留概率(权重越高越可能保留)
|
||||
keep_probability = weight / total_weight
|
||||
# 对每个点进行随机选择
|
||||
for point, weight in weighted_points:
|
||||
# 计算保留概率(权重越高越可能保留)
|
||||
keep_probability = weight / total_weight
|
||||
|
||||
if len(remaining_points) < 10:
|
||||
# 如果还没达到30条,直接保留
|
||||
remaining_points.append(point)
|
||||
if len(remaining_points) < 10:
|
||||
# 如果还没达到30条,直接保留
|
||||
remaining_points.append(point)
|
||||
else:
|
||||
# 随机决定是否保留
|
||||
if random.random() < keep_probability:
|
||||
# 保留这个点,随机移除一个已保留的点
|
||||
idx_to_remove = random.randrange(len(remaining_points))
|
||||
points_to_move.append(remaining_points[idx_to_remove])
|
||||
remaining_points[idx_to_remove] = point
|
||||
else:
|
||||
# 随机决定是否保留
|
||||
if random.random() < keep_probability:
|
||||
# 保留这个点,随机移除一个已保留的点
|
||||
idx_to_remove = random.randrange(len(remaining_points))
|
||||
points_to_move.append(remaining_points[idx_to_remove])
|
||||
remaining_points[idx_to_remove] = point
|
||||
else:
|
||||
# 不保留这个点
|
||||
points_to_move.append(point)
|
||||
# 不保留这个点
|
||||
points_to_move.append(point)
|
||||
|
||||
# 更新points和forgotten_points
|
||||
current_points = remaining_points
|
||||
forgotten_points.extend(points_to_move)
|
||||
# 更新points和forgotten_points
|
||||
current_points = remaining_points
|
||||
forgotten_points.extend(points_to_move)
|
||||
|
||||
# 检查forgotten_points是否达到5条
|
||||
if len(forgotten_points) >= 10:
|
||||
# 构建压缩总结提示词
|
||||
alias_str = ", ".join(global_config.bot.alias_names)
|
||||
# 检查forgotten_points是否达到10条
|
||||
if len(forgotten_points) >= 10:
|
||||
# 构建压缩总结提示词
|
||||
alias_str = ", ".join(global_config.bot.alias_names)
|
||||
|
||||
# 按时间排序forgotten_points
|
||||
forgotten_points.sort(key=lambda x: x[2])
|
||||
# 按时间排序forgotten_points
|
||||
forgotten_points.sort(key=lambda x: x[2])
|
||||
|
||||
# 构建points文本
|
||||
points_text = "\n".join(
|
||||
[f"时间:{point[2]}\n权重:{point[1]}\n内容:{point[0]}" for point in forgotten_points]
|
||||
)
|
||||
# 构建points文本
|
||||
points_text = "\n".join(
|
||||
[f"时间:{point[2]}\n权重:{point[1]}\n内容:{point[0]}" for point in forgotten_points]
|
||||
)
|
||||
|
||||
impression = await person_info_manager.get_value(person_id, "impression") or ""
|
||||
impression = await person_info_manager.get_value(person_id, "impression") or ""
|
||||
|
||||
compress_prompt = f"""
|
||||
compress_prompt = f"""
|
||||
你的名字是{global_config.bot.nickname},{global_config.bot.nickname}的别名是{alias_str}。
|
||||
请不要混淆你自己和{global_config.bot.nickname}和{person_name}。
|
||||
|
||||
@@ -466,17 +443,17 @@ class RelationshipManager:
|
||||
你记得ta最近做的事:
|
||||
{points_text}
|
||||
|
||||
请输出一段平文本,以陈诉自白的语气,输出你对{person_name}的了解,不要输出任何其他内容。
|
||||
请输出一段{max_impression_length}字左右的平文本,以陈诉自白的语气,输出你对{person_name}的了解,不要输出任何其他内容。
|
||||
"""
|
||||
# 调用LLM生成压缩总结
|
||||
compressed_summary, _ = await self.relationship_llm.generate_response_async(prompt=compress_prompt)
|
||||
# 调用LLM生成压缩总结
|
||||
compressed_summary, _ = await self.relationship_llm.generate_response_async(prompt=compress_prompt)
|
||||
|
||||
current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
|
||||
compressed_summary = f"截至{current_time},你对{person_name}的了解:{compressed_summary}"
|
||||
current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
|
||||
compressed_summary = f"截至{current_time},你对{person_name}的了解:{compressed_summary}"
|
||||
|
||||
await person_info_manager.update_one_field(person_id, "impression", compressed_summary)
|
||||
await person_info_manager.update_one_field(person_id, "impression", compressed_summary)
|
||||
|
||||
compress_short_prompt = f"""
|
||||
compress_short_prompt = f"""
|
||||
你的名字是{global_config.bot.nickname},{global_config.bot.nickname}的别名是{alias_str}。
|
||||
请不要混淆你自己和{global_config.bot.nickname}和{person_name}。
|
||||
|
||||
@@ -487,107 +464,76 @@ class RelationshipManager:
|
||||
1.对{person_name}的直观印象
|
||||
2.{global_config.bot.nickname}与{person_name}的关系
|
||||
3.{person_name}的关键信息
|
||||
请输出一段平文本,以陈诉自白的语气,输出你对{person_name}的概括,不要输出任何其他内容。
|
||||
请输出一段{max_short_impression_length}字左右的平文本,以陈诉自白的语气,输出你对{person_name}的概括,不要输出任何其他内容。
|
||||
"""
|
||||
compressed_short_summary, _ = await self.relationship_llm.generate_response_async(
|
||||
prompt=compress_short_prompt
|
||||
)
|
||||
compressed_short_summary, _ = await self.relationship_llm.generate_response_async(
|
||||
prompt=compress_short_prompt
|
||||
)
|
||||
|
||||
# current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
|
||||
# compressed_short_summary = f"截至{current_time},你对{person_name}的了解:{compressed_short_summary}"
|
||||
# current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
|
||||
# compressed_short_summary = f"截至{current_time},你对{person_name}的了解:{compressed_short_summary}"
|
||||
|
||||
await person_info_manager.update_one_field(person_id, "short_impression", compressed_short_summary)
|
||||
await person_info_manager.update_one_field(person_id, "short_impression", compressed_short_summary)
|
||||
|
||||
relation_value_prompt = f"""
|
||||
relation_value_prompt = f"""
|
||||
你的名字是{global_config.bot.nickname}。
|
||||
你最近对{person_name}的了解如下:
|
||||
{points_text}
|
||||
|
||||
请根据以上信息,评估你和{person_name}的关系,给出两个维度的值:熟悉度和好感度。
|
||||
1. 了解度 (familiarity_value): 0-100的整数,表示这些信息让你对ta的了解增进程度。
|
||||
- 0: 没有任何进一步了解
|
||||
- 25: 有点进一步了解
|
||||
- 50: 有进一步了解
|
||||
- 75: 有更多了解
|
||||
- 100: 有了更多重要的了解
|
||||
请根据以上信息,评估你和{person_name}的关系,给出你对ta的态度。
|
||||
|
||||
2. **好感度 (liking_value)**: 0-100的整数,表示这些信息让你对ta的喜。
|
||||
- 0: 非常厌恶
|
||||
- 25: 有点反感
|
||||
- 50: 中立/无感(或者文本中无法明显看出)
|
||||
- 75: 喜欢这个人
|
||||
- 100: 非常喜欢/开心对这个人
|
||||
态度: 0-100的整数,表示这些信息让你对ta的态度。
|
||||
- 0: 非常厌恶
|
||||
- 25: 有点反感
|
||||
- 50: 中立/无感(或者文本中无法明显看出)
|
||||
- 75: 喜欢这个人
|
||||
- 100: 非常喜欢/开心对这个人
|
||||
|
||||
请严格按照json格式输出,不要有其他多余内容:
|
||||
{{
|
||||
"familiarity_value": <0-100之间的整数>,
|
||||
"liking_value": <0-100之间的整数>
|
||||
"attitude": <0-100之间的整数>,
|
||||
}}
|
||||
"""
|
||||
try:
|
||||
relation_value_response, _ = await self.relationship_llm.generate_response_async(
|
||||
prompt=relation_value_prompt
|
||||
)
|
||||
relation_value_json = json.loads(repair_json(relation_value_response))
|
||||
|
||||
# 从LLM获取新生成的值
|
||||
new_familiarity_value = int(relation_value_json.get("familiarity_value", 0))
|
||||
new_liking_value = int(relation_value_json.get("liking_value", 50))
|
||||
|
||||
# 获取当前的关系值
|
||||
old_familiarity_value = await person_info_manager.get_value(person_id, "familiarity_value") or 0
|
||||
liking_value = await person_info_manager.get_value(person_id, "liking_value") or 50
|
||||
|
||||
# 更新熟悉度
|
||||
if new_familiarity_value > 25:
|
||||
familiarity_value = old_familiarity_value + (new_familiarity_value - 25) / 75
|
||||
else:
|
||||
familiarity_value = old_familiarity_value
|
||||
|
||||
# 更新好感度
|
||||
if new_liking_value > 50:
|
||||
liking_value += (new_liking_value - 50) / 50
|
||||
elif new_liking_value < 50:
|
||||
liking_value -= (50 - new_liking_value) / 50 * 1.5
|
||||
|
||||
await person_info_manager.update_one_field(person_id, "familiarity_value", familiarity_value)
|
||||
await person_info_manager.update_one_field(person_id, "liking_value", liking_value)
|
||||
logger.info(f"更新了与 {person_name} 的关系值: 熟悉度={familiarity_value}, 好感度={liking_value}")
|
||||
except (json.JSONDecodeError, ValueError, TypeError) as e:
|
||||
logger.error(f"解析relation_value JSON失败或值无效: {e}, 响应: {relation_value_response}")
|
||||
|
||||
forgotten_points = []
|
||||
info_list = []
|
||||
await person_info_manager.update_one_field(
|
||||
person_id, "info_list", json.dumps(info_list, ensure_ascii=False, indent=None)
|
||||
try:
|
||||
relation_value_response, _ = await self.relationship_llm.generate_response_async(
|
||||
prompt=relation_value_prompt
|
||||
)
|
||||
relation_value_json = json.loads(repair_json(relation_value_response))
|
||||
|
||||
# 从LLM获取新生成的值
|
||||
new_attitude = int(relation_value_json.get("attitude", 50))
|
||||
|
||||
# 获取当前的关系值
|
||||
old_attitude = await person_info_manager.get_value(person_id, "attitude") or 50
|
||||
|
||||
# 更新熟悉度
|
||||
if new_attitude > 25:
|
||||
attitude = old_attitude + (new_attitude - 25) / 75
|
||||
else:
|
||||
attitude = old_attitude
|
||||
|
||||
# 更新好感度
|
||||
if new_attitude > 50:
|
||||
attitude += (new_attitude - 50) / 50
|
||||
elif new_attitude < 50:
|
||||
attitude -= (50 - new_attitude) / 50 * 1.5
|
||||
|
||||
await person_info_manager.update_one_field(person_id, "attitude", attitude)
|
||||
logger.info(f"更新了与 {person_name} 的态度: {attitude}")
|
||||
except (json.JSONDecodeError, ValueError, TypeError) as e:
|
||||
logger.error(f"解析relation_value JSON失败或值无效: {e}, 响应: {relation_value_response}")
|
||||
|
||||
forgotten_points = []
|
||||
info_list = []
|
||||
await person_info_manager.update_one_field(
|
||||
person_id, "forgotten_points", json.dumps(forgotten_points, ensure_ascii=False, indent=None)
|
||||
person_id, "info_list", json.dumps(info_list, ensure_ascii=False, indent=None)
|
||||
)
|
||||
|
||||
# 更新数据库
|
||||
await person_info_manager.update_one_field(
|
||||
person_id, "points", json.dumps(current_points, ensure_ascii=False, indent=None)
|
||||
person_id, "forgotten_points", json.dumps(forgotten_points, ensure_ascii=False, indent=None)
|
||||
)
|
||||
know_times = await person_info_manager.get_value(person_id, "know_times") or 0
|
||||
await person_info_manager.update_one_field(person_id, "know_times", know_times + 1)
|
||||
know_since = await person_info_manager.get_value(person_id, "know_since") or 0
|
||||
if know_since == 0:
|
||||
await person_info_manager.update_one_field(person_id, "know_since", timestamp)
|
||||
await person_info_manager.update_one_field(person_id, "last_know", timestamp)
|
||||
|
||||
logger.info(f"{person_name} 的印象更新完成")
|
||||
|
||||
def build_focus_readable_messages(self, messages: list, target_person_id: str = None) -> str:
|
||||
"""格式化消息,处理所有消息内容"""
|
||||
if not messages:
|
||||
return ""
|
||||
|
||||
# 直接处理所有消息,不进行过滤
|
||||
return build_readable_messages(
|
||||
messages=messages, replace_bot_name=True, timestamp_mode="normal_no_YMD", truncate=True
|
||||
)
|
||||
return current_points
|
||||
|
||||
def calculate_time_weight(self, point_time: str, current_time: str) -> float:
|
||||
"""计算基于时间的权重系数"""
|
||||
|
||||
@@ -11,7 +11,7 @@ from src.plugin_system.apis import emoji_api
|
||||
from src.plugins.built_in.core_actions.no_reply import NoReplyAction
|
||||
|
||||
|
||||
logger = get_logger("core_actions")
|
||||
logger = get_logger("emoji")
|
||||
|
||||
|
||||
class EmojiAction(BaseAction):
|
||||
@@ -65,7 +65,7 @@ class EmojiAction(BaseAction):
|
||||
return False, f"未找到匹配 '{description}' 的表情包"
|
||||
|
||||
emoji_base64, emoji_description, matched_emotion = emoji_result
|
||||
logger.info(f"{self.log_prefix} 找到表情包: {emoji_description}, 匹配情感: {matched_emotion}")
|
||||
logger.info(f"{self.log_prefix} 找到表达{matched_emotion}的表情包")
|
||||
|
||||
# 使用BaseAction的便捷方法发送表情包
|
||||
success = await self.send_emoji(emoji_base64)
|
||||
|
||||
@@ -103,6 +103,7 @@ class NoReplyAction(BaseAction):
|
||||
|
||||
logger.info(f"{self.log_prefix} 选择不回复(第{count}次),开始摸鱼,原因: {reason}")
|
||||
|
||||
# 进入等待状态
|
||||
while True:
|
||||
current_time = time.time()
|
||||
elapsed_time = current_time - start_time
|
||||
@@ -110,7 +111,9 @@ class NoReplyAction(BaseAction):
|
||||
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分钟完全没有发言,退出专注模式")
|
||||
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}秒,或完全没有说话,感觉群里没有新内容,决定退出专注模式,稍作休息"
|
||||
@@ -139,19 +142,9 @@ class NoReplyAction(BaseAction):
|
||||
|
||||
# 判定条件:累计3条消息或等待超过5秒且有新消息
|
||||
time_since_last_judge = current_time - last_judge_time
|
||||
should_judge = (
|
||||
new_message_count >= 3 # 累计3条消息
|
||||
or (new_message_count > 0 and time_since_last_judge >= 15.0) # 等待超过5秒且有新消息
|
||||
)
|
||||
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:
|
||||
# 判断触发原因
|
||||
trigger_reason = ""
|
||||
if new_message_count >= 3:
|
||||
trigger_reason = f"累计{new_message_count}条消息"
|
||||
elif time_since_last_judge >= 10.0:
|
||||
trigger_reason = f"等待{time_since_last_judge:.1f}秒且有新消息"
|
||||
|
||||
logger.info(f"{self.log_prefix} 触发判定({trigger_reason}),进行智能判断...")
|
||||
|
||||
# 获取最近的消息内容用于判断
|
||||
@@ -164,7 +157,10 @@ class NoReplyAction(BaseAction):
|
||||
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
|
||||
messages=recent_messages,
|
||||
timestamp_mode="normal_no_YMD",
|
||||
truncate=False,
|
||||
show_actions=False,
|
||||
)
|
||||
|
||||
# 获取身份信息
|
||||
@@ -187,81 +183,13 @@ class NoReplyAction(BaseAction):
|
||||
history_block += "\n"
|
||||
|
||||
# 检查过去10分钟的发言频率
|
||||
frequency_block = ""
|
||||
should_skip_llm_judge = False # 是否跳过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:
|
||||
# 检查消息发送者是否是bot
|
||||
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
|
||||
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
|
||||
|
||||
# 根据配置和概率决定是否跳过LLM判断
|
||||
if self._skip_judge_when_tired and random.random() < skip_probability:
|
||||
should_skip_llm_judge = True
|
||||
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},添加疲惫提示"
|
||||
)
|
||||
else:
|
||||
# 回复次数少时的正向提示
|
||||
under_count = talk_frequency_threshold - bot_message_count
|
||||
|
||||
if under_count >= talk_frequency_threshold * 0.8: # 回复很少(少于20%)
|
||||
frequency_block = "你感觉精力充沛,状态很好,积极参与聊天。\n"
|
||||
elif under_count >= talk_frequency_threshold * 0.5: # 回复较少(少于50%)
|
||||
frequency_block = "你感觉状态不错。\n"
|
||||
else: # 刚好达到阈值
|
||||
frequency_block = ""
|
||||
|
||||
logger.info(
|
||||
f"{self.log_prefix} 过去10分钟发言{bot_message_count}条,未超过阈值{talk_frequency_threshold},添加正向提示"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"{self.log_prefix} 检查发言频率时出错: {e}")
|
||||
frequency_block = ""
|
||||
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判断,继续循环等待
|
||||
|
||||
# 构建判断上下文
|
||||
@@ -352,17 +280,14 @@ class NoReplyAction(BaseAction):
|
||||
logger.error(f"{self.log_prefix} 模型判断异常: {e},继续等待")
|
||||
last_judge_time = time.time() # 异常时也更新时间,避免频繁重试
|
||||
|
||||
|
||||
|
||||
# 每10秒输出一次等待状态
|
||||
logger.info(f"{self.log_prefix} 开始等待新消息...")
|
||||
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) % 60 == 0 and int(elapsed_time) > 0:
|
||||
logger.debug(f"{self.log_prefix} 已等待{elapsed_time / 60:.0f}分钟,等待新消息...")
|
||||
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)
|
||||
|
||||
# 短暂等待后继续检查
|
||||
@@ -380,6 +305,109 @@ class NoReplyAction(BaseAction):
|
||||
)
|
||||
return False, f"不回复动作执行失败: {e}"
|
||||
|
||||
def _should_trigger_judge(self, new_message_count: int, time_since_last_judge: float) -> Tuple[bool, str]:
|
||||
"""判断是否应该触发智能判断,并返回触发原因。
|
||||
|
||||
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, ""
|
||||
|
||||
# 判断触发原因
|
||||
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分钟是否完全没有发言,决定是否退出专注模式
|
||||
|
||||
|
||||
Reference in New Issue
Block a user