打通自动回复与表情语义库联动\n\n- 新增表情语义解析与表情资产查询模块,支持从历史表情中提取可读中文语义\n- 为 ai_auto_response 增加短回复表情匹配器,命中语义时优先发送表情并支持失败回退文本\n- 调整自动回复提示词与配置项,强化短情绪回复场景的表情替换能力

This commit is contained in:
liuwei
2026-04-27 11:40:44 +08:00
parent 884ffb81e8
commit 623ca505d4
6 changed files with 540 additions and 2 deletions

View File

@@ -38,6 +38,7 @@ from .memory.social_memory import SocialMemoryService
from .profile.group_profile import GroupProfileResolver
from .context.conversation_hints import build_conversation_hints
from .core.decision_flow import DecisionFlow
from .core.emoji_reply import EmojiReplySelector
from .core.triggers import TriggerRouter
from .core.llm_result_parser import LLMResultParser
from .core.reply_formatter import finalize_reply, preview_text
@@ -101,6 +102,7 @@ class AIAutoResponsePlugin(MessagePluginInterface):
self.queue_maxsize = 200
self.queue_workers: List[asyncio.Task] = []
self.reply_limits: Dict[str, Any] = {}
self.emoji_reply_config: Dict[str, Any] = {}
self.prompt_compact_config: Dict[str, Any] = {}
self.message_expire_sec = 0.0
self.room_message_seq_counter = 0
@@ -142,8 +144,10 @@ class AIAutoResponsePlugin(MessagePluginInterface):
self.mode_config = self._config.get("mode", {}) or {}
self.cooldown_config = self._config.get("cooldown", {}) or {}
self.reply_limits = self._config.get("reply", {}) or {}
self.emoji_reply_config = self._config.get("emoji_reply", {}) or {}
self.prompt_compact_config = self._config.get("prompt_compact", {}) or {}
self.cooldown = CooldownManager(self.cooldown_config)
self.emoji_reply_selector = EmojiReplySelector(self.db_manager, self.emoji_reply_config)
self.image_config = self._config.get("image", {}) or {}
self.spam_config = self._config.get("spam_guard", {}) or {}
runtime_config = self._config.get("runtime", {}) or {}
@@ -681,8 +685,37 @@ class AIAutoResponsePlugin(MessagePluginInterface):
)
return False, "duplicate_reply"
for chunk in reply_chunks:
await bot.send_text_message(room_id, chunk, sender)
# 这里让“自动回复文本”先经过一次本地表情匹配:
# 1. 模型仍然只负责输出自然语言,不需要知道 md5
# 2. 只有命中中文语义库且回复足够短时,才会切换成表情发送;
# 3. 若表情发送失败,立刻回退到原始文本,避免因为表情链路影响主回复成功率。
sent_as_emoji = False
emoji_asset = self.emoji_reply_selector.match_reply_to_emoji(final_response_text, reply_chunks)
if emoji_asset and emoji_asset.get("md5") and int(emoji_asset.get("total_length") or 0) > 0:
try:
await bot.send_emoji_message(
room_id,
str(emoji_asset.get("md5")),
int(emoji_asset.get("total_length") or 0),
)
sent_as_emoji = True
except Exception as emoji_error:
self._log_event(
"emoji_fallback",
room_id=room_id,
sender=sender,
trigger_type=trigger.trigger_type,
reply_mode=reply_mode,
topic=selected_topic,
response_preview=preview_text(final_response_text),
emoji_semantic=emoji_asset.get("semantic_text", ""),
emoji_match_score=emoji_asset.get("match_score", 0),
error=str(emoji_error),
)
if not sent_as_emoji:
for chunk in reply_chunks:
await bot.send_text_message(room_id, chunk, sender)
self.cooldown.note_reply(room_id)
self.flow_manager.note_bot_reply(room_id)
self.memory_store.note_bot_reply(room_id, sender, selected_topic)
@@ -698,6 +731,9 @@ class AIAutoResponsePlugin(MessagePluginInterface):
response_preview=preview_text(final_response_text),
response_len=len(final_response_text),
chunk_count=len(reply_chunks),
sent_as_emoji=yn(sent_as_emoji),
emoji_semantic=(emoji_asset or {}).get("semantic_text", ""),
emoji_match_score=(emoji_asset or {}).get("match_score", 0),
)
return False, "replied"
finally: