Skip to content

Commit 4db462a

Browse files
committed
Add team agent retrieval and reuse in agent templates
Introduces logic to retrieve and reuse existing team agents from the database in FoundryAgentTemplate and ReasoningAgentTemplate. Adds get_database_team_agent to MCPEnabledBase for agent lookup, reducing redundant agent initialization and ensuring agent state consistency.
1 parent d4cd3f6 commit 4db462a

File tree

3 files changed

+134
-83
lines changed

3 files changed

+134
-83
lines changed

src/backend/v4/magentic_agents/common/lifecycle.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from agent_framework_azure_ai import AzureAIAgentClient
2525
from azure.ai.agents.aio import AgentsClient
2626
from azure.identity.aio import DefaultAzureCredential
27-
from common.models.messages_af import TeamConfiguration
27+
from common.models.messages_af import CurrentTeamAgent, TeamConfiguration
2828
from common.database.database_base import DatabaseBase
2929
from v4.common.services.team_service import TeamService
3030
from v4.config.agent_registry import agent_registry
@@ -125,6 +125,39 @@ async def _after_open(self) -> None:
125125
"""Subclasses must build self._agent here."""
126126
raise NotImplementedError
127127

128+
async def get_database_team_agent(self) -> Optional[CurrentTeamAgent]:
129+
"""Retrieve existing team agent from database, if any."""
130+
agent = None
131+
try:
132+
currentAgent = await self.memory_store.get_team_agent(
133+
team_id=self.team_config.team_id,
134+
agent_name=self.agent_name
135+
)
136+
if currentAgent and currentAgent.agent_foundry_id:
137+
agent = self.client.get_agent(
138+
id=currentAgent.agent_foundry_id
139+
)
140+
141+
except Exception as ex:
142+
self.logger.error("Failed to initialize ReasoningAgentTemplate: %s", ex)
143+
return agent
144+
145+
async def save_database_team_agent(self, agent_name, description, instructions) -> None:
146+
"""Save current team agent to database."""
147+
try:
148+
currentAgent = CurrentTeamAgent(
149+
team_id=self.team_config.team_id,
150+
agent_name=agent_name,
151+
agent_foundry_id=self._agent.id,
152+
agent_description=description,
153+
agent_instructions=instructions,
154+
)
155+
await self.memory_store.add_team_agent(currentAgent)
156+
157+
except Exception as ex:
158+
self.logger.error("Failed to save ReasoningAgentTemplate: %s", ex)
159+
160+
128161
async def _prepare_mcp_tool(self) -> None:
129162
"""Translate MCPConfig to a HostedMCPTool (agent_framework construct)."""
130163
if not self.mcp_cfg:

src/backend/v4/magentic_agents/foundry_agent.py

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from azure.ai.projects.models import ConnectionType
99
from common.config.app_config import config
1010
from common.models.messages_af import TeamConfiguration
11-
from src.backend.common.database.database_base import DatabaseBase
11+
from common.database.database_base import DatabaseBase
1212
from v4.common.services.team_service import TeamService
1313
from v4.config.agent_registry import agent_registry
1414
from v4.magentic_agents.common.lifecycle import AzureAgentBase
@@ -214,55 +214,59 @@ async def _create_azure_search_enabled_client(self):
214214
# -------------------------
215215
async def _after_open(self) -> None:
216216
"""Initialize ChatAgent after connections are established."""
217-
try:
218-
agent = self.client.get_agent(
219-
agent_name=self.agent_name
220-
)
221217

