diff --git a/apps/scheduler/executor/agent.py b/apps/scheduler/executor/agent.py
index 3997d89ad039a056ec38ce1cbb5cebfa8fb39671..4d34b85b6855478d0686c8fa76a1b51433587067 100644
--- a/apps/scheduler/executor/agent.py
+++ b/apps/scheduler/executor/agent.py
@@ -364,7 +364,7 @@ class MCPAgentExecutor(BaseExecutor):
else:
user_info = await UserManager.get_userinfo_by_user_sub(self.task.ids.user_sub)
if user_info.auto_execute:
- self.push_message(
+ await self.push_message(
EventType.STEP_ERROR,
data={
"message": self.task.state.error_message,
@@ -390,7 +390,7 @@ class MCPAgentExecutor(BaseExecutor):
# 如果是参数错误,生成参数补充
await self.generate_params_with_null()
else:
- self.push_message(
+ await self.push_message(
EventType.STEP_ERROR,
data={
"message": self.task.state.error_message,
diff --git a/apps/scheduler/mcp_agent/host.py b/apps/scheduler/mcp_agent/host.py
index 44b04d1fff502037e9c35f1232dfce6959a57009..f43506769772d9f1e573c6a862f68e1011075115 100644
--- a/apps/scheduler/mcp_agent/host.py
+++ b/apps/scheduler/mcp_agent/host.py
@@ -10,11 +10,13 @@ from jinja2.sandbox import SandboxedEnvironment
from mcp.types import TextContent
from apps.common.mongo import MongoDB
+from apps.llm.reasoning import ReasoningLLM
from apps.llm.function import JsonGenerator
+from apps.scheduler.mcp_agent.base import McpBase
from apps.scheduler.mcp.prompt import MEMORY_TEMPLATE
from apps.scheduler.pool.mcp.client import MCPClient
from apps.scheduler.pool.mcp.pool import MCPPool
-from apps.scheduler.mcp_agent.prompt import REPAIR_PARAMS
+from apps.scheduler.mcp_agent.prompt import GEN_PARAMS, REPAIR_PARAMS
from apps.schemas.enum_var import StepStatus
from apps.schemas.mcp import MCPPlanItem, MCPTool
from apps.schemas.task import Task, FlowStepHistory
@@ -37,7 +39,7 @@ def tojson_filter(value):
_env.filters['tojson'] = tojson_filter
-class MCPHost:
+class MCPHost(McpBase):
"""MCP宿主服务"""
@staticmethod
@@ -48,31 +50,29 @@ class MCPHost:
context_list=task.context,
)
- async def _get_first_input_params(mcp_tool: MCPTool, goal: str, query: str, task: Task) -> dict[str, Any]:
+ async def _get_first_input_params(mcp_tool: MCPTool, goal: str, current_goal: str, task: Task,
+ resoning_llm: ReasoningLLM = ReasoningLLM()) -> dict[str, Any]:
"""填充工具参数"""
# 更清晰的输入·指令,这样可以调用generate
- llm_query = rf"""
- 你是一个MCP工具参数生成器,你的任务是根据用户的目标和查询,生成MCP工具的输入参数。
- 请充分利用背景中的信息,生成满足以下目标的工具参数:
- {query}
- 注意:
- 1. 你需要根据工具的输入schema来生成参数。
- 2. 如果工具的输入schema中有枚举类型的字段,请确保生成的参数符合这些枚举值。
- 下面是工具的简介:
- 工具名称:{mcp_tool.name}
- 工具描述:{mcp_tool.description}
- """
-
- # 进行生成
- json_generator = JsonGenerator(
- llm_query,
- [
- {"role": "system", "content": "You are a helpful assistant."},
- {"role": "user", "content": "用户的总目标是:\n"+goal+(await MCPHost.assemble_memory(task))},
- ],
+ prompt = _env.from_string(GEN_PARAMS).render(
+ tool_name=mcp_tool.name,
+ tool_description=mcp_tool.description,
+ goal=goal,
+ current_goal=current_goal,
+ input_schema=mcp_tool.input_schema,
+ background_info=await MCPHost.assemble_memory(task),
+ )
+ logger.info("[MCPHost] 填充工具参数: %s", prompt)
+ result = await MCPHost.get_resoning_result(
+ prompt,
+ resoning_llm
+ )
+ # 使用JsonGenerator解析结果
+ result = await MCPHost._parse_result(
+ result,
mcp_tool.input_schema,
)
- return await json_generator.generate()
+ return result
async def _fill_params(mcp_tool: MCPTool,
goal: str,
diff --git a/apps/scheduler/mcp_agent/plan.py b/apps/scheduler/mcp_agent/plan.py
index 84c08d60227147849f8b9f4b3d91f79dc43297b0..b539482d97aa511a928361bffd5e3b0e63f97ec7 100644
--- a/apps/scheduler/mcp_agent/plan.py
+++ b/apps/scheduler/mcp_agent/plan.py
@@ -3,7 +3,7 @@
from typing import Any, AsyncGenerator
from jinja2 import BaseLoader
from jinja2.sandbox import SandboxedEnvironment
-
+import logging
from apps.llm.reasoning import ReasoningLLM
from apps.llm.function import JsonGenerator
from apps.scheduler.mcp_agent.base import McpBase
@@ -43,7 +43,7 @@ _env = SandboxedEnvironment(
trim_blocks=True,
lstrip_blocks=True,
)
-
+logger = logging.getLogger(__name__)
class MCPPlanner(McpBase):
"""MCP 用户目标拆解与规划"""
@@ -186,6 +186,7 @@ class MCPPlanner(McpBase):
for tool in tools:
schema["properties"]["tool_id"]["enum"].append(tool.id)
step = await MCPPlanner._parse_result(result, schema)
+ logger.info("[MCPPlanner] 创建下一步的执行步骤: %s", step)
# 使用Step模型解析结果
return Step.model_validate(step)
diff --git a/apps/scheduler/mcp_agent/prompt.py b/apps/scheduler/mcp_agent/prompt.py
index 31e8ce5415dad9bca6ef8e5069a125975070d658..824ece8a35dfb3b02afb0e5bd5602d6f5d0d1342 100644
--- a/apps/scheduler/mcp_agent/prompt.py
+++ b/apps/scheduler/mcp_agent/prompt.py
@@ -511,7 +511,7 @@ GEN_STEP = dedent(r"""
2.能够基于当前的计划和历史,完成阶段性的任务。
3.不要选择不存在的工具。
4.如果你认为当前已经达成了用户的目标,可以直接返回Final工具,表示计划执行结束。
-
+
# 样例 1
# 目标
我需要扫描当前mysql数据库,分析性能瓶颈, 并调优,我的ip是192.168.1.1,数据库端口是3306,用户名是root,密码是password
@@ -582,7 +582,7 @@ GEN_STEP = dedent(r"""
{% for tool in tools %}
- {{tool.id}} {{tool.name}};{{tool.description}}
{% endfor %}
-
+
""")
TOOL_SKIP = dedent(r"""
@@ -794,7 +794,7 @@ IS_PARAM_ERROR = dedent(r"""
}
# 工具运行报错
执行MySQL性能分析命令时,出现了错误:`host is not correct`。
-
+
# 输出
```json
{
@@ -967,6 +967,82 @@ GET_MISSING_PARAMS = dedent(r"""
# 输出
"""
)
+GEN_PARAMS = dedent(r"""
+ 你是一个工具参数生成器。
+ 你的任务是根据总的目标、阶段性的目标、工具信息、工具入参的schema和背景信息生成工具的入参。
+ 注意:
+ 1.生成的参数在格式上必须符合工具入参的schema。
+ 2.总的目标、阶段性的目标和背景信息必须被充分理解,利用其中的信息来生成工具入参。
+ 3.生成的参数必须符合阶段性目标。
+
+ # 样例
+ # 工具信息
+ < tool >
+ < name > mysql_analyzer < /name >
+ < description > 分析MySQL数据库性能 < /description >
+ < / tool >
+ # 总目标
+ 我需要扫描当前mysql数据库,分析性能瓶颈, 并调优,ip地址是192.168.1.1,端口是3306,用户名是root,密码是password。
+ # 当前阶段目标
+ 我要连接MySQL数据库,分析性能瓶颈,并调优。
+ # 工具入参的schema
+ {
+ "type": "object",
+ "properties": {
+ "host": {
+ "type": "string",
+ "description": "MySQL数据库的主机地址"
+ },
+ "port": {
+ "type": "integer",
+ "description": "MySQL数据库的端口号"
+ },
+ "username": {
+ "type": "string",
+ "description": "MySQL数据库的用户名"
+ },
+ "password": {
+ "type": "string",
+ "description": "MySQL数据库的密码"
+ }
+ },
+ "required": ["host", "port", "username", "password"]
+ }
+ # 背景信息
+ 第1步:生成端口扫描命令
+ - 调用工具 `command_generator`,并提供参数 `帮我生成一个mysql端口扫描命令`
+ - 执行状态:成功
+ - 得到数据:`{"command": "nmap -sS -p--open 192.168.1.1"}`
+ 第2步:执行端口扫描命令
+ - 调用工具 `command_executor`,并提供参数 `{"command": "nmap -sS -p--open 192.168.1.1"}`
+ - 执行状态:成功
+ - 得到数据:`{"result": "success"}`
+ # 输出
+ ```json
+ {
+ "host": "192.168.1.1",
+ "port": 3306,
+ "username": "root",
+ "password": "password"
+ }
+ ```
+ # 工具
+ < tool >
+ < name > {{tool_name}} < /name >
+ < description > {{tool_description}} < /description >
+ < / tool >
+ # 总目标
+ {{goal}}
+ # 当前阶段目标
+ {{current_goal}}
+ # 工具入参scheme
+ {{input_schema}}
+ # 背景信息
+ {{background_info}}
+ # 输出
+ """
+ )
+
REPAIR_PARAMS = dedent(r"""
你是一个工具参数修复器。
你的任务是根据当前的工具信息、目标、工具入参的schema、工具当前的入参、工具的报错、补充的参数和补充的参数描述,修复当前工具的入参。
@@ -1074,10 +1150,10 @@ FINAL_ANSWER = dedent(r"""
""")
MEMORY_TEMPLATE = dedent(r"""
- { % for ctx in context_list % }
+ {% for ctx in context_list % }
- 第{{loop.index}}步:{{ctx.step_description}}
- 调用工具 `{{ctx.step_id}}`,并提供参数 `{{ctx.input_data}}`
- 执行状态:{{ctx.step_status}}
- 得到数据:`{{ctx.output_data}}`
- { % endfor % }
+ {% endfor % }
""")