Compare commits

...

7 Commits

Author SHA1 Message Date
6fbfe735c9 feat: 添加选项必须检索长期记忆
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 2m53s
2025-12-10 12:57:32 +08:00
25a5e72c07 fix: 修复回复分割问题 2025-12-10 12:57:16 +08:00
6ce381bc1d feat: 添加 ffmpeg 2025-12-10 12:57:13 +08:00
5a1ea326e9 fix: 分析记忆时修复引号内容 2025-12-10 12:55:42 +08:00
46e3b04d8f fix: 修复 VLM 解析 2025-12-10 12:55:21 +08:00
5a04e76934 chore: 添加本地构建配置 2025-12-10 12:55:21 +08:00
2c39b77ca7 fix: 记忆提取添加末尾逗号 2025-12-10 12:55:21 +08:00
9 changed files with 91 additions and 169 deletions

View File

@@ -0,0 +1,32 @@
name: Build and Push Docker Image
on:
push:
branches:
- dev
- gitea
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Registry
uses: docker/login-action@v3
with:
registry: docker.gardel.top
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and Push Docker Image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: docker.gardel.top/gardel/mofox:dev
build-args: |
BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
VCS_REF=${{ github.sha }}

View File

@@ -1,149 +0,0 @@
name: Docker Build and Push
on:
push:
branches:
- master
- dev
tags:
- "v*.*.*"
- "v*"
- "*.*.*"
- "*.*.*-*"
workflow_dispatch: # 允许手动触发工作流
# Workflow's jobs
jobs:
build-amd64:
name: Build AMD64 Image
runs-on: ubuntu-24.04
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- name: Check out git repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- 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 }}/mofox
# 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 }}/mofox:amd64-buildcache
cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/mofox:amd64-buildcache,mode=max
outputs: type=image,name=${{ secrets.DOCKERHUB_USERNAME }}/mofox,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-24.04-arm
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- name: Check out git repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- 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 }}/mofox
# 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 }}/mofox:arm64-buildcache
cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/mofox:arm64-buildcache,mode=max
outputs: type=image,name=${{ secrets.DOCKERHUB_USERNAME }}/mofox,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-24.04
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
uses: docker/metadata-action@v5
with:
images: ${{ secrets.DOCKERHUB_USERNAME }}/mofox
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,prefix=${{ github.ref_name }}-,enable=${{ github.ref_type == 'branch' }}
- 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 }}/mofox@${{ needs.build-amd64.outputs.digest }} \
${{ secrets.DOCKERHUB_USERNAME }}/mofox@${{ needs.build-arm64.outputs.digest }}
done

View File

@@ -9,6 +9,10 @@ RUN apt-get update && apt-get install -y build-essential
# 复制依赖列表和锁文件
COPY pyproject.toml uv.lock ./
COPY --from=mwader/static-ffmpeg:latest /ffmpeg /usr/local/bin/ffmpeg
COPY --from=mwader/static-ffmpeg:latest /ffprobe /usr/local/bin/ffprobe
RUN ldconfig && ffmpeg -version
# 安装依赖(使用 --frozen 确保使用锁文件中的版本)
RUN uv sync --frozen --no-dev

View File

