Skip to content

Commit f47daa5

Browse files
authored
Merge pull request #195 from microsoft/fix/agent-changes
feat: Rework agent structure - solve foundry leaks - interop with foundry
2 parents 349e596 + c8333be commit f47daa5

12 files changed

+591
-304
lines changed

src/backend/app_config.py

Lines changed: 8 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
# app_config.py
2-
import os
32
import logging
4-
from typing import Optional, List, Dict, Any
5-
from dotenv import load_dotenv
6-
from azure.identity import DefaultAzureCredential, ClientSecretCredential
7-
from azure.cosmos.aio import CosmosClient
3+
import os
4+
from typing import Any, Dict, List, Optional
5+
86
from azure.ai.projects.aio import AIProjectClient
9-
from semantic_kernel.kernel import Kernel
10-
from semantic_kernel.contents import ChatHistory
7+
from azure.cosmos.aio import CosmosClient
8+
from azure.identity import ClientSecretCredential, DefaultAzureCredential
9+
from dotenv import load_dotenv
1110
from semantic_kernel.agents.azure_ai.azure_ai_agent import AzureAIAgent
11+
from semantic_kernel.contents import ChatHistory
1212
from semantic_kernel.functions import KernelFunction
13+
from semantic_kernel.kernel import Kernel
1314

1415
# Load environment variables from .env file
1516
load_dotenv()
@@ -189,94 +190,6 @@ def get_ai_project_client(self):
189190
logging.error("Failed to create AIProjectClient: %s", exc)
190191
raise
191192

192-
async def create_azure_ai_agent(
193-
self,
194-
agent_name: str,
195-
instructions: str,
196-
tools: Optional[List[KernelFunction]] = None,
197-
client=None,
198-
response_format=None,
199-
temperature: float = 0.0,
200-
):
201-
"""
202-
Creates a new Azure AI Agent with the specified name and instructions using AIProjectClient.
203-
If an agent with the given name (assistant_id) already exists, it tries to retrieve it first.
204-
205-
Args:
206-
kernel: The Semantic Kernel instance
207-
agent_name: The name of the agent (will be used as assistant_id)
208-
instructions: The system message / instructions for the agent
209-
agent_type: The type of agent (defaults to "assistant")
210-
tools: Optional tool definitions for the agent
211-
tool_resources: Optional tool resources required by the tools
212-
response_format: Optional response format to control structured output
213-
temperature: The temperature setting for the agent (defaults to 0.0)
214-
215-
Returns:
216-
A new AzureAIAgent instance
217-
"""
218-
try:
219-
# Get the AIProjectClient
220-
if client is None:
221-
client = self.get_ai_project_client()
222-
223-
# # ToDo: This is the fixed code but commenting it out as agent clean up is no happening yet
224-
# # and there are multiple versions of agents due to testing
225-
# # First try to get an existing agent with this name as assistant_id
226-
# try:
227-
# agent_id = None
228-
# agent_list = await client.agents.list_agents()
229-
# for agent in agent_list.data:
230-
# if agent.name == agent_name:
231-
# agent_id = agent.id
232-
# break
233-
# # If the agent already exists, we can use it directly
234-
# # Get the existing agent definition
235-
# existing_definition = await client.agents.get_agent(agent_id)
236-
# # Create the agent instance directly with project_client and existing definition
237-
# agent = AzureAIAgent(
238-
# client=client,
239-
# definition=existing_definition,
240-
# plugins=tools,
241-
# )
242-
243-
# client.agents.list_agents()
244-
245-
# return agent
246-
# except Exception as e:
247-
# # The Azure AI Projects SDK throws an exception when the agent doesn't exist
248-
# # (not returning None), so we catch it and proceed to create a new agent
249-
# if "ResourceNotFound" in str(e) or "404" in str(e):
250-
# logging.info(
251-
# f"Agent with ID {agent_name} not found. Will create a new one."
252-
# )
253-
# else:
254-
# # Log unexpected errors but still try to create a new agent
255-
# logging.warning(
256-
# f"Unexpected error while retrieving agent {agent_name}: {str(e)}. Attempting to create new agent."
257-
# )
258-
259-
# Create the agent using the project client with the agent_name as both name and assistantId
260-
agent_definition = await client.agents.create_agent(
261-
model=self.AZURE_OPENAI_DEPLOYMENT_NAME,
262-
name=agent_name,
263-
instructions=instructions,
264-
temperature=temperature,
265-
response_format=response_format,
266-
)
267-
268-
# Create the agent instance directly with project_client and definition
269-
agent = AzureAIAgent(
270-
client=client,
271-
definition=agent_definition,
272-
plugins=tools,
273-
)
274-
275-
return agent
276-
except Exception as exc:
277-
logging.error("Failed to create Azure AI Agent: %s", exc)
278-
raise
279-
280193

281194
# Create a global instance of AppConfig
282195
config = AppConfig()

src/backend/kernel_agents/agent_base.py

Lines changed: 92 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,21 @@
11
import json
22
import logging
33
import os
4-
from typing import Any, Awaitable, Callable, Dict, List, Mapping, Optional, Union
4+
from abc import ABC, abstractmethod
5+
from typing import (Any, Awaitable, Callable, Dict, List, Mapping, Optional,
6+
Union)
57

68
import semantic_kernel as sk
7-
from semantic_kernel.agents.azure_ai.azure_ai_agent import AzureAIAgent
8-
from semantic_kernel.functions import KernelFunction
9-
from semantic_kernel.functions.kernel_arguments import KernelArguments
10-
from semantic_kernel.functions.kernel_function_decorator import kernel_function
11-
from semantic_kernel.agents import AzureAIAgentThread
12-
13-
149
# Import the new AppConfig instance
1510
from app_config import config
1611
from context.cosmos_memory_kernel import CosmosMemoryContext
1712
from event_utils import track_event_if_configured
18-
from models.messages_kernel import (
19-
ActionRequest,
20-
ActionResponse,
21-
AgentMessage,
22-
Step,
23-
StepStatus,
24-
)
13+
from models.messages_kernel import (ActionRequest, ActionResponse,
14+
AgentMessage, Step, StepStatus)
15+
from semantic_kernel.agents.azure_ai.azure_ai_agent import AzureAIAgent
16+
from semantic_kernel.functions import KernelFunction
17+
from semantic_kernel.functions.kernel_arguments import KernelArguments
18+
from semantic_kernel.functions.kernel_function_decorator import kernel_function
2519

2620
# Default formatting instructions used across agents
2721
DEFAULT_FORMATTING_INSTRUCTIONS = "Instructions: returning the output of this function call verbatim to the user in markdown. Then write AGENT SUMMARY: and then include a summary of what you did."
@@ -65,6 +59,7 @@ def __init__(
6559
endpoint=None, # Set as needed
6660
api_version=None, # Set as needed
6761
token=None, # Set as needed
62+
model=config.AZURE_OPENAI_DEPLOYMENT_NAME,
6863
agent_name=agent_name,
6964
system_prompt=system_message,
7065
client=client,
@@ -79,7 +74,7 @@ def __init__(
7974
self._tools = tools
8075
self._system_message = system_message
8176
self._chat_history = [{"role": "system", "content": self._system_message}]
82-
self._agent = None # Will be initialized in async_init
77+
# self._agent = None # Will be initialized in async_init
8378

8479
# Required properties for AgentGroupChat compatibility
8580
self.name = agent_name # This is crucial for AgentGroupChat to identify agents
@@ -97,24 +92,6 @@ def default_system_message(agent_name=None) -> str:
9792
name = agent_name
9893
return f"You are an AI assistant named {name}. Help the user by providing accurate and helpful information."
9994

100-
async def async_init(self):
101-
"""Asynchronously initialize the agent after construction.
102-
103-
This method must be called after creating the agent to complete initialization.
104-
"""
105-
logging.info(f"Initializing agent: {self._agent_name}")
106-
# Create Azure AI Agent or fallback
107-
if not self._agent:
108-
self._agent = await config.create_azure_ai_agent(
109-
agent_name=self._agent_name,
110-
instructions=self._system_message,
111-
tools=self._tools,
112-
)
113-
else:
114-
logging.info(f"Agent {self._agent_name} already initialized.")
115-
# Tools are registered with the kernel via get_tools_from_config
116-
return self
117-
11895
async def handle_action_request(self, action_request: ActionRequest) -> str:
11996
"""Handle an action request from another agent or the system.
12097
@@ -160,7 +137,7 @@ async def handle_action_request(self, action_request: ActionRequest) -> str:
160137
# thread = self.client.agents.get_thread(
161138
# thread=step.session_id
162139
# ) # AzureAIAgentThread(thread_id=step.session_id)
163-
async_generator = self._agent.invoke(
140+
async_generator = self.invoke(
164141
messages=f"{str(self._chat_history)}\n\nPlease perform this action",
165142
thread=thread,
166143
)
@@ -264,3 +241,83 @@ def save_state(self) -> Mapping[str, Any]:
264241
def load_state(self, state: Mapping[str, Any]) -> None:
265242
"""Load the state of this agent."""
266243
self._memory_store.load_state(state["memory"])
244+
245+
@classmethod
246+
@abstractmethod
247+
async def create(cls, **kwargs) -> "BaseAgent":
248+
"""Create an instance of the agent."""
249+
pass
250+
251+
@staticmethod
252+
async def _create_azure_ai_agent_definition(
253+
agent_name: str,
254+
instructions: str,
255+
tools: Optional[List[KernelFunction]] = None,
256+
client=None,
257+
response_format=None,
258+
temperature: float = 0.0,
259+
):
260+
"""
261+
Creates a new Azure AI Agent with the specified name and instructions using AIProjectClient.
262+
If an agent with the given name (assistant_id) already exists, it tries to retrieve it first.
263+
264+
Args:
265+
kernel: The Semantic Kernel instance
266+
agent_name: The name of the agent (will be used as assistant_id)
267+
instructions: The system message / instructions for the agent
268+
agent_type: The type of agent (defaults to "assistant")
269+
tools: Optional tool definitions for the agent
270+
tool_resources: Optional tool resources required by the tools
271+
response_format: Optional response format to control structured output
272+
temperature: The temperature setting for the agent (defaults to 0.0)
273+
274+
Returns:
275+
A new AzureAIAgent definition or an existing one if found
276+
"""
277+
try:
278+
# Get the AIProjectClient
279+
if client is None:
280+
client = config.get_ai_project_client()
281+
282+
# # First try to get an existing agent with this name as assistant_id
283+
try:
284+
agent_id = None
285+
agent_list = await client.agents.list_agents()
286+
for agent in agent_list.data:
287+
if agent.name == agent_name:
288+
agent_id = agent.id
289+
break
290+
# If the agent already exists, we can use it directly
291+
# Get the existing agent definition
292+
if agent_id is not None:
293+
logging.info(f"Agent with ID {agent_id} exists.")
294+
295+
existing_definition = await client.agents.get_agent(agent_id)
296+
297+
return existing_definition
298+
except Exception as e:
299+
# The Azure AI Projects SDK throws an exception when the agent doesn't exist
300+
# (not returning None), so we catch it and proceed to create a new agent
301+
if "ResourceNotFound" in str(e) or "404" in str(e):
302+
logging.info(
303+
f"Agent with ID {agent_name} not found. Will create a new one."
304+
)
305+
else:
306+
# Log unexpected errors but still try to create a new agent
307+
logging.warning(
308+
f"Unexpected error while retrieving agent {agent_name}: {str(e)}. Attempting to create new agent."
309+
)
310+
311+
# Create the agent using the project client with the agent_name as both name and assistantId
312+
agent_definition = await client.agents.create_agent(
313+
model=config.AZURE_OPENAI_DEPLOYMENT_NAME,
314+
name=agent_name,
315+
instructions=instructions,
316+
temperature=temperature,
317+
response_format=response_format,
318+
)
319+
320+
return agent_definition
321+
except Exception as exc:
322+
logging.error("Failed to create Azure AI Agent: %s", exc)
323+
raise

0 commit comments

Comments
 (0)