refactor: unify system tools as resources

This commit is contained in:
Xin Wang
2026-08-04 17:05:26 +08:00
parent d1b05f16c7
commit 74a8be2357
26 changed files with 788 additions and 507 deletions

View File

@@ -44,7 +44,7 @@ from services.message_stage import (
MessageStageSpec,
)
from services.runtime_variables import DynamicVariableError, DynamicVariableStore
from services.system_tools import SYSTEM_TOOL_KINDS, state_update_properties
from services.system_tools import state_update_properties, system_tool_kind
from services.tool_executor import ToolExecutionError, ToolExecutor
from services.tool_policy import policy_for_tool
@@ -128,11 +128,16 @@ class PromptBrain(BaseBrain):
if llm_tool_ids is not None and tool.id not in llm_tool_ids:
continue
if tool.type == "system":
schema, handler = self._make_end_call_tool(tool, runtime)
schema, handler = self._make_system_tool(tool, runtime)
elif tool.type in {"http", "mcp", "client"}:
schema, handler = self._make_remote_tool(tool, runtime)
else:
continue
if schema.name in registered_names:
logger.warning(
f"跳过工具 {tool.id}: 函数名 {schema.name} 已被占用"
)
continue
schemas.append(schema)
registered_names.add(schema.name)
policy = policy_for_tool(tool)
@@ -141,19 +146,6 @@ class PromptBrain(BaseBrain):
handler,
cancel_on_interruption=policy.cancel_on_interruption,
)
for kind in cfg.system_tools or []:
if kind not in SYSTEM_TOOL_KINDS:
logger.warning(f"忽略未知系统工具: {kind}")
continue
schema, handler = self._make_system_tool(kind, runtime)
if schema.name in registered_names:
logger.warning(
f"跳过系统工具 {schema.name}: 与已绑定工具函数名冲突"
)
continue
registered_names.add(schema.name)
schemas.append(schema)
runtime.llm.register_function(schema.name, handler)
runtime.set_tools(schemas)
async def run_preflight(self) -> None:
@@ -598,50 +590,23 @@ class PromptBrain(BaseBrain):
)
return schema, end_call
# ---------- 内置系统工具(助手配置 system_tools 开关) ----------
# ---------- System 工具资源 ----------
def _make_system_tool(self, kind: str, runtime: BrainRuntime):
def _make_system_tool(self, tool, runtime: BrainRuntime):
kind = system_tool_kind(tool.definition or {})
if not kind:
raise ValueError(f"系统工具 {tool.id} 缺少有效 kind")
if kind == "end_conversation":
return self._make_end_conversation_tool(runtime)
return self._make_end_call_tool(tool, runtime)
if kind == "update_state":
return self._make_update_state_tool(runtime)
return self._make_update_state_tool(tool, runtime)
if kind == "skip_turn":
return self._make_skip_turn_tool()
return self._make_skip_turn_tool(tool)
if kind == "request_human_handoff":
return self._make_handoff_tool(runtime)
return self._make_handoff_tool(tool, runtime)
raise ValueError(f"未知系统工具: {kind}")
def _make_end_conversation_tool(self, runtime: BrainRuntime):
"""结束本次对话,等待模型已生成的告别语播完后再挂断。"""
async def end_conversation(params: FunctionCallParams) -> None:
reason = str(
params.arguments.get("reason") or "end_conversation"
).strip()
self._waiting_for_generated_end_speech = True
runtime.call_end.begin(reason)
await params.result_callback(
{"status": "success", "action": "ending_call"},
properties=FunctionCallResultProperties(run_llm=False),
)
schema = FunctionSchema(
name="end_conversation",
description=(
"礼貌地结束本次对话。当用户明确告别、表示任务已完成"
"或要求挂断时调用。"
),
properties={
"reason": {
"type": "string",
"description": "结束对话的简短原因。",
}
},
required=[],
)
return schema, end_conversation
def _make_update_state_tool(self, runtime: BrainRuntime):
def _make_update_state_tool(self, tool, runtime: BrainRuntime):
"""更新已声明的动态变量(会话状态),并让模型继续当前回答。"""
writable = state_update_properties(self._cfg.dynamic_variable_definitions)
@@ -676,8 +641,8 @@ class PromptBrain(BaseBrain):
)
schema = FunctionSchema(
name="update_state",
description=(
name=tool.function_name,
description=tool.description or (
"静默更新本次对话中已经声明并明确列出的动态变量。"
"只提交本轮获得或确认的信息,更新后继续当前回答。"
),
@@ -686,7 +651,7 @@ class PromptBrain(BaseBrain):
)
return schema, update_state
def _make_skip_turn_tool(self):
def _make_skip_turn_tool(self, tool):
"""跳过当前轮次,不生成任何语音回复。"""
async def skip_turn(params: FunctionCallParams) -> None:
@@ -700,8 +665,8 @@ class PromptBrain(BaseBrain):
)
schema = FunctionSchema(
name="skip_turn",
description=(
name=tool.function_name,
description=tool.description or (
"跳过当前轮次,不生成任何语音回复。"
"仅当用户明确要求稍等、话还没说完,或输入可确认只是噪音时调用。"
),
@@ -715,7 +680,7 @@ class PromptBrain(BaseBrain):
)
return schema, skip_turn
def _make_handoff_tool(self, runtime: BrainRuntime):
def _make_handoff_tool(self, tool, runtime: BrainRuntime):
"""提交人工接管请求;请求完成前保持当前 AI 会话可用。"""
async def request_human_handoff(params: FunctionCallParams) -> None:
@@ -741,8 +706,8 @@ class PromptBrain(BaseBrain):
)
schema = FunctionSchema(
name="request_human_handoff",
description=(
name=tool.function_name,
description=tool.description or (
"提交人工接管请求。当用户明确要求人工服务、投诉升级或 AI 无法"
"解决时调用。该工具只提交请求,不代表人工已经接通;调用后继续"
"回复用户并说明正在等待人工响应。"