feat(affinity_flow_chatter): 重构计划器以支持多动作并优化思考逻辑
本次提交对亲和流聊天器(AFC)的计划与决策核心进行了重大重构和功能增强,旨在提升其响应的灵活性、鲁棒性和可观测性。
主要变更包括:
1. **多动作支持与解析重构**:
- `PlanFilter` 现在能够正确解析并处理 LLM 返回的动作列表(`"actions": [...]`),而不仅限于单个动作,这使得机器人能够执行更复杂的组合行为。
- 增强了动作解析的鲁棒性,当找不到 `target_message_id` 时会优雅降级(如 `reply` 变为 `no_action`),并会根据当前实际可用的动作列表对 LLM 的选择进行验证。
2. **提示词工程与思考模式优化**:
- 重新设计了核心 Planner 提示词,将 `thinking` 字段定义为“思绪流”,引导 LLM 生成更自然、更符合角色的内心独白,而非简单的决策理由,从而提升决策质量和角色扮演的沉浸感。
- 强制要求 LLM 为需要目标消息的动作提供 `target_message_id`,提高了动作执行的准确性。
3. **上下文构建与鲁棒性增强**:
- 在 `PlanFilter` 中增加了上下文回退机制,当内存中缺少历史消息时(如冷启动),会自动从数据库加载最近的消息记录,确保决策所需上下文的完整性。
- 简化了提供给 LLM 的未读消息格式,移除了兴趣度分数等内部信息,并加入了用户昵称,使其更易于理解和处理。
4. **可观测性与日志改进**:
- 在 AFC 的多个关键节点(消息接收、决策、动作执行)增加了彩色的详细日志,使其决策流程像 HFC 一样清晰可见,极大地方便了调试。
- 将系统中多个模块(视频分析、兴趣度匹配、情绪管理)的常规日志级别从 `INFO` 调整为 `DEBUG`,以减少在生产环境中的日志噪音。
5. **动作描述优化**:
- 优化了 `set_emoji_like` 和 `emoji` 等动作的描述,使其意图更清晰,帮助 LLM 做出更准确的动作选择。
This commit is contained in:
@@ -464,7 +464,7 @@ class BotInterestManager:
|
||||
low_similarity_count += 1
|
||||
result.add_match(tag.tag_name, enhanced_score, [tag.tag_name])
|
||||
|
||||
logger.info(
|
||||
logger.debug(
|
||||
f"匹配统计: {match_count}/{len(active_tags)} 个标签命中 | "
|
||||
f"高(>{high_threshold}): {high_similarity_count}, "
|
||||
f"中(>{medium_threshold}): {medium_similarity_count}, "
|
||||
@@ -492,9 +492,9 @@ class BotInterestManager:
|
||||
if result.matched_tags:
|
||||
top_tag_name = max(result.match_scores.items(), key=lambda x: x[1])[0]
|
||||
result.top_tag = top_tag_name
|
||||
logger.info(f"最佳匹配: '{top_tag_name}' (分数: {result.match_scores[top_tag_name]:.3f})")
|
||||
logger.debug(f"最佳匹配: '{top_tag_name}' (分数: {result.match_scores[top_tag_name]:.3f})")
|
||||
|
||||
logger.info(
|
||||
logger.debug(
|
||||
f"最终结果: 总分={result.overall_score:.3f}, 置信度={result.confidence:.3f}, 匹配标签数={len(result.matched_tags)}"
|
||||
)
|
||||
return result
|
||||
|
||||
@@ -59,10 +59,59 @@ class VideoAnalyzer:
|
||||
|
||||
# 人格与提示模板
|
||||
try:
|
||||
persona = global_config.personality
|
||||
self.personality_core = getattr(persona, "personality_core", "是一个积极向上的女大学生")
|
||||
self.personality_side = getattr(persona, "personality_side", "用一句话或几句话描述人格的侧面特点")
|
||||
except Exception: # pragma: no cover
|
||||
import cv2
|
||||
|
||||
opencv_available = True
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
if not RUST_VIDEO_AVAILABLE and not opencv_available:
|
||||
logger.error("❌ 没有可用的视频处理实现,视频分析器将被禁用")
|
||||
self.disabled = True
|
||||
return
|
||||
elif not RUST_VIDEO_AVAILABLE:
|
||||
logger.warning("⚠️ Rust视频处理模块不可用,将使用Python降级实现")
|
||||
elif not opencv_available:
|
||||
logger.warning("⚠️ OpenCV不可用,仅支持Rust关键帧模式")
|
||||
|
||||
self.disabled = False
|
||||
|
||||
# 使用专用的视频分析配置
|
||||
try:
|
||||
self.video_llm = LLMRequest(
|
||||
model_set=model_config.model_task_config.video_analysis, request_type="video_analysis"
|
||||
)
|
||||
logger.debug("✅ 使用video_analysis模型配置")
|
||||
except (AttributeError, KeyError) as e:
|
||||
# 如果video_analysis不存在,使用vlm配置
|
||||
self.video_llm = LLMRequest(model_set=model_config.model_task_config.vlm, request_type="vlm")
|
||||
logger.warning(f"video_analysis配置不可用({e}),回退使用vlm配置")
|
||||
|
||||
# 从配置文件读取参数,如果配置不存在则使用默认值
|
||||
config = global_config.video_analysis
|
||||
|
||||
# 使用 getattr 统一获取配置参数,如果配置不存在则使用默认值
|
||||
self.max_frames = getattr(config, "max_frames", 6)
|
||||
self.frame_quality = getattr(config, "frame_quality", 85)
|
||||
self.max_image_size = getattr(config, "max_image_size", 600)
|
||||
self.enable_frame_timing = getattr(config, "enable_frame_timing", True)
|
||||
|
||||
# Rust模块相关配置
|
||||
self.rust_keyframe_threshold = getattr(config, "rust_keyframe_threshold", 2.0)
|
||||
self.rust_use_simd = getattr(config, "rust_use_simd", True)
|
||||
self.rust_block_size = getattr(config, "rust_block_size", 8192)
|
||||
self.rust_threads = getattr(config, "rust_threads", 0)
|
||||
self.ffmpeg_path = getattr(config, "ffmpeg_path", "ffmpeg")
|
||||
|
||||
# 从personality配置中获取人格信息
|
||||
try:
|
||||
personality_config = global_config.personality
|
||||
self.personality_core = getattr(personality_config, "personality_core", "是一个积极向上的女大学生")
|
||||
self.personality_side = getattr(
|
||||
personality_config, "personality_side", "用一句话或几句话描述人格的侧面特点"
|
||||
)
|
||||
except AttributeError:
|
||||
# 如果没有personality配置,使用默认值
|
||||
self.personality_core = "是一个积极向上的女大学生"
|
||||
self.personality_side = "用一句话或几句话描述人格的侧面特点"
|
||||
|
||||
@@ -72,12 +121,76 @@ class VideoAnalyzer:
|
||||
"""请以第一人称视角阅读这些按时间顺序提取的关键帧。\n核心:{personality_core}\n人格:{personality_side}\n请详细描述视频(主题/人物与场景/动作与时间线/视觉风格/情绪氛围/特殊元素)。""",
|
||||
)
|
||||
|
||||
# 新增的线程池配置
|
||||
self.use_multiprocessing = getattr(config, "use_multiprocessing", True)
|
||||
self.max_workers = getattr(config, "max_workers", 2)
|
||||
self.frame_extraction_mode = getattr(config, "frame_extraction_mode", "fixed_number")
|
||||
self.frame_interval_seconds = getattr(config, "frame_interval_seconds", 2.0)
|
||||
|
||||
# 将配置文件中的模式映射到内部使用的模式名称
|
||||
config_mode = getattr(config, "analysis_mode", "auto")
|
||||
if config_mode == "batch_frames":
|
||||
self.analysis_mode = "batch"
|
||||
elif config_mode == "frame_by_frame":
|
||||
self.analysis_mode = "sequential"
|
||||
elif config_mode == "auto":
|
||||
self.analysis_mode = "auto"
|
||||
else:
|
||||
logger.warning(f"无效的分析模式: {config_mode},使用默认的auto模式")
|
||||
self.analysis_mode = "auto"
|
||||
|
||||
self.frame_analysis_delay = 0.3 # API调用间隔(秒)
|
||||
self.frame_interval = 1.0 # 抽帧时间间隔(秒)
|
||||
self.batch_size = 3 # 批处理时每批处理的帧数
|
||||
self.timeout = 60.0 # 分析超时时间(秒)
|
||||
|
||||
if config:
|
||||
logger.debug("✅ 从配置文件读取视频分析参数")
|
||||
else:
|
||||
logger.warning("配置文件中缺少video_analysis配置,使用默认值")
|
||||
|
||||
# 系统提示词
|
||||
self.system_prompt = "你是一个专业的视频内容分析助手。请仔细观察用户提供的视频关键帧,详细描述视频内容。"
|
||||
|
||||
logger.debug(f"✅ 视频分析器初始化完成,分析模式: {self.analysis_mode}, 线程池: {self.use_multiprocessing}")
|
||||
|
||||
# 获取Rust模块系统信息
|
||||
self._log_system_info()
|
||||
|
||||
def _log_system_info(self):
|
||||
"""记录系统信息"""
|
||||
if not RUST_VIDEO_AVAILABLE:
|
||||
logger.info("⚠️ Rust模块不可用,跳过系统信息获取")
|
||||
return
|
||||
|
||||
try:
|
||||
self.video_llm = LLMRequest(
|
||||
model_set=model_config.model_task_config.video_analysis, request_type="video_analysis"
|
||||
)
|
||||
except Exception:
|
||||
self.video_llm = LLMRequest(model_set=model_config.model_task_config.vlm, request_type="vlm")
|
||||
system_info = rust_video.get_system_info()
|
||||
logger.debug(f"🔧 系统信息: 线程数={system_info.get('threads', '未知')}")
|
||||
|
||||
# 记录CPU特性
|
||||
features = []
|
||||
if system_info.get("avx2_supported"):
|
||||
features.append("AVX2")
|
||||
if system_info.get("sse2_supported"):
|
||||
features.append("SSE2")
|
||||
if system_info.get("simd_supported"):
|
||||
features.append("SIMD")
|
||||
|
||||
if features:
|
||||
logger.debug(f"🚀 CPU特性: {', '.join(features)}")
|
||||
else:
|
||||
logger.debug("⚠️ 未检测到SIMD支持")
|
||||
|
||||
logger.debug(f"📦 Rust模块版本: {system_info.get('version', '未知')}")
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"获取系统信息失败: {e}")
|
||||
|
||||
def _calculate_video_hash(self, video_data: bytes) -> str:
|
||||
"""计算视频文件的hash值"""
|
||||
hash_obj = hashlib.sha256()
|
||||
hash_obj.update(video_data)
|
||||
return hash_obj.hexdigest()
|
||||
|
||||
self._log_system()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user