222-
except Exception as ex:
223-
self.logger.error("Failed to initialize ReasoningAgentTemplate: %s", ex)
224218
try:
225-
if self._use_azure_search:
226-
# Azure Search mode (skip MCP + Code Interpreter due to incompatibility)
227-
self.logger.info(
228-
"Initializing agent in Azure AI Search mode (exclusive)."
229-
)
230-
chat_client = await self._create_azure_search_enabled_client()
231-
if not chat_client:
232-
raise RuntimeError(
233-
"Azure AI Search mode requested but setup failed."
219+
agent = await self.get_database_team_agent()
220+
if agent is None:
221+
if self._use_azure_search:
222+
# Azure Search mode (skip MCP + Code Interpreter due to incompatibility)
223+
self.logger.info(
224+
"Initializing agent in Azure AI Search mode (exclusive)."
225+
)
226+
chat_client = await self._create_azure_search_enabled_client()
227+
if not chat_client:
228+
raise RuntimeError(
229+
"Azure AI Search mode requested but setup failed."
230+
)
231+
232+
# In Azure Search raw tool path, tools/tool_choice are handled server-side.
233+
self._agent = ChatAgent(
234+
chat_client=chat_client,
235+
instructions=self.agent_instructions,
236+
name=self.agent_name,
237+
description=self.agent_description,
238+
tool_choice="required", # Force usage
239+
temperature=0.7,
240+
model_id=self.model_deployment_name,
241+
)
242+
else:
243+
# use MCP path
244+
self.logger.info("Initializing agent in MCP mode.")
245+
tools = await self._collect_tools()
246+
self._agent = ChatAgent(
247+
chat_client=AzureAIAgentClient(
248+
project_endpoint=self.project_endpoint,
249+
model_deployment_name=self.model_deployment_name,
250+
async_credential=self.creds,
251+
),
252+
instructions=self.agent_instructions,
253+
name=self.agent_name,
254+
description=self.agent_description,
255+
tools=tools if tools else None,
256+
tool_choice="auto" if tools else "none",
257+
temperature=0.7,
258+
model_id=self.model_deployment_name,
234259
)
235260

236-
# In Azure Search raw tool path, tools/tool_choice are handled server-side.
237-
self._agent = ChatAgent(
238-
chat_client=chat_client,
239-
instructions=self.agent_instructions,
240-
name=self.agent_name,
241-
description=self.agent_description,
242-
tool_choice="required", # Force usage
243-
temperature=0.7,
244-
model_id=self.model_deployment_name,
245-
)
246-
else:
247-
# use MCP path
248-
self.logger.info("Initializing agent in MCP mode.")
249-
tools = await self._collect_tools()
250-
self._agent = ChatAgent(
251-
chat_client=AzureAIAgentClient(
252-
project_endpoint=self.project_endpoint,
253-
model_deployment_name=self.model_deployment_name,
254-
async_credential=self.creds,
255-
),
256-
instructions=self.agent_instructions,
257-
name=self.agent_name,
258-
description=self.agent_description,
259-
tools=tools if tools else None,
260-
tool_choice="auto" if tools else "none",
261-
temperature=0.7,
262-
model_id=self.model_deployment_name,
261+
self.logger.info("Initialized ChatAgent '%s'", self.agent_name)
262+
263+
await self.save_database_team_agent(
264+
self.agent_name, self.agent_description, self.agent_instructions
263265
)
264266

265-
self.logger.info("Initialized ChatAgent '%s'", self.agent_name)
267+
else:
268+
self.logger.info("Using existing ChatAgent '%s'", self.agent_name)
269+
self._agent = agent
266270
except Exception as ex:
267271
self.logger.error("Failed to initialize ChatAgent: %s", ex)
268272
raise

src/backend/v4/magentic_agents/reasoning_agent.py

Lines changed: 52 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from azure.identity.aio import DefaultAzureCredential
1414

1515

16-
#from agent_framework.azure import AzureOpenAIChatClient
16+
# from agent_framework.azure import AzureOpenAIChatClient
1717

1818
from common.models.messages_af import TeamConfiguration
1919
from common.database.database_base import DatabaseBase
@@ -29,7 +29,7 @@
2929
class ReasoningAgentTemplate(MCPEnabledBase):
3030
"""
3131
Reasoning agent using agent_framework with direct Azure AI Search integration.
32-
32+
3333
This agent:
3434
- Uses reasoning models (o1, o3-mini, etc.)
3535
- Augments prompts with search results from Azure AI Search
@@ -52,7 +52,7 @@ def __init__(
5252
memory_store: DatabaseBase | None = None,
5353
) -> None:
5454
"""Initialize reasoning agent.
55-
55+
5656
Args:
5757
agent_name: Name of the agent
5858
agent_description: Description of the agent's purpose
@@ -63,17 +63,21 @@ def __init__(
6363
mcp_config: Optional MCP server configuration
6464
max_search_docs: Maximum number of search documents to retrieve
6565
"""
66-
super().__init__(mcp=mcp_config, team_service=team_service, team_config=team_config, project_endpoint=project_endpoint, memory_store=memory_store)
66+
super().__init__(
67+
mcp=mcp_config,
68+
team_service=team_service,
69+
team_config=team_config,
70+
project_endpoint=project_endpoint,
71+
memory_store=memory_store,
72+
)
6773
self.agent_name = agent_name
6874
self.agent_description = agent_description
6975
self.base_instructions = agent_instructions
7076
self.model_deployment_name = model_deployment_name
7177

7278
self.search_config = search_config
7379
self.max_search_docs = max_search_docs
74-
75-
76-
80+
7781
self.logger = logging.getLogger(__name__)
7882

7983
async def _after_open(self) -> None:
@@ -82,43 +86,54 @@ async def _after_open(self) -> None:
8286

8387
self.logger.info(
8488
"Initialized AzureAIAgentClient for model '%s'",
85-
self.model_deployment_name
89+
self.model_deployment_name,
8690
)
8791

88-
8992
# Initialize MCP tools (called after stack is ready)
9093
await self._prepare_mcp_tool()
91-
94+
9295
if self.mcp_tool:
9396
self.logger.info(
9497
"MCP tool '%s' ready with %d functions",
9598
self.mcp_tool.name,
96-
len(self.mcp_tool.functions) if hasattr(self.mcp_tool, 'functions') else 0
99+
(
100+
len(self.mcp_tool.functions)
101+
if hasattr(self.mcp_tool, "functions")
102+
else 0
103+
),
97104
)
98105

99106
# Prepare tools for the agent
100107
tools = self._prepare_tools()
101108

102-
# Create ChatAgent instance (similar to foundry_agent)
103-
self._agent = ChatAgent(
104-
chat_client=self.client,
105-
instructions=self.base_instructions,
106-
name=self.agent_name,
107-
description=self.agent_description,
108-
tools=tools if tools else None,
109-
tool_choice="auto" if tools else "none",
110-
temperature=1.0, # Reasoning models use fixed temperature
111-
model_id=self.model_deployment_name,
112-
)
109+
agent = await self.get_database_team_agent()
110+
if not agent:
111+
self._agent = ChatAgent(
112+
chat_client=self.client,
113+
instructions=self.base_instructions,
114+
name=self.agent_name,
115+
description=self.agent_description,
116+
tools=tools if tools else None,
117+
tool_choice="auto" if tools else "none",
118+
temperature=1.0, # Reasoning models use fixed temperature
119+
model_id=self.model_deployment_name,
120+
)
121+
await self.save_database_team_agent(
122+
self.agent_name, self.agent_description, self.base_instructions
123+
)
124+
125+
else:
126+
self._agent = agent
127+
self.logger.info("Using existing ReasoningAgent '%s'", self.agent_name)
113128
# Register agent globally
114129
try:
115130
agent_registry.register_agent(self)
116-
self.logger.info("Registered agent '%s' in global registry.", self.agent_name)
131+
self.logger.info(
132+
"Registered agent '%s' in global registry.", self.agent_name
133+
)
117134
except Exception as reg_ex:
118135
self.logger.warning(
119-
"Could not register agent '%s': %s",
120-
self.agent_name,
121-
reg_ex
136+
"Could not register agent '%s': %s", self.agent_name, reg_ex
122137
)
123138

124139
except Exception as ex:
@@ -132,7 +147,7 @@ async def close(self) -> None:
132147
# Unregister from registry
133148
try:
134149
agent_registry.unregister_agent(self)
135-
except Exception:
150+
except Exception:
136151
pass
137152

138153
finally:
@@ -143,18 +158,17 @@ async def close(self) -> None:
143158
def _prepare_tools(self) -> list:
144159
"""
145160
Prepare tools for reasoning model invocation.
146-
161+
147162
Returns:
148163
List of tools (currently only MCP tools supported for reasoning models)
149164
"""
150165
tools = []
151-
166+
152167
if self.mcp_tool:
153168
tools.append(self.mcp_tool)
154169
self.logger.debug("Added MCP tool '%s' to tools list", self.mcp_tool.name)
155-
156-
return tools
157170

171+
return tools
158172

159173

160174
# -------------------------
@@ -171,7 +185,7 @@ async def create_reasoning_agent(
171185
) -> ReasoningAgentTemplate:
172186
"""
173187
Factory to create and open a ReasoningAgentTemplate.
174-
188+
175189
Args:
176190
agent_name: Name of the agent
177191
agent_description: Description of the agent's purpose
@@ -180,14 +194,14 @@ async def create_reasoning_agent(
180194
azure_ai_project_endpoint: Azure AI Project endpoint (defaults to env var)
181195
search_config: Optional Azure AI Search configuration
182196
mcp_config: Optional MCP server configuration
183-
197+
184198
Returns:
185199
Initialized and opened ReasoningAgentTemplate instance
186-
200+
187201
Example:
188202
```python
189203
from af.magentic_agents.models.agent_models import SearchConfig, MCPConfig
190-
204+
191205
# With search augmentation and MCP tools
192206
agent = await create_reasoning_agent(
193207
agent_name="ReasoningAgent",
@@ -205,11 +219,11 @@ async def create_reasoning_agent(
205219
description="HR data access tools"
206220
),
207221
)
208-
222+
209223
```
210224
"""
211225
# Get endpoint from env if not provided
212-
endpoint = azure_ai_project_endpoint
226+
endpoint = azure_ai_project_endpoint
213227
if not endpoint:
214228
raise RuntimeError(
215229
"AZURE_AI_PROJECT_ENDPOINT must be provided or set as environment variable"
@@ -225,4 +239,4 @@ async def create_reasoning_agent(
225239
mcp_config=mcp_config,
226240
)
227241
await agent.open()
228-
return agent
242+
return agent

0 commit comments

Comments
 (0)