fix: Use AI-generated tool code in MCP server instead of template
This commit is contained in:
+136
-87
@@ -382,21 +382,15 @@ async def {func_name}({params_str}) -> str:
|
|||||||
auth = tool.get("auth", {})
|
auth = tool.get("auth", {})
|
||||||
request_params = tool.get("request_params", {})
|
request_params = tool.get("request_params", {})
|
||||||
timeout = tool.get("timeout", 30)
|
timeout = tool.get("timeout", 30)
|
||||||
|
generated_code = tool.get("generated_code", "")
|
||||||
|
|
||||||
# 构建参数
|
# 构建参数信息(用于 TOOL_LIST)
|
||||||
params = []
|
|
||||||
params_doc = []
|
|
||||||
properties = {}
|
properties = {}
|
||||||
required_params = []
|
required_params = []
|
||||||
|
|
||||||
# 支持两种格式:
|
|
||||||
# 1. {"properties": {"symbol": {...}}}
|
|
||||||
# 2. {"symbol": {...}} (直接参数格式)
|
|
||||||
param_props = request_params
|
param_props = request_params
|
||||||
if request_params and request_params.get("properties"):
|
if request_params and request_params.get("properties"):
|
||||||
param_props = request_params["properties"]
|
param_props = request_params["properties"]
|
||||||
elif request_params and not any(k in request_params for k in ["type", "required", "description"]):
|
elif request_params and not any(k in request_params for k in ["type", "required", "description"]):
|
||||||
# 直接参数格式
|
|
||||||
param_props = request_params
|
param_props = request_params
|
||||||
else:
|
else:
|
||||||
param_props = {}
|
param_props = {}
|
||||||
@@ -405,93 +399,48 @@ async def {func_name}({params_str}) -> str:
|
|||||||
for p_name, p_info in param_props.items():
|
for p_name, p_info in param_props.items():
|
||||||
if not isinstance(p_info, dict):
|
if not isinstance(p_info, dict):
|
||||||
continue
|
continue
|
||||||
p_type = self._json_type_to_python(p_info.get("type", "string"))
|
|
||||||
p_desc = p_info.get("description", "")
|
p_desc = p_info.get("description", "")
|
||||||
is_required = p_info.get("required", False)
|
is_required = p_info.get("required", False)
|
||||||
default = p_info.get("default")
|
|
||||||
|
|
||||||
if is_required:
|
if is_required:
|
||||||
params.append(f"{p_name}: {p_type}")
|
|
||||||
required_params.append(p_name)
|
required_params.append(p_name)
|
||||||
else:
|
|
||||||
default_val = f'"{default}"' if isinstance(default, str) else (default if default is not None else "None")
|
|
||||||
params.append(f"{p_name}: Optional[{p_type}] = {default_val}")
|
|
||||||
|
|
||||||
params_doc.append(f" {p_name}: {p_desc}")
|
|
||||||
properties[p_name] = {"type": p_info.get("type", "string"), "description": p_desc}
|
properties[p_name] = {"type": p_info.get("type", "string"), "description": p_desc}
|
||||||
|
|
||||||
params_str = ", ".join(params) if params else ""
|
# 如果有 AI 生成的代码,使用它;否则使用模板
|
||||||
params_doc_str = "\n".join(params_doc) if params_doc else " 无参数"
|
if generated_code:
|
||||||
|
# 从 AI 生成的代码中提取函数并添加 @server.tool() 装饰器
|
||||||
# 生成认证代码
|
import re
|
||||||
auth_headers = self._get_auth_headers_code(auth)
|
# 移除开头的 docstring 和 import 语句
|
||||||
|
code_lines = generated_code.split('\n')
|
||||||
# 构建参数字典代码
|
func_start = -1
|
||||||
params_dict_code = ""
|
for i, line in enumerate(code_lines):
|
||||||
if param_props:
|
if line.strip().startswith('async def '):
|
||||||
params_dict_code = "params = {"
|
func_start = i
|
||||||
for p_name in param_props.keys():
|
break
|
||||||
if isinstance(param_props[p_name], dict):
|
|
||||||
params_dict_code += f'"{p_name}": {p_name}, '
|
|
||||||
params_dict_code = params_dict_code.rstrip(", ") + "}"
|
|
||||||
else:
|
|
||||||
params_dict_code = "params = {}"
|
|
||||||
|
|
||||||
# API Key in query
|
|
||||||
if auth and auth.get("type") == "api_key" and auth.get("in") == "query":
|
|
||||||
key_name = auth.get("name", "apikey")
|
|
||||||
params_dict_code += f'\n params["{key_name}"] = os.getenv("TOOL_API_KEY", "")'
|
|
||||||
|
|
||||||
# 生成函数代码
|
|
||||||
func_code = f'''
|
|
||||||
@server.tool()
|
|
||||||
async def {func_name}({params_str}) -> str:
|
|
||||||
"""
|
|
||||||
{desc}
|
|
||||||
|
|
||||||
Args:
|
|
||||||
{params_doc_str}
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
API 响应结果 (JSON 格式)
|
|
||||||
"""
|
|
||||||
import httpx
|
|
||||||
|
|
||||||
url = "{url}"
|
|
||||||
{auth_headers}
|
|
||||||
{params_dict_code}
|
|
||||||
|
|
||||||
try:
|
|
||||||
async with httpx.AsyncClient(timeout={timeout}) as client:
|
|
||||||
response = await client.request(
|
|
||||||
method="{method}",
|
|
||||||
url=url,
|
|
||||||
headers=headers,
|
|
||||||
params={{k: v for k, v in params.items() if v is not None}}
|
|
||||||
)
|
|
||||||
|
|
||||||
if response.status_code == 200:
|
|
||||||
return json.dumps({{
|
|
||||||
"success": True,
|
|
||||||
"data": response.json() if response.headers.get("content-type", "").startswith("application/json") else response.text
|
|
||||||
}}, ensure_ascii=False, indent=2)
|
|
||||||
else:
|
|
||||||
return json.dumps({{
|
|
||||||
"success": False,
|
|
||||||
"status_code": response.status_code,
|
|
||||||
"error": response.text[:500]
|
|
||||||
}}, ensure_ascii=False)
|
|
||||||
|
|
||||||
except Exception as e:
|
if func_start >= 0:
|
||||||
# 使用 AI Agent 作为后备
|
# 提取函数代码
|
||||||
result = await get_agent().run(f"请帮我处理这个请求: {params}")
|
func_code_lines = code_lines[func_start:]
|
||||||
return json.dumps({{
|
func_body = '\n'.join(func_code_lines)
|
||||||
"success": True,
|
|
||||||
"source": "ai_agent",
|
# 添加 @server.tool() 装饰器
|
||||||
"result": result.output
|
func_code = f'''
|
||||||
}}, ensure_ascii=False, indent=2)
|
@server.tool()
|
||||||
|
{func_body}
|
||||||
'''
|
'''
|
||||||
tool_functions.append(func_code)
|
tool_functions.append(func_code)
|
||||||
|
else:
|
||||||
|
# 无法解析,使用原始代码
|
||||||
|
logger.warning(f"无法解析 AI 生成的代码: {name}")
|
||||||
|
func_code = self._generate_fallback_tool_code(
|
||||||
|
func_name, desc, url, method, auth, param_props, timeout
|
||||||
|
)
|
||||||
|
tool_functions.append(func_code)
|
||||||
|
else:
|
||||||
|
# 使用模板生成代码
|
||||||
|
func_code = self._generate_fallback_tool_code(
|
||||||
|
func_name, desc, url, method, auth, param_props, timeout
|
||||||
|
)
|
||||||
|
tool_functions.append(func_code)
|
||||||
tool_map_entries.append(f" '{func_name}': {func_name},")
|
tool_map_entries.append(f" '{func_name}': {func_name},")
|
||||||
|
|
||||||
tool_list_entries.append(f''' {{
|
tool_list_entries.append(f''' {{
|
||||||
@@ -603,6 +552,106 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
return "headers = {}"
|
return "headers = {}"
|
||||||
|
|
||||||
|
def _generate_fallback_tool_code(
|
||||||
|
self,
|
||||||
|
func_name: str,
|
||||||
|
desc: str,
|
||||||
|
url: str,
|
||||||
|
method: str,
|
||||||
|
auth: Dict,
|
||||||
|
param_props: Dict,
|
||||||
|
timeout: int
|
||||||
|
) -> str:
|
||||||
|
"""生成后备工具代码(当没有 AI 生成代码时使用)"""
|
||||||
|
# 构建参数
|
||||||
|
params = []
|
||||||
|
params_doc = []
|
||||||
|
|
||||||
|
if param_props:
|
||||||
|
for p_name, p_info in param_props.items():
|
||||||
|
if not isinstance(p_info, dict):
|
||||||
|
continue
|
||||||
|
p_type = self._json_type_to_python(p_info.get("type", "string"))
|
||||||
|
p_desc = p_info.get("description", "")
|
||||||
|
is_required = p_info.get("required", False)
|
||||||
|
default = p_info.get("default")
|
||||||
|
|
||||||
|
if is_required:
|
||||||
|
params.append(f"{p_name}: {p_type}")
|
||||||
|
else:
|
||||||
|
default_val = f'"{default}"' if isinstance(default, str) else (default if default is not None else "None")
|
||||||
|
params.append(f"{p_name}: Optional[{p_type}] = {default_val}")
|
||||||
|
|
||||||
|
params_doc.append(f" {p_name}: {p_desc}")
|
||||||
|
|
||||||
|
params_str = ", ".join(params) if params else ""
|
||||||
|
params_doc_str = "\n".join(params_doc) if params_doc else " 无参数"
|
||||||
|
|
||||||
|
# 生成认证代码
|
||||||
|
auth_headers = self._get_auth_headers_code(auth)
|
||||||
|
|
||||||
|
# 构建参数字典代码
|
||||||
|
params_dict_code = ""
|
||||||
|
if param_props:
|
||||||
|
params_dict_code = "params = {"
|
||||||
|
for p_name in param_props.keys():
|
||||||
|
if isinstance(param_props[p_name], dict):
|
||||||
|
params_dict_code += f'"{p_name}": {p_name}, '
|
||||||
|
params_dict_code = params_dict_code.rstrip(", ") + "}"
|
||||||
|
else:
|
||||||
|
params_dict_code = "params = {}"
|
||||||
|
|
||||||
|
# API Key in query
|
||||||
|
if auth and auth.get("type") == "api_key" and auth.get("in") == "query":
|
||||||
|
key_name = auth.get("name", "apikey")
|
||||||
|
params_dict_code += f'\n params["{key_name}"] = os.getenv("TOOL_API_KEY", "")'
|
||||||
|
|
||||||
|
return f'''
|
||||||
|
@server.tool()
|
||||||
|
async def {func_name}({params_str}) -> str:
|
||||||
|
"""
|
||||||
|
{desc}
|
||||||
|
|
||||||
|
Args:
|
||||||
|
{params_doc_str}
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
API 响应结果 (JSON 格式)
|
||||||
|
"""
|
||||||
|
import httpx
|
||||||
|
|
||||||
|
api_url = "{url}"
|
||||||
|
{auth_headers}
|
||||||
|
{params_dict_code}
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with httpx.AsyncClient(timeout={timeout}) as client:
|
||||||
|
response = await client.request(
|
||||||
|
method="{method}",
|
||||||
|
url=api_url,
|
||||||
|
headers=headers,
|
||||||
|
params={{k: v for k, v in params.items() if v is not None}}
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
return json.dumps({{
|
||||||
|
"success": True,
|
||||||
|
"data": response.json() if response.headers.get("content-type", "").startswith("application/json") else response.text
|
||||||
|
}}, ensure_ascii=False, indent=2)
|
||||||
|
else:
|
||||||
|
return json.dumps({{
|
||||||
|
"success": False,
|
||||||
|
"status_code": response.status_code,
|
||||||
|
"error": response.text[:500]
|
||||||
|
}}, ensure_ascii=False)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return json.dumps({{
|
||||||
|
"success": False,
|
||||||
|
"error": str(e)
|
||||||
|
}}, ensure_ascii=False, indent=2)
|
||||||
|
'''
|
||||||
|
|
||||||
def _generate_system_prompt(
|
def _generate_system_prompt(
|
||||||
self,
|
self,
|
||||||
agent_name: str,
|
agent_name: str,
|
||||||
|
|||||||
@@ -844,6 +844,8 @@ async def create_agent_with_tools(request: CreateAgentWithToolsRequest):
|
|||||||
tools_config = []
|
tools_config = []
|
||||||
for tool in tools:
|
for tool in tools:
|
||||||
config = tool.get("config", {})
|
config = tool.get("config", {})
|
||||||
|
# 获取 AI 生成的工具代码
|
||||||
|
tool_code = tool_storage.get_tool_code(tool.get("tool_ref_id", ""))
|
||||||
tools_config.append({
|
tools_config.append({
|
||||||
"name": tool["name"],
|
"name": tool["name"],
|
||||||
"description": tool.get("description", ""),
|
"description": tool.get("description", ""),
|
||||||
@@ -852,7 +854,8 @@ async def create_agent_with_tools(request: CreateAgentWithToolsRequest):
|
|||||||
"auth": config.get("auth"),
|
"auth": config.get("auth"),
|
||||||
"request_params": config.get("request_params"),
|
"request_params": config.get("request_params"),
|
||||||
"request_body": config.get("request_body"),
|
"request_body": config.get("request_body"),
|
||||||
"timeout": config.get("timeout", 30)
|
"timeout": config.get("timeout", 30),
|
||||||
|
"generated_code": tool_code # 传递 AI 生成的代码
|
||||||
})
|
})
|
||||||
|
|
||||||
# 生成唯一后缀,确保不同用户创建同名 Agent 不会冲突
|
# 生成唯一后缀,确保不同用户创建同名 Agent 不会冲突
|
||||||
|
|||||||
Reference in New Issue
Block a user