From f566d43cd5729b85111fd68d69f9760e78fc4ea5 Mon Sep 17 00:00:00 2001 From: zxstty Date: Wed, 13 Aug 2025 11:08:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=8F=82=E6=95=B0=E7=94=9F?= =?UTF-8?q?=E6=88=90=E7=9A=84prompt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/scheduler/executor/agent.py | 4 +- apps/scheduler/mcp_agent/host.py | 46 ++++++++-------- apps/scheduler/mcp_agent/plan.py | 5 +- apps/scheduler/mcp_agent/prompt.py | 86 ++++++++++++++++++++++++++++-- 4 files changed, 109 insertions(+), 32 deletions(-) diff --git a/apps/scheduler/executor/agent.py b/apps/scheduler/executor/agent.py index 3997d89a..4d34b85b 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 44b04d1f..f4350676 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 84c08d60..b539482d 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 31e8ce54..824ece8a 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 % } """) -- Gitee