feat:将timing分离成单独的subagent
This commit is contained in:
@@ -12,19 +12,15 @@
|
||||
|
||||
|
||||
你可以使用这些工具:
|
||||
- wait(seconds) - 暂时停止对话,等待(seconds)秒,把话语权交给用户,等待对方新的发言。
|
||||
- no_reply() - 当你判断{bot_name}现在不应该发言,结束对话,不进行任何回复,直到对方有新消息。
|
||||
- reply():当你判断{bot_name}现在应该正式对用户发出一条可见回复时调用。调用后系统会基于你当前这轮的想法生成一条真正展示给用户的回复。你可以针对某个用户回复,也可以对所有用户回复。
|
||||
- query_jargon():当你认为某些词的含义不明确,或用户询问某些词的含义,需要进行查询
|
||||
- 其他定义的工具,你可以视情况合适使用
|
||||
|
||||
工具使用规则:
|
||||
1.如果{bot_name}已经回复,但用户暂时没有新的回复,且没有新信息需要搜集,使用wait或者no_reply进行等待
|
||||
2.如果用户有新发言,但是你评估用户还有后续发言尚未发送,可以适当等待让用户说完
|
||||
3.在特定情况下也可以连续回复,例如想要追问,或者补充自己先前的发言,可以不使用no_reply或者wait
|
||||
4.你需要控制自己发言的频率,如果用户一对一聊天,可以以均匀地频率发言,如果用户较多,不要每句都回复,控制回复频率。当你决定暂时不发言,可以使用wait暂时等待一定时间或者no_reply等待新消息
|
||||
5.不要每条消息都回复,不要直接回复别的用户发送的表情包消息,控制回复频率,控制你的发言占所有用户的1/10,也就是其他用户10条发言左右你回复一条。
|
||||
6.如果存在用户的疑问,或者对某些概念的不确定,你可以使用工具来搜集信息或者查询含义,你可以使用多个工具
|
||||
1. 你当前处于 Action Loop 阶段,节奏控制由独立的 timing gate 负责;如果系统让你继续,就专注于分析、搜集信息和执行真正需要的工具。
|
||||
2. 如果存在用户的疑问,或者对某些概念的不确定,你可以使用工具来搜集信息或者查询含义,你可以使用多个工具。
|
||||
3. 当你判断 {bot_name} 现在应该正式发出可见回复时,调用 reply()。
|
||||
4. 如果需要补充上下文、查看消息、查询黑话、检索记忆或使用其他可用工具,可以按需调用,不要为了“先等一下”而停在这一层。
|
||||
|
||||
你的分析规则:
|
||||
1. 默认直接输出你当前的最新分析,不要重复之前的分析内容。最新分析应尽量具体,贴近上下文,不要空泛重复。
|
||||
@@ -35,4 +31,4 @@
|
||||
|
||||
{group_chat_attention_block}
|
||||
|
||||
现在,请你输出你对{bot_name}发言的分析,你必须先输出文本内容的分析,然后再进行工具调用:
|
||||
现在,请你输出你对{bot_name}发言的分析,你必须先输出文本内容的分析,然后再进行工具调用,输出json形式的function call:
|
||||
|
||||
26
prompts/zh-CN/maisaka_timing_gate.prompt
Normal file
26
prompts/zh-CN/maisaka_timing_gate.prompt
Normal file
@@ -0,0 +1,26 @@
|
||||
你的任务是分析当前聊天节奏,并只决定 {bot_name} 下一步应当继续、等待,还是暂停本轮发言。
|
||||
你不是回复生成器,也不是信息搜集器;你只负责做节奏控制判断。
|
||||
|
||||
【参考信息】
|
||||
{bot_name} 的人设:{identity}
|
||||
【参考信息结束】
|
||||
|
||||
你需要根据提供的参考信息、当前场景和输出规则来进行节奏判断。
|
||||
在当前场景中,不同的用户正在互动({bot_name} 也是一位参与的用户),用户也可能正在连续发送消息或彼此互动。
|
||||
你的任务不是生成对用户可见的发言,也不是直接使用查询类工具,而是判断当前是否应该:
|
||||
- continue:立刻进入下一轮完整思考、搜集信息、回复与其他工具执行
|
||||
- wait:再等待一段时间,然后重新判断,可选几秒的等待,也可等待数分钟
|
||||
- no_reply:本轮不继续,直接等待新的外部消息
|
||||
|
||||
节奏控制规则:
|
||||
1. 如果 {bot_name} 已经回复,但用户暂时没有新的回复,且没有新信息需要搜集,使用 wait 或者 no_reply 进行等待。
|
||||
2. 如果用户有新发言,但是你评估用户还有后续发言尚未发送,可以适当等待让用户说完。
|
||||
3. 在特定情况下也可以连续回复,例如想要追问,或者补充自己先前的发言,这时应调用 continue,让主流程继续执行。
|
||||
4. 你需要控制自己发言的频率,如果是一对一聊天,可以以较均匀的频率发言;如果用户较多,不要每句都回复,控制回复频率。
|
||||
5. 不要每条消息都回复,不要直接因为别的用户发送了表情包就发言。
|
||||
6. 如果你判断现在需要真正回复、查询信息、查看上下文或做进一步分析,不要在这里完成,直接调用 continue,把工作交给主流程。
|
||||
7. 你必须且只能调用一个工具,不要连续调用多个工具,也不要只输出文本不调用工具。
|
||||
|
||||
{group_chat_attention_block}
|
||||
|
||||
现在,请先输出你对当前聊天节奏的简短分析,然后只调用一个工具:
|
||||
@@ -8,6 +8,8 @@ from src.core.tooling import ToolExecutionContext, ToolExecutionResult, ToolInvo
|
||||
from src.llm_models.payload_content.tool_option import ToolDefinitionInput
|
||||
|
||||
from .context import BuiltinToolRuntimeContext
|
||||
from .continue_tool import get_tool_spec as get_continue_tool_spec
|
||||
from .continue_tool import handle_tool as handle_continue_tool
|
||||
from .no_reply import get_tool_spec as get_no_reply_tool_spec
|
||||
from .no_reply import handle_tool as handle_no_reply_tool
|
||||
from .query_jargon import get_tool_spec as get_query_jargon_tool_spec
|
||||
@@ -28,45 +30,71 @@ from .wait import handle_tool as handle_wait_tool
|
||||
BuiltinToolHandler = Callable[[ToolInvocation, Optional[ToolExecutionContext]], Awaitable[ToolExecutionResult]]
|
||||
|
||||
|
||||
def get_builtin_tool_specs() -> List[ToolSpec]:
|
||||
"""获取默认启用的内置工具声明列表。"""
|
||||
def get_timing_tool_specs() -> List[ToolSpec]:
|
||||
"""获取 Timing Gate 阶段可用的内置工具声明。"""
|
||||
|
||||
return [
|
||||
get_wait_tool_spec(),
|
||||
get_no_reply_tool_spec(),
|
||||
get_continue_tool_spec(),
|
||||
]
|
||||
|
||||
|
||||
def get_action_tool_specs() -> List[ToolSpec]:
|
||||
"""获取 Action Loop 阶段可用的内置工具声明。"""
|
||||
|
||||
return [
|
||||
get_reply_tool_spec(),
|
||||
get_view_complex_message_tool_spec(),
|
||||
get_query_jargon_tool_spec(),
|
||||
get_query_memory_tool_spec(enabled=bool(global_config.maisaka.enable_memory_query_tool)),
|
||||
get_no_reply_tool_spec(),
|
||||
get_send_emoji_tool_spec(),
|
||||
]
|
||||
|
||||
|
||||
def get_builtin_tool_specs() -> List[ToolSpec]:
|
||||
"""获取默认暴露的 Maisaka 内置工具声明。"""
|
||||
|
||||
return get_action_tool_specs()
|
||||
|
||||
|
||||
def get_all_builtin_tool_specs() -> List[ToolSpec]:
|
||||
"""获取全部内置工具声明列表。"""
|
||||
"""获取全部内置工具声明。"""
|
||||
|
||||
return [
|
||||
get_wait_tool_spec(),
|
||||
*get_timing_tool_specs(),
|
||||
get_reply_tool_spec(),
|
||||
get_view_complex_message_tool_spec(),
|
||||
get_query_jargon_tool_spec(),
|
||||
get_query_memory_tool_spec(enabled=True),
|
||||
get_query_person_info_tool_spec(),
|
||||
get_no_reply_tool_spec(),
|
||||
get_send_emoji_tool_spec(),
|
||||
]
|
||||
|
||||
|
||||
def get_builtin_tools() -> List[ToolDefinitionInput]:
|
||||
"""获取兼容旧模型层的内置工具定义。"""
|
||||
def get_timing_tools() -> List[ToolDefinitionInput]:
|
||||
"""获取 Timing Gate 阶段的兼容工具定义。"""
|
||||
|
||||
return [tool_spec.to_llm_definition() for tool_spec in get_builtin_tool_specs()]
|
||||
return [tool_spec.to_llm_definition() for tool_spec in get_timing_tool_specs()]
|
||||
|
||||
|
||||
def get_action_tools() -> List[ToolDefinitionInput]:
|
||||
"""获取 Action Loop 阶段的兼容工具定义。"""
|
||||
|
||||
return [tool_spec.to_llm_definition() for tool_spec in get_action_tool_specs()]
|
||||
|
||||
|
||||
def get_builtin_tools() -> List[ToolDefinitionInput]:
|
||||
"""获取默认暴露给模型层的内置工具定义。"""
|
||||
|
||||
return get_action_tools()
|
||||
|
||||
|
||||
def build_builtin_tool_handlers(tool_ctx: BuiltinToolRuntimeContext) -> Dict[str, BuiltinToolHandler]:
|
||||
"""构建内置工具处理器映射。"""
|
||||
|
||||
return {
|
||||
"continue": lambda invocation, context=None: handle_continue_tool(tool_ctx, invocation, context),
|
||||
"reply": lambda invocation, context=None: handle_reply_tool(tool_ctx, invocation, context),
|
||||
"no_reply": lambda invocation, context=None: handle_no_reply_tool(tool_ctx, invocation, context),
|
||||
"query_jargon": lambda invocation, context=None: handle_query_jargon_tool(tool_ctx, invocation, context),
|
||||
|
||||
37
src/maisaka/builtin_tool/continue_tool.py
Normal file
37
src/maisaka/builtin_tool/continue_tool.py
Normal file
@@ -0,0 +1,37 @@
|
||||
"""continue 内置工具。"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from src.core.tooling import ToolExecutionContext, ToolExecutionResult, ToolInvocation, ToolSpec
|
||||
|
||||
from .context import BuiltinToolRuntimeContext
|
||||
|
||||
|
||||
def get_tool_spec() -> ToolSpec:
|
||||
"""获取 continue 工具声明。"""
|
||||
|
||||
return ToolSpec(
|
||||
name="continue",
|
||||
brief_description="允许当前会话继续进入下一轮思考和工具执行。",
|
||||
provider_name="maisaka_builtin",
|
||||
provider_type="builtin",
|
||||
)
|
||||
|
||||
|
||||
async def handle_tool(
|
||||
tool_ctx: BuiltinToolRuntimeContext,
|
||||
invocation: ToolInvocation,
|
||||
context: Optional[ToolExecutionContext] = None,
|
||||
) -> ToolExecutionResult:
|
||||
"""执行 continue 内置工具。"""
|
||||
|
||||
del tool_ctx, context
|
||||
return ToolExecutionResult(
|
||||
tool_name=invocation.tool_name,
|
||||
success=True,
|
||||
content="当前对话继续进入下一轮思考和工具执行。",
|
||||
metadata={
|
||||
"pause_execution": True,
|
||||
"timing_action": "continue",
|
||||
},
|
||||
)
|
||||
@@ -290,18 +290,22 @@ class MaisakaChatLoopService:
|
||||
"""
|
||||
async with self._prompt_load_lock:
|
||||
try:
|
||||
self._chat_system_prompt = load_prompt(
|
||||
"maisaka_chat",
|
||||
file_tools_section=tools_section,
|
||||
bot_name=global_config.bot.nickname,
|
||||
group_chat_attention_block=self._build_group_chat_attention_block(),
|
||||
identity=self._personality_prompt,
|
||||
)
|
||||
self._chat_system_prompt = load_prompt("maisaka_chat", **self.build_prompt_template_context(tools_section))
|
||||
except Exception:
|
||||
self._chat_system_prompt = f"{self._personality_prompt}\n\nYou are a helpful AI assistant."
|
||||
|
||||
self._prompts_loaded = True
|
||||
|
||||
def build_prompt_template_context(self, tools_section: str = "") -> dict[str, str]:
|
||||
"""构造 Maisaka prompt 模板的公共渲染参数。"""
|
||||
|
||||
return {
|
||||
"bot_name": global_config.bot.nickname,
|
||||
"file_tools_section": tools_section,
|
||||
"group_chat_attention_block": self._build_group_chat_attention_block(),
|
||||
"identity": self._personality_prompt,
|
||||
}
|
||||
|
||||
def _build_group_chat_attention_block(self) -> str:
|
||||
"""构建当前聊天场景下的额外注意事项块。"""
|
||||
|
||||
@@ -700,6 +704,7 @@ class MaisakaChatLoopService:
|
||||
self,
|
||||
chat_history: List[LLMContextMessage],
|
||||
*,
|
||||
request_kind: str = "planner",
|
||||
response_format: RespFormat | None = None,
|
||||
tool_definitions: Sequence[ToolDefinitionInput] | None = None,
|
||||
) -> ChatResponse:
|
||||
@@ -760,18 +765,28 @@ class MaisakaChatLoopService:
|
||||
if isinstance(raw_tool_definitions, list):
|
||||
all_tools = [item for item in raw_tool_definitions if isinstance(item, dict)]
|
||||
|
||||
ordered_panels = PromptCLIVisualizer.build_prompt_panels(
|
||||
built_messages,
|
||||
image_display_mode="path_link" if global_config.maisaka.show_image_path else "legacy",
|
||||
)
|
||||
|
||||
if global_config.debug.show_maisaka_thinking and ordered_panels:
|
||||
if global_config.debug.show_maisaka_thinking:
|
||||
panel_title, panel_border_style = PromptCLIVisualizer.get_request_panel_style(request_kind)
|
||||
image_display_mode: str = "path_link" if global_config.maisaka.show_image_path else "legacy"
|
||||
if global_config.debug.fold_maisaka_thinking:
|
||||
prompt_renderable = PromptCLIVisualizer.build_prompt_access_panel(
|
||||
built_messages,
|
||||
request_kind=request_kind,
|
||||
selection_reason=selection_reason,
|
||||
image_display_mode=image_display_mode,
|
||||
)
|
||||
else:
|
||||
ordered_panels = PromptCLIVisualizer.build_prompt_panels(
|
||||
built_messages,
|
||||
image_display_mode=image_display_mode,
|
||||
)
|
||||
prompt_renderable = Group(*ordered_panels)
|
||||
console.print(
|
||||
Panel(
|
||||
Group(*ordered_panels),
|
||||
title="MaiSaka 大模型请求 - 对话单步",
|
||||
prompt_renderable,
|
||||
title=panel_title,
|
||||
subtitle=selection_reason,
|
||||
border_style="cyan",
|
||||
border_style=panel_border_style,
|
||||
padding=(0, 1),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Maisaka 推理引擎。"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Any, Optional
|
||||
from typing import TYPE_CHECKING, Any, Literal, Optional
|
||||
|
||||
import asyncio
|
||||
import difflib
|
||||
@@ -14,13 +14,16 @@ from src.chat.message_receive.message import SessionMessage
|
||||
from src.chat.utils.utils import process_llm_response
|
||||
from src.common.data_models.message_component_data_model import EmojiComponent, ImageComponent, MessageSequence, TextComponent
|
||||
from src.common.logger import get_logger
|
||||
from src.common.prompt_i18n import load_prompt
|
||||
from src.config.config import global_config
|
||||
from src.core.tooling import ToolExecutionContext, ToolExecutionResult, ToolInvocation, ToolSpec
|
||||
from src.llm_models.exceptions import ReqAbortException
|
||||
from src.llm_models.payload_content.tool_option import ToolCall
|
||||
from src.services import database_service as database_api
|
||||
|
||||
from .builtin_tool import get_action_tool_specs
|
||||
from .builtin_tool import build_builtin_tool_handlers as build_split_builtin_tool_handlers
|
||||
from .builtin_tool import get_timing_tools
|
||||
from .builtin_tool.context import BuiltinToolRuntimeContext
|
||||
from .context_messages import (
|
||||
AssistantMessage,
|
||||
@@ -43,6 +46,12 @@ if TYPE_CHECKING:
|
||||
|
||||
logger = get_logger("maisaka_reasoning_engine")
|
||||
|
||||
TIMING_GATE_CONTEXT_LIMIT = 24
|
||||
TIMING_GATE_MAX_TOKENS = 384
|
||||
TIMING_GATE_TOOL_NAMES = {"continue", "no_reply", "wait"}
|
||||
ACTION_HIDDEN_TOOL_NAMES = {"continue", "no_reply", "wait"}
|
||||
ACTION_BUILTIN_TOOL_NAMES = {tool_spec.name for tool_spec in get_action_tool_specs()}
|
||||
|
||||
|
||||
class MaisakaReasoningEngine:
|
||||
"""负责内部思考、推理与工具执行。"""
|
||||
@@ -78,6 +87,168 @@ class MaisakaReasoningEngine:
|
||||
|
||||
return build_split_builtin_tool_handlers(BuiltinToolRuntimeContext(self, self._runtime))
|
||||
|
||||
async def _run_interruptible_planner(
|
||||
self,
|
||||
*,
|
||||
tool_definitions: Optional[list[dict[str, Any]]] = None,
|
||||
) -> Any:
|
||||
"""运行一轮可被新消息打断的主 planner 请求。"""
|
||||
|
||||
interrupt_flag = asyncio.Event()
|
||||
self._runtime._planner_interrupt_flag = interrupt_flag
|
||||
self._runtime._chat_loop_service.set_interrupt_flag(interrupt_flag)
|
||||
try:
|
||||
return await self._runtime._chat_loop_service.chat_loop_step(
|
||||
self._runtime._chat_history,
|
||||
tool_definitions=tool_definitions,
|
||||
)
|
||||
finally:
|
||||
if self._runtime._planner_interrupt_flag is interrupt_flag:
|
||||
self._runtime._planner_interrupt_flag = None
|
||||
self._runtime._chat_loop_service.set_interrupt_flag(None)
|
||||
|
||||
async def _run_interruptible_sub_agent(
|
||||
self,
|
||||
*,
|
||||
context_message_limit: int,
|
||||
system_prompt: str,
|
||||
tool_definitions: list[dict[str, Any]],
|
||||
) -> Any:
|
||||
"""运行一轮可被新消息打断的临时子代理请求。"""
|
||||
|
||||
interrupt_flag = asyncio.Event()
|
||||
self._runtime._planner_interrupt_flag = interrupt_flag
|
||||
try:
|
||||
return await self._runtime.run_sub_agent(
|
||||
context_message_limit=context_message_limit,
|
||||
system_prompt=system_prompt,
|
||||
request_kind="timing_gate",
|
||||
interrupt_flag=interrupt_flag,
|
||||
max_tokens=TIMING_GATE_MAX_TOKENS,
|
||||
temperature=0.1,
|
||||
tool_definitions=tool_definitions,
|
||||
)
|
||||
finally:
|
||||
if self._runtime._planner_interrupt_flag is interrupt_flag:
|
||||
self._runtime._planner_interrupt_flag = None
|
||||
|
||||
@staticmethod
|
||||
def _build_timing_gate_fallback_prompt() -> str:
|
||||
"""构造 Timing Gate 子代理的兜底提示词。"""
|
||||
|
||||
return (
|
||||
"你是 Maisaka 的 timing gate 子代理,只负责决定当前会话下一步的节奏控制。\n"
|
||||
"你必须且只能调用一个工具,不要输出普通文本答案。\n"
|
||||
"可用工具只有三个:\n"
|
||||
"1. wait: 适合暂时等待一段时间,再重新判断是否继续。\n"
|
||||
"2. no_reply: 适合当前不继续本轮,直接等待新的外部消息。\n"
|
||||
"3. continue: 适合现在立刻进入下一轮正常思考、回复、查询和其他工具执行。\n"
|
||||
"如果需要真正回复消息、查询信息或使用其他工具,应该调用 continue,让主分支继续执行,而不是在这里完成。\n"
|
||||
"不要连续调用多个工具,也不要输出工具之外的计划。"
|
||||
)
|
||||
|
||||
def _build_timing_gate_system_prompt(self) -> str:
|
||||
"""构造 Timing Gate 子代理使用的系统提示词。"""
|
||||
|
||||
try:
|
||||
return load_prompt(
|
||||
"maisaka_timing_gate",
|
||||
**self._runtime._chat_loop_service.build_prompt_template_context(),
|
||||
)
|
||||
except Exception:
|
||||
return self._build_timing_gate_fallback_prompt()
|
||||
|
||||
async def _build_action_tool_definitions(self) -> list[dict[str, Any]]:
|
||||
"""构造 Action Loop 阶段可见的工具定义。"""
|
||||
|
||||
if self._runtime._tool_registry is None:
|
||||
return []
|
||||
|
||||
tool_specs = await self._runtime._tool_registry.list_tools()
|
||||
return [
|
||||
tool_spec.to_llm_definition()
|
||||
for tool_spec in tool_specs
|
||||
if tool_spec.name not in ACTION_HIDDEN_TOOL_NAMES
|
||||
and (
|
||||
tool_spec.provider_name != "maisaka_builtin"
|
||||
or tool_spec.name in ACTION_BUILTIN_TOOL_NAMES
|
||||
)
|
||||
]
|
||||
|
||||
async def _invoke_tool_call(
|
||||
self,
|
||||
tool_call: ToolCall,
|
||||
latest_thought: str,
|
||||
anchor_message: SessionMessage,
|
||||
*,
|
||||
append_history: bool = True,
|
||||
store_record: bool = True,
|
||||
) -> tuple[ToolInvocation, ToolExecutionResult, Optional[ToolSpec]]:
|
||||
"""执行单个工具调用,并按需写入记录与历史。"""
|
||||
|
||||
invocation = self._build_tool_invocation(tool_call, latest_thought)
|
||||
if self._runtime._tool_registry is None:
|
||||
result = ToolExecutionResult(
|
||||
tool_name=tool_call.func_name,
|
||||
success=False,
|
||||
error_message="统一工具注册表尚未初始化。",
|
||||
)
|
||||
if store_record:
|
||||
await self._store_tool_execution_record(invocation, result, None)
|
||||
if append_history:
|
||||
self._append_tool_execution_result(tool_call, result)
|
||||
return invocation, result, None
|
||||
|
||||
execution_context = self._build_tool_execution_context(latest_thought, anchor_message)
|
||||
tool_spec = await self._runtime._tool_registry.get_tool_spec(invocation.tool_name)
|
||||
result = await self._runtime._tool_registry.invoke(invocation, execution_context)
|
||||
if store_record:
|
||||
await self._store_tool_execution_record(invocation, result, tool_spec)
|
||||
if append_history:
|
||||
self._append_tool_execution_result(tool_call, result)
|
||||
return invocation, result, tool_spec
|
||||
|
||||
async def _run_timing_gate(
|
||||
self,
|
||||
anchor_message: SessionMessage,
|
||||
) -> tuple[Literal["continue", "no_reply", "wait"], Any, list[str]]:
|
||||
"""运行 Timing Gate 子代理并返回控制决策。"""
|
||||
|
||||
response = await self._run_interruptible_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] = []
|
||||
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
|
||||
break
|
||||
|
||||
if selected_tool_call is None:
|
||||
logger.warning(f"{self._runtime.log_prefix} Timing Gate 未返回有效控制工具,默认继续执行 Action Loop")
|
||||
return "continue", response, tool_result_summaries
|
||||
|
||||
append_history = selected_tool_call.func_name != "continue"
|
||||
store_record = selected_tool_call.func_name != "continue"
|
||||
_, result, _ = await self._invoke_tool_call(
|
||||
selected_tool_call,
|
||||
response.content or "",
|
||||
anchor_message,
|
||||
append_history=append_history,
|
||||
store_record=store_record,
|
||||
)
|
||||
tool_result_summaries.append(self._build_tool_result_summary(selected_tool_call, result))
|
||||
|
||||
timing_action = str(result.metadata.get("timing_action") or selected_tool_call.func_name).strip()
|
||||
if timing_action not in TIMING_GATE_TOOL_NAMES:
|
||||
logger.warning(
|
||||
f"{self._runtime.log_prefix} Timing Gate 返回未知动作 {timing_action!r},将按 continue 处理"
|
||||
)
|
||||
return "continue", response, tool_result_summaries
|
||||
return timing_action, response, tool_result_summaries
|
||||
|
||||
async def run_loop(self) -> None:
|
||||
"""独立消费消息批次,并执行对应的内部思考轮次。"""
|
||||
try:
|
||||
@@ -108,23 +279,36 @@ class MaisakaReasoningEngine:
|
||||
for round_index in range(self._runtime._max_internal_rounds):
|
||||
cycle_detail = self._start_cycle()
|
||||
self._runtime._log_cycle_started(cycle_detail, round_index)
|
||||
planner_started_at = time.time()
|
||||
planner_started_at = 0.0
|
||||
try:
|
||||
timing_started_at = time.time()
|
||||
timing_action, timing_response, timing_tool_results = await self._run_timing_gate(anchor_message)
|
||||
cycle_detail.time_records["timing_gate"] = time.time() - timing_started_at
|
||||
self._runtime._render_context_usage_panel(
|
||||
selected_history_count=timing_response.selected_history_count,
|
||||
prompt_tokens=timing_response.prompt_tokens,
|
||||
planner_response=timing_response.content or "",
|
||||
tool_calls=timing_response.tool_calls,
|
||||
tool_results=timing_tool_results,
|
||||
)
|
||||
if timing_action != "continue":
|
||||
logger.info(
|
||||
f"{self._runtime.log_prefix} Timing Gate 结束当前回合: "
|
||||
f"回合={round_index + 1} 动作={timing_action}"
|
||||
)
|
||||
break
|
||||
|
||||
planner_started_at = time.time()
|
||||
action_tool_definitions = await self._build_action_tool_definitions()
|
||||
logger.info(
|
||||
f"{self._runtime.log_prefix} 规划器开始执行: "
|
||||
f"回合={round_index + 1} "
|
||||
f"历史消息数={len(self._runtime._chat_history)} "
|
||||
f"开始时间={planner_started_at:.3f}"
|
||||
)
|
||||
interrupt_flag = asyncio.Event()
|
||||
self._runtime._planner_interrupt_flag = interrupt_flag
|
||||
self._runtime._chat_loop_service.set_interrupt_flag(interrupt_flag)
|
||||
try:
|
||||
response = await self._runtime._chat_loop_service.chat_loop_step(self._runtime._chat_history)
|
||||
finally:
|
||||
if self._runtime._planner_interrupt_flag is interrupt_flag:
|
||||
self._runtime._planner_interrupt_flag = None
|
||||
self._runtime._chat_loop_service.set_interrupt_flag(None)
|
||||
response = await self._run_interruptible_planner(
|
||||
tool_definitions=action_tool_definitions,
|
||||
)
|
||||
cycle_detail.time_records["planner"] = time.time() - planner_started_at
|
||||
logger.info(
|
||||
f"{self._runtime.log_prefix} 规划器执行完成: "
|
||||
@@ -166,10 +350,8 @@ class MaisakaReasoningEngine:
|
||||
prompt_tokens=response.prompt_tokens,
|
||||
planner_response=response.content or "",
|
||||
)
|
||||
if response.content:
|
||||
continue
|
||||
|
||||
break
|
||||
if not response.content:
|
||||
break
|
||||
except ReqAbortException:
|
||||
interrupted_at = time.time()
|
||||
logger.info(
|
||||
@@ -633,6 +815,9 @@ class MaisakaReasoningEngine:
|
||||
if invocation.tool_name == "no_reply":
|
||||
return "你暂停了当前对话循环,等待新的外部消息。"
|
||||
|
||||
if invocation.tool_name == "continue":
|
||||
return "你允许当前对话继续进入下一轮完整思考与工具执行。"
|
||||
|
||||
if invocation.tool_name == "query_jargon":
|
||||
words = invocation.arguments.get("words", [])
|
||||
if isinstance(words, list):
|
||||
|
||||
@@ -204,7 +204,9 @@ class MaisakaHeartFlowChatting:
|
||||
*,
|
||||
context_message_limit: int,
|
||||
system_prompt: str,
|
||||
request_kind: str = "sub_agent",
|
||||
extra_messages: Optional[Sequence[LLMContextMessage]] = None,
|
||||
interrupt_flag: asyncio.Event | None = None,
|
||||
max_tokens: int = 512,
|
||||
response_format: RespFormat | None = None,
|
||||
temperature: float = 0.2,
|
||||
@@ -227,8 +229,10 @@ class MaisakaHeartFlowChatting:
|
||||
temperature=temperature,
|
||||
max_tokens=max_tokens,
|
||||
)
|
||||
sub_agent.set_interrupt_flag(interrupt_flag)
|
||||
return await sub_agent.chat_loop_step(
|
||||
sub_agent_history,
|
||||
request_kind=request_kind,
|
||||
response_format=response_format,
|
||||
tool_definitions=[] if tool_definitions is None else tool_definitions,
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@ from typing import Dict, Optional
|
||||
|
||||
from src.core.tooling import ToolExecutionContext, ToolExecutionResult, ToolInvocation, ToolProvider, ToolSpec
|
||||
|
||||
from .builtin_tool import get_builtin_tool_specs
|
||||
from .builtin_tool import get_all_builtin_tool_specs
|
||||
|
||||
BuiltinToolHandler = Callable[[ToolInvocation, Optional[ToolExecutionContext]], Awaitable[ToolExecutionResult]]
|
||||
|
||||
@@ -30,7 +30,7 @@ class MaisakaBuiltinToolProvider(ToolProvider):
|
||||
async def list_tools(self) -> list[ToolSpec]:
|
||||
"""列出全部内置工具。"""
|
||||
|
||||
return list(get_builtin_tool_specs())
|
||||
return list(get_all_builtin_tool_specs())
|
||||
|
||||
async def invoke(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user