Maisaka 推理引擎
Maisaka 是 MaiBot 的核心 AI 运行时,负责对话推理、节奏控制和工具调用。本文详述其内部架构、状态机和执行流程。
架构总览
MaisakaHeartFlowChatting
源码位置:src/maisaka/runtime.py
每个聊天会话对应一个 MaisakaHeartFlowChatting 实例,由 HeartflowManager 管理生命周期。
状态机
运行时具有三种状态:
running— 正在执行推理循环wait— 等待状态,wait 工具设定了超时时间stop— 空闲状态,等待新的外部消息触发
核心属性
session_idstr— 会话 ID_chat_historylist[LLMContextMessage]— 内部上下文历史message_cachelist[SessionMessage]— 待处理消息缓存_internal_turn_queueasyncio.Queue— 内部循环触发队列("message" / "timeout")_tool_registryToolRegistry— 统一工具注册表_reasoning_engineMaisakaReasoningEngine— 推理引擎_chat_loop_serviceMaisakaChatLoopService— 对话循环服务_max_internal_roundsint— 最大内部轮次(默认 10)_max_context_sizeint— 最大上下文消息数_message_debounce_secondsfloat— 消息防抖秒数(默认 1.0)_talk_frequency_adjustfloat— 说话频率倍率deferred_tool_specs_by_namedict[str, ToolSpec]— 延迟发现工具池discovered_tool_namesset[str]— 已发现的延迟工具
消息触发机制
触发阈值计算:
effective_frequency = talk_value * _talk_frequency_adjust # 回复频率
trigger_threshold = ceil(1.0 / effective_frequency) # 所需消息数空窗补偿:当新消息数不足但空窗时间较长时,按最近平均回复时长折算等价消息数。
强制 continue 机制
当检测到 @ 或提及时,_arm_force_next_timing_continue() 设置标记,使下一次 Timing Gate 直接返回 continue,确保 bot 回应直接呼叫。
MaisakaReasoningEngine
源码位置:src/maisaka/reasoning_engine.py
核心推理引擎,负责内部思考循环和工具执行。
关键常量
TIMING_GATE_CONTEXT_LIMIT—_max_context_size(可配置) · Timing Gate 上下文消息上限(读取global_config.chat.max_context_size/max_private_context_size)TIMING_GATE_MAX_TOKENS— 384 · Timing Gate 最大输出 tokenTIMING_GATE_TOOL_NAMES—{"continue", "no_action", "wait"}· Timing Gate 可用工具ACTION_HIDDEN_TOOL_NAMES—{"continue", "no_action"}· Action Loop 隐藏的工具MAX_INTERNAL_ROUNDS— 10 · 最大内部思考轮次
run_loop 主循环
async def run_loop(self) -> None:
while runtime._running:
# 1. 等待触发信号
queued_trigger = await runtime._internal_turn_queue.get()
message_triggered, timeout_triggered = _drain_ready_turn_triggers(queued_trigger)
# 2. 消息防抖
if message_triggered:
await runtime._wait_for_message_quiet_period()
# 3. 收集待处理消息
cached_messages = runtime._collect_pending_messages()
# 4. 消息注入历史
await _ingest_messages(cached_messages)
# 5. 内部思考循环
for round_index in range(max_internal_rounds):
# 5a. Timing Gate(如果需要)
if timing_gate_required:
timing_action = await _run_timing_gate(anchor_message)
if timing_action != "continue":
break # wait 或 no_action,结束本轮
# 5b. Planner(Action Loop)
response = await _run_interruptible_planner()
# 5c. 相似度检测
if _should_replace_reasoning(response.content):
# 替换为重新思考提示
response.content = "我应该根据我上面思考的内容进行反思..."
# 5d. 工具执行
if response.tool_calls:
should_pause, summaries, monitors = await _handle_tool_calls(...)
if should_pause:
break
continue # 工具执行后有新信息,继续循环
break # 无工具调用且无内容,结束Timing Gate
Timing Gate 是一个独立的子代理,决定对话节奏:
Timing Gate 系统提示词:
- 优先从
maisaka_timing_gate模板加载 - 兜底提示词强调 只调用一个工具,不要输出普通文本
- 可用工具仅
wait、no_action、continue三个
Planner(Action Loop)
Planner 是主要的推理和工具执行阶段:
构建工具定义:
_build_action_tool_definitions()- 过滤
ACTION_HIDDEN_TOOL_NAMES(continue、no_action) - 内置 Action 工具直接暴露
- 默认第三方/插件工具放入 deferred 池,通过
tool_search发现;声明core_tool=True或visibility="visible"的插件工具会直接暴露
- 过滤
运行可打断 Planner:
_run_interruptible_planner()- 绑定
asyncio.Event中断标记 - 新消息到达时设置标记 → LLM 请求中止(
ReqAbortException) - 连续打断有上限(
planner_interrupt_max_consecutive_count)
- 绑定
思考去重:
_should_replace_reasoning()- 当前后思考与上一轮相似度 > 90% 时
- 替换为"重新思考"提示,避免循环空转
Planner 打断机制
打断后行为:
- 如果
has_pending_messages且未达最大轮次 → 跳过 Timing Gate,重新进入 Planner - 否则 → 结束当前循环
工具执行
工具调用通过统一 ToolRegistry 路由:
内置工具定义
源码位置:src/maisaka/builtin_tool/
Timing Gate 工具
continue—continue_tool.py· 允许继续进入下一轮思考 · 关键参数:无no_action—no_action.py· 停止当前循环,等待新外部消息 · 关键参数:无wait—wait.py· 暂停对话 N 秒后重新判断 · 关键参数:seconds(默认 30)
Action 工具
reply—reply.py· 生成并发送回复消息 · 关键参数:msg_id、set_quote、reference_infosend_emoji—send_emoji.py· 发送表情包 · 关键参数:无(自动根据上下文选择)finish—finish.py· 结束当前思考轮次 · 关键参数:无query_jargon—query_jargon.py· 查询黑话/词条 · 关键参数:wordsquery_memory—query_memory.py· 查询长期记忆 · 关键参数:query、mode、limitquery_person_profile—query_person_profile.py· 查询人物画像 · 关键参数:person_nameview_complex_message—view_complex_message.py· 查看完整转发消息 · 关键参数:message_idtool_search—tool_search.py· 搜索延迟发现的工具 · 关键参数:query、limit
Deferred Tool 发现机制
Action Loop 中,普通第三方/插件工具默认不直接暴露给 Planner,而是通过两步发现:
- tool_search:搜索 deferred 工具池,匹配到的工具名标记为"已发现"
- 下一轮 Planner:已发现的工具加入可见工具列表
这减少了 Planner 一次看到的工具数量,避免选择困难。
插件 Tool 如果声明了 core_tool=True 或 visibility="visible",会跳过 deferred 发现流程,直接进入 Planner 可见工具列表。该机制适合高频、低风险、上下文强相关的工具;普通插件工具仍建议保持默认 deferred 行为。
MaisakaChatLoopService
源码位置:src/maisaka/chat_loop_service.py
负责单步 LLM 请求的封装,包括上下文选择、Prompt 构建和 Hook 触发。
chat_loop_step 流程
上下文选择策略
select_llm_context_messages() 从历史中选择给 LLM 的上下文:
- 按
request_kind过滤(planner请求隐藏 Timing Gate 工具链) - 从末尾向前遍历,选择能成功转换为 LLM 消息的条目
- 计数仅计算
count_in_context=True的消息(ToolResultMessage和ReferenceMessage不占窗口) - 达到
max_context_size后停止 - 隐藏最早 50% 的 assistant 文本消息(保留工具调用链路)
Hook Specs
maisaka.planner.before_request— 可中止 ✗ · 可改写 ✓ · 可改写消息列表和工具定义maisaka.planner.after_response— 可中止 ✗ · 可改写 ✓ · 可调整文本结果和工具调用列表maisaka.replyer.before_request— 可中止 ✗ · 可改写 ✓ · 可改写 replyer 任务名、指定模型、额外提示和reply_tool_argsmaisaka.replyer.before_model_request— 可中止 ✗ · 可改写 ✓ · 可改写 replyer 已构造完成、即将发给模型的messagesmaisaka.replyer.after_response— 可中止 ✗ · 可改写 ✓ · 可改写回复文本或要求 replyer 重生成
上下文消息类型
源码位置:src/maisaka/context_messages.py
ReferenceMessageType
custom— 自定义参考消息jargon— 黑话/词条查询结果memory— 长期记忆检索结果tool_hint— 工具提示信息(如 deferred tools 提醒)
上下文窗口占用
SessionBackedMessage— 占用窗口 ✓ · 真实用户消息ComplexSessionMessage— 占用窗口 ✓ · 复杂/转发消息ReferenceMessage— 占用窗口 ✗ · 参考信息(不占用窗口)AssistantMessage(assistant) — 占用窗口 ✓ · 内部思考文本AssistantMessage(perception) — 占用窗口 ✗ · 感知类文本(打断提示等)ToolResultMessage— 占用窗口 ✗ · 工具执行结果
Planner 消息前缀
源码位置:src/maisaka/planner_message_utils.py
每条用户消息注入 Planner 时,会添加结构化前缀:
[时间]HH:MM:SS
[用户名]nickname
[用户群昵称]group_card
[msg_id]message_id
[发言内容]实际消息文本build_planner_prefix() 构建前缀,build_planner_user_prefix_from_session_message() 从 SessionMessage 提取参数。
监控事件
源码位置:src/maisaka/monitor_events.py
通过 WebSocket 向前端监控面板广播事件:
session.start— 运行时启动 · 关键数据:session_id, session_namemessage.ingested— 消息注入历史 · 关键数据:speaker_name, content, message_idcycle.start— 思考循环开始 · 关键数据:cycle_id, round_index, max_roundstiming_gate.result— Timing Gate 决策完成 · 关键数据:action, content, tool_calls, prompt_tokensplanner.finalized— 规划器完成 · 关键数据:完整 cycle 数据、token 统计、耗时
完整推理流程示例
以群聊中用户发送一条 @bot 消息为例: