Forum Discussion
Issue with: Connected Agent Tool Forcing from an Orchestrator Agent
Hi Team,
I am trying to force tool selection for my Connected Agents from an Orchestrator Agent for my Multi-Agent Model. Not sure if that is possible
Apologies in advance for too much detail as I really need this to work! Please let me know if there is a flaw in my approach!
The main intention behind going towards Tool forcing was because with current set of instructions provided to my Orchestrator Agent, It was providing hallucinated responses from my Child Agents for each query.
I have an Orchestrator Agent which is connected to the following Child Agents (Each with detailed instructions)
- Child Agent 1 - Connects to SQL DB in Fabric to fetch information from Log tables.
- Child Agent 2 - Invokes OpenAPI Action tool for Azure Functions to run pipelines in Fabric.
I have provided details on 3 approaches.
Approach 1:
I have checked the MS docs "CONNECTED_AGENT" is a valid property for ToolChoiceType "https://learn.microsoft.com/en-us/python/api/azure-ai-agents/azure.ai.agents.models.agentsnamedtoolchoicetype?view=azure-python"
Installed the latest Python AI Agents SDK Beta version as it also supports "Connected Agents": https://pypi.org/project/azure-ai-agents/1.2.0b6/#create-an-agent-using-another-agents The following code is integrated into a streamlit UI code.
Python Code:
agents_client = AgentsClient(
endpoint=PROJECT_ENDPOINT,
credential=DefaultAzureCredential(
exclude_environment_credential=True,
exclude_managed_identity_credential=True
)
)
# -------------------------------------------------------------------
# UPDATE ORCHESTRATOR TOOLS (executed once)
# -------------------------------------------------------------------
fabric_tool = ConnectedAgentTool(
id=FABRIC_AGENT_ID,
name="Fabric_Agent",
description="Handles Fabric pipeline questions"
)
openapi_tool = ConnectedAgentTool(
id=OPENAPI_AGENT_ID,
name="Fabric_Pipeline_Trigger",
description="Handles OpenAPI pipeline triggers"
)
# Update orchestrator agent to include child agent tools
agents_client.update_agent(
agent_id=ORCH_AGENT_ID,
tools=[
fabric_tool.definitions[0],
openapi_tool.definitions[0]
],
instructions="""
You are the Master Orchestrator Agent.
Use:
- "Fabric_Agent" when the user's question includes:
"Ingestion", "Trigger", "source", "Connection"
- "Fabric_Pipeline_Trigger" when the question mentions:
"OpenAPI", "Trigger", "API call", "Pipeline start"
Only call tools when needed. Respond clearly and concisely.
"""
)
# ------------------------- TOOL ROUTING LOGIC -------------------------
def choose_tool(user_input: str):
text = user_input.lower()
if any(k in text for k in ["log", "trigger","pipeline","connection"]):
return fabric_tool
if any(k in text for k in ["openapi", "api call", "pipeline start"]):
return openapi_tool
# No forced routing → let orchestrator decide
return None
forced_tool = choose_tool(user_query)
run = agents_client.runs.create_and_process(
thread_id=st.session_state.thread.id,
agent_id=ORCH_AGENT_ID,
tool_choice={
"type": "connected_agent",
"function": forced_tool.definitions[0]
} Error: Azure.core.exceptions.HttpResponseError: (invalid_value) Invalid value: 'connected_agent'. Supported values are: 'code_interpreter', 'function', 'file_search', 'openapi', 'azure_function', 'azure_ai_search', 'bing_grounding', 'bing_custom_search', 'deep_research', 'sharepoint_grounding', 'fabric_dataagent', 'computer_use_preview', and 'image_generation'. Code: invalid_value Message: Invalid value: 'connected_agent'. Supported values are: 'code_interpreter', 'function', 'file_search', 'openapi', 'azure_function', 'azure_ai_search', 'bing_grounding', 'bing_custom_search', 'deep_research', 'sharepoint_grounding', 'fabric_dataagent', 'computer_use_preview', and 'image_generation'."
Approach 2:
- Create ConnectedAgentTool as you do, and pass its definitions to update_agent(...).
- Force a tool by name using tool_choice={"type": "function", "function": {"name": "<tool-name>"}}.
- Do not set type: "connected_agent" anywhere—there is no such tool_choice.type.
Code:
from azure.identity import DefaultAzureCredential
from azure.ai.agents import AgentsClient
# Adjust imports to your SDK layout:
# e.g., from azure.ai.agents.tool import ConnectedAgentTool
agents_client = AgentsClient(
endpoint=PROJECT_ENDPOINT,
credential=DefaultAzureCredential(
exclude_environment_credential=True,
exclude_managed_identity_credential=True # keep your current credential choices
)
)
# -------------------------------------------------------------------
# CREATE CONNECTED AGENT TOOLS (child agents exposed as function tools)
# -------------------------------------------------------------------
fabric_tool = ConnectedAgentTool(
id=FABRIC_AGENT_ID, # the **child agent ID** you created elsewhere
name="Fabric_Agent", # **tool name** visible to the orchestrator
description="Handles Fabric pipeline questions"
)
openapi_tool = ConnectedAgentTool(
id=OPENAPI_AGENT_ID, # another child agent ID
name="Fabric_Pipeline_Trigger", # tool name visible to the orchestrator
description="Handles OpenAPI pipeline triggers"
)
# -------------------------------------------------------------------
# UPDATE ORCHESTRATOR: attach child tools
# -------------------------------------------------------------------
# NOTE: definitions is usually a list of ToolDefinition objects produced by the helper
agents_client.update_agent(
agent_id=ORCH_AGENT_ID,
tools=[
fabric_tool.definitions[0],
openapi_tool.definitions[0]
],
instructions="""
You are the Master Orchestrator Agent.
Use:
- "Fabric_Agent" when the user's question includes: "Ingestion", "Trigger", "source", "Connection"
- "Fabric_Pipeline_Trigger" when the question mentions: "OpenAPI", "Trigger", "API call", "Pipeline start"
Only call tools when needed. Respond clearly and concisely.
"""
)
# ------------------------- TOOL ROUTING LOGIC -------------------------
def choose_tool(user_input: str):
text = user_input.lower()
if any(k in text for k in ["log", "trigger", "pipeline", "connection"]):
return "Fabric_Agent" # return the **tool name**
if any(k in text for k in ["openapi", "api call", "pipeline start"]):
return "Fabric_Pipeline_Trigger" # return the **tool name**
return None
forced_tool_name = choose_tool(user_query)
# ------------------------- RUN INVOCATION -------------------------
if forced_tool_name:
# FORCE a specific connected agent by **function name**
run = agents_client.runs.create_and_process(
thread_id=st.session_state.thread.id,
agent_id=ORCH_AGENT_ID,
tool_choice={
"type": "function", # <-- REQUIRED
"function": {
"name": forced_tool_name # <-- must match the tool's name as registered
}
}
)
else:
# Let the orchestrator auto-select (no tool_choice → "auto")
run = agents_client.runs.create_and_process(
thread_id=st.session_state.thread.id,
agent_id=ORCH_AGENT_ID
)
Error: azure.core.exceptions.HttpResponseError: (None) Invalid tool_choice: Fabric_Agent. You must also pass this tool in the 'tools' list on the Run. Code: None Message: Invalid tool_choice: Fabric_Agent. You must also pass this tool in the 'tools' list on the Run.
Approach 3: Modified version of the 2nd Approach with Took Definitions call:
# ------------------------- TOOL ROUTING LOGIC -------------------------
def choose_tool(user_input: str):
text = user_input.lower()
if any(k in text for k in ["log", "trigger","pipeline","connection"]):
# return "Fabric_Agent"
return (
"Fabric_Agent",
fabric_tool.definitions[0]
)
if any(k in text for k in ["openapi", "api call", "pipeline start"]):
# return "Fabric_Pipeline_Trigger"
return (
"Fabric_Pipeline_Trigger",
openapi_tool.definitions[0]
)
# No forced routing → let orchestrator decide
# return None
return (None, None)
# forced_tool = choose_tool(user_query)
forced_tool_name, forced_tool_def = choose_tool(user_query)
# ------------------------- ORCHESTRATOR CALL -------------------------
if forced_tool_name:
tool_choice = {
"type": "function",
"function": { "name": forced_tool_name }
}
run = agents_client.runs.create_and_process(
thread_id=st.session_state.thread.id,
agent_id=ORCH_AGENT_ID,
tool_choice=tool_choice,
tools=[ forced_tool_def ] # << only the specific tool
)
else:
# no forced tool, orchestrator decides
run = agents_client.runs.create_and_process(
thread_id=st.session_state.thread.id,
agent_id=ORCH_AGENT_ID
)
Error: TypeError: azure.ai.agents.operations._patch.RunsOperations.create() got multiple values for keyword argument 'tools'