@@ -4,6 +4,7 @@ import binascii
import hashlib
import io
import json
import json_repair
import os
import random
import re
@@ -1022,6 +1023,15 @@ class EmojiManager:
- 必须是表情包,非普通截图。
- 图中文字不超过5个。
请确保你的最终输出是严格的JSON对象不要添加任何额外解释或文本。
输出格式:
```json
{{
"detailed_description": "",
"keywords": [],
"refined_sentence": "",
"is_compliant": true
}}
```
"""
image_data_for_vlm, image_format_for_vlm = image_base64, image_format
@@ -1041,16 +1051,14 @@ class EmojiManager:
if not vlm_response_str:
continue
match = re.search(r"\{.*\}", vlm_response_str, re.DOTALL)
if match:
vlm_response_json = json.loads(match.group(0))
description = vlm_response_json.get("detailed_description", "")
emotions = vlm_response_json.get("keywords", [])
refined_description = vlm_response_json.get("refined_sentence", "")
is_compliant = vlm_response_json.get("is_compliant", False)
if description and emotions and refined_description:
logger.info("[VLM分析] 成功解析VLM返回的JSON数据。")
break
vlm_response_json = self._parse_json_response(vlm_response_str)
description = vlm_response_json.get("detailed_description", "")
emotions = vlm_response_json.get("keywords", [])
refined_description = vlm_response_json.get("refined_sentence", "")
is_compliant = vlm_response_json.get("is_compliant", False)
if description and emotions and refined_description:
logger.info("[VLM分析] 成功解析VLM返回的JSON数据。")
break
logger.warning("[VLM分析] VLM返回的JSON数据不完整或格式错误准备重试。")
except (json.JSONDecodeError, AttributeError) as e:
logger.error(f"VLM JSON解析失败 (第 {i+1}/3 次): {e}")
@@ -1195,6 +1203,29 @@ class EmojiManager:
logger.error(f"[错误] 删除异常处理文件时出错: {remove_error}")
return False
@classmethod
def _parse_json_response(cls, response: str) -> dict[str, Any] | None:
"""解析 LLM 的 JSON 响应"""
try:
# 尝试提取 JSON 代码块
json_match = re.search(r"```json\s*(.*?)\s*```", response, re.DOTALL)
if json_match:
json_str = json_match.group(1)
else:
# 尝试直接解析
json_str = response.strip()
# 移除可能的注释
json_str = re.sub(r"//.*", "", json_str)
json_str = re.sub(r"/\*.*?\*/", "", json_str, flags=re.DOTALL)
data = json_repair.loads(json_str)
return data
except json.JSONDecodeError as e:
logger.warning(f"JSON 解析失败: {e}, 响应: {response[:200]}")
return None
emoji_manager = None

View File

@@ -614,7 +614,7 @@ class DefaultReplyer:
# 使用统一管理器的智能检索Judge模型决策
search_result = await unified_manager.search_memories(
query_text=query_text,
use_judge=True,
use_judge=global_config.memory.use_judge,
recent_chat_history=chat_history, # 传递最近聊天历史
)
@@ -1799,8 +1799,9 @@ class DefaultReplyer:
)
if content:
# 移除 [SPLIT] 标记,防止消息被分割
content = content.replace("[SPLIT]", "")
if not global_config.response_splitter.enable or global_config.response_splitter.split_mode != 'llm':
# 移除 [SPLIT] 标记,防止消息被分割
content = content.replace("[SPLIT]", "")
# 应用统一的格式过滤器
from src.chat.utils.utils import filter_system_format_content

View File

@@ -508,6 +508,7 @@ class MemoryConfig(ValidatedConfigBase):
short_term_decay_factor: float = Field(default=0.98, description="衰减因子")
# 长期记忆层配置
use_judge: bool = Field(default=True, description="使用评判模型决定是否检索长期记忆")
long_term_batch_size: int = Field(default=10, description="批量转移大小")
long_term_decay_factor: float = Field(default=0.95, description="衰减因子")
long_term_auto_transfer_interval: int = Field(default=60, description="自动转移间隔(秒)")

View File

@@ -11,6 +11,7 @@ import asyncio
import json
import re
import uuid
import json_repair
from pathlib import Path
from typing import Any
@@ -186,8 +187,8 @@ class ShortTermMemoryManager:
"importance": 0.7,
"attributes": {{
"time": "时间信息",
"attribute1": "其他属性1"
"attribute2": "其他属性2"
"attribute1": "其他属性1",
"attribute2": "其他属性2",
...
}}
}}
@@ -530,7 +531,7 @@ class ShortTermMemoryManager:
json_str = re.sub(r"//.*", "", json_str)
json_str = re.sub(r"/\*.*?\*/", "", json_str, flags=re.DOTALL)
data = json.loads(json_str)
data = json_repair.loads(json_str)
return data
except json.JSONDecodeError as e:

View File

@@ -235,7 +235,7 @@ class KFCContextBuilder:
search_result = await unified_manager.search_memories(
query_text=query_text,
use_judge=True,
use_judge=config.memory.use_judge,
recent_chat_history=chat_history,
)

View File

@@ -1,5 +1,5 @@
[inner]
version = "7.9.8"
version = "7.9.9"
#----以下是给开发人员阅读的如果你只是部署了MoFox-Bot不需要阅读----
#如果你想要修改配置文件请递增version的值
@@ -311,6 +311,7 @@ short_term_search_top_k = 5 # 搜索时返回的最大数量
short_term_decay_factor = 0.98 # 衰减因子
# 长期记忆层配置
use_judge = true # 使用评判模型决定是否检索长期记忆
long_term_batch_size = 10 # 批量转移大小
long_term_decay_factor = 0.95 # 衰减因子
long_term_auto_transfer_interval = 180 # 自动转移间隔(秒)