You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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输出最终结果。

解决步骤

  1. 替换单次响应为多轮对话循环
    放弃使用get_response,改用AzureAIAgentThread发送消息后,循环调用client.threads.runs.create_and_poll()处理完整工具调用流程,包括执行工具和返回结果。

  2. 修改客户端对话逻辑
    将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)
    
  3. 检查Agent配置

    • 确认Azure AI Studio中Agent的指令未覆盖本地设置,且允许工具调用。
    • 确保Agent的工具名称、参数与MCP服务器返回的完全匹配。
  4. 版本适配检查
    当前使用的azure.ai.projects==1.0.0b8为预览版,部分工具调用逻辑可能存在限制,建议升级至最新预览版或确认版本兼容性。


内容的提问来源于stack exchange,提问作者Prajwal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 12:45:56