-
Notifications
You must be signed in to change notification settings - Fork 4k
Python: Allow for Agents as Kernel Functions #11184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 5 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
c920c15
agent as func
moonbox3 779428e
Merge branch 'main' into agent-as-func
moonbox3 6c4b457
Support any SK agent to be used as a kernel function. Adds samples.
moonbox3 0f63ac2
PR feedback
moonbox3 3943fd3
Merge branch 'main' into agent-as-func
moonbox3 fb25e9b
Fix mypy error
moonbox3 16a0660
Merge branch 'agent-as-func' of github.com:moonbox3/semantic-kernel i…
moonbox3 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
162 changes: 162 additions & 0 deletions
162
python/samples/concepts/agents/azure_ai_agent/azure_ai_agent_as_kernel_function.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
# Copyright (c) Microsoft. All rights reserved. | ||
|
||
import asyncio | ||
|
||
from azure.identity.aio import DefaultAzureCredential | ||
|
||
from semantic_kernel import Kernel | ||
from semantic_kernel.agents import ( | ||
AzureAIAgent, | ||
AzureAIAgentSettings, | ||
ChatCompletionAgent, | ||
ChatHistoryAgentThread, | ||
) | ||
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion | ||
from semantic_kernel.filters import FunctionInvocationContext | ||
|
||
""" | ||
The following sample demonstrates how to create an Azure AI Agent Agent | ||
and a ChatCompletionAgent use them as tools available for a Triage Agent | ||
to delegate requests to the appropriate agent. A Function Invocation Filter | ||
is used to show the function call content and the function result content so the caller | ||
can see which agent was called and what the response was. | ||
""" | ||
|
||
|
||
# Define the auto function invocation filter that will be used by the kernel | ||
async def function_invocation_filter(context: FunctionInvocationContext, next): | ||
"""A filter that will be called for each function call in the response.""" | ||
if "messages" not in context.arguments: | ||
await next(context) | ||
return | ||
print(f" Agent [{context.function.name}] called with messages: {context.arguments['messages']}") | ||
await next(context) | ||
print(f" Response from agent [{context.function.name}]: {context.result.value}") | ||
|
||
|
||
async def chat(triage_agent: ChatCompletionAgent, thread: ChatHistoryAgentThread = None) -> bool: | ||
""" | ||
Continuously prompt the user for input and show the assistant's response. | ||
Type 'exit' to exit. | ||
""" | ||
try: | ||
user_input = input("User:> ") | ||
except (KeyboardInterrupt, EOFError): | ||
print("\n\nExiting chat...") | ||
return False | ||
|
||
if user_input.lower().strip() == "exit": | ||
print("\n\nExiting chat...") | ||
return False | ||
|
||
response = await triage_agent.get_response( | ||
messages=user_input, | ||
thread=thread, | ||
) | ||
|
||
if response: | ||
print(f"Agent :> {response}") | ||
|
||
return True | ||
|
||
|
||
async def main() -> None: | ||
# Create and configure the kernel. | ||
kernel = Kernel() | ||
|
||
# The filter is used for demonstration purposes to show the function invocation. | ||
kernel.add_filter("function_invocation", function_invocation_filter) | ||
|
||
ai_agent_settings = AzureAIAgentSettings.create() | ||
|
||
async with ( | ||
DefaultAzureCredential() as creds, | ||
AzureAIAgent.create_client( | ||
credential=creds, | ||
conn_str=ai_agent_settings.project_connection_string.get_secret_value(), | ||
) as client, | ||
): | ||
# Create the agent definition | ||
agent_definition = await client.agents.create_agent( | ||
model=ai_agent_settings.model_deployment_name, | ||
name="BillingAgent", | ||
instructions=( | ||
"You specialize in handling customer questions related to billing issues. " | ||
"This includes clarifying invoice charges, payment methods, billing cycles, " | ||
"explaining fees, addressing discrepancies in billed amounts, updating payment details, " | ||
"assisting with subscription changes, and resolving payment failures. " | ||
"Your goal is to clearly communicate and resolve issues specifically about payments and charges." | ||
), | ||
) | ||
|
||
# Create the AzureAI Agent | ||
billing_agent = AzureAIAgent( | ||
client=client, | ||
definition=agent_definition, | ||
) | ||
|
||
refund_agent = ChatCompletionAgent( | ||
service=AzureChatCompletion(), | ||
name="RefundAgent", | ||
instructions=( | ||
"You specialize in addressing customer inquiries regarding refunds. " | ||
"This includes evaluating eligibility for refunds, explaining refund policies, " | ||
"processing refund requests, providing status updates on refunds, handling complaints related to " | ||
"refunds, and guiding customers through the refund claim process. " | ||
"Your goal is to assist users clearly and empathetically to successfully resolve their refund-related " | ||
"concerns." | ||
), | ||
) | ||
|
||
triage_agent = ChatCompletionAgent( | ||
service=AzureChatCompletion(), | ||
kernel=kernel, | ||
name="TriageAgent", | ||
instructions=( | ||
"Your role is to evaluate the user's request and forward it to the appropriate agent based on the " | ||
"nature of the query. Forward requests about charges, billing cycles, payment methods, fees, or " | ||
"payment issues to the BillingAgent. Forward requests concerning refunds, refund eligibility, " | ||
"refund policies, or the status of refunds to the RefundAgent. Your goal is accurate identification " | ||
"of the appropriate specialist to ensure the user receives targeted assistance." | ||
), | ||
plugins=[billing_agent, refund_agent], | ||
) | ||
|
||
thread: ChatHistoryAgentThread = None | ||
|
||
print("Welcome to the chat bot!\n Type 'exit' to exit.\n Try to get some billing or refund help.") | ||
|
||
chatting = True | ||
while chatting: | ||
chatting = await chat(triage_agent, thread) | ||
|
||
""" | ||
Sample Output: | ||
|
||
I canceled my subscription but I was still charged. | ||
Agent [BillingAgent] called with messages: I canceled my subscription but I was still charged. | ||
Response from agent [BillingAgent]: I understand how concerning that can be. It's possible that the charge you | ||
received is for a billing cycle that was initiated before your cancellation was processed. Here are a few | ||
steps you can take: | ||
|
||
1. **Check Cancellation Confirmation**: Make sure you received a confirmation of your cancellation. | ||
This usually comes via email. | ||
|
||
2. **Billing Cycle**: Review your billing cycle to confirm whether the charge aligns with your subscription terms. | ||
If your billing is monthly, charges can occur even if you cancel before the period ends. | ||
|
||
3. **Contact Support**: If you believe the charge was made in error, please reach out to customer support for | ||
further clarification and to rectify the situation. | ||
|
||
If you can provide more details about the subscription and when you canceled it, I can help you further understand | ||
the charges. | ||
|
||
Agent :> It's possible that the charge you received is for a billing cycle initiated before your cancellation was | ||
processed. Please check if you received a cancellation confirmation, review your billing cycle, and contact | ||
support for further clarification if you believe the charge was made in error. If you have more details, | ||
I can help you understand the charges better. | ||
""" | ||
|
||
|
||
if __name__ == "__main__": | ||
asyncio.run(main()) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
147 changes: 147 additions & 0 deletions
147
...samples/concepts/agents/chat_completion_agent/chat_completion_agent_as_kernel_function.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
# Copyright (c) Microsoft. All rights reserved. | ||
|
||
import asyncio | ||
|
||
from semantic_kernel import Kernel | ||
from semantic_kernel.agents import ChatCompletionAgent, ChatHistoryAgentThread | ||
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion | ||
from semantic_kernel.filters import FunctionInvocationContext | ||
|
||
""" | ||
The following sample demonstrates how to create Chat Completion Agents | ||
and use them as tools available for a Triage Agent to delegate requests | ||
to the appropriate agent. A Function Invocation Filter is used to show | ||
the function call content and the function result content so the caller | ||
can see which agent was called and what the response was. | ||
""" | ||
|
||
|
||
# Define the auto function invocation filter that will be used by the kernel | ||
async def function_invocation_filter(context: FunctionInvocationContext, next): | ||
"""A filter that will be called for each function call in the response.""" | ||
if "messages" not in context.arguments: | ||
await next(context) | ||
return | ||
print(f" Agent [{context.function.name}] called with messages: {context.arguments['messages']}") | ||
await next(context) | ||
print(f" Response from agent [{context.function.name}]: {context.result.value}") | ||
|
||
|
||
# Create and configure the kernel. | ||
kernel = Kernel() | ||
|
||
# The filter is used for demonstration purposes to show the function invocation. | ||
kernel.add_filter("function_invocation", function_invocation_filter) | ||
|
||
billing_agent = ChatCompletionAgent( | ||
service=AzureChatCompletion(), | ||
name="BillingAgent", | ||
instructions=( | ||
"You specialize in handling customer questions related to billing issues. " | ||
"This includes clarifying invoice charges, payment methods, billing cycles, " | ||
"explaining fees, addressing discrepancies in billed amounts, updating payment details, " | ||
"assisting with subscription changes, and resolving payment failures. " | ||
"Your goal is to clearly communicate and resolve issues specifically about payments and charges." | ||
), | ||
) | ||
|
||
refund_agent = ChatCompletionAgent( | ||
service=AzureChatCompletion(), | ||
name="RefundAgent", | ||
instructions=( | ||
"You specialize in addressing customer inquiries regarding refunds. " | ||
"This includes evaluating eligibility for refunds, explaining refund policies, " | ||
"processing refund requests, providing status updates on refunds, handling complaints related to refunds, " | ||
"and guiding customers through the refund claim process. " | ||
"Your goal is to assist users clearly and empathetically to successfully resolve their refund-related concerns." | ||
), | ||
) | ||
|
||
triage_agent = ChatCompletionAgent( | ||
service=AzureChatCompletion(), | ||
kernel=kernel, | ||
name="TriageAgent", | ||
instructions=( | ||
"Your role is to evaluate the user's request and forward it to the appropriate agent based on the nature of " | ||
"the query. Forward requests about charges, billing cycles, payment methods, fees, or payment issues to the " | ||
"BillingAgent. Forward requests concerning refunds, refund eligibility, refund policies, or the status of " | ||
"refunds to the RefundAgent. Your goal is accurate identification of the appropriate specialist to ensure the " | ||
"user receives targeted assistance." | ||
), | ||
plugins=[billing_agent, refund_agent], | ||
) | ||
|
||
thread: ChatHistoryAgentThread = None | ||
|
||
|
||
async def chat() -> bool: | ||
""" | ||
Continuously prompt the user for input and show the assistant's response. | ||
Type 'exit' to exit. | ||
""" | ||
try: | ||
user_input = input("User:> ") | ||
except (KeyboardInterrupt, EOFError): | ||
print("\n\nExiting chat...") | ||
return False | ||
|
||
if user_input.lower().strip() == "exit": | ||
print("\n\nExiting chat...") | ||
return False | ||
|
||
response = await triage_agent.get_response( | ||
messages=user_input, | ||
thread=thread, | ||
) | ||
|
||
if response: | ||
print(f"Agent :> {response}") | ||
|
||
return True | ||
|
||
|
||
""" | ||
Sample Output: | ||
|
||
User:> I was charged twice for my subscription last month, can I get one of those payments refunded? | ||
Agent [BillingAgent] called with messages: I was charged twice for my subscription last month. | ||
Agent [RefundAgent] called with messages: Can I get one of those payments refunded? | ||
Response from agent RefundAgent: Of course, I'll be happy to help you with your refund inquiry. Could you please | ||
provide a bit more detail about the specific payment you are referring to? For instance, the item or service | ||
purchased, the transaction date, and the reason why you're seeking a refund? This will help me understand your | ||
situation better and provide you with accurate guidance regarding our refund policy and process. | ||
Response from agent BillingAgent: I'm sorry to hear about the duplicate charge. To resolve this issue, could | ||
you please provide the following details: | ||
|
||
1. The date(s) of the transaction(s). | ||
2. The last four digits of the card used for the transaction or any other payment method details. | ||
3. The subscription plan you are on. | ||
|
||
Once I have this information, I can look into the charges and help facilitate a refund for the duplicate transaction. | ||
Let me know if you have any questions in the meantime! | ||
|
||
Agent :> To address your concern about being charged twice and seeking a refund for one of those payments, please | ||
provide the following information: | ||
|
||
1. **Duplicate Charge Details**: Please share the date(s) of the transaction(s), the last four digits of the card used | ||
or details of any other payment method, and the subscription plan you are on. This information will help us verify | ||
the duplicate charge and assist you with a refund. | ||
|
||
2. **Refund Inquiry Details**: Please specify the transaction date, the item or service related to the payment you want | ||
refunded, and the reason why you're seeking a refund. This will allow us to provide accurate guidance concerning | ||
our refund policy and process. | ||
|
||
Once we have these details, we can proceed with resolving the duplicate charge and consider your refund request. If you | ||
have any more questions, feel free to ask! | ||
""" | ||
|
||
|
||
async def main() -> None: | ||
print("Welcome to the chat bot!\n Type 'exit' to exit.\n Try to get some billing or refund help.") | ||
chatting = True | ||
while chatting: | ||
chatting = await chat() | ||
|
||
|
||
if __name__ == "__main__": | ||
asyncio.run(main()) |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.