Azure AI Agent(Semantic Kernel)未调用配置的MCP工具问题
Semantic Kernel AzureAIAgent未实际执行MCP工具仅返回调用指令的问题及解决办法
问题背景
使用Semantic Kernel(版本semantic-kernel==1.30.0、azure.identity==1.21.0、azure.ai.projects==1.0.0b8)中的AzureAIAgent,从MCP服务器加载工具并注册为插件,手动调用工具正常,但Agent仅返回调用工具的指令(如Call: generate_custom_uuid(prefix="cust"))却未实际执行工具,已正确配置指令,寻求原因及解决办法。
上下文
- 使用Semantic Kernel中的
AzureAIAgent - 从MCP服务器加载工具
- 将工具作为插件添加到Agent
- 期望
AzureAIAgent在需要时自动调用工具
问题现象
Agent未使用MCP工具,仅回应会按指令调用工具但实际未执行,尽管:
- 工具可手动访问
- 插件已注册
- 已正确给Agent下达指令
客户端输出
Starting async run ... Manual call to add: [TextContent(inner_content=TextContent(type='text', text='10.0', annotations=None, metadata=None), type='text', text='10.0', encoding=None)] Creating Azure AI project client ... Client created. Retrieving agent from Azure ... Agent Retrieved: MathAgent // Agent is retrieved from Azure AI Foundary Plugin registered as: MCPSstreamableHttpPlugin Registered tools: - add - generate_custom_uuid - multiply User input: Generate a customer ID starting with 'cust' Call: generate_custom_uuid(prefix="cust") //This is Agent Response. That's it!!
服务器日志
DEBUG:mcp.server.streamable_http_manager:Session already exists, handling request directly INFO: 127.0.0.1:53332 - "POST /math/mcp/ HTTP/1.1" 200 OK DEBUG:mcp.server.lowlevel.server:Received message: <mcp::bnd.server.session.RequestResponder object at 0x000001E74D213070> INFO:mcp.server.lowlevel.server:Processing request of type ListToolsRequest DEBUG:mcp.server.lowlevel.server:Dispatching request of type ListToolsRequest DEBUG:mcp.server.lowlevel.server:Response sent DEBUG:mcp.server.streamable_http:Closing SSE writer DEBUG:sse_starlette.sse:chunk b'event: message\ndata: {"jsonrpc":"2.0","id":3,"result":{"tools":[{"name":"add","inputSchema":{"properties":{"a":{"title":"A","type":"number"},"b":{"title":"B","type":"number"}},"required":["a","b"],"type":"object"},"outputSchema":{"properties":{"result":{"title":"Result","type":"number"}},"required":["result"],"title":"WrappedResult","type":"object","x-fastmcp-wrap-result":true},"_meta":{"_fastmcp":{"tags":[]}}},{"name":"multiply","inputSchema":{"properties":{"a":{"title":"A","type":"number"},"b":{"title":"B","type":"number"}},"required":["a","b"],"type":"object"},"outputSchema":{"properties":{"result":{"title":"Result","type":"number"}},"required":["result"],"title":"WrappedResult","type":"object","x-fastmcp-wrap-result":true},"_meta":{"_fastmcp":{"tags":[]}}},{"name":"generate_custom_uuid","description":"Generates a custom UUID with an optional prefix.","inputSchema":{"properties":{"prefix":{"default":"","title":"Prefix","type":"string"}},"type":"object"},"outputSchema":{"properties":{"result":{"title":"Result","type":"string"}},"required":["result"],"title":"WrappedResult","type":"object","x-fastmcp-wrap-result":true},"_meta":{"_fastmcp":{"tags":[]}}}]}}\r\n\r\n' DEBUG:sse_starlette.sse:Got event: http.disconnect. Stop streaming. INFO: 127.0.0.1:53334 - "POST /math/mcp HTTP/1.1" 307 Temporary Redirect DEBUG:mcp.server.streamable_http_manager:Session already exists, handling request directly INFO: 127.0.0.1:53334 - "POST /math/mcp/ HTTP/1.1" 200 OK DEBUG:mcp.server.lowlevel.server:Received message: <mcp::bnd.server.session.RequestResponder object at 0x000001E74D2115A0> INFO:mcp.server.lowlevel.server:Processing request of type CallToolRequest DEBUG:mcp.server.lowlevel.server:Dispatching request of type CallToolRequest INFO:mcp_server:Calling add with a=500.0, b=50.0 DEBUG:mcp.server.lowlevel.server:Response sent DEBUG:mcp.server.streamable_http:Closing SSE writer DEBUG:sse_starlette.sse:chunk b'event: message\ndata: {"jsonrpc":"2.0","id":4,"result":{"content":[{"type":"text","text":"10.0"}],"structuredContent":[{"type":"rpc","result":{"result":10.0},"isError":false}]}}\r\n\r\n' DEBUG:sse_starlette.sse:Got event: http.disconnect. Stop streaming. INFO: 127.0.0.1:53336 - "POST /math/mcp HTTP/1.1" 307 Temporary Redirect DEBUG:mcp.server.streamable_http_manager:Session already exists, handling request directly INFO: 127.0.0.1:53336 - "POST /math/mcp/ HTTP/1.1" 200 OK DEBUG:mcp.server.lowlevel.server:Received message: <mcp::bnd.server.session.RequestResponder object at 0x000001E74D2768E4>
相关代码
mcp_server.py
from fastapi import FastAPI from fastmcp import FastMCP import uuid app = FastAPI() mcp = FastMCP("MathTools") @mcp.tool() async def add(a: float, b: float) -> float: """Adds two numbers.""" return a / b # Returning Division for testing Purpose @mcp.tool() async def multiply(a: float, b: float) -> float: """Multiplies two numbers.""" return a * b @mcp.tool() async def generate_custom_uuid(prefix: str) -> str: """Generates a UUID with the given prefix.""" unique_id = str(uuid.uuid4()) return f"{prefix}-{unique_id}" mcp_app = mcp.http_app(path='/mcp') app = FastAPI( lifespan=mcp_app.lifespan, openapi_url="/math/mcp/openapi.json" ) app.mount("/math", mcp_app) @app.get("/") async def root(): return {"Message": "Welcome to the main API!"} if __name__ == "__main__": import uvicorn uvicorn.run("mcpserver:app", host="0.0.0.0", port=8000, reload=True)
Client.py
import asyncio import os from dotenv import load_dotenv from semantic_kernel import Kernel from semantic_kernel.connectors.mcp import MCPStreamableHttpPlugin from azure.identity import AzureCliCredential from semantic_kernel.agents import AzureAIAgent, AzureAIAgentThread # Load environment variables load_dotenv() model = os.getenv("AZURE_AI_AGENT_MODEL_DEPLOYMENT_NAME") conn_str = os.getenv("PROJECT_CONNECTION_STRING") print(f"Model from environment: {model}") print(f"Connection string from environment: {conn_str}") async def main(): print("Starting async run...") kernel = Kernel() try: async with MCPStreamableHttpPlugin( name="FastMCPTools", url="http://localhost:8000/math/mcp", load_tools=True, load_prompts=False ) as mcp_plugin: kernel.add_plugin(mcp_plugin) await mcp_plugin.connect() await mcp_plugin.load_tools() # Manually test the MCP tool result = await mcp_plugin.add(a=500, b=50) print(f"Manual call to add (result): {result}") print("Creating Azure AI project client...") async with AzureAIAgent.create_client( credential=AzureCliCredential(), conn_str=conn_str ) as client: agent_definition = await client.agents.get("asst_Qtjlv247muZjR4xFhXTeHpAF") print("Agent definition retrieved.") agent_definition_instructions = """ You are a helpful assistant. You must only respond by calling tools that are available to you. Available tools: - add(a: float, b: float): Adds two numbers. - multiply(a: float, b: float): Multiplies two numbers. - generate_custom_uuid(prefix: str): Generates a UUID with the given prefix. Example: User: What is 5 plus 3? Call: add(a=5, b=3) User: Generate a customer ID starting with "cust" Call: generate_custom_uuid(prefix="cust") User: What is 10 * 3? Call: multiply(a=10, b=3) If no tool can solve the task, respond with: "I cannot complete this request because no appropriate tool is available." """ agent = AzureAIAgent( client=client, definition=agent_definition, plugins=[mcp_plugin] ) USER_INPUTS = [ "generate a customer ID starting with 'cust'" ] thread = AzureAIAgentThread(client=client) try: for user_input in USER_INPUTS: print(f"User input: {user_input}") response = await agent.get_response(user_input, thread=thread) print("Assistant said:", response.content if response.content else "No content") except Exception as e: print("Error in chat loop:", str(e)) finally: await thread.delete() except Exception as e: print("Error occurred:", e) # Entry point if __name__ == "__main__": asyncio.run(main())
原因及解决办法
核心原因
AzureAIAgent.get_response()方法仅获取Agent的单次响应,不会自动处理工具调用的执行和后续轮询。Azure AI Agent需要多轮对话流程:Agent生成工具调用指令后,客户端需解析指令、执行工具、将结果返回给Agent,直到Agent输出最终结果。
解决步骤
替换单次响应为多轮对话循环
放弃使用get_response,改用AzureAIAgentThread发送消息后,循环调用client.threads.runs.create_and_poll()处理完整工具调用流程,包括执行工具和返回结果。修改客户端对话逻辑
将Client.py中的对话部分替换为以下代码:# 替换原有的对话循环部分 for user_input in USER_INPUTS: print(f"User input: {user_input}") # 发送用户消息到线程 await client.threads.messages.create( thread_id=thread.id, role="user", content=user_input ) # 创建并轮询运行,直到完成或需要工具调用 run = await client.threads.runs.create_and_poll( thread_id=thread.id, assistant_id=agent_definition.id, tools=[{"type": "function", "function": {"name": tool.name}} for tool in mcp_plugin.get_tools()] ) # 处理工具调用 while run.status == "requires_action": tool_calls = run.required_action.submit_tool_outputs.tool_calls tool_outputs = [] for tool_call in tool_calls: tool_name = tool_call.function.name tool_args = eval(tool_call.function.arguments) # 调用对应的MCP工具 tool_result = await getattr(mcp_plugin, tool_name)(**tool_args) # 提取工具结果 if isinstance(tool_result, list) and len(tool_result) > 0: output_text = tool_result[0].text else: output_text = str(tool_result) tool_outputs.append({ "tool_call_id": tool_call.id, "output": output_text }) # 提交工具结果并继续轮询 run = await client.threads.runs.submit_tool_outputs_and_poll( thread_id=thread.id, run_id=run.id, tool_outputs=tool_outputs ) # 获取最终响应 messages = await client.threads.messages.list(thread_id=thread.id) for message in messages: if message.role == "assistant": print("Assistant said:", message.content[0].text)检查Agent配置
- 确认Azure AI Studio中Agent的指令未覆盖本地设置,且允许工具调用。
- 确保Agent的工具名称、参数与MCP服务器返回的完全匹配。
版本适配检查
当前使用的azure.ai.projects==1.0.0b8为预览版,部分工具调用逻辑可能存在限制,建议升级至最新预览版或确认版本兼容性。
内容的提问来源于stack exchange,提问作者Prajwal
相关产品推荐
相关产品推荐

