Skip to content

Commit 9810754

Browse files
committed
Merge branch 'feature/azd-semantickernel' of https://github.com/microsoft/Multi-Agent-Custom-Automation-Engine-Solution-Accelerator into azd-semantickernel-marktayl
2 parents 3cfd53a + abd4b02 commit 9810754

File tree

2 files changed

+76
-63
lines changed

2 files changed

+76
-63
lines changed

src/backend/app_config.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,10 @@ async def create_azure_ai_agent(
219219
tool_definitions = tools
220220

221221
# Create the agent using the project client
222-
logging.info("Creating agent '%s' with model '%s'", agent_name, self.AZURE_OPENAI_DEPLOYMENT_NAME)
222+
if response_format is not None:
223+
logging.info("Response format provided: %s", response_format)
224+
225+
223226
agent_definition = await project_client.agents.create_agent(
224227
model=self.AZURE_OPENAI_DEPLOYMENT_NAME,
225228
name=agent_name,

src/backend/kernel_agents/planner_agent.py

Lines changed: 72 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,7 @@ async def handle_input_task(self, input_task: InputTask) -> str:
128128
# Parse the input task
129129
logging.info("Handling input task")
130130

131-
logging.info(f"Parsed input task: {input_task}")
132-
133-
# Generate a structured plan with steps
134-
135-
logging.info(f"Received input task: {input_task.description}")
136-
logging.info(f"Session ID: {input_task.session_id}, User ID: {self._user_id}")
131+
137132
plan, steps = await self._create_structured_plan(input_task)
138133

139134
logging.info(f"Plan created: {plan}")
@@ -268,16 +263,9 @@ async def _create_structured_plan(self, input_task: InputTask) -> Tuple[Plan, Li
268263
"""
269264
try:
270265
# Generate the instruction for the LLM
271-
logging.info("Generating instruction for the LLM")
272-
logging.debug(f"Input: {input_task}")
273-
logging.debug(f"Available agents: {self._available_agents}")
274266

275267
instruction = self._generate_instruction(input_task.description)
276268

277-
logging.info(f"Generated instruction: {instruction}")
278-
# Log the input task for debugging
279-
logging.info(f"Creating plan for task: '{input_task.description}'")
280-
logging.info(f"Using available agents: {self._available_agents}")
281269

282270
# Use the Azure AI Agent instead of direct function invocation
283271
if self._azure_ai_agent is None:
@@ -587,74 +575,90 @@ def _generate_instruction(self, objective: str) -> str:
587575
# Create a list of available agents
588576
agents_str = ", ".join(self._available_agents)
589577

590-
# Create list of available tools
591-
# If _agent_tools_list is empty but we have agent instances available elsewhere,
592-
# we should retrieve tools directly from agent instances
593-
tools_str = ""
578+
# Create list of available tools in JSON-like format
579+
tools_list = []
580+
581+
# Check if we have agent instances to extract tools from
594582
if hasattr(self, '_agent_instances') and self._agent_instances:
595-
# Extract tools from agent instances
596-
agent_tools_sections = []
597-
598583
# Process each agent to get their tools
599584
for agent_name, agent in self._agent_instances.items():
600585
if hasattr(agent, '_tools') and agent._tools:
601-
# Create a section header for this agent
602-
agent_tools_sections.append(f"### {agent_name} Tools ###")
603-
604586
# Add each tool from this agent
605587
for tool in agent._tools:
606588
if hasattr(tool, 'name') and hasattr(tool, 'description'):
607-
tool_desc = f"Agent: {agent_name} - Function: {tool.name} - {tool.description}"
608-
agent_tools_sections.append(tool_desc)
609-
610-
# Add a blank line after each agent's tools
611-
agent_tools_sections.append("")
589+
# Extract function parameters/arguments
590+
args_dict = {}
591+
if hasattr(tool, 'parameters'):
592+
for param in tool.parameters:
593+
param_type = "string" # Default type
594+
if hasattr(param, 'type'):
595+
param_type = param.type
596+
597+
args_dict[param.name] = {
598+
'description': param.description,
599+
'title': param.name.replace('_', ' ').title(),
600+
'type': param_type
601+
}
602+
603+
# Create tool entry
604+
tool_entry = {
605+
'agent': agent_name,
606+
'function': tool.name,
607+
'description': tool.description,
608+
'arguments': str(args_dict)
609+
}
610+
611+
tools_list.append(tool_entry)
612612

613-
# Join all sections
614-
if agent_tools_sections:
615-
tools_str = "\n".join(agent_tools_sections)
616-
# Log the tools for debugging
617-
logging.debug(f"Generated tools list from agent instances with {len(agent_tools_sections)} entries")
618-
else:
619-
tools_str = "Various specialized tools (No tool details available from agent instances)"
620-
logging.warning("No tools found in agent instances")
621-
elif self._agent_tools_list:
622-
# Fall back to the existing tools list if available
623-
tools_str = "\n".join(self._agent_tools_list)
624-
logging.debug(f"Using existing agent_tools_list with {len(self._agent_tools_list)} entries")
625-
else:
626-
# Default fallback
627-
tools_str = "Various specialized tools"
628-
logging.warning("No tools information available for planner instruction")
629-
630-
# Build the instruction, avoiding backslashes in f-string expressions
631-
objective_part = f"Your objective is:\n{objective}" if objective else "When given an objective, analyze it and create a plan to accomplish it."
632-
633-
return f"""
613+
logging.debug(f"Generated {len(tools_list)} tools from agent instances")
614+
615+
# If we couldn't extract tools from agent instances, create a simplified format
616+
if not tools_list:
617+
logging.warning("No tool details extracted from agent instances, creating simplified format")
618+
if self._agent_tools_list:
619+
# Create dummy entries from the existing tool list strings
620+
for tool_str in self._agent_tools_list:
621+
if ":" in tool_str:
622+
parts = tool_str.split(":")
623+
if len(parts) >= 2:
624+
agent_part = parts[0].strip()
625+
function_part = parts[1].strip()
626+
627+
# Extract agent name if format is "Agent: AgentName"
628+
agent_name = agent_part.replace("Agent", "").strip()
629+
if not agent_name:
630+
agent_name = "GenericAgent"
631+
632+
tools_list.append({
633+
'agent': agent_name,
634+
'function': function_part,
635+
'description': f"Function {function_part} from {agent_name}",
636+
'arguments': "{}"
637+
})
638+
639+
# Convert the tools list to a string representation
640+
tools_str = str(tools_list)
641+
642+
# Build the instruction, avoiding backslashes in f-string expressions
643+
644+
instruction_template = f"""
634645
You are the Planner, an AI orchestrator that manages a group of AI agents to accomplish tasks.
635646
636647
For the given objective, come up with a simple step-by-step plan.
637648
This plan should involve individual tasks that, if executed correctly, will yield the correct answer. Do not add any superfluous steps.
638649
The result of the final step should be the final answer. Make sure that each step has all the information needed - do not skip steps.
639650
640651
These actions are passed to the specific agent. Make sure the action contains all the information required for the agent to execute the task.
641-
642-
{objective_part}
652+
653+
Your objective is:
654+
{objective}
643655
644656
The agents you have access to are:
645657
{agents_str}
646658
647659
These agents have access to the following functions:
648660
{tools_str}
649661
650-
IMPORTANT AGENT SELECTION GUIDANCE:
651-
- HrAgent: ALWAYS use for ALL employee-related tasks like onboarding, hiring, benefits, payroll, training, employee records, ID cards, mentoring, background checks, etc.
652-
- MarketingAgent: Use for marketing campaigns, branding, market research, content creation, social media, etc.
653-
- ProcurementAgent: Use for purchasing, vendor management, supply chain, asset management, etc.
654-
- ProductAgent: Use for product development, roadmaps, features, product feedback, etc.
655-
- TechSupportAgent: Use for technical issues, software/hardware setup, troubleshooting, IT support, etc.
656-
- GenericAgent: Use only for general knowledge tasks that don't fit other categories
657-
- HumanAgent: Use only when human input is absolutely required and no other agent can handle the task
658662
659663
The first step of your plan should be to ask the user for any additional information required to progress the rest of steps planned.
660664
@@ -664,15 +668,21 @@ def _generate_instruction(self, objective: str) -> str:
664668
665669
If there is a single function call that can directly solve the task, only generate a plan with a single step. For example, if someone asks to be granted access to a database, generate a plan with only one step involving the grant_database_access function, with no additional steps.
666670
671+
When generating the action in the plan, frame the action as an instruction you are passing to the agent to execute. It should be a short, single sentence. Include the function to use. For example, "Set up an Office 365 Account for Jessica Smith. Function: set_up_office_365_account"
672+
673+
Ensure the summary of the plan and the overall steps is less than 50 words.
674+
675+
Identify any additional information that might be required to complete the task. Include this information in the plan in the human_clarification_request field of the plan. If it is not required, leave it as null. Do not include information that you are waiting for clarification on in the string of the action field, as this otherwise won't get updated.
676+
667677
You must prioritise using the provided functions to accomplish each step. First evaluate each and every function the agents have access too. Only if you cannot find a function needed to complete the task, and you have reviewed each and every function, and determined why each are not suitable, there are two options you can take when generating the plan.
668678
First evaluate whether the step could be handled by a typical large language model, without any specialised functions. For example, tasks such as "add 32 to 54", or "convert this SQL code to a python script", or "write a 200 word story about a fictional product strategy".
669-
670679
If a general Large Language Model CAN handle the step/required action, add a step to the plan with the action you believe would be needed, and add "EXCEPTION: No suitable function found. A generic LLM model is being used for this step." to the end of the action. Assign these steps to the GenericAgent. For example, if the task is to convert the following SQL into python code (SELECT * FROM employees;), and there is no function to convert SQL to python, write a step with the action "convert the following SQL into python code (SELECT * FROM employees;) EXCEPTION: No suitable function found. A generic LLM model is being used for this step." and assign it to the GenericAgent.
671-
672680
Alternatively, if a general Large Language Model CAN NOT handle the step/required action, add a step to the plan with the action you believe would be needed, and add "EXCEPTION: Human support required to do this step, no suitable function found." to the end of the action. Assign these steps to the HumanAgent. For example, if the task is to find the best way to get from A to B, and there is no function to calculate the best route, write a step with the action "Calculate the best route from A to B. EXCEPTION: Human support required, no suitable function found." and assign it to the HumanAgent.
673681
682+
674683
Limit the plan to 6 steps or less.
675684
676685
Choose from {agents_str} ONLY for planning your steps.
677686
678-
"""
687+
"""
688+
return instruction_template

0 commit comments

Comments
 (0)