Skip to content

Commit be7a8c6

Browse files
authored
Merge pull request #490 from microsoft/macae-v3-dev-marktayl
Exception checking for agent creation
2 parents 52ed165 + 66bc081 commit be7a8c6

File tree

11 files changed

+193
-50
lines changed

11 files changed

+193
-50
lines changed

data/agent_teams/marketing.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"name": "ProductAgent",
1313
"deployment_name": "gpt-4.1-mini",
1414
"icon": "",
15-
"system_message": "You are a Product agent. You have knowledge about product management, development, and compliance guidelines. When asked to call a function, you should summarize back what was done.",
15+
"system_message": "You are a Product agent. You have access to MCP tools which allow you to obtain knowledge about products, product management, development, and compliance guidelines. When asked to call one of these tools, you should summarize back what was done.",
1616
"description": "This agent specializes in product management, development, and related tasks. It can provide information about products, manage inventory, handle product launches, analyze sales data, and coordinate with other teams like marketing and tech support.",
1717
"use_rag": false,
1818
"use_mcp": true,
@@ -29,7 +29,7 @@
2929
"name": "MarketingAgent",
3030
"deployment_name": "gpt-4.1-mini",
3131
"icon": "",
32-
"system_message": "You are a Marketing agent. You specialize in marketing strategy, campaign development, content creation, and market analysis. You help create effective marketing campaigns, analyze market data, and develop promotional content for products and services.",
32+
"system_message": "You are a Marketing agent. You have access to a number of HR related MCP tools for tasks like campaign development, content creation, and market analysis. You help create effective marketing campaigns, analyze market data, and develop promotional content for products and services.",
3333
"description": "This agent specializes in marketing, campaign management, and analyzing market data.",
3434
"use_rag": false,
3535
"use_mcp": true,

src/backend/app_kernel.py

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,20 @@
22
import asyncio
33
import logging
44
import os
5-
65
# Azure monitoring
76
import re
87
import uuid
9-
108
from typing import Dict, List, Optional
119

12-
1310
from azure.monitor.opentelemetry import configure_azure_monitor
1411
from common.config.app_config import config
15-
from common.models.messages_kernel import (
16-
UserLanguage,
17-
)
18-
19-
12+
from common.models.messages_kernel import UserLanguage
2013
# FastAPI imports
21-
from fastapi import (
22-
FastAPI,
23-
Query,
24-
Request,
25-
)
14+
from fastapi import FastAPI, Query, Request
2615
from fastapi.middleware.cors import CORSMiddleware
27-
28-
2916
# Local imports
3017
from middleware.health_check import HealthCheckMiddleware
3118
from v3.api.router import app_v3
32-
3319
# Semantic Kernel imports
3420
from v3.orchestration.orchestration_manager import OrchestrationManager
3521

@@ -118,4 +104,4 @@ async def user_browser_language_endpoint(user_language: UserLanguage, request: R
118104
if __name__ == "__main__":
119105
import uvicorn
120106

121-
uvicorn.run("app_kernel:app", host="127.0.0.1", port=8000, reload=True)
107+
uvicorn.run("app_kernel:app", host="127.0.0.1", port=8000, reload=True, log_level="info", access_log=False)

src/backend/common/config/app_config.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ def __init__(self):
3333
"APPLICATIONINSIGHTS_CONNECTION_STRING"
3434
)
3535
self.APP_ENV = self._get_required("APP_ENV", "prod")
36-
self.AZURE_AI_MODEL_DEPLOYMENT_NAME = self._get_required(
37-
"AZURE_AI_MODEL_DEPLOYMENT_NAME", "gpt-4o"
38-
)
36+
# self.AZURE_AI_MODEL_DEPLOYMENT_NAME = self._get_required(
37+
# "AZURE_AI_MODEL_DEPLOYMENT_NAME", "gpt-4o"
38+
# )
3939

