Skip to content

Commit 093e551

Browse files
moonbox3musale
authored andcommitted
Python: Allow plugins via agent constructors. Update samples. (microsoft#10707)
### Motivation and Context In Semantic Kernel, our goal is to simplify working with agents. When an agent is created without a specified kernel, one is automatically created on the user’s behalf. This enables developers to pass in a list of plugins that are then added to the underlying kernel. If both the agent constructor and the provided kernel include plugins, those specified via the agent constructor take precedence—since plugins are stored in a dictionary, any duplicate names result in the agent’s plugin overwriting the existing one. For the `ChatCompletionAgent`, usage is further simplified by allowing a `ChatCompletionClientBase` service to be passed via the constructor. If a kernel is supplied and already contains a chat completion service, the new service is added to the kernel. Moreover, if no execution settings are provided via KernelArguments, the first service registered on the kernel is used. New usage to create a ChatCompletionAgent ```python agent = ChatCompletionAgent( service=AzureChatCompletion(), name="<name>", instructions="<instructions>", ) ``` New usage to create a ChatCompletionAgent with plugins: ```python agent = ChatCompletionAgent( service=AzureChatCompletion(), name="<name>", instructions="<instructions>", plugins=[SamplePlugin()], ) ``` Previously, the `service_id` constructor argument was retained after introducing KernelArguments. However, with our transition to using the kernel’s AI service selector, the `service_id` parameter has become redundant. As we move from the experimental phase toward a release candidate, removing `service_id` is a necessary, albeit breaking, change. The `getting_started_with_agents/chat_completion` samples now begin by demonstrating the simplest way to configure the agent. The subsequent step illustrates the original method for setting up the chat completion service on the kernel. Similarly, for plugins, the initial approach is the easiest to follow, while the following step shows the traditional method of managing the kernel. Updated documentation will follow, once these changes are released in a new package. !-- Thank you for your contribution to the semantic-kernel repo! Please help reviewers and future users, providing the following information: 1. Why is this change required? 2. What problem does it solve? 3. What scenario does it contribute to? 4. If it fixes an open issue, please link to the issue here. --> ### Description Simplifying interactions with agents. - Updated the `ChatCompletionAgent`, `AzureAssistantAgent`, and `OpenAIAssistantAgent` classes with the `release_candidate` decorator in place of the `experimental` decorator. - Add a chat completion agent structured outputs sample - Closes microsoft#10604 <!-- Describe your changes, the overall approach, the underlying design. These notes will help understanding how your code works. Thanks! --> ### Contribution Checklist <!-- Before submitting this PR, please make sure: --> - [X] The code builds clean without any errors or warnings - [X] The PR follows the [SK Contribution Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md) and the [pre-submission formatting script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts) raises no violations - [X] All unit tests pass, and I have added new tests where possible - [ ] I didn't break anyone 😄
1 parent 3abf516 commit 093e551

37 files changed

+579
-216
lines changed

python/samples/concepts/agents/chat_completion_agent/chat_completion_function_termination.py

-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ async def main():
105105

106106
# Create the agent
107107
agent = ChatCompletionAgent(
108-
service_id=service_id,
109108
kernel=kernel,
110109
name=HOST_NAME,
111110
instructions=HOST_INSTRUCTIONS,

python/samples/concepts/agents/mixed_chat/mixed_chat_agents.py

-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ def _create_kernel_with_chat_completion(service_id: str) -> Kernel:
5252

5353
async def main():
5454
agent_reviewer = ChatCompletionAgent(
55-
service_id="artdirector",
5655
kernel=_create_kernel_with_chat_completion("artdirector"),
5756
name=REVIEWER_NAME,
5857
instructions=REVIEWER_INSTRUCTIONS,

python/samples/concepts/agents/mixed_chat/mixed_chat_agents_plugins.py

-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ async def main():
8181
# Configure the function choice behavior to auto invoke kernel functions
8282
settings.function_choice_behavior = FunctionChoiceBehavior.Auto()
8383
agent_reviewer = ChatCompletionAgent(
84-
service_id="artdirector",
8584
kernel=kernel,
8685
name=REVIEWER_NAME,
8786
instructions=REVIEWER_INSTRUCTIONS,

python/samples/concepts/agents/mixed_chat/mixed_chat_files.py

-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ async def main():
6262

6363
service_id = "summary"
6464
summary_agent = ChatCompletionAgent(
65-
service_id=service_id,
6665
kernel=_create_kernel_with_chat_completion(service_id=service_id),
6766
instructions="Summarize the entire conversation for the user in natural language.",
6867
name="SummaryAgent",

python/samples/concepts/agents/mixed_chat/mixed_chat_images.py

-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ async def main():
4747

4848
service_id = "summary"
4949
summary_agent = ChatCompletionAgent(
50-
service_id=service_id,
5150
kernel=_create_kernel_with_chat_completion(service_id=service_id),
5251
instructions="Summarize the entire conversation for the user in natural language.",
5352
name="Summarizer",

python/samples/concepts/agents/mixed_chat/mixed_chat_reset.py

-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ def _create_kernel_with_chat_completion(service_id: str) -> Kernel:
3030
async def main():
3131
# First create the ChatCompletionAgent
3232
chat_agent = ChatCompletionAgent(
33-
service_id="chat",
3433
kernel=_create_kernel_with_chat_completion("chat"),
3534
name="chat_agent",
3635
instructions="""

python/samples/concepts/agents/mixed_chat/mixed_chat_streaming.py

-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ def _create_kernel_with_chat_completion(service_id: str) -> Kernel:
3434
async def main():
3535
# First create a ChatCompletionAgent
3636
agent_reviewer = ChatCompletionAgent(
37-
service_id="artdirector",
3837
kernel=_create_kernel_with_chat_completion("artdirector"),
3938
name="ArtDirector",
4039
instructions="""

python/samples/concepts/agents/openai_assistant/openai_assistant_chart_maker.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright (c) Microsoft. All rights reserved.
22
import asyncio
33

4-
from samples.concepts.agents.assistant_agent.assistant_sample_utils import download_response_images
4+
from samples.concepts.agents.openai_assistant.openai_assistant_sample_utils import download_response_images
55
from semantic_kernel.agents.open_ai import AzureAssistantAgent
66
from semantic_kernel.contents.file_reference_content import FileReferenceContent
77

python/samples/getting_started_with_agents/README.md

+9-6
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,15 @@ The getting started with agents examples include:
2121

2222
Example|Description
2323
---|---
24-
[step1_chat_completion_agent](../getting_started_with_agents/chat_completion/step1_chat_completion_agent.py)|How to create and use an agent.
25-
[step2_chat_completion_agent_plugin](../getting_started_with_agents/chat_completion/step2_chat_completion_agent_plugin.py)|How to create and use an agent with plugins.
26-
[step3_chat_completion_agent_group_chat](../getting_started_with_agents/chat_completion/step3_chat_completion_agent_group_chat.py)|How to create a conversation between agents.
27-
[step4_kernel_function_strategies](../getting_started_with_agents/chat_completion/step4_kernel_function_strategies.py)|How to utilize a `KernelFunction` as a chat strategy.
28-
[step5_chat_completion_agent_json_result](../getting_started_with_agents/chat_completion/step5_chat_completion_agent_json_result.py)|How to have an agent produce JSON.
29-
[step6_chat_completion_agent_logging](../getting_started_with_agents/chat_completion/step6_chat_completion_agent_logging.py)|How to enable logging for agents.
24+
[step1_chat_completion_agent_simple](../getting_started_with_agents/chat_completion/step1_chat_completion_agent_simple.py)|How to create and use a simple chat completion agent.
25+
[step2_chat_completion_agent_with_kernel](../getting_started_with_agents/chat_completion/step2_chat_completion_agent_with_kernel.py)|How to create and use a a chat completion agent with the AI service created on the kernel.
26+
[step3_chat_completion_agent_plugin_simple](../getting_started_with_agents/chat_completion/step3_chat_completion_agent_plugin_simple.py)|How to create a simple chat completion agent and specify plugins via the constructor with a kernel.
27+
[step4_chat_completion_agent_plugin_with_kernel](../getting_started_with_agents/chat_completion/step4_chat_completion_agent_plugin_with_kernel.py)|How to create and use a chat completion agent by registering plugins on the kernel.
28+
[step5_chat_completion_agent_group_chat](../getting_started_with_agents/chat_completion/step5_chat_completion_agent_group_chat.py)|How to create a conversation between agents.
29+
[step6_kernel_function_strategies](../getting_started_with_agents/chat_completion/step6_kernel_function_strategies.py)|How to utilize a `KernelFunction` as a chat strategy.
30+
[step7_chat_completion_agent_json_result](../getting_started_with_agents/chat_completion/step7_chat_completion_agent_json_result.py)|How to have an agent produce JSON.
31+
[step8_chat_completion_agent_logging](../getting_started_with_agents/chat_completion/step8_chat_completion_agent_logging.py)|How to enable logging for agents.
32+
[step9_chat_completion_agent_structured_outputs](../getting_started_with_agents/chat_completion/step9_chat_completion_agent_structured_outputs.py)|How to use have a chat completion agent use structured outputs
3033

3134
## OpenAI Assistant Agent
3235

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Copyright (c) Microsoft. All rights reserved.
2+
3+
import asyncio
4+
5+
from semantic_kernel.agents import ChatCompletionAgent
6+
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
7+
from semantic_kernel.contents import ChatHistory
8+
9+
"""
10+
The following sample demonstrates how to create a chat completion agent that
11+
answers user questions using the Azure Chat Completion service. The Chat Completion
12+
Service is passed directly via the ChatCompletionAgent constructor. This sample
13+
demonstrates the basic steps to create an agent and simulate a conversation
14+
with the agent.
15+
16+
The interaction with the agent is via the `get_response` method, which sends a
17+
user input to the agent and receives a response from the agent. The conversation
18+
history needs to be maintained by the caller in the chat history object.
19+
"""
20+
21+
# Simulate a conversation with the agent
22+
USER_INPUTS = [
23+
"Hello, I am John Doe.",
24+
"What is your name?",
25+
"What is my name?",
26+
]
27+
28+
29+
async def main():
30+
# 1. Create the agent by specifying the service
31+
agent = ChatCompletionAgent(
32+
service=AzureChatCompletion(),
33+
name="Assistant",
34+
instructions="Answer the user's questions.",
35+
)
36+
37+
# 2. Create a chat history to hold the conversation
38+
chat_history = ChatHistory()
39+
40+
for user_input in USER_INPUTS:
41+
# 3. Add the user input to the chat history
42+
chat_history.add_user_message(user_input)
43+
print(f"# User: {user_input}")
44+
# 4. Invoke the agent for a response
45+
response = await agent.get_response(chat_history)
46+
print(f"# {response.name}: {response}")
47+
# 5. Add the agent response to the chat history
48+
chat_history.add_message(response)
49+
50+
"""
51+
Sample output:
52+
# User: Hello, I am John Doe.
53+
# Assistant: Hello, John Doe! How can I assist you today?
54+
# User: What is your name?
55+
# Assistant: I don't have a personal name like a human does, but you can call me Assistant.?
56+
# User: What is my name?
57+
# Assistant: You mentioned that your name is John Doe. How can I assist you further, John?
58+
"""
59+
60+
61+
if __name__ == "__main__":
62+
asyncio.run(main())

python/samples/getting_started_with_agents/chat_completion/step1_chat_completion_agent.py renamed to python/samples/getting_started_with_agents/chat_completion/step2_chat_completion_agent_with_kernel.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@
99

1010
"""
1111
The following sample demonstrates how to create a chat completion agent that
12-
answers user questions using the Azure Chat Completion service. This sample
13-
demonstrates the basic steps to create an agent and simulate a conversation
14-
with the agent.
12+
answers user questions using the Azure Chat Completion service. The Chat Completion
13+
Service is first added to the kernel, and the kernel is passed in to the
14+
ChatCompletionAgent constructor. This sample demonstrates the basic steps to
15+
create an agent and simulate a conversation with the agent.
16+
17+
Note: if both a service and a kernel are provided, the service will be used.
1518
1619
The interaction with the agent is via the `get_response` method, which sends a
1720
user input to the agent and receives a response from the agent. The conversation
@@ -28,13 +31,11 @@
2831

2932
async def main():
3033
# 1. Create the instance of the Kernel to register an AI service
31-
service_id = "agent"
3234
kernel = Kernel()
33-
kernel.add_service(AzureChatCompletion(service_id=service_id))
35+
kernel.add_service(AzureChatCompletion())
3436

3537
# 2. Create the agent
3638
agent = ChatCompletionAgent(
37-
service_id=service_id,
3839
kernel=kernel,
3940
name="Assistant",
4041
instructions="Answer the user's questions.",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Copyright (c) Microsoft. All rights reserved.
2+
3+
import asyncio
4+
from typing import Annotated
5+
6+
from semantic_kernel.agents import ChatCompletionAgent
7+
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
8+
from semantic_kernel.contents import ChatHistory
9+
from semantic_kernel.functions import kernel_function
10+
11+
"""
12+
The following sample demonstrates how to create a chat completion agent that
13+
answers questions about a sample menu using a Semantic Kernel Plugin. The Chat
14+
Completion Service is passed directly via the ChatCompletionAgent constructor.
15+
Additionally, the plugin is supplied via the constructor.
16+
"""
17+
18+
19+
# Define a sample plugin for the sample
20+
class MenuPlugin:
21+
"""A sample Menu Plugin used for the concept sample."""
22+
23+
@kernel_function(description="Provides a list of specials from the menu.")
24+
def get_specials(self) -> Annotated[str, "Returns the specials from the menu."]:
25+
return """
26+
Special Soup: Clam Chowder
27+
Special Salad: Cobb Salad
28+
Special Drink: Chai Tea
29+
"""
30+
31+
@kernel_function(description="Provides the price of the requested menu item.")
32+
def get_item_price(
33+
self, menu_item: Annotated[str, "The name of the menu item."]
34+
) -> Annotated[str, "Returns the price of the menu item."]:
35+
return "$9.99"
36+
37+
38+
# Simulate a conversation with the agent
39+
USER_INPUTS = [
40+
"Hello",
41+
"What is the special soup?",
42+
"What does that cost?",
43+
"Thank you",
44+
]
45+
46+
47+
async def main():
48+
# 1. Create the agent
49+
agent = ChatCompletionAgent(
50+
service=AzureChatCompletion(),
51+
name="Host",
52+
instructions="Answer questions about the menu.",
53+
plugins=[MenuPlugin()],
54+
)
55+
56+
# 2. Create a chat history to hold the conversation
57+
chat_history = ChatHistory()
58+
59+
for user_input in USER_INPUTS:
60+
# 3. Add the user input to the chat history
61+
chat_history.add_user_message(user_input)
62+
print(f"# User: {user_input}")
63+
# 4. Invoke the agent for a response
64+
response = await agent.get_response(chat_history)
65+
print(f"# {response.name}: {response.content} ")
66+
67+
"""
68+
Sample output:
69+
# User: Hello
70+
# Host: Hello! How can I assist you today?
71+
# User: What is the special soup?
72+
# Host: The special soup is Clam Chowder.
73+
# User: What does that cost?
74+
# Host: The special soup, Clam Chowder, costs $9.99.
75+
# User: Thank you
76+
# Host: You're welcome! If you have any more questions, feel free to ask. Enjoy your day!
77+
"""
78+
79+
80+
if __name__ == "__main__":
81+
asyncio.run(main())

python/samples/getting_started_with_agents/chat_completion/step2_chat_completion_agent_plugin.py renamed to python/samples/getting_started_with_agents/chat_completion/step4_chat_completion_agent_plugin_with_kernel.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@
1212

1313
"""
1414
The following sample demonstrates how to create a chat completion agent that
15-
answers questions about a sample menu using a Semantic Kernel Plugin.
15+
answers questions about a sample menu using a Semantic Kernel Plugin. The Chat
16+
Completion Service is first added to the kernel, and the kernel is passed in to the
17+
ChatCompletionAgent constructor. Additionally, the plugin is supplied via the kernel.
18+
To enable auto-function calling, the prompt execution settings are retrieved from the kernel
19+
using the specified `service_id`. The function choice behavior is set to `Auto` to allow the
20+
agent to automatically execute the plugin's functions when needed.
1621
"""
1722

1823

@@ -58,7 +63,6 @@ async def main():
5863

5964
# 3. Create the agent
6065
agent = ChatCompletionAgent(
61-
service_id=service_id,
6266
kernel=kernel,
6367
name="Host",
6468
instructions="Answer questions about the menu.",
-2
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,13 @@ async def should_agent_terminate(self, agent, history):
5252
async def main():
5353
# 1. Create the reviewer agent based on the chat completion service
5454
agent_reviewer = ChatCompletionAgent(
55-
service_id="artdirector",
5655
kernel=_create_kernel_with_chat_completion("artdirector"),
5756
name=REVIEWER_NAME,
5857
instructions=REVIEWER_INSTRUCTIONS,
5958
)
6059

6160
# 2. Create the copywriter agent based on the chat completion service
6261
agent_writer = ChatCompletionAgent(
63-
service_id="copywriter",
6462
kernel=_create_kernel_with_chat_completion("copywriter"),
6563
name=COPYWRITER_NAME,
6664
instructions=COPYWRITER_INSTRUCTIONS,

python/samples/getting_started_with_agents/chat_completion/step4_kernel_function_strategies.py renamed to python/samples/getting_started_with_agents/chat_completion/step6_kernel_function_strategies.py

-2
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,13 @@ def _create_kernel_with_chat_completion(service_id: str) -> Kernel:
4747
async def main():
4848
# 1. Create the reviewer agent based on the chat completion service
4949
agent_reviewer = ChatCompletionAgent(
50-
service_id="artdirector",
5150
kernel=_create_kernel_with_chat_completion("artdirector"),
5251
name=REVIEWER_NAME,
5352
instructions=REVIEWER_INSTRUCTIONS,
5453
)
5554

5655
# 2. Create the copywriter agent based on the chat completion service
5756
agent_writer = ChatCompletionAgent(
58-
service_id="copywriter",
5957
kernel=_create_kernel_with_chat_completion("copywriter"),
6058
name=COPYWRITER_NAME,
6159
instructions=COPYWRITER_INSTRUCTIONS,
-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ async def main():
6969

7070
# 3. Create the agent
7171
agent = ChatCompletionAgent(
72-
service_id=service_id,
7372
kernel=kernel,
7473
name="Tutor",
7574
instructions=INSTRUCTION,

python/samples/getting_started_with_agents/chat_completion/step6_chat_completion_agent_logging.py renamed to python/samples/getting_started_with_agents/chat_completion/step8_chat_completion_agent_logging.py

-2
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,13 @@ def _create_kernel_with_chat_completion(service_id: str) -> Kernel:
5959
async def main():
6060
# 1. Create the reviewer agent based on the chat completion service
6161
agent_reviewer = ChatCompletionAgent(
62-
service_id="artdirector",
6362
kernel=_create_kernel_with_chat_completion("artdirector"),
6463
name=REVIEWER_NAME,
6564
instructions=REVIEWER_INSTRUCTIONS,
6665
)
6766

6867
# 2. Create the copywriter agent based on the chat completion service
6968
agent_writer = ChatCompletionAgent(
70-
service_id="copywriter",
7169
kernel=_create_kernel_with_chat_completion("copywriter"),
7270
name=COPYWRITER_NAME,
7371
instructions=COPYWRITER_INSTRUCTIONS,

0 commit comments

Comments
 (0)