Merge branch 'Mai-with-u:dev' into dev

This commit is contained in:
Dawn ARC
2026-04-30 18:28:23 +08:00
committed by GitHub
83 changed files with 139 additions and 13060 deletions

View File

@@ -240,7 +240,7 @@ class MaisakaExpressionSelector:
if candidate.get("id") in selected_ids
]
self._update_last_active_time(selected_ids)
logger.info(
logger.debug(
f"表达方式直接注入session_id={session_id} 已选数={len(selected_ids)} "
f"selected_ids={selected_ids!r} 已选预览={self._format_candidate_preview(selected_expressions)}"
)

View File

@@ -93,12 +93,14 @@ class BaseMaisakaReplyGenerator:
alias_names = global_config.bot.alias_names
bot_aliases = f",也有人叫你{','.join(alias_names)}" if alias_names else ""
prompt_personality = global_config.personality.personality
prompt_personality = global_config.personality.personality.strip()
if not prompt_personality:
prompt_personality = "是人类。"
return f"你的名字是{bot_name}{bot_aliases},你{prompt_personality};"
return f"你的名字是{bot_name}{bot_aliases}\n{prompt_personality}"
except Exception as exc:
logger.warning(f"构建 Maisaka 人设提示词失败: {exc}")
return "你的名字是麦麦,你是一个活泼可爱的 AI 助手"
return "你的名字是麦麦\n是人类"
@staticmethod
def _normalize_content(content: str, limit: int = 500) -> str:

View File

@@ -36,7 +36,6 @@ class ReplyerManager:
generator_type = self._get_maisaka_generator_type() if replyer_type == "maisaka" else ""
cache_key = f"{replyer_type}:{generator_type}:{stream_id}"
if cache_key in self._repliers:
logger.info(f"[ReplyerManager] 命中缓存 replyer: cache_key={cache_key}")
return self._repliers[cache_key]
target_stream = chat_stream or _chat_manager.get_session_by_session_id(stream_id)

View File

@@ -56,7 +56,7 @@ BOT_CONFIG_PATH: Path = (CONFIG_DIR / "bot_config.toml").resolve().absolute()
MODEL_CONFIG_PATH: Path = (CONFIG_DIR / "model_config.toml").resolve().absolute()
LEGACY_ENV_PATH: Path = (PROJECT_ROOT / ".env").resolve().absolute()
MMC_VERSION: str = "1.0.0"
CONFIG_VERSION: str = "8.9.18"
CONFIG_VERSION: str = "8.9.19"
MODEL_CONFIG_VERSION: str = "1.14.3"
logger = get_logger("config")

View File

@@ -75,13 +75,13 @@ class PersonalityConfig(ConfigBase):
__ui_icon__ = "user-circle"
personality: str = Field(
default="是一个大二女大学生,现在正在上网和群友聊天。",
default="是一个大二女大学生,现在正在上网和群友聊天。",
json_schema_extra={
"x-widget": "textarea",
"x-icon": "user-circle",
},
)
"""人格,建议100字以内描述人格特质和身份特征"""
"""人格,建议200字以内描述人格特质和身份特征;可以写完整设定。要求第二人称"""
reply_style: str = Field(
default="你的风格平淡简短。可以参考贴吧,知乎和微博的回复风格。不浮夸不长篇大论,不要过分修辞和复杂句。尽量回复的简短一些,平淡一些",
@@ -1262,7 +1262,7 @@ class DebugConfig(ConfigBase):
__ui_icon__ = "more-horizontal"
enable_maisaka_stage_board: bool = Field(
default=True,
default=False,
json_schema_extra={
"x-widget": "switch",
"x-icon": "layout-dashboard",

View File

@@ -939,7 +939,7 @@ class LLMOrchestrator:
audio_base64=audio_base64,
)
if self.request_type.startswith("maisaka_"):
logger.info(
logger.debug(
f"LLMOrchestrator[{self.request_type}] 正在向模型 model={model_info.name} 发送请求 "
f"(tool_options={len(tool_options or [])})"
)
@@ -949,7 +949,7 @@ class LLMOrchestrator:
request=request,
)
if self.request_type.startswith("maisaka_"):
logger.info(
logger.debug(
f"LLMOrchestrator[{self.request_type}] 模型 model={model_info.name} 已返回 API 响应"
)
total_tokens, penalty, usage_penalty = self.model_usage[model_info.name]
@@ -962,7 +962,7 @@ class LLMOrchestrator:
total_tokens, penalty, usage_penalty = self.model_usage[model_info.name]
self.model_usage[model_info.name] = (total_tokens, penalty, usage_penalty - 1)
if self.request_type.startswith("maisaka_"):
logger.info(
logger.debug(
f"LLMOrchestrator[{self.request_type}] 模型 model={model_info.name} 的请求已被外部信号中断"
)
raise e

View File

@@ -282,15 +282,17 @@ class MaisakaChatLoopService:
try:
bot_name = global_config.bot.nickname
if global_config.bot.alias_names:
bot_nickname = f", also known as {','.join(global_config.bot.alias_names)}"
bot_nickname = f",也有人叫你{','.join(global_config.bot.alias_names)}"
else:
bot_nickname = ""
prompt_personality = global_config.personality.personality
prompt_personality = global_config.personality.personality.strip()
if not prompt_personality:
prompt_personality = "是人类。"
return f"Your name is {bot_name}{bot_nickname}; persona: {prompt_personality};"
return f"你的名字是{bot_name}{bot_nickname}\n{prompt_personality}"
except Exception:
return "Your name is MaiMai; persona: lively and cute AI assistant."
return "你的名字是麦麦。\n是人类。"
async def ensure_chat_prompt_loaded(self, tools_section: str = "") -> None:
"""确保主聊天提示词已经加载完成。

View File

@@ -54,6 +54,7 @@ logger = get_logger("maisaka_reasoning_engine")
TIMING_GATE_CONTEXT_LIMIT = 24
TIMING_GATE_MAX_TOKENS = 384
TIMING_GATE_MAX_ATTEMPTS = 3
TIMING_GATE_TOOL_NAMES = {"continue", "no_reply", "wait"}
HISTORY_SILENT_TOOL_NAMES = {"finish"}
@@ -247,36 +248,69 @@ class MaisakaReasoningEngine:
if self._runtime._force_next_timing_continue:
return self._build_forced_continue_timing_result()
response = await self._run_timing_gate_sub_agent(
context_message_limit=TIMING_GATE_CONTEXT_LIMIT,
system_prompt=self._build_timing_gate_system_prompt(),
tool_definitions=get_timing_tools(),
)
tool_result_summaries: list[str] = []
tool_monitor_results: list[dict[str, Any]] = []
response: Any = None
selected_tool_call: Optional[ToolCall] = None
for tool_call in response.tool_calls:
if tool_call.func_name in TIMING_GATE_TOOL_NAMES:
selected_tool_call = tool_call
invalid_tool_text = ""
for attempt_index in range(TIMING_GATE_MAX_ATTEMPTS):
response = await self._run_timing_gate_sub_agent(
context_message_limit=TIMING_GATE_CONTEXT_LIMIT,
system_prompt=self._build_timing_gate_system_prompt(),
tool_definitions=get_timing_tools(),
)
selected_tool_call = None
for tool_call in response.tool_calls:
if tool_call.func_name in TIMING_GATE_TOOL_NAMES:
selected_tool_call = tool_call
break
if selected_tool_call is not None:
break
if selected_tool_call is None:
invalid_tool_names = [
str(tool_call.func_name).strip()
for tool_call in response.tool_calls
if str(tool_call.func_name).strip()
]
invalid_tool_text = "".join(invalid_tool_names) if invalid_tool_names else "无工具"
logger.warning(
f"{self._runtime.log_prefix} Timing Gate 未返回有效控制工具:{invalid_tool_text},将按 no_reply 处理"
)
self._append_timing_gate_invalid_tool_hint(invalid_tool_text)
remaining_attempts = TIMING_GATE_MAX_ATTEMPTS - attempt_index - 1
if remaining_attempts > 0:
logger.warning(
f"{self._runtime.log_prefix} Timing Gate 未返回有效控制工具:{invalid_tool_text}"
f"将重试 ({attempt_index + 1}/{TIMING_GATE_MAX_ATTEMPTS})"
)
tool_result_summaries.append(
f"- retry [非法 Timing 工具]: 返回了 {invalid_tool_text},将重试 "
f"({attempt_index + 1}/{TIMING_GATE_MAX_ATTEMPTS})"
)
continue
logger.warning(
f"{self._runtime.log_prefix} Timing Gate 连续 {TIMING_GATE_MAX_ATTEMPTS} 次未返回有效控制工具:"
f"{invalid_tool_text},将按 no_reply 处理"
)
self._runtime._enter_stop_state()
tool_result_summaries.append(
f"- no_reply [非法 Timing 工具]: 返回了 {invalid_tool_text},已停止本轮并等待新消息"
)
return "no_reply", response, tool_result_summaries, tool_monitor_results
if selected_tool_call is None:
self._runtime._enter_stop_state()
tool_result_summaries.append(
"- no_reply [非法 Timing 工具]: 已停止本轮并等待新消息"
)
return "no_reply", response, tool_result_summaries, tool_monitor_results
if invalid_tool_text:
self._runtime._chat_history = [
message
for message in self._runtime._chat_history
if message.source != TIMING_GATE_INVALID_TOOL_HINT_SOURCE
]
append_history = False
store_record = selected_tool_call.func_name != "continue"
invocation, result, tool_spec = await self._invoke_tool_call(

View File

@@ -429,7 +429,7 @@ class MaisakaHeartFlowChatting:
self._reply_latency_measurement_started_at = None
self._recent_reply_latencies.append((time.time(), reply_duration))
self._prune_recent_reply_latencies()
logger.info(
logger.debug(
f"{self.log_prefix} 已记录消息回复时长: {reply_duration:.2f}"
f"最近10分钟样本数={len(self._recent_reply_latencies)}"
)
@@ -943,7 +943,7 @@ class MaisakaHeartFlowChatting:
if self._pending_wait_tool_call_id != tool_call_id:
return
logger.info(f"{self.log_prefix} Maisaka 等待已超时")
logger.debug(f"{self.log_prefix} Maisaka 等待已超时")
self._agent_state = self._STATE_RUNNING
await self._internal_turn_queue.put("timeout")
except asyncio.CancelledError:
@@ -1645,7 +1645,7 @@ class MaisakaHeartFlowChatting:
return panels
def _log_cycle_started(self, cycle_detail: CycleDetail, round_index: int) -> None:
logger.info(
logger.debug(
f"{self.log_prefix} MaiSaka 轮次开始: 循环编号={cycle_detail.cycle_id} "
f"回合={round_index + 1}/{self._max_internal_rounds} "
f"上下文消息数={len(self._chat_history)}"
@@ -1653,14 +1653,14 @@ class MaisakaHeartFlowChatting:
def _log_cycle_completed(self, cycle_detail: CycleDetail, timer_strings: list[str]) -> None:
end_time = cycle_detail.end_time if cycle_detail.end_time is not None else cycle_detail.start_time
logger.info(
logger.debug(
f"{self.log_prefix} MaiSaka 轮次结束: 循环编号={cycle_detail.cycle_id} "
f"总耗时={end_time - cycle_detail.start_time:.2f} 秒; "
f"阶段耗时={', '.join(timer_strings) if timer_strings else ''}"
)
def _log_history_trimmed(self, removed_count: int, user_message_count: int) -> None:
logger.info(
logger.debug(
f"{self.log_prefix} 已裁剪 {removed_count} 条历史消息; "
# f"剩余计入上下文的消息数={user_message_count}"
)