4040
self.AZURE_COGNITIVE_SERVICES = self._get_optional(
4141
"AZURE_COGNITIVE_SERVICES", "https://cognitiveservices.azure.com/.default"

src/backend/v3/config/settings.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -197,32 +197,25 @@ async def send_status_update_async(
197197
)
198198
return
199199

200-
print(f" websocket original message: {message}")
201-
print(f" websocket message type: {type(message)}")
202200

203201
# Convert message to proper format for frontend
204202
try:
205203
if hasattr(message, "to_dict"):
206204
# Use the custom to_dict method if available
207205
message_data = message.to_dict()
208-
print(f" websocket used to_dict(): {message_data}")
209206
elif hasattr(message, "data") and hasattr(message, "type"):
210207
# Handle structured messages with data property
211208
message_data = message.data
212-
print(f" websocket used message.data: {message_data}")
213209
elif isinstance(message, dict):
214210
# Already a dictionary
215211
message_data = message
216-
print(f" websocket already dict: {message_data}")
217212
else:
218213
# Convert to string if it's a simple type
219214
message_data = str(message)
220-
print(f" websocket converted to string: {message_data}")
221215
except Exception as e:
222-
print(f"Error processing message data: {e}")
216+
logger.error("Error processing message data: %s", e)
223217
message_data = str(message)
224218

225-
print(f" websocket final message_data: {message_data}")
226219

227220
standard_message = {
228221
"type": message_type,

src/backend/v3/magentic_agents/foundry_agent.py

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
""" Agent template for building foundry agents with Azure AI Search, Bing, and MCP plugins. """
22

3+
import asyncio
34
import logging
45
from typing import List, Optional
56

@@ -39,7 +40,7 @@ def __init__(self, agent_name: str,
3940
self._bing_connection = None
4041
self.logger = logging.getLogger(__name__)
4142
# input validation
42-
if self.model_deployment_name is any(["o3", "o4-mini"]):
43+
if self.model_deployment_name in ["o3", "o4-mini"]:
4344
raise ValueError("The current version of Foundry agents do not support reasoning models.")
4445

4546
# async def _make_bing_tool(self) -> Optional[BingGroundingTool]:
@@ -76,9 +77,11 @@ async def _make_azure_search_tool(self) -> Optional[AzureAISearchTool]:
7677
return search_tool
7778

7879
except Exception as ex:
79-
self.logger.error(f"Azure AI Search tool creation failed: {ex} Connection name: " /
80-
f"{self.search.connection_name} Index name: {self.search.index_name}" /
81-
" Make sure the connection exists in Azure AI Foundry portal")
80+
self.logger.error(
81+
"Azure AI Search tool creation failed: %s | Connection name: %s | Index name: %s | "
82+
"Make sure the connection exists in Azure AI Foundry portal",
83+
ex, self.search.connection_name, self.search.index_name
84+
)
8285
return None
8386

8487
async def _collect_tools_and_resources(self) -> tuple[List, dict]:
@@ -133,14 +136,49 @@ async def _after_open(self) -> None:
133136
# Add MCP plugins if available
134137
plugins = [self.mcp_plugin] if self.mcp_plugin else []
135138

136-
self._agent = AzureAIAgent(
137-
client=self.client,
138-
definition=definition,
139-
plugins=plugins,
140-
)
139+
try:
140+
self._agent = AzureAIAgent(
141+
client=self.client,
142+
definition=definition,
143+
plugins=plugins,
144+
)
145+
except Exception as ex:
146+
self.logger.error("Failed to create AzureAIAgent: %s", ex)
147+
raise
148+
149+
# After self._agent creation in _after_open:
150+
# Diagnostics
151+
try:
152+
tool_names = [t.get("function", {}).get("name") for t in (definition.tools or []) if isinstance(t, dict)]
153+
self.logger.info(
154+
"Foundry agent '%s' initialized. Azure tools: %s | MCP plugin: %s",
155+
self.agent_name,
156+
tool_names,
157+
getattr(self.mcp_plugin, 'name', None)
158+
)
159+
if not tool_names and not plugins:
160+
self.logger.warning(
161+
"Foundry agent '%s' has no Azure tool definitions and no MCP plugin. "
162+
"Subsequent tool calls may fail.", self.agent_name
163+
)
164+
except Exception as diag_ex:
165+
self.logger.warning("Diagnostics collection failed: %s", diag_ex)
141166

142167
self.logger.info("%s initialized with %d tools and %d plugins", self.agent_name, len(tools), len(plugins))
143168

169+
async def fetch_run_details(self, thread_id: str, run_id: str):
170+
"""Fetch and log run details after a failure."""
171+
try:
172+
run = await self.client.agents.runs.get(thread=thread_id, run=run_id)
173+
self.logger.error(
174+
"Run failure details | status=%s | id=%s | last_error=%s | usage=%s",
175+
getattr(run, 'status', None),
176+
run_id,
177+
getattr(run, 'last_error', None),
178+
getattr(run, 'usage', None),
179+
)
180+
except Exception as ex:
181+
self.logger.error("Could not fetch run details: %s", ex)
144182

145183
async def create_foundry_agent(agent_name:str,
146184
agent_description:str,

src/backend/v3/orchestration/human_approval_manager.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@
66
import asyncio
77
import re
88
from typing import Any, List, Optional
9-
# Create a progress ledger that indicates the request is satisfied (task complete)
10-
from semantic_kernel.agents.orchestration.magentic import (
11-
ProgressLedger, ProgressLedgerItem)
9+
1210
import v3.models.messages as messages
1311
from semantic_kernel.agents import Agent
1412
from semantic_kernel.agents.orchestration.magentic import (
15-
MagenticContext, ProgressLedger, StandardMagenticManager)
13+
MagenticContext, ProgressLedger, ProgressLedgerItem,
14+
StandardMagenticManager)
1615
from semantic_kernel.agents.orchestration.prompts._magentic_prompts import (
1716
ORCHESTRATOR_TASK_LEDGER_FACTS_PROMPT,
1817
ORCHESTRATOR_TASK_LEDGER_PLAN_PROMPT,
@@ -22,6 +21,8 @@
2221
orchestration_config)
2322
from v3.models.models import MPlan, MStep
2423

24+
# Create a progress ledger that indicates the request is satisfied (task complete)
25+
2526

2627
class HumanApprovalMagenticManager(StandardMagenticManager):
2728
"""
@@ -45,6 +46,11 @@ def __init__(self, *args, **kwargs):
4546
"""
4647

4748
plan_append = """
49+
IMPORTANT: Never ask the user for information or clarification until all agents on the team have been asked first.
50+
51+
EXAMPLE: If the user request involves product information, first ask all agents on the team to provide the information.
52+
Do not ask the user unless all agents have been consulted and the information is still missing.
53+
4854
Plan steps should always include a bullet point, followed by an agent name, followed by a description of the action
4955
to be taken. If a step involves multiple actions, separate them into distinct steps with an agent included in each step. If the step is taken by an agent that
5056
is not part of the team, such as the MagenticManager, please always list the MagenticManager as the agent for that step. At any time, if more information is

src/frontend/frontend_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,4 @@ async def serve_app(full_path: str):
6060
return FileResponse(INDEX_HTML)
6161

6262
if __name__ == "__main__":
63-
uvicorn.run(app, host="127.0.0.1", port=3000)
63+
uvicorn.run(app, host="127.0.0.1", port=3000, access_log=False, log_level="info")

src/mcp_server/mcp_server.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
3232
factory.register_service(TechSupportService())
3333
factory.register_service(MarketingService())
3434
factory.register_service(ProductService())
35-
factory.register_service(GeneralService())
35+
36+
# General service has tests for llm to mcp connectivity
37+
#factory.register_service(GeneralService())
3638

3739
# Register DataToolService with the dataset path
3840
#factory.register_service(DataToolService(dataset_path="datasets"))

src/mcp_server/services/hr_service.py

Lines changed: 121 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
Human Resources MCP tools service.
33
"""
44

5-
from typing import Dict, Any
6-
from core.factory import MCPToolBase, Domain
5+
from typing import Any, Dict
6+
7+
from core.factory import Domain, MCPToolBase
78
from utils.date_utils import format_date_for_user
8-
from utils.formatters import format_success_response, format_error_response
9+
from utils.formatters import format_error_response, format_success_response
910

1011

1112
class HRService(MCPToolBase):
@@ -17,6 +18,123 @@ def __init__(self):
1718
def register_tools(self, mcp) -> None:
1819
"""Register HR tools with the MCP server."""
1920

21+
@mcp.tool(tags={self.domain.value})
22+
async def employee_onboarding_blueprint_flat(
23+
employee_name: str | None = None,
24+
start_date: str | None = None,
25+
role: str | None = None
26+
) -> dict:
27+
"""
28+
Ultra-minimal onboarding blueprint (flat list).
29+
Agent usage:
30+
1. Call this first when onboarding intent detected.
31+
2. Filter steps to its own domain.
32+
3. Execute in listed order while honoring depends_on.
33+
"""
34+
return {
35+
"version": "1.0",
36+
"intent": "employee_onboarding",
37+
"employee": {
38+
"name": employee_name,
39+
"start_date": start_date,
40+
"role": role
41+
},
42+
"steps": [
43+
# Pre-boarding
44+
{
45+
"id": "bg_check",
46+
"domain": "HR",
47+
"action": "Initiate background check",
48+
"tool": "initiate_background_check",
49+
"required": True,
50+
"params": ["employee_name", "check_type?"]
51+
},
52+
{
53+
"id": "configure_laptop",
54+
"domain": "TECH_SUPPORT",
55+
"action": "Provision and configure laptop",
56+
"tool": "configure_laptop",
57+
"required": True
58+
},
59+
{
60+
"id": "create_accounts",
61+
"domain": "TECH_SUPPORT",
62+
"action": "Create system accounts",
63+
"tool": "create_system_accounts",
64+
"required": True
65+
},
66+
67+
# Day 1
68+
{
69+
"id": "orientation",
70+
"domain": "HR",
71+
"action": "Schedule orientation session",
72+
"tool": "schedule_orientation_session",
73+
"required": True,
74+
"depends_on": ["bg_check"],
75+
"params": ["employee_name", "date"]
76+
},
77+
{
78+
"id": "handbook",
79+
"domain": "HR",
80+
"action": "Provide employee handbook",
81+
"tool": "provide_employee_handbook",
82+
"required": True,
83+
"params": ["employee_name"]
84+
},
85+
{
86+
"id": "welcome_email",
87+
"domain": "TECH_SUPPORT",
88+
"action": "Send welcome email",
89+
"tool": "send_welcome_email",
90+
"required": False,
91+
"depends_on": ["create_accounts"]
92+
},
93+
94+
# Week 1
95+
{
96+
"id": "mentor",
97+
"domain": "HR",
98+
"action": "Assign mentor",
99+
"tool": "assign_mentor",
100+
"required": False,
101+
"params": ["employee_name", "mentor_name?"]
102+
},
103+
{
104+
"id": "vpn",
105+
"domain": "TECH_SUPPORT",
106+
"action": "Set up VPN access",
107+
"tool": "setup_vpn_access",
108+
"required": False,
109+
"depends_on": ["create_accounts"]
110+
},
111+
{
112+
"id": "benefits",
113+
"domain": "HR",
114+
"action": "Register employee for benefits",
115+
"tool": "register_for_benefits",
116+
"required": True,
117+
"params": ["employee_name", "benefits_package?"]
118+
},
119+
{
120+
"id": "payroll",
121+
"domain": "HR",
122+
"action": "Set up payroll",
123+
"tool": "set_up_payroll",
124+
"required": True,
125+
"params": ["employee_name", "salary?"]
126+
},
127+
{
128+
"id": "id_card",
129+
"domain": "HR",
130+
"action": "Request ID card",
131+
"tool": "request_id_card",
132+
"required": False,
133+
"depends_on": ["bg_check"],
134+
"params": ["employee_name", "department?"]
135+
}
136+
]
137+
}
20138
@mcp.tool(tags={self.domain.value})
21139
async def schedule_orientation_session(employee_name: str, date: str) -> str:
22140
"""Schedule an orientation session for a new employee."""

src/mcp_server/services/marketing_service.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class MarketingService(MCPToolBase):
1313
"""Marketing tools for employee onboarding and management."""
1414

1515
def __init__(self):
16-
super().__init__(Domain.HR)
16+
super().__init__(Domain.MARKETING)
1717

1818
def register_tools(self, mcp) -> None:
1919
"""Register Marketing tools with the MCP server."""

0 commit comments

Comments
 (0)