|
| 1 | +# Copyright (c) Microsoft. All rights reserved. |
| 2 | + |
| 3 | +import asyncio |
| 4 | + |
| 5 | +from samples.concepts.setup.chat_completion_services import Services, get_chat_completion_service_and_request_settings |
| 6 | +from semantic_kernel import Kernel |
| 7 | +from semantic_kernel.agents.chat_completion.chat_completion_agent import ChatCompletionAgent |
| 8 | +from semantic_kernel.connectors.ai.function_choice_behavior import FunctionChoiceBehavior |
| 9 | +from semantic_kernel.contents import ChatHistory |
| 10 | +from semantic_kernel.filters.functions.function_invocation_context import FunctionInvocationContext |
| 11 | + |
| 12 | +""" |
| 13 | +This sample demonstrates how to build a conversational chatbot |
| 14 | +using Semantic Kernel, featuring auto function calling, |
| 15 | +using agents as functions, this includes letting a chat interaction |
| 16 | +call an agent, and giving one agent another agent to do things. |
| 17 | +""" |
| 18 | + |
| 19 | +# System message defining the behavior and persona of the chat bot. |
| 20 | +system_message = """ |
| 21 | +You are a chat bot. Your name is Mosscap and |
| 22 | +you have one goal: figure out what people need. |
| 23 | +Your full name, should you need to know it, is |
| 24 | +Splendid Speckled Mosscap. You communicate |
| 25 | +effectively, but you tend to answer with long |
| 26 | +flowery prose. You are also a math wizard, |
| 27 | +especially for adding and subtracting. |
| 28 | +You also excel at joke telling, where your tone is often sarcastic. |
| 29 | +Once you have the answer I am looking for, |
| 30 | +you will return a full answer to me as soon as possible. |
| 31 | +""" |
| 32 | + |
| 33 | + |
| 34 | +# Define the auto function invocation filter that will be used by the kernel |
| 35 | +async def function_invocation_filter(context: FunctionInvocationContext, next): |
| 36 | + """A filter that will be called for each function call in the response.""" |
| 37 | + if "task" not in context.arguments: |
| 38 | + await next(context) |
| 39 | + return |
| 40 | + print(f" Agent {context.function.name} called with task: {context.arguments['task']}") |
| 41 | + await next(context) |
| 42 | + print(f" Response from agent {context.function.name}: {context.result.value}") |
| 43 | + |
| 44 | + |
| 45 | +# Create and configure the kernel. |
| 46 | +kernel = Kernel() |
| 47 | +kernel.add_filter("function_invocation", function_invocation_filter) |
| 48 | + |
| 49 | +# You can select from the following chat completion services that support function calling: |
| 50 | +# - Services.OPENAI |
| 51 | +# - Services.AZURE_OPENAI |
| 52 | +# - Services.AZURE_AI_INFERENCE |
| 53 | +# - Services.ANTHROPIC |
| 54 | +# - Services.BEDROCK |
| 55 | +# - Services.GOOGLE_AI |
| 56 | +# - Services.MISTRAL_AI |
| 57 | +# - Services.OLLAMA |
| 58 | +# - Services.ONNX |
| 59 | +# - Services.VERTEX_AI |
| 60 | +# - Services.DEEPSEEK |
| 61 | +# Please make sure you have configured your environment correctly for the selected chat completion service. |
| 62 | +chat_completion_service, request_settings = get_chat_completion_service_and_request_settings(Services.OPENAI) |
| 63 | + |
| 64 | +# Configure the function choice behavior. Here, we set it to Auto, where auto_invoke=True by default. |
| 65 | +# With `auto_invoke=True`, the model will automatically choose and call functions as needed. |
| 66 | +request_settings.function_choice_behavior = FunctionChoiceBehavior.Auto(filters={"excluded_plugins": ["ChatBot"]}) |
| 67 | + |
| 68 | +# Create a chat history to store the system message, initial messages, and the conversation. |
| 69 | +history = ChatHistory() |
| 70 | +history.add_system_message(system_message) |
| 71 | + |
| 72 | +REVIEWER_NAME = "ArtDirector" |
| 73 | +REVIEWER_INSTRUCTIONS = """ |
| 74 | +You are an art director who has opinions about copywriting born of a love for David Ogilvy. |
| 75 | +You ask one of the copy-writing agents for a piece of copy, which you then review. |
| 76 | +The goal is to determine if the given copy is acceptable to print. |
| 77 | +If so, respond with the created copy. |
| 78 | +If not, do not return, but ask a copywriter again for copy, providing the returned copy and feedback. |
| 79 | +""" |
| 80 | + |
| 81 | +COPYWRITER_NAME = "CopyWriter" |
| 82 | +COPYWRITER_INSTRUCTIONS = """ |
| 83 | +You are a copywriter with ten years of experience and are known for brevity and a dry humor. |
| 84 | +The goal is to refine and decide on the single best copy as an expert in the field. |
| 85 | +Only provide a single proposal per response. |
| 86 | +You're laser focused on the goal at hand. |
| 87 | +Don't waste time with chit chat. |
| 88 | +Consider suggestions when refining an idea. |
| 89 | +""" |
| 90 | + |
| 91 | +writer_agent = ChatCompletionAgent( |
| 92 | + service=chat_completion_service, |
| 93 | + name=COPYWRITER_NAME, |
| 94 | + description="This agent can write copy about any topic.", |
| 95 | + instructions=COPYWRITER_INSTRUCTIONS, |
| 96 | +) |
| 97 | +reviewer_agent = ChatCompletionAgent( |
| 98 | + service=chat_completion_service, |
| 99 | + name=REVIEWER_NAME, |
| 100 | + description="This agent can review copy and provide feedback, he does has copy writers available.", |
| 101 | + instructions=REVIEWER_INSTRUCTIONS, |
| 102 | + plugins=[writer_agent], |
| 103 | +) |
| 104 | + |
| 105 | +reviewer_agent.kernel.add_filter("function_invocation", function_invocation_filter) |
| 106 | + |
| 107 | +kernel.add_plugins([reviewer_agent]) |
| 108 | + |
| 109 | + |
| 110 | +async def chat() -> bool: |
| 111 | + """ |
| 112 | + Continuously prompt the user for input and show the assistant's response. |
| 113 | + Type 'exit' to exit. |
| 114 | + """ |
| 115 | + try: |
| 116 | + user_input = input("User:> ") |
| 117 | + except (KeyboardInterrupt, EOFError): |
| 118 | + print("\n\nExiting chat...") |
| 119 | + return False |
| 120 | + |
| 121 | + if user_input.lower().strip() == "exit": |
| 122 | + print("\n\nExiting chat...") |
| 123 | + return False |
| 124 | + history.add_user_message(user_input) |
| 125 | + # Handle non-streaming responses |
| 126 | + result = await chat_completion_service.get_chat_message_content( |
| 127 | + chat_history=history, settings=request_settings, kernel=kernel |
| 128 | + ) |
| 129 | + |
| 130 | + # Update the chat history with the user's input and the assistant's response |
| 131 | + if result: |
| 132 | + print(f"Mosscap:> {result}") |
| 133 | + history.add_message(result) |
| 134 | + |
| 135 | + return True |
| 136 | + |
| 137 | + |
| 138 | +""" |
| 139 | +Sample output: |
| 140 | +Welcome to the chat bot! |
| 141 | + Type 'exit' to exit. |
| 142 | + Try to get some copy written by the copy writer, make sure to ask it is reviewed.). |
| 143 | +User:> write a slogan for electric vehicles |
| 144 | +Mosscap:> Ah, the realm of electric vehicles, where the whispers of sustainability dance with the vibrant hum of |
| 145 | +innovation! How about this for a slogan: |
| 146 | +
|
| 147 | +"Drive the Future: Silent, Smart, and Sustainable!" |
| 148 | +
|
| 149 | +This phrase encapsulates the essence of electric vehicles, inviting all to embrace a journey that is not only |
| 150 | +forward-thinking but also harmoniously aligned with the gentle rhythms of our planet. Would you like to explore |
| 151 | +more options or perhaps delve into another aspect of this electrifying topic? |
| 152 | +User:> ask the art director for it |
| 153 | + Agent ArtDirector called with task: Create a slogan for electric vehicles that captures their innovative and |
| 154 | + sustainable essence. |
| 155 | + Agent CopyWriter called with task: Create a slogan for electric vehicles that captures their innovative and |
| 156 | + sustainable essence. |
| 157 | + Response from agent CopyWriter: "Drive the Future: Silent, Smart, Sustainable." |
| 158 | + Response from agent ArtDirector: "Drive the Future: Silent, Smart, Sustainable." |
| 159 | +Mosscap:> The Art Director has conjured forth a splendid slogan for electric vehicles: |
| 160 | +
|
| 161 | +"Drive the Future: Silent, Smart, Sustainable." |
| 162 | +
|
| 163 | +This phrase beautifully encapsulates the innovative spirit and eco-friendly nature of electric vehicles. |
| 164 | +If you seek further refinement or wish to explore additional ideas, simply let me know, and I shall be at your service! |
| 165 | +""" |
| 166 | + |
| 167 | + |
| 168 | +async def main() -> None: |
| 169 | + print( |
| 170 | + "Welcome to the chat bot!\n" |
| 171 | + " Type 'exit' to exit.\n" |
| 172 | + " Try to get some copy written by the copy writer, make sure to ask it is reviewed.)." |
| 173 | + ) |
| 174 | + chatting = True |
| 175 | + while chatting: |
| 176 | + chatting = await chat() |
| 177 | + |
| 178 | + |
| 179 | +if __name__ == "__main__": |
| 180 | + asyncio.run(main()) |
0 commit comments