From 97615761e9de68b6c19f2f7ca0f9ea6921fc5205 Mon Sep 17 00:00:00 2001 From: Markus Date: Sun, 21 Sep 2025 22:45:51 -0700 Subject: [PATCH 1/2] new final prompt to restrict further action --- src/backend/v3/orchestration/human_approval_manager.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/backend/v3/orchestration/human_approval_manager.py b/src/backend/v3/orchestration/human_approval_manager.py index 5d91133cd..54c8123fe 100644 --- a/src/backend/v3/orchestration/human_approval_manager.py +++ b/src/backend/v3/orchestration/human_approval_manager.py @@ -62,8 +62,7 @@ def __init__(self, *args, **kwargs): """ final_append = """ -The final answer should not include any offers of further conversation or assistance. The application will not all further interaction with the user. -The final answer should be a complete and final response to the user's original request. + DO NOT EVER OFFER TO HELP FURTHER IN THE FINAL ANSWER! Just provide the final answer and end with a polite closing. """ # kwargs["task_ledger_facts_prompt"] = ORCHESTRATOR_TASK_LEDGER_FACTS_PROMPT + facts_append From f2bd8ad7219b672b558bb294956da5d019bbe057 Mon Sep 17 00:00:00 2001 From: Markus Date: Mon, 22 Sep 2025 15:52:58 -0700 Subject: [PATCH 2/2] turn off code interpreter on all agents, reuse existing foundry agent definitions where possible. --- data/agent_teams/marketing.json | 2 +- data/agent_teams/retail.json | 2 +- .../v3/magentic_agents/foundry_agent.py | 110 ++++++++++++------ 3 files changed, 78 insertions(+), 36 deletions(-) diff --git a/data/agent_teams/marketing.json b/data/agent_teams/marketing.json index 4817df765..a6de2ec5f 100644 --- a/data/agent_teams/marketing.json +++ b/data/agent_teams/marketing.json @@ -37,7 +37,7 @@ "use_reasoning": false, "index_name": "", "index_foundry_name": "", - "coding_tools": true + "coding_tools": false }, { "input_key": "", diff --git a/data/agent_teams/retail.json b/data/agent_teams/retail.json index 86bdfed40..34fb89a81 100644 --- a/data/agent_teams/retail.json +++ b/data/agent_teams/retail.json @@ -37,7 +37,7 @@ "use_reasoning": false, "index_name": "macae-index", "index_foundry_name": "", - "coding_tools": true + "coding_tools": false }, { "input_key": "", diff --git a/src/backend/v3/magentic_agents/foundry_agent.py b/src/backend/v3/magentic_agents/foundry_agent.py index 1e91539ad..2cc62f639 100644 --- a/src/backend/v3/magentic_agents/foundry_agent.py +++ b/src/backend/v3/magentic_agents/foundry_agent.py @@ -2,11 +2,11 @@ import asyncio import logging -from typing import List, Optional +from typing import Awaitable, List, Optional from azure.ai.agents.models import (AzureAISearchTool, BingGroundingTool, CodeInterpreterToolDefinition) -from semantic_kernel.agents import AzureAIAgent # pylint: disable=E0611 +from semantic_kernel.agents import Agent, AzureAIAgent # pylint: disable=E0611 from v3.magentic_agents.common.lifecycle import AzureAgentBase from v3.magentic_agents.models.agent_models import MCPConfig, SearchConfig @@ -43,6 +43,7 @@ def __init__(self, agent_name: str, if self.model_deployment_name in ["o3", "o4-mini"]: raise ValueError("The current version of Foundry agents do not support reasoning models.") + # Uncomment to enable bing grounding capabilities (requires Bing connection in Foundry and uncommenting other code) # async def _make_bing_tool(self) -> Optional[BingGroundingTool]: # """Create Bing search tool for web search.""" # if not all([self.client, self.bing.connection_name]): @@ -119,19 +120,24 @@ async def _collect_tools_and_resources(self) -> tuple[List, dict]: return tools, tool_resources async def _after_open(self) -> None: - - # Collect all tools - tools, tool_resources = await self._collect_tools_and_resources() - - # Create agent definition with all tools - definition = await self.client.agents.create_agent( - model=self.model_deployment_name, - name=self.agent_name, - description=self.agent_description, - instructions=self.agent_instructions, - tools=tools, - tool_resources=tool_resources - ) + """Initialize the AzureAIAgent with the collected tools and MCP plugin.""" + + # Try to get existing agent definition from Foundry + definition = await self._get_azure_ai_agent_definition(self.agent_name) + # If not found in Foundry, create a new one + if definition is None: + # Collect all tools + tools, tool_resources = await self._collect_tools_and_resources() + + # Create agent definition with all tools + definition = await self.client.agents.create_agent( + model=self.model_deployment_name, + name=self.agent_name, + description=self.agent_description, + instructions=self.agent_instructions, + tools=tools, + tool_resources=tool_resources + ) # Add MCP plugins if available plugins = [self.mcp_plugin] if self.mcp_plugin else [] @@ -146,25 +152,25 @@ async def _after_open(self) -> None: self.logger.error("Failed to create AzureAIAgent: %s", ex) raise - # After self._agent creation in _after_open: - # Diagnostics - try: - tool_names = [t.get("function", {}).get("name") for t in (definition.tools or []) if isinstance(t, dict)] - self.logger.info( - "Foundry agent '%s' initialized. Azure tools: %s | MCP plugin: %s", - self.agent_name, - tool_names, - getattr(self.mcp_plugin, 'name', None) - ) - if not tool_names and not plugins: - self.logger.warning( - "Foundry agent '%s' has no Azure tool definitions and no MCP plugin. " - "Subsequent tool calls may fail.", self.agent_name - ) - except Exception as diag_ex: - self.logger.warning("Diagnostics collection failed: %s", diag_ex) - - self.logger.info("%s initialized with %d tools and %d plugins", self.agent_name, len(tools), len(plugins)) + # # After self._agent creation in _after_open: + # # Diagnostics + # try: + # tool_names = [t.get("function", {}).get("name") for t in (definition.tools or []) if isinstance(t, dict)] + # self.logger.info( + # "Foundry agent '%s' initialized. Azure tools: %s | MCP plugin: %s", + # self.agent_name, + # tool_names, + # getattr(self.mcp_plugin, 'name', None) + # ) + # if not tool_names and not plugins: + # self.logger.warning( + # "Foundry agent '%s' has no Azure tool definitions and no MCP plugin. " + # "Subsequent tool calls may fail.", self.agent_name + # ) + # except Exception as diag_ex: + # self.logger.warning("Diagnostics collection failed: %s", diag_ex) + + # self.logger.info("%s initialized with %d tools and %d plugins", self.agent_name, len(tools), len(plugins)) async def fetch_run_details(self, thread_id: str, run_id: str): """Fetch and log run details after a failure.""" @@ -180,6 +186,42 @@ async def fetch_run_details(self, thread_id: str, run_id: str): except Exception as ex: self.logger.error("Could not fetch run details: %s", ex) + async def _get_azure_ai_agent_definition(self, agent_name: str)-> Awaitable[Agent | None]: + """ + Gets an Azure AI Agent with the specified name and instructions using AIProjectClient if it is already created. + """ + # # First try to get an existing agent with this name as assistant_id + try: + agent_id = None + agent_list = self.client.agents.list_agents() + async for agent in agent_list: + if agent.name == agent_name: + agent_id = agent.id + break + # If the agent already exists, we can use it directly + # Get the existing agent definition + if agent_id is not None: + logging.info(f"Agent with ID {agent_id} exists.") + + existing_definition = await self.client.agents.get_agent(agent_id) + + return existing_definition + else: + return None + except Exception as e: + # The Azure AI Projects SDK throws an exception when the agent doesn't exist + # (not returning None), so we catch it and proceed to create a new agent + if "ResourceNotFound" in str(e) or "404" in str(e): + logging.info( + f"Agent with ID {agent_name} not found. Will create a new one." + ) + else: + # Log unexpected errors but still try to create a new agent + logging.warning( + f"Unexpected error while retrieving agent {agent_name}: {str(e)}. Attempting to create new agent." + ) + + async def create_foundry_agent(agent_name:str, agent_description:str, agent_instructions:str,