Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -458,3 +458,4 @@ __pycache__/
*.whl
!autogen_core-0.3.dev0-py3-none-any.whl
.azure
.github/copilot-instructions.md
51 changes: 9 additions & 42 deletions src/backend/app_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
logging.info("Added health check middleware")


@app.post("/input_task")
@app.post("/api/input_task")
async def input_task_endpoint(input_task: InputTask, request: Request):
"""
Receive the initial input task from the user.
Expand Down Expand Up @@ -175,7 +175,7 @@ async def input_task_endpoint(input_task: InputTask, request: Request):
raise HTTPException(status_code=400, detail="Error creating plan")


@app.post("/human_feedback")
@app.post("/api/human_feedback")
async def human_feedback_endpoint(human_feedback: HumanFeedback, request: Request):

"""
Expand Down Expand Up @@ -268,7 +268,7 @@ async def human_feedback_endpoint(human_feedback: HumanFeedback, request: Reques
}


@app.post("/human_clarification_on_plan")
@app.post("/api/human_clarification_on_plan")
async def human_clarification_endpoint(
human_clarification: HumanClarification, request: Request
):
Expand Down Expand Up @@ -349,7 +349,7 @@ async def human_clarification_endpoint(
}


@app.post("/approve_step_or_steps")
@app.post("/api/approve_step_or_steps")
async def approve_step_endpoint(
human_feedback: HumanFeedback, request: Request
) -> Dict[str, str]:
Expand Down Expand Up @@ -453,7 +453,7 @@ async def approve_step_endpoint(
return {"status": "All steps approved"}


@app.get("/plans", response_model=List[PlanWithSteps])
@app.get("/api/plans", response_model=List[PlanWithSteps])
async def get_plans(
request: Request, session_id: Optional[str] = Query(None)
) -> List[PlanWithSteps]:
Expand Down Expand Up @@ -553,7 +553,7 @@ async def get_plans(
return list_of_plans_with_steps


@app.get("/steps/{plan_id}", response_model=List[Step])
@app.get("/api/steps/{plan_id}", response_model=List[Step])
async def get_steps_by_plan(plan_id: str, request: Request) -> List[Step]:
"""
Retrieve steps for a specific plan.
Expand Down Expand Up @@ -616,7 +616,7 @@ async def get_steps_by_plan(plan_id: str, request: Request) -> List[Step]:
return steps


@app.get("/agent_messages/{session_id}", response_model=List[AgentMessage])
@app.get("/api/agent_messages/{session_id}", response_model=List[AgentMessage])
async def get_agent_messages(session_id: str, request: Request) -> List[AgentMessage]:
"""
Retrieve agent messages for a specific session.
Expand Down Expand Up @@ -680,7 +680,7 @@ async def get_agent_messages(session_id: str, request: Request) -> List[AgentMes
return agent_messages


@app.delete("/messages")
@app.delete("/api/messages")
async def delete_all_messages(request: Request) -> Dict[str, str]:
"""
Delete all messages across sessions.
Expand Down Expand Up @@ -723,7 +723,7 @@ async def delete_all_messages(request: Request) -> Dict[str, str]:
return {"status": "All messages deleted"}


@app.get("/messages")
@app.get("/api/messages")
async def get_all_messages(request: Request):
"""
Retrieve all messages across sessions.
Expand Down Expand Up @@ -804,39 +804,6 @@ async def get_agent_tools():
return []


# Initialize the application when it starts
@app.on_event("startup")
async def startup_event():
"""Initialize the application on startup.

This function runs when the FastAPI application starts up.
It sets up the agent types and tool loaders so the first request is faster.
"""
# Log startup
logging.info("Application starting up. Initializing agent factory...")

try:
# Create a temporary session and user ID to pre-initialize agents
# This ensures tools are loaded into the factory on startup
temp_session_id = "startup-session"
temp_user_id = "startup-user"

# Create a test agent to initialize the tool loading system
# This will pre-load tool configurations into memory
test_agent = await AgentFactory.create_agent(
agent_type=AgentType.GENERIC,
session_id=temp_session_id,
user_id=temp_user_id
)

# Clean up initialization resources
AgentFactory.clear_cache(temp_session_id)
logging.info("Agent factory successfully initialized")

except Exception as e:
# Don't fail startup, but log the error
logging.error(f"Error initializing agent factory: {e}")


# Run the app
if __name__ == "__main__":
Expand Down
94 changes: 56 additions & 38 deletions src/backend/kernel_agents/planner_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,11 +147,17 @@ async def async_init(self) -> None:
"""
try:
logging.info("Initializing PlannerAgent from async init azure AI Agent")
# Create the Azure AI Agent using AppConfig

# Generate instructions as a string rather than returning an object
# We'll use a blank input since this is just for initialization
instruction_args = self._generate_instruction("")
instructions = self._get_template().format(**instruction_args)

# Create the Azure AI Agent using AppConfig with string instructions
self._azure_ai_agent = await config.create_azure_ai_agent(
kernel=self._kernel,
agent_name="PlannerAgent",
instructions=self._generate_instruction(""),
instructions=instructions, # Pass the formatted string, not an object
temperature=0.0
)
logging.info("Successfully created Azure AI Agent for PlannerAgent")
Expand Down Expand Up @@ -311,8 +317,11 @@ async def _create_structured_plan(self, input_task: InputTask) -> Tuple[Plan, Li
logging.debug(f"Input: {input_task}")
logging.debug(f"Available agents: {self._available_agents}")

instruction = self._generate_instruction(input_task.description)
# Get template variables as a dictionary
args = self._generate_instruction(input_task.description)

# Format the template with the arguments
instruction = self._get_template().format(**args)
logging.info(f"Generated instruction: {instruction}")
# Log the input task for debugging
logging.info(f"Creating plan for task: '{input_task.description}'")
Expand Down Expand Up @@ -651,14 +660,14 @@ async def _create_fallback_plan_from_text(self, input_task: InputTask, text_cont

return plan, steps

def _generate_instruction(self, objective: str) -> str:
def _generate_instruction(self, objective: str) -> any:
"""Generate instruction for the LLM to create a plan.

Args:
objective: The user's objective

Returns:
Instruction string for the LLM
Dictionary containing the variables to populate the template
"""
# Create a list of available agents
agents_str = ", ".join(self._available_agents)
Expand Down Expand Up @@ -766,53 +775,62 @@ def _generate_instruction(self, objective: str) -> str:
# Convert the tools list to a string representation
tools_str = str(tools_list)

# Build the instruction, avoiding backslashes in f-string expressions
# Return a dictionary with template variables
return {
"objective": objective,
"agents_str": agents_str,
"tools_str": tools_str,
}

def _get_template(self):
"""Generate the instruction template for the LLM."""
# Build the instruction with proper format placeholders for .format() method

instruction_template = f"""
You are the Planner, an AI orchestrator that manages a group of AI agents to accomplish tasks.
instruction_template = """
You are the Planner, an AI orchestrator that manages a group of AI agents to accomplish tasks.

For the given objective, come up with a simple step-by-step plan.
This plan should involve individual tasks that, if executed correctly, will yield the correct answer. Do not add any superfluous steps.
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.
For the given objective, come up with a simple step-by-step plan.
This plan should involve individual tasks that, if executed correctly, will yield the correct answer. Do not add any superfluous steps.
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.

These actions are passed to the specific agent. Make sure the action contains all the information required for the agent to execute the task.

Your objective is:
{objective}
These actions are passed to the specific agent. Make sure the action contains all the information required for the agent to execute the task.
Your objective is:
{objective}

The agents you have access to are:
{agents_str}
The agents you have access to are:
{agents_str}

These agents have access to the following functions:
{tools_str}
These agents have access to the following functions:
{tools_str}

IMPORTANT AGENT SELECTION GUIDANCE:
- HrAgent: ALWAYS use for ALL employee-related tasks like onboarding, hiring, benefits, payroll, training, employee records, ID cards, mentoring, background checks, etc.
- MarketingAgent: Use for marketing campaigns, branding, market research, content creation, social media, etc.
- ProcurementAgent: Use for purchasing, vendor management, supply chain, asset management, etc.
- ProductAgent: Use for product development, roadmaps, features, product feedback, etc.
- TechSupportAgent: Use for technical issues, software/hardware setup, troubleshooting, IT support, etc.
- GenericAgent: Use only for general knowledge tasks that don't fit other categories
- HumanAgent: Use only when human input is absolutely required and no other agent can handle the task
IMPORTANT AGENT SELECTION GUIDANCE:
- HrAgent: ALWAYS use for ALL employee-related tasks like onboarding, hiring, benefits, payroll, training, employee records, ID cards, mentoring, background checks, etc.
- MarketingAgent: Use for marketing campaigns, branding, market research, content creation, social media, etc.
- ProcurementAgent: Use for purchasing, vendor management, supply chain, asset management, etc.
- ProductAgent: Use for product development, roadmaps, features, product feedback, etc.
- TechSupportAgent: Use for technical issues, software/hardware setup, troubleshooting, IT support, etc.
- GenericAgent: Use only for general knowledge tasks that don't fit other categories
- HumanAgent: Use only when human input is absolutely required and no other agent can handle the task

The first step of your plan should be to ask the user for any additional information required to progress the rest of steps planned.
The first step of your plan should be to ask the user for any additional information required to progress the rest of steps planned.

Only use the functions provided as part of your plan. If the task is not possible with the agents and tools provided, create a step with the agent of type Exception and mark the overall status as completed.
Only use the functions provided as part of your plan. If the task is not possible with the agents and tools provided, create a step with the agent of type Exception and mark the overall status as completed.

Do not add superfluous steps - only take the most direct path to the solution, with the minimum number of steps. Only do the minimum necessary to complete the goal.
Do not add superfluous steps - only take the most direct path to the solution, with the minimum number of steps. Only do the minimum necessary to complete the goal.

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.
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.

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.
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".
You must prioritize 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.
First evaluate whether the step could be handled by a typical large language model, without any specialized 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".

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.
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.

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.
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.

Limit the plan to 6 steps or less.
Limit the plan to 6 steps or less.

Choose from {agents_str} ONLY for planning your steps.
Choose from {agents_str} ONLY for planning your steps.

"""
"""
return instruction_template
Loading
Loading