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 python/samples/concepts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ This section contains code snippets that demonstrate the usage of Semantic Kerne

| Features | Description |
| -------- | ----------- |
| Agents | Creating and using agents in Semantic Kernel |
| AutoFunctionCalling | Using `Auto Function Calling` to allow function call capable models to invoke Kernel Functions automatically |
| ChatCompletion | Using [`ChatCompletion`](https://github.com/microsoft/semantic-kernel/blob/main/python/semantic_kernel/connectors/ai/chat_completion_client_base.py) messaging capable service with models |
| Filtering | Creating and using Filters |
Expand Down
30 changes: 30 additions & 0 deletions python/samples/concepts/agents/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Semantic Kernel Agents - Getting Started

This project contains a step by step guide to get started with _Semantic Kernel Agents_ in Python.


#### PyPI:
- For the use of agents, the minimum allowed Semantic Kernel pypi version is 1.3 # TODO Update

#### Source
- [Semantic Kernel Agent Framework](../../../semantic_kernel/agents/)

## Examples

The getting started with agents examples include:

Example|Description
---|---
[step1_agent](../agents/step1_agent.py)|How to create and use an agent.
[step2_plugins](../agents/step2_plugins.py)|How to associate plugins with an agent.

## Configuring the Kernel

Similar to the Semantic Kernel Python concept samples, it is necessary to configure the secrets
and keys used by the kernel. See the follow "Configuring the Kernel" [guide](../README.md#configuring-the-kernel) for
more information.

## Running Concept Samples

Concept samples can be run in an IDE or via the command line. After setting up the required api key
for your AI connector, the samples run without any extra command line arguments.
67 changes: 67 additions & 0 deletions python/samples/concepts/agents/step1_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Copyright (c) Microsoft. All rights reserved.

import asyncio
from functools import reduce

from semantic_kernel.agents.chat_completion_agent import ChatCompletionAgent
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.contents.utils.author_role import AuthorRole
from semantic_kernel.kernel import Kernel

###################################################################
# The following sample demonstrates how to create a simple, #
# non-group agent that repeats the user message in the voice #
# of a pirate and then ends with a parrot sound. #
###################################################################

# To toggle streaming or non-streaming mode, change the following boolean
streaming = True

# Define the agent name and instructions
PARROT_NAME = "Parrot"
Comment thread
moonbox3 marked this conversation as resolved.
PARROT_INSTRUCTIONS = "Repeat the user message in the voice of a pirate and then end with a parrot sound."


async def invoke_agent(agent: ChatCompletionAgent, input: str, chat: ChatHistory):
"""Invoke the agent with the user input."""
chat.add_user_message(input)

print(f"# {AuthorRole.USER}: '{input}'")

if streaming:
contents = []
content_name = ""
async for content in agent.invoke_stream(chat):
content_name = content.name
Comment thread
moonbox3 marked this conversation as resolved.
contents.append(content)
streaming_chat_message = reduce(lambda first, second: first + second, contents)
print(f"# {content.role} - {content_name or '*'}: '{streaming_chat_message}'")
chat.add_message(content)
else:
async for content in agent.invoke(chat):
print(f"# {content.role} - {content.name or '*'}: '{content.content}'")
Comment thread
crickman marked this conversation as resolved.
chat.add_message(content)


async def main():
# Create the instance of the Kernel
kernel = Kernel()

# Add the OpenAIChatCompletion AI Service to the Kernel
kernel.add_service(AzureChatCompletion(service_id="agent"))

# Create the agent
agent = ChatCompletionAgent(service_id="agent", kernel=kernel, name=PARROT_NAME, instructions=PARROT_INSTRUCTIONS)

# Define the chat history
chat = ChatHistory()

# Respond to user input
await invoke_agent(agent, "Fortune favors the bold.", chat)
await invoke_agent(agent, "I came, I saw, I conquered.", chat)
await invoke_agent(agent, "Practice makes perfect.", chat)


if __name__ == "__main__":
asyncio.run(main())
99 changes: 99 additions & 0 deletions python/samples/concepts/agents/step2_plugins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Copyright (c) Microsoft. All rights reserved.

import asyncio
from typing import Annotated

from semantic_kernel.agents.chat_completion_agent import ChatCompletionAgent
from semantic_kernel.connectors.ai.function_choice_behavior import FunctionChoiceBehavior
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
from semantic_kernel.contents.chat_history import ChatHistory
from semantic_kernel.contents.utils.author_role import AuthorRole
from semantic_kernel.functions.kernel_function_decorator import kernel_function
from semantic_kernel.kernel import Kernel

###################################################################
# The following sample demonstrates how to create a simple, #
# non-group agent that utilizes plugins defined as part of #
# the Kernel. #
###################################################################

# This sample allows for a streaming response verus a non-streaming response
streaming = True

# Define the agent name and instructions
HOST_NAME = "Host"
HOST_INSTRUCTIONS = "Answer questions about the menu."


# Define a sample plugin for the sample
class MenuPlugin:
"""A sample Menu Plugin used for the concept sample."""

@kernel_function(description="Provides a list of specials from the menu.")
def get_specials(self) -> Annotated[str, "Returns the specials from the menu."]:
return """
Special Soup: Clam Chowder
Special Salad: Cobb Salad
Special Drink: Chai Tea
"""

@kernel_function(description="Provides the price of the requested menu item.")
def get_item_price(
self, menu_item: Annotated[str, "The name of the menu item."]
) -> Annotated[str, "Returns the price of the menu item."]:
return "$9.99"


# A helper method to invoke the agent with the user input
async def invoke_agent(agent: ChatCompletionAgent, input: str, chat: ChatHistory) -> None:
"""Invoke the agent with the user input."""
chat.add_user_message(input)

print(f"# {AuthorRole.USER}: '{input}'")

if streaming:
contents = []
content_name = ""
async for content in agent.invoke_stream(chat):
content_name = content.name
contents.append(content)
message_content = "".join([content.content for content in contents])
print(f"# {content.role} - {content_name or '*'}: '{message_content}'")
chat.add_assistant_message(message_content)
else:
async for content in agent.invoke(chat):
print(f"# {content.role} - {content.name or '*'}: '{content.content}'")
chat.add_message(content)


async def main():
# Create the instance of the Kernel
kernel = Kernel()

# Add the OpenAIChatCompletion AI Service to the Kernel
service_id = "agent"
kernel.add_service(AzureChatCompletion(service_id=service_id))

settings = kernel.get_prompt_execution_settings_from_service_id(service_id=service_id)
# Configure the function choice behavior to auto invoke kernel functions
settings.function_choice_behavior = FunctionChoiceBehavior.Auto()

kernel.add_plugin(plugin=MenuPlugin(), plugin_name="menu")

# Create the agent
agent = ChatCompletionAgent(
service_id="agent", kernel=kernel, name=HOST_NAME, instructions=HOST_INSTRUCTIONS, execution_settings=settings
)

# Define the chat history
chat = ChatHistory()

# Respond to user input
await invoke_agent(agent, "Hello", chat)
await invoke_agent(agent, "What is the special soup?", chat)
await invoke_agent(agent, "What is the special drink?", chat)
await invoke_agent(agent, "Thank you", chat)


if __name__ == "__main__":
asyncio.run(main())
7 changes: 7 additions & 0 deletions python/semantic_kernel/agents/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright (c) Microsoft. All rights reserved.

from semantic_kernel.agents.chat_completion_agent import ChatCompletionAgent

__all__ = [
Comment thread
moonbox3 marked this conversation as resolved.
"ChatCompletionAgent",
]
57 changes: 57 additions & 0 deletions python/semantic_kernel/agents/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Copyright (c) Microsoft. All rights reserved.

import uuid
from abc import ABC
from typing import ClassVar

from pydantic import Field

from semantic_kernel.agents.agent_channel import AgentChannel
from semantic_kernel.kernel import Kernel
from semantic_kernel.kernel_pydantic import KernelBaseModel
from semantic_kernel.utils.experimental_decorator import experimental_class


@experimental_class
class Agent(ABC, KernelBaseModel):
Comment thread
moonbox3 marked this conversation as resolved.
Comment thread
moonbox3 marked this conversation as resolved.
"""Base abstraction for all Semantic Kernel agents.

An agent instance may participate in one or more conversations.
A conversation may include one or more agents.
In addition to identity and descriptive meta-data, an Agent
must define its communication protocol, or AgentChannel.

Attributes:
name: The name of the agent (optional).
description: The description of the agent (optional).
id: The unique identifier of the agent (optional). If no id is provided,
a new UUID will be generated.
instructions: The instructions for the agent (optional
"""

id: str = Field(default_factory=lambda: str(uuid.uuid4()))
description: str | None = None
name: str | None = None
instructions: str | None = None
kernel: Kernel = Field(default_factory=Kernel)
channel_type: ClassVar[type[AgentChannel] | None] = None

def get_channel_keys(self) -> list[str]:
"""Get the channel keys.

Returns:
A list of channel keys.
"""
if not self.channel_type:
raise NotImplementedError("Unable to get channel keys. Channel type not configured.")
return [self.channel_type.__name__]

def create_channel(self) -> AgentChannel:
"""Create a channel.

Returns:
An instance of AgentChannel.
"""
if not self.channel_type:
raise NotImplementedError("Unable to create channel. Channel type not configured.")
return self.channel_type()
59 changes: 59 additions & 0 deletions python/semantic_kernel/agents/agent_channel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Copyright (c) Microsoft. All rights reserved.

from abc import ABC, abstractmethod
from collections.abc import AsyncIterable
from typing import TYPE_CHECKING

from semantic_kernel.utils.experimental_decorator import experimental_class

if TYPE_CHECKING:
from semantic_kernel.agents.agent import Agent
from semantic_kernel.contents.chat_message_content import ChatMessageContent


@experimental_class
class AgentChannel(ABC):
"""Defines the communication protocol for a particular Agent type.

An agent provides it own AgentChannel via CreateChannel.
"""

@abstractmethod
async def receive(
self,
history: list["ChatMessageContent"],
) -> None:
"""Receive the conversation messages.

Used when joining a conversation and also during each agent interaction.

Args:
history: The history of messages in the conversation.
"""
...

@abstractmethod
def invoke(
self,
agent: "Agent",
) -> AsyncIterable["ChatMessageContent"]:
"""Perform a discrete incremental interaction between a single Agent and AgentChat.

Args:
agent: The agent to interact with.

Returns:
An async iterable of ChatMessageContent.
"""
...

@abstractmethod
def get_history(
self,
) -> AsyncIterable["ChatMessageContent"]:
"""Retrieve the message history specific to this channel.

Returns:
An async iterable of ChatMessageContent.
"""
...
Loading