From 22913a5bdfbd3fd91094451406c6d2b407e6f960 Mon Sep 17 00:00:00 2001 From: infinitycat Date: Mon, 7 Jul 2025 11:30:55 +0800 Subject: [PATCH 01/13] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0docker-compose.?= =?UTF-8?q?yml=E5=92=8CGitHub=E5=B7=A5=E4=BD=9C=E6=B5=81=EF=BC=8C=E7=A7=BB?= =?UTF-8?q?=E9=99=A4sqlite-web=E6=9C=8D=E5=8A=A1=EF=BC=8C=E8=B0=83?= =?UTF-8?q?=E6=95=B4chat2db=E7=AB=AF=E5=8F=A3=EF=BC=8C=E4=BC=98=E5=8C=96Do?= =?UTF-8?q?cker=E9=95=9C=E5=83=8F=E6=9E=84=E5=BB=BA=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=EF=BC=8C=E5=90=88=E5=B9=B6AMD64=E5=92=8CARM64=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=E6=AD=A5=E9=AA=A4=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker-image.yml | 142 ++++++----------------------- docker-compose.yml | 26 ++++-- 2 files changed, 43 insertions(+), 125 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index f9b5e6658..9b736e9d8 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -12,35 +12,17 @@ on: - "*.*.*" - "*.*.*-*" +# Workflow's jobs jobs: - build-amd64: - name: Build AMD64 Image + docker: runs-on: ubuntu-latest - env: - DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USERNAME }} steps: - - name: Checkout code + - name: Check out git repository uses: actions/checkout@v4 with: fetch-depth: 0 - - 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 Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - buildkitd-flags: --debug - - - name: Login 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 @@ -55,120 +37,50 @@ jobs: type=semver,pattern={{major}} type=sha - - name: Build and Push AMD64 Docker Image - uses: docker/build-push-action@v5 - with: - context: . - file: ./Dockerfile - platforms: linux/amd64 - tags: ${{ secrets.DOCKERHUB_USERNAME }}/maibot:amd64-${{ github.sha }} - push: true - 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 - build-args: | - BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') - VCS_REF=${{ github.sha }} - outputs: type=image,push=true + # Outputting basic information + - name: Print basic information + run: | + echo "Generated tags: ${{ steps.meta.outputs.tags }}" + echo "Generated labels: ${{ steps.meta.outputs.labels }}" - build-arm64: - name: Build ARM64 Image - runs-on: ubuntu-latest - env: - DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USERNAME }} - steps: - - name: Checkout code - 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 + + - 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 - - - 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 + with: + platforms: amd64,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 }} - - 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 + # Packaging and sending to Docker + - name: Build and push uses: docker/build-push-action@v5 with: context: . - 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 + platforms: linux/amd64,linux/arm64/v8 + tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + file: ./Dockerfile + cache-from: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maibot:buildcache + cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maibot:buildcache,mode=max provenance: true sbom: 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 - runs-on: ubuntu-latest - needs: - - build-amd64 - - build-arm64 - steps: - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - 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: Create and Push Manifest - run: | - # 为每个标签创建多架构镜像 - 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 }} - done \ No newline at end of file + VCS_REF=${{ github.sha }} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index b2ce0a31e..a85b00748 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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,20 +55,28 @@ services: image: mlikiowa/napcat-docker:latest networks: - maim_bot - - sqlite-web: - image: coleifer/sqlite-web - container_name: sqlite-web + # sqlite-web: + # image: coleifer/sqlite-web + # container_name: sqlite-web + # restart: always + # ports: + # - "8120:8080" + # volumes: + # - ./data/MaiMBot:/data/MaiMBot + # environment: + # - SQLITE_DATABASE=MaiMBot/MaiBot.db # 你的数据库文件 + # networks: + # - maim_bot + chat2db: + image: chat2db/chat2db:latest + container_name: maim-bot-chat2db restart: always ports: - - "8120:8080" + - "10824:10824" volumes: - ./data/MaiMBot:/data/MaiMBot - environment: - - SQLITE_DATABASE=MaiMBot/MaiBot.db # 你的数据库文件 networks: - maim_bot - networks: maim_bot: driver: bridge From 0d5721d7864568179760626e71f6d7cf7c0b6331 Mon Sep 17 00:00:00 2001 From: infinitycat Date: Mon, 7 Jul 2025 11:37:29 +0800 Subject: [PATCH 02/13] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96docker=E7=9A=84?= =?UTF-8?q?workflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker-image.yml | 176 +++++++++++++++++++++-------- 1 file changed, 129 insertions(+), 47 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 9b736e9d8..26dab7c04 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -14,14 +14,134 @@ on: # Workflow's jobs jobs: - docker: + build-amd64: + name: Build AMD64 Image runs-on: ubuntu-latest + outputs: + digest: ${{ steps.build.outputs.digest }} steps: - 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 + + - name: Clone lpmm + run: git clone https://github.com/MaiM-with-u/MaiMBot-LPMM.git MaiMBot-LPMM + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + buildkitd-flags: --debug + + # 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 + + # Build and push AMD64 image by digest + - name: Build and push AMD64 + id: build + uses: docker/build-push-action@v5 + with: + context: . + platforms: linux/amd64 + 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 + 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 }} + + build-arm64: + name: Build ARM64 Image + runs-on: ubuntu-latest + outputs: + digest: ${{ steps.build.outputs.digest }} + steps: + - 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 + + - 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 + + # 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 + + # 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 + cache-from: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maibot:arm64-buildcache + cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maibot:arm64-buildcache,mode=max + 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 }} + + create-manifest: + name: Create Multi-Arch Manifest + runs-on: ubuntu-latest + needs: + - build-amd64 + - build-arm64 + steps: + - 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 @@ -37,50 +157,12 @@ jobs: type=semver,pattern={{major}} type=sha - # Outputting basic information - - name: Print basic information + - name: Create and Push Manifest run: | - echo "Generated tags: ${{ steps.meta.outputs.tags }}" - echo "Generated labels: ${{ steps.meta.outputs.labels }}" - - # 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: amd64,arm64 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - with: - buildkitd-flags: --debug - - # Log in docker hub - - name: Log in to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - # Packaging and sending to Docker - - name: Build and push - uses: docker/build-push-action@v5 - with: - context: . - push: true - platforms: linux/amd64,linux/arm64/v8 - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - file: ./Dockerfile - cache-from: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maibot:buildcache - cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maibot:buildcache,mode=max - provenance: true - sbom: true - build-args: | - BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') - VCS_REF=${{ github.sha }} \ No newline at end of file + # 为每个标签创建多架构镜像 + 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@${{ needs.build-amd64.outputs.digest }} \ + ${{ secrets.DOCKERHUB_USERNAME }}/maibot@${{ needs.build-arm64.outputs.digest }} + done \ No newline at end of file From b31892374c8f6958857f5dfbc16b3021132f6b94 Mon Sep 17 00:00:00 2001 From: infinitycat Date: Mon, 7 Jul 2025 12:21:36 +0800 Subject: [PATCH 03/13] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96tag=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 26dab7c04..47fdf5b7f 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -155,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: | From 1ea5d28d73b334481d55a6e404d6401aa90892d2 Mon Sep 17 00:00:00 2001 From: UnCLAS-Prommer Date: Mon, 7 Jul 2025 16:57:44 +0800 Subject: [PATCH 04/13] =?UTF-8?q?=E9=98=B2=E6=AD=A2=E6=96=B0=E7=89=88?= =?UTF-8?q?=E7=9A=84notify=E7=82=B8=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/message_receive/bot.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/chat/message_receive/bot.py b/src/chat/message_receive/bot.py index 0bc5bec58..0e94991b6 100644 --- a/src/chat/message_receive/bot.py +++ b/src/chat/message_receive/bot.py @@ -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) From 26e14bd6b726dcd4ace1c2ccc7d16641b82097d5 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Mon, 7 Jul 2025 20:01:03 +0800 Subject: [PATCH 05/13] =?UTF-8?q?better=EF=BC=9A=E4=BC=98=E5=8C=96log?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=EF=BC=8C=E4=B8=8D=E6=98=BE=E7=A4=BA=E6=9D=82?= =?UTF-8?q?=E4=B9=B1=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/heart_flow/heartflow.py | 2 +- src/chat/memory_system/memory_activator.py | 3 ++- src/chat/normal_chat/normal_chat.py | 30 ++++++++++++++-------- src/chat/planner_actions/action_manager.py | 2 +- src/chat/planner_actions/planner.py | 5 ++++ src/chat/replyer/default_generator.py | 10 +++++--- src/common/logger.py | 5 +--- src/person_info/relationship_builder.py | 10 ++++---- src/plugins/built_in/core_actions/emoji.py | 4 +-- 9 files changed, 42 insertions(+), 29 deletions(-) diff --git a/src/chat/heart_flow/heartflow.py b/src/chat/heart_flow/heartflow.py index f0e01e838..ca6e8be7b 100644 --- a/src/chat/heart_flow/heartflow.py +++ b/src/chat/heart_flow/heartflow.py @@ -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: diff --git a/src/chat/memory_system/memory_activator.py b/src/chat/memory_system/memory_activator.py index b9a6248ff..560fe01a6 100644 --- a/src/chat/memory_system/memory_activator.py +++ b/src/chat/memory_system/memory_activator.py @@ -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[:]: diff --git a/src/chat/normal_chat/normal_chat.py b/src/chat/normal_chat/normal_chat.py index 569584eb5..314c2c1b5 100644 --- a/src/chat/normal_chat/normal_chat.py +++ b/src/chat/normal_chat/normal_chat.py @@ -469,9 +469,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 +488,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 +525,19 @@ 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 # 新增:保存并行执行标志 @@ -623,18 +628,21 @@ 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。") diff --git a/src/chat/planner_actions/action_manager.py b/src/chat/planner_actions/action_manager.py index c7f9bd6c1..3918831ca 100644 --- a/src/chat/planner_actions/action_manager.py +++ b/src/chat/planner_actions/action_manager.py @@ -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}") diff --git a/src/chat/planner_actions/planner.py b/src/chat/planner_actions/planner.py index 135ea6bac..edd5d010d 100644 --- a/src/chat/planner_actions/planner.py +++ b/src/chat/planner_actions/planner.py @@ -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}") diff --git a/src/chat/replyer/default_generator.py b/src/chat/replyer/default_generator.py index d9a7feda0..0b3c25f14 100644 --- a/src/chat/replyer/default_generator.py +++ b/src/chat/replyer/default_generator.py @@ -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") diff --git a/src/common/logger.py b/src/common/logger.py index 6be06d241..7202b993f 100644 --- a/src/common/logger.py +++ b/src/common/logger.py @@ -322,7 +322,7 @@ MODULE_COLORS = { "main": "\033[1;97m", # 亮白色+粗体 (主程序) "api": "\033[92m", # 亮绿色 "emoji": "\033[92m", # 亮绿色 - "chat": "\033[94m", # 亮蓝色 + "chat": "\033[92m", # 亮蓝色 "config": "\033[93m", # 亮黄色 "common": "\033[95m", # 亮紫色 "tools": "\033[96m", # 亮青色 @@ -346,10 +346,7 @@ MODULE_COLORS = { # 聊天相关模块 "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", # 灰色 diff --git a/src/person_info/relationship_builder.py b/src/person_info/relationship_builder.py index 11d7e5b47..7d56e0774 100644 --- a/src/person_info/relationship_builder.py +++ b/src/person_info/relationship_builder.py @@ -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,7 @@ 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 +384,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 +422,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 +450,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() # 调用原有的更新方法 diff --git a/src/plugins/built_in/core_actions/emoji.py b/src/plugins/built_in/core_actions/emoji.py index 128214427..cb429dd4c 100644 --- a/src/plugins/built_in/core_actions/emoji.py +++ b/src/plugins/built_in/core_actions/emoji.py @@ -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) From e64db2fbe5126ef18da6f0a15db3a68aa31bb5e2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 7 Jul 2025 12:01:49 +0000 Subject: [PATCH 06/13] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/normal_chat/normal_chat.py | 14 ++++++-------- src/person_info/relationship_builder.py | 4 +++- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/chat/normal_chat/normal_chat.py b/src/chat/normal_chat/normal_chat.py index 314c2c1b5..46366e800 100644 --- a/src/chat/normal_chat/normal_chat.py +++ b/src/chat/normal_chat/normal_chat.py @@ -526,18 +526,14 @@ class NormalChat: is_parallel = plan_result["action_result"].get("is_parallel", False) if action_type == "no_action": - logger.info( - f"[{self.stream_name}] {global_config.bot.nickname} 决定进行回复" - ) + 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}动作" - ) - + logger.info(f"[{self.stream_name}] {global_config.bot.nickname} 决定执行{action_type}动作") + self.action_type = action_type # 更新实例属性 self.is_parallel_action = is_parallel # 新增:保存并行执行标志 @@ -637,7 +633,9 @@ class NormalChat: if not response_set: 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}] {global_config.bot.nickname} 原本想要回复:{content},但选择执行{self.action_type},不发表回复") + 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 diff --git a/src/person_info/relationship_builder.py b/src/person_info/relationship_builder.py index 7d56e0774..33ed61c73 100644 --- a/src/person_info/relationship_builder.py +++ b/src/person_info/relationship_builder.py @@ -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.debug(f"{self.log_prefix} 重新眼熟用户 {person_name} 创建新消息段(超过10条消息间隔): {new_segment}") + logger.debug( + f"{self.log_prefix} 重新眼熟用户 {person_name} 创建新消息段(超过10条消息间隔): {new_segment}" + ) self._save_cache() From a0b4037a2668fb979a12e088b808a2558ad66033 Mon Sep 17 00:00:00 2001 From: infinitycat Date: Mon, 7 Jul 2025 20:03:53 +0800 Subject: [PATCH 07/13] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96docker-compose.?= =?UTF-8?q?yaml,=E6=96=B0=E5=A2=9Echat2db=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=B7=A5=E5=85=B7=EF=BC=88=E5=8F=AF=E9=80=89?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 4 +++- docker-compose.yml | 36 ++++++++++++++++++++---------------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/.dockerignore b/.dockerignore index fac1bf99a..e1f125bd5 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,4 +4,6 @@ __pycache__ *.pyd .DS_Store mongodb -napcat \ No newline at end of file +napcat +docs/ +.github/ \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index a85b00748..bcc8a57a8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -55,28 +55,32 @@ services: image: mlikiowa/napcat-docker:latest networks: - maim_bot - # sqlite-web: - # image: coleifer/sqlite-web - # container_name: sqlite-web - # restart: always - # ports: - # - "8120:8080" - # volumes: - # - ./data/MaiMBot:/data/MaiMBot - # environment: - # - SQLITE_DATABASE=MaiMBot/MaiBot.db # 你的数据库文件 - # networks: - # - maim_bot - chat2db: - image: chat2db/chat2db:latest - container_name: maim-bot-chat2db + sqlite-web: + # 注意:coleifer/sqlite-web 镜像不支持arm64 + image: coleifer/sqlite-web + container_name: sqlite-web restart: always ports: - - "10824:10824" + - "8120:8080" volumes: - ./data/MaiMBot:/data/MaiMBot + environment: + - 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 From de44e05030981ce4cde075afe0b3d8e2ce88f845 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 7 Jul 2025 12:35:22 +0000 Subject: [PATCH 08/13] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/built_in/core_actions/no_reply.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/built_in/core_actions/no_reply.py b/src/plugins/built_in/core_actions/no_reply.py index d06c958a9..160fbb626 100644 --- a/src/plugins/built_in/core_actions/no_reply.py +++ b/src/plugins/built_in/core_actions/no_reply.py @@ -110,7 +110,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}秒,或完全没有说话,感觉群里没有新内容,决定退出专注模式,稍作休息" @@ -259,7 +261,7 @@ class NoReplyAction(BaseAction): frequency_block = "" # 如果决定跳过LLM判断,直接更新时间并继续等待 - + if should_skip_llm_judge: last_judge_time = time.time() # 更新判断时间,避免立即重新判断 continue # 跳过本次LLM判断,继续循环等待 @@ -352,8 +354,6 @@ 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: From 3c46d996fe3223d85e8ce0a827794431406bc032 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Mon, 7 Jul 2025 21:08:23 +0800 Subject: [PATCH 09/13] =?UTF-8?q?feat=20=E5=8C=BA=E5=88=86is=5Femoji?= =?UTF-8?q?=E5=92=8Chas=5Femoji=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/message_receive/message.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/chat/message_receive/message.py b/src/chat/message_receive/message.py index 091570839..7575e0e53 100644 --- a/src/chat/message_receive/message.py +++ b/src/chat/message_receive/message.py @@ -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" From 08ae2e83e36cee13106eb78e03658190aa327f98 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Tue, 8 Jul 2025 00:27:33 +0800 Subject: [PATCH 10/13] =?UTF-8?q?fix=EF=BC=9Anormal=E5=87=BA=E7=8E=B0?= =?UTF-8?q?=E6=9C=AA=E6=AD=A3=E5=B8=B8=E8=BF=9B=E8=A1=8C=E7=9A=84=E4=B8=80?= =?UTF-8?q?=E6=AD=A5=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/normal_chat/normal_chat.py | 72 +++++++++---------- src/plugins/built_in/core_actions/no_reply.py | 5 +- 2 files changed, 34 insertions(+), 43 deletions(-) diff --git a/src/chat/normal_chat/normal_chat.py b/src/chat/normal_chat/normal_chat.py index 46366e800..5bf4abb17 100644 --- a/src/chat/normal_chat/normal_chat.py +++ b/src/chat/normal_chat/normal_chat.py @@ -302,50 +302,42 @@ 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) + + tasks = [ + process_and_acquire(msg_id, message, interest_value, is_mentioned) + for msg_id, (message, interest_value, is_mentioned) in items_to_process + ] - # 并行执行所有任务,限制并发数量避免资源过度消耗 - if coroutines: - # 使用信号量控制并发数,最多同时处理5个消息 - semaphore = asyncio.Semaphore(5) - - 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}] 处理上下文时任务被取消") diff --git a/src/plugins/built_in/core_actions/no_reply.py b/src/plugins/built_in/core_actions/no_reply.py index 160fbb626..c3cf0201c 100644 --- a/src/plugins/built_in/core_actions/no_reply.py +++ b/src/plugins/built_in/core_actions/no_reply.py @@ -355,14 +355,13 @@ class NoReplyAction(BaseAction): 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) # 短暂等待后继续检查 From 723870bcdeffe9066561d68a81635721f1998829 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 7 Jul 2025 16:27:55 +0000 Subject: [PATCH 11/13] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/normal_chat/normal_chat.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/chat/normal_chat/normal_chat.py b/src/chat/normal_chat/normal_chat.py index 5bf4abb17..48e8f4191 100644 --- a/src/chat/normal_chat/normal_chat.py +++ b/src/chat/normal_chat/normal_chat.py @@ -310,7 +310,9 @@ class NormalChat: try: # 在处理每个消息前检查停止状态 if self._disabled: - logger.debug(f"[{self.stream_name}] 处理消息时检测到停用,跳过消息 {msg_id}") + logger.debug( + f"[{self.stream_name}] 处理消息时检测到停用,跳过消息 {msg_id}" + ) return # 处理消息 @@ -330,7 +332,7 @@ class NormalChat: finally: # 无论如何都要清理消息 self.interest_dict.pop(msg_id, None) - + tasks = [ process_and_acquire(msg_id, message, interest_value, is_mentioned) for msg_id, (message, interest_value, is_mentioned) in items_to_process From f17f5cf46c0831bb8dc4652bc06d4c77b42eb7f9 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Tue, 8 Jul 2025 02:04:31 +0800 Subject: [PATCH 12/13] =?UTF-8?q?feat=EF=BC=9A=E4=BF=AE=E6=94=B9log,?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=85=B3=E7=B3=BB=E6=9E=84=E5=BB=BA=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E8=8A=82=E7=9C=81token=EF=BC=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/emoji_system/emoji_manager.py | 4 +- .../heart_flow/heartflow_message_processor.py | 16 +- src/chat/message_receive/storage.py | 2 +- src/chat/normal_chat/normal_chat.py | 17 +- src/chat/utils/chat_message_builder.py | 3 + src/common/database/database_model.py | 3 +- src/common/logger.py | 17 +- src/person_info/person_info.py | 1 + src/person_info/relationship_manager.py | 370 ++++++++---------- src/plugins/built_in/core_actions/no_reply.py | 192 +++++---- 10 files changed, 303 insertions(+), 322 deletions(-) diff --git a/src/chat/emoji_system/emoji_manager.py b/src/chat/emoji_system/emoji_manager.py index b10d8b0bf..3511d938b 100644 --- a/src/chat/emoji_system/emoji_manager.py +++ b/src/chat/emoji_system/emoji_manager.py @@ -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("[扫描] 开始扫描新表情包...") diff --git a/src/chat/heart_flow/heartflow_message_processor.py b/src/chat/heart_flow/heartflow_message_processor.py index 66ddf362e..5de0d7015 100644 --- a/src/chat/heart_flow/heartflow_message_processor.py +++ b/src/chat/heart_flow/heartflow_message_processor.py @@ -119,15 +119,15 @@ 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: diff --git a/src/chat/message_receive/storage.py b/src/chat/message_receive/storage.py index 146a4372a..c40c4eb75 100644 --- a/src/chat/message_receive/storage.py +++ b/src/chat/message_receive/storage.py @@ -131,7 +131,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("未找到匹配的消息") diff --git a/src/chat/normal_chat/normal_chat.py b/src/chat/normal_chat/normal_chat.py index 48e8f4191..43ab5803e 100644 --- a/src/chat/normal_chat/normal_chat.py +++ b/src/chat/normal_chat/normal_chat.py @@ -585,10 +585,19 @@ class NormalChat: ) response_set, plan_result = results except asyncio.TimeoutError: - logger.warning( - f"[{self.stream_name}] 并行执行回复生成和动作规划超时 ({gather_timeout}秒),正在取消相关任务..." - ) - print(f"111{self.timeout_count}") + 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}] {timeout_source} 任务超时 ({global_config.chat.thinking_timeout}秒),正在取消相关任务...") + # print(f"111{self.timeout_count}") self.timeout_count += 1 if self.timeout_count > 5: logger.warning( diff --git a/src/chat/utils/chat_message_builder.py b/src/chat/utils/chat_message_builder.py index 2359abf30..ab97f395b 100644 --- a/src/chat/utils/chat_message_builder.py +++ b/src/chat/utils/chat_message_builder.py @@ -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: diff --git a/src/common/database/database_model.py b/src/common/database/database_model.py index 500852d00..8c2bf423b 100644 --- a/src/common/database/database_model.py +++ b/src/common/database/database_model.py @@ -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 diff --git a/src/common/logger.py b/src/common/logger.py index 7202b993f..40fd15070 100644 --- a/src/common/logger.py +++ b/src/common/logger.py @@ -321,14 +321,13 @@ MODULE_COLORS = { # 核心模块 "main": "\033[1;97m", # 亮白色+粗体 (主程序) "api": "\033[92m", # 亮绿色 - "emoji": "\033[92m", # 亮绿色 + "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,8 +338,7 @@ 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", # 深粉色 # 聊天相关模块 @@ -356,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", # 橙红色 @@ -386,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", # 灰色 @@ -402,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" diff --git a/src/person_info/person_info.py b/src/person_info/person_info.py index 86e3b6fcd..7f22fc2d4 100644 --- a/src/person_info/person_info.py +++ b/src/person_info/person_info.py @@ -48,6 +48,7 @@ person_info_default = { "points": None, "forgotten_points": None, "relation_value": None, + "attitude": 50, } diff --git a/src/person_info/relationship_manager.py b/src/person_info/relationship_manager.py index 2d37bcda8..813036c69 100644 --- a/src/person_info/relationship_manager.py +++ b/src/person_info/relationship_manager.py @@ -21,66 +21,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,18 +113,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 +127,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) @@ -239,8 +173,10 @@ class RelationshipManager: user_count += 1 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 +321,121 @@ 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 +450,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 +471,77 @@ 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) + + return current_points - 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 - ) def calculate_time_weight(self, point_time: str, current_time: str) -> float: """计算基于时间的权重系数""" diff --git a/src/plugins/built_in/core_actions/no_reply.py b/src/plugins/built_in/core_actions/no_reply.py index c3cf0201c..a5c8d637b 100644 --- a/src/plugins/built_in/core_actions/no_reply.py +++ b/src/plugins/built_in/core_actions/no_reply.py @@ -103,6 +103,8 @@ class NoReplyAction(BaseAction): logger.info(f"{self.log_prefix} 选择不回复(第{count}次),开始摸鱼,原因: {reason}") + + # 进入等待状态 while True: current_time = time.time() elapsed_time = current_time - start_time @@ -141,19 +143,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}),进行智能判断...") # 获取最近的消息内容用于判断 @@ -166,7 +158,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, ) # 获取身份信息 @@ -189,81 +184,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判断,继续循环等待 # 构建判断上下文 @@ -379,6 +306,105 @@ 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分钟是否完全没有发言,决定是否退出专注模式 From d49a6b840e1328e4a667f0f3e5be89190803ee4e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 7 Jul 2025 18:04:52 +0000 Subject: [PATCH 13/13] =?UTF-8?q?=F0=9F=A4=96=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3=E7=A0=81=20[skip=20ci]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../heart_flow/heartflow_message_processor.py | 4 +-- src/chat/normal_chat/normal_chat.py | 4 ++- src/person_info/relationship_manager.py | 26 +++++++------------ src/plugins/built_in/core_actions/no_reply.py | 9 ++++--- 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/src/chat/heart_flow/heartflow_message_processor.py b/src/chat/heart_flow/heartflow_message_processor.py index 5de0d7015..52a9751ea 100644 --- a/src/chat/heart_flow/heartflow_message_processor.py +++ b/src/chat/heart_flow/heartflow_message_processor.py @@ -123,9 +123,7 @@ class HeartFCMessageReceiver: picid_pattern = r"\[picid:([^\]]+)\]" processed_plain_text = re.sub(picid_pattern, "[图片]", message.processed_plain_text) - logger.info( - f"[{mes_name}]{userinfo.user_nickname}:{processed_plain_text}" - ) + logger.info(f"[{mes_name}]{userinfo.user_nickname}:{processed_plain_text}") logger.debug(f"[{mes_name}][当前时段回复频率: {current_talk_frequency}]") diff --git a/src/chat/normal_chat/normal_chat.py b/src/chat/normal_chat/normal_chat.py index 43ab5803e..51642a700 100644 --- a/src/chat/normal_chat/normal_chat.py +++ b/src/chat/normal_chat/normal_chat.py @@ -596,7 +596,9 @@ class NormalChat: timeout_source = " 和 ".join(timeout_details) - logger.warning(f"[{self.stream_name}] {timeout_source} 任务超时 ({global_config.chat.thinking_timeout}秒),正在取消相关任务...") + logger.warning( + f"[{self.stream_name}] {timeout_source} 任务超时 ({global_config.chat.thinking_timeout}秒),正在取消相关任务..." + ) # print(f"111{self.timeout_count}") self.timeout_count += 1 if self.timeout_count > 5: diff --git a/src/person_info/relationship_manager.py b/src/person_info/relationship_manager.py index 813036c69..12891235c 100644 --- a/src/person_info/relationship_manager.py +++ b/src/person_info/relationship_manager.py @@ -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 @@ -26,7 +24,6 @@ class RelationshipManager: request_type="relationship", # 用于动作规划 ) - @staticmethod async def is_known_some_one(platform, user_id): """判断是否认识某人""" @@ -113,7 +110,6 @@ class RelationshipManager: return relation_prompt - async def update_person_impression(self, person_id, timestamp, bot_engaged_messages=None): """更新用户印象 @@ -173,7 +169,7 @@ class RelationshipManager: user_count += 1 name_mapping[replace_person_name] = f"用户{current_user}{user_count if user_count > 1 else ''}" current_user = chr(ord(current_user) + 1) - + readable_messages = build_readable_messages( messages=user_messages, replace_bot_name=True, timestamp_mode="normal_no_YMD", truncate=True ) @@ -327,7 +323,7 @@ class RelationshipManager: 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: @@ -335,17 +331,16 @@ class RelationshipManager: 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 @@ -362,14 +357,12 @@ class RelationshipManager: else: max_impression_length = 100 max_short_impression_length = 50 - + # 根据好感度,调整印象和简短印象的最大长度 - attitude_multiplier = (abs(100-attitude) / 100) + 1 + 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: @@ -539,9 +532,8 @@ class RelationshipManager: await person_info_manager.update_one_field( person_id, "forgotten_points", json.dumps(forgotten_points, ensure_ascii=False, indent=None) ) - - return current_points + return current_points def calculate_time_weight(self, point_time: str, current_time: str) -> float: """计算基于时间的权重系数""" diff --git a/src/plugins/built_in/core_actions/no_reply.py b/src/plugins/built_in/core_actions/no_reply.py index a5c8d637b..a573a39fd 100644 --- a/src/plugins/built_in/core_actions/no_reply.py +++ b/src/plugins/built_in/core_actions/no_reply.py @@ -103,7 +103,6 @@ class NoReplyAction(BaseAction): logger.info(f"{self.log_prefix} 选择不回复(第{count}次),开始摸鱼,原因: {reason}") - # 进入等待状态 while True: current_time = time.time() @@ -387,7 +386,9 @@ class NoReplyAction(BaseAction): f"{self.log_prefix} 发言过多(超过{over_count}条),随机决定跳过此次LLM判断(概率{skip_probability * 100:.0f}%)" ) - logger.info(f"{self.log_prefix} 过去10分钟发言{bot_message_count}条,超过阈值{talk_frequency_threshold},添加疲惫提示") + logger.info( + f"{self.log_prefix} 过去10分钟发言{bot_message_count}条,超过阈值{talk_frequency_threshold},添加疲惫提示" + ) return frequency_block, should_skip_judge else: # 回复次数少时的正向提示 @@ -398,7 +399,9 @@ class NoReplyAction(BaseAction): elif under_count >= talk_frequency_threshold * 0.5: frequency_block = "你感觉状态不错。\n" - logger.info(f"{self.log_prefix} 过去10分钟发言{bot_message_count}条,未超过阈值{talk_frequency_threshold},添加正向提示") + logger.info( + f"{self.log_prefix} 过去10分钟发言{bot_message_count}条,未超过阈值{talk_frequency_threshold},添加正向提示" + ) return frequency_block, False except Exception as e: