# Copyright (c) Microsoft. All rights reserved. import asyncio import os from azure.identity import AzureCliCredential from semantic_kernel import Kernel from semantic_kernel.agents import AgentGroupChat, ChatCompletionAgent from semantic_kernel.agents.strategies import ( KernelFunctionSelectionStrategy, KernelFunctionTerminationStrategy, ) from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion from semantic_kernel.contents import ChatHistoryTruncationReducer from semantic_kernel.functions import KernelFunctionFromPrompt """ The following sample demonstrates how to create a simple, agent group chat that utilizes a Reviewer Chat Completion Agent along with a Writer Chat Completion Agent to complete a user's task. This is the full code sample for the Semantic Kernel Learn Site: How-To: Coordinate Agent Collaboration using Agent Group Chat https://learn.microsoft.com/semantic-kernel/frameworks/agent/examples/example-agent-collaboration?pivots=programming-language-python """ # Define agent names REVIEWER_NAME = "Reviewer" WRITER_NAME = "Writer" def create_kernel() -> Kernel: """Creates a Kernel instance with an Azure OpenAI ChatCompletion service.""" kernel = Kernel() kernel.add_service(service=AzureChatCompletion(credential=AzureCliCredential())) return kernel async def main(): # Create a single kernel instance for all agents. kernel = create_kernel() # Create ChatCompletionAgents using the same kernel. agent_reviewer = ChatCompletionAgent( kernel=kernel, name=REVIEWER_NAME, instructions=""" Your responsibility is to review and identify how to improve user provided content. If the user has provided input or direction for content already provided, specify how to address this input. Never directly perform the correction or provide an example. Once the content has been updated in a subsequent response, review it again until it is satisfactory. RULES: - Only identify suggestions that are specific and actionable. - Verify previous suggestions have been addressed. - Never repeat previous suggestions. """, ) agent_writer = ChatCompletionAgent( kernel=kernel, name=WRITER_NAME, instructions=""" Your sole responsibility is to rewrite content according to review suggestions. - Always apply all review directions. - Always revise the content in its entirety without explanation. - Never address the user. """, ) # Define a selection function to determine which agent should take the next turn. selection_function = KernelFunctionFromPrompt( function_name="selection", prompt=f""" Examine the provided RESPONSE and choose the next participant. State only the name of the chosen participant without explanation. Never choose the participant named in the RESPONSE. Choose only from these participants: - {REVIEWER_NAME} - {WRITER_NAME} Rules: - If RESPONSE is user input, it is {REVIEWER_NAME}'s turn. - If RESPONSE is by {REVIEWER_NAME}, it is {WRITER_NAME}'s turn. - If RESPONSE is by {WRITER_NAME}, it is {REVIEWER_NAME}'s turn. RESPONSE: {{{{$lastmessage}}}} """, ) # Define a termination function where the reviewer signals completion with "yes". termination_keyword = "yes" termination_function = KernelFunctionFromPrompt( function_name="termination", prompt=f""" Examine the RESPONSE and determine whether the content has been deemed satisfactory. If the content is satisfactory, respond with a single word without explanation: {termination_keyword}. If specific suggestions are being provided, it is not satisfactory. If no correction is suggested, it is satisfactory. RESPONSE: {{{{$lastmessage}}}} """, ) history_reducer = ChatHistoryTruncationReducer(target_count=1) # Create the AgentGroupChat with selection and termination strategies. chat = AgentGroupChat( agents=[agent_reviewer, agent_writer], selection_strategy=KernelFunctionSelectionStrategy( initial_agent=agent_reviewer, function=selection_function, kernel=kernel, result_parser=lambda result: str(result.value[0]).strip() if result.value[0] is not None else WRITER_NAME, history_variable_name="lastmessage", history_reducer=history_reducer, ), termination_strategy=KernelFunctionTerminationStrategy( agents=[agent_reviewer], function=termination_function, kernel=kernel, result_parser=lambda result: termination_keyword in str(result.value[0]).lower(), history_variable_name="lastmessage", maximum_iterations=10, history_reducer=history_reducer, ), ) print( "Ready! Type your input, or 'exit' to quit, 'reset' to restart the conversation. " "You may pass in a file path using @." ) is_complete = False while not is_complete: print() user_input = input("User > ").strip() if not user_input: continue if user_input.lower() == "exit": is_complete = True break if user_input.lower() == "reset": await chat.reset() print("[Conversation has been reset]") continue # Try to grab files from the script's current directory if user_input.startswith("@") and len(user_input) > 1: file_name = user_input[1:] script_dir = os.path.dirname(os.path.abspath(__file__)) file_path = os.path.join(script_dir, file_name) try: if not os.path.exists(file_path): print(f"Unable to access file: {file_path}") continue with open(file_path, encoding="utf-8") as file: user_input = file.read() except Exception: print(f"Unable to access file: {file_path}") continue # Add the current user_input to the chat await chat.add_chat_message(message=user_input) try: async for response in chat.invoke(): if response is None or not response.name: continue print() print(f"# {response.name.upper()}:\n{response.content}") except Exception as e: print(f"Error during chat invocation: {e}") # Reset the chat's complete flag for the new conversation round. chat.is_complete = False if __name__ == "__main__": asyncio.run(main())