chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:21:23 +08:00
commit b957a53def
5423 changed files with 863745 additions and 0 deletions
@@ -0,0 +1,45 @@
# Chat Completion Agent Samples
The following samples demonstrate advanced usage of the `ChatCompletionAgent`.
---
## Chat History Reduction Strategies
When configuring chat history management, there are two important settings to consider:
### `reducer_msg_count`
- **Purpose:** Defines the target number of messages to retain after applying truncation or summarization.
- **Controls:** Determines how much recent conversation history is preserved, while older messages are either discarded or summarized.
- **Recommendations for adjustment:**
- **Smaller values:** Ideal for memory-constrained environments or scenarios where brief context is sufficient.
- **Larger values:** Useful when retaining extensive conversational context is critical for accurate responses or complex dialogue.
### `reducer_threshold`
- **Purpose:** Provides a buffer to prevent premature reduction when the message count slightly exceeds `reducer_msg_count`.
- **Controls:** Ensures essential message pairs (e.g., a user query and the assistants response) aren't unintentionally truncated.
- **Recommendations for adjustment:**
- **Smaller values:** Use to enforce stricter message reduction criteria, potentially truncating older message pairs sooner.
- **Larger values:** Recommended for preserving critical conversation segments, particularly in sensitive interactions involving API function calls or detailed responses.
### Interaction Between Parameters
The combination of these parameters determines **when** history reduction occurs and **how much** of the conversation is retained.
**Example:**
- If `reducer_msg_count = 10` and `reducer_threshold = 5`, message history won't be truncated until the total message count exceeds 15. This strategy maintains conversational context flexibility while respecting memory limitations.
---
## Recommendations for Effective Configuration
- **Performance-focused environments:**
- Lower `reducer_msg_count` to conserve memory and accelerate processing.
- **Context-sensitive scenarios:**
- Higher `reducer_msg_count` and `reducer_threshold` help maintain continuity across multiple interactions, crucial for multi-turn conversations or complex workflows.
- **Iterative Experimentation:**
- Start with default values (`reducer_msg_count = 10`, `reducer_threshold = 10`), and adjust according to the specific behavior and response quality required by your application.
@@ -0,0 +1,151 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from azure.identity import AzureCliCredential
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)
credential = AzureCliCredential()
billing_agent = ChatCompletionAgent(
service=AzureChatCompletion(credential=credential),
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(credential=credential),
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(credential=credential),
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())
@@ -0,0 +1,144 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from typing import Annotated
from azure.identity import AzureCliCredential
from semantic_kernel.agents import ChatCompletionAgent, ChatHistoryAgentThread
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
from semantic_kernel.contents import ChatMessageContent, FunctionCallContent, FunctionResultContent
from semantic_kernel.filters import AutoFunctionInvocationContext
from semantic_kernel.functions import kernel_function
from semantic_kernel.kernel import Kernel
"""
The following sample demonstrates how to configure the auto
function invocation filter while using a ChatCompletionAgent.
This allows the developer or user to view the function call content
and the function result content.
"""
# Define the auto function invocation filter that will be used by the kernel
async def auto_function_invocation_filter(context: AutoFunctionInvocationContext, next):
"""A filter that will be called for each function call in the response."""
# if we don't call next, it will skip this function, and go to the next one
await next(context)
if context.function.plugin_name == "menu":
context.terminate = True
# 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"
def _create_kernel_with_chat_completionand_filter() -> Kernel:
"""A helper function to create a kernel with a chat completion service and a filter."""
kernel = Kernel()
kernel.add_service(AzureChatCompletion(credential=AzureCliCredential()))
kernel.add_filter("auto_function_invocation", auto_function_invocation_filter)
kernel.add_plugin(plugin=MenuPlugin(), plugin_name="menu")
return kernel
def _write_content(content: ChatMessageContent) -> None:
"""Write the content to the console based on the content type."""
last_item_type = type(content.items[-1]).__name__ if content.items else "(empty)"
message_content = ""
if isinstance(last_item_type, FunctionCallContent):
message_content = f"tool request = {content.items[-1].function_name}"
elif isinstance(last_item_type, FunctionResultContent):
message_content = f"function result = {content.items[-1].result}"
else:
message_content = str(content.items[-1])
print(f"[{last_item_type}] {content.role} : '{message_content}'")
async def main():
# 1. Create the agent with a kernel instance that contains
# the auto function invocation filter and the AI service
agent = ChatCompletionAgent(
kernel=_create_kernel_with_chat_completionand_filter(),
name="Host",
instructions="Answer questions about the menu.",
)
# 2. Define the thread
thread: ChatHistoryAgentThread = None
user_inputs = [
"Hello",
"What is the special soup?",
"What is the special drink?",
"Thank you",
]
for user_input in user_inputs:
print(f"# User: '{user_input}'")
# 3. Get the response from the agent
response = await agent.get_response(messages=user_input, thread=thread)
thread = response.thread
_write_content(response)
print("================================")
print("CHAT HISTORY")
print("================================")
# 4. Print out the chat history to view the different types of messages
async for message in thread.get_messages():
_write_content(message)
"""
Sample output:
# AuthorRole.USER: 'Hello'
[TextContent] AuthorRole.ASSISTANT : 'Hello! How can I assist you today?'
# AuthorRole.USER: 'What is the special soup?'
[FunctionResultContent] AuthorRole.TOOL : '
Special Soup: Clam Chowder
Special Salad: Cobb Salad
Special Drink: Chai Tea
'
# AuthorRole.USER: 'What is the special drink?'
[TextContent] AuthorRole.ASSISTANT : 'The special drink is Chai Tea.'
# AuthorRole.USER: 'Thank you'
[TextContent] AuthorRole.ASSISTANT : 'You're welcome! If you have any more questions or need assistance with
anything else, feel free to ask!'
================================
CHAT HISTORY
================================
[TextContent] AuthorRole.USER : 'Hello'
[TextContent] AuthorRole.ASSISTANT : 'Hello! How can I assist you today?'
[TextContent] AuthorRole.USER : 'What is the special soup?'
[FunctionCallContent] AuthorRole.ASSISTANT : 'menu-get_specials({})'
[FunctionResultContent] AuthorRole.TOOL : '
Special Soup: Clam Chowder
Special Salad: Cobb Salad
Special Drink: Chai Tea
'
[TextContent] AuthorRole.USER : 'What is the special drink?'
[TextContent] AuthorRole.ASSISTANT : 'The special drink is Chai Tea.'
[TextContent] AuthorRole.USER : 'Thank you'
[TextContent] AuthorRole.ASSISTANT : 'You're welcome! If you have any more questions or need assistance with
anything else, feel free to ask!'
"""
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,112 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from typing import Annotated
from azure.identity import AzureCliCredential
from semantic_kernel.agents.chat_completion.chat_completion_agent import ChatCompletionAgent, ChatHistoryAgentThread
from semantic_kernel.connectors.ai.open_ai.services.azure_chat_completion import AzureChatCompletion
from semantic_kernel.contents import FunctionCallContent, FunctionResultContent
from semantic_kernel.contents.chat_message_content import ChatMessageContent
from semantic_kernel.functions import kernel_function
"""
The following sample demonstrates how to create a chat completion agent
and use it with functions. In order to answer user questions, the
agent internally uses the functions. These internal steps are returned
to the user as part of the agent's response. Thus, the invoke method
configures a message callback to receive the agent's internal messages.
The agent is configured to use a plugin that provides a list of
specials from the menu and the price of the requested menu item.
"""
# 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"
# This callback function will be called for each intermediate message
# Which will allow one to handle FunctionCallContent and FunctionResultContent
# If the callback is not provided, the agent will return the final response
# with no intermediate tool call steps.
async def handle_intermediate_steps(message: ChatMessageContent) -> None:
for item in message.items or []:
if isinstance(item, FunctionCallContent):
print(f"Function Call:> {item.name} with arguments: {item.arguments}")
elif isinstance(item, FunctionResultContent):
print(f"Function Result:> {item.result} for function: {item.name}")
else:
print(f"{message.role}: {message.content}")
async def main() -> None:
agent = ChatCompletionAgent(
service=AzureChatCompletion(credential=AzureCliCredential()),
name="Assistant",
instructions="Answer questions about the menu.",
plugins=[MenuPlugin()],
)
# Create a thread for the agent
# If no thread is provided, a new thread will be
# created and returned with the initial response
thread: ChatHistoryAgentThread = None
user_inputs = [
"Hello",
"What is the special soup?",
"How much does that cost?",
"Thank you",
]
for user_input in user_inputs:
print(f"# User: '{user_input}'")
async for response in agent.invoke(
messages=user_input,
thread=thread,
on_intermediate_message=handle_intermediate_steps,
):
print(f"# {response.role}: {response}")
thread = response.thread
"""
Sample Output:
# User: 'Hello'
# AuthorRole.ASSISTANT: Hi there! How can I assist you today?
# User: 'What is the special soup?'
Function Call:> MenuPlugin-get_specials with arguments: {}
Function Result:>
Special Soup: Clam Chowder
Special Salad: Cobb Salad
Special Drink: Chai Tea
for function: MenuPlugin-get_specials
# AuthorRole.ASSISTANT: The special soup today is Clam Chowder. Would you like to know anything else from the menu?
# User: 'How much does that cost?'
Function Call:> MenuPlugin-get_item_price with arguments: {"menu_item":"Clam Chowder"}
Function Result:> $9.99 for function: MenuPlugin-get_item_price
# AuthorRole.ASSISTANT: The Clam Chowder costs $9.99. Would you like to know more about the menu or anything else?
# User: 'Thank you'
# AuthorRole.ASSISTANT: You're welcome! If you have any more questions, feel free to ask. Enjoy your day!
"""
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,114 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from typing import Annotated
from azure.identity import AzureCliCredential
from semantic_kernel.agents import ChatCompletionAgent, ChatHistoryAgentThread
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
from semantic_kernel.contents import ChatMessageContent, FunctionCallContent, FunctionResultContent
from semantic_kernel.functions import kernel_function
"""
The following sample demonstrates how to create a chat completion agent
and use it with streaming responses. Additionally, the invoke_stream
configures a message callback to receive fully formed messages once
the streaming invocation is complete. The agent is configured to use
a plugin that provides a list of specials from the menu and the price
of the requested menu item.
"""
# 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"
# This callback function will be called for each intermediate message
# Which will allow one to handle FunctionCallContent and FunctionResultContent
# If the callback is not provided, the agent will return the final response
# with no intermediate tool call steps.
async def handle_streaming_intermediate_steps(message: ChatMessageContent) -> None:
for item in message.items or []:
if isinstance(item, FunctionCallContent):
print(f"Function Call:> {item.name} with arguments: {item.arguments}")
elif isinstance(item, FunctionResultContent):
print(f"Function Result:> {item.result} for function: {item.name}")
else:
print(f"{message.role}: {message.content}")
async def main() -> None:
agent = ChatCompletionAgent(
service=AzureChatCompletion(credential=AzureCliCredential()),
name="Assistant",
instructions="Answer questions about the menu.",
plugins=[MenuPlugin()],
)
# Create a thread for the agent
# If no thread is provided, a new thread will be
# created and returned with the initial response
thread: ChatHistoryAgentThread = None
user_inputs = [
"Hello",
"What is the special soup?",
"How much does that cost?",
"Thank you",
]
for user_input in user_inputs:
print(f"\n# User: '{user_input}'")
async for response in agent.invoke_stream(
messages=user_input,
thread=thread,
on_intermediate_message=handle_streaming_intermediate_steps,
):
if response.content:
print(response.content, end="", flush=True)
thread = response.thread
print()
"""
Sample Output:
# User: 'Hello'
Hello! How can I assist you today?
# User: 'What is the special soup?'
Function Call:> MenuPlugin-get_specials with arguments: {}
Function Result:>
Special Soup: Clam Chowder
Special Salad: Cobb Salad
Special Drink: Chai Tea
for function: MenuPlugin-get_specials
The special soup today is Clam Chowder. Is there anything else you'd like to know?
# User: 'How much does that cost?'
Function Call:> MenuPlugin-get_item_price with arguments: {"menu_item":"Clam Chowder"}
Function Result:> $9.99 for function: MenuPlugin-get_item_price
The Clam Chowder costs $9.99. Would you like to know anything else about the menu?
# User: 'Thank you'
You're welcome! If you have any more questions, feel free to ask. Have a great day!
"""
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,99 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from azure.identity import AzureCliCredential
from semantic_kernel.agents import ChatCompletionAgent, ChatHistoryAgentThread
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
from semantic_kernel.functions import KernelArguments
from semantic_kernel.prompt_template import PromptTemplateConfig
"""
The following sample demonstrates how to create a chat completion
agent using Azure OpenAI within Semantic Kernel.
It uses parameterized prompts and shows how to swap between
"semantic-kernel," "jinja2," and "handlebars" template formats,
This sample highlights the agent's chat history conversation
is managed and how kernel arguments are passed in and used.
"""
# Define the inputs and styles to be used in the agent
inputs = [
("Home cooking is great.", None),
("Talk about world peace.", "iambic pentameter"),
("Say something about doing your best.", "e. e. cummings"),
("What do you think about having fun?", "old school rap"),
]
async def invoke_chat_completion_agent(agent: ChatCompletionAgent, inputs):
"""Invokes the given agent with each (input, style) in inputs."""
thread: ChatHistoryAgentThread = None
for user_input, style in inputs:
print(f"[USER]: {user_input}\n")
# If style is specified, override the 'style' argument
argument_overrides = None
if style:
argument_overrides = KernelArguments(style=style)
# Stream agent responses
async for response in agent.invoke_stream(messages=user_input, thread=thread, arguments=argument_overrides):
print(f"{response.content}", end="", flush=True)
thread = response.thread
print()
async def invoke_agent_with_template(template_str: str, template_format: str, default_style: str = "haiku"):
"""Creates an agent with the specified template and format, then invokes it using invoke_chat_completion_agent."""
# Configure the prompt template
prompt_config = PromptTemplateConfig(template=template_str, template_format=template_format)
agent = ChatCompletionAgent(
service=AzureChatCompletion(credential=AzureCliCredential()),
name="MyPoetAgent",
prompt_template_config=prompt_config,
arguments=KernelArguments(style=default_style),
)
await invoke_chat_completion_agent(agent, inputs)
async def main():
# 1) Using "semantic-kernel" format
print("\n===== SEMANTIC-KERNEL FORMAT =====\n")
semantic_kernel_template = """
Write a one verse poem on the requested topic in the style of {{$style}}.
Always state the requested style of the poem.
"""
await invoke_agent_with_template(
template_str=semantic_kernel_template,
template_format="semantic-kernel",
default_style="haiku",
)
# 2) Using "jinja2" format
print("\n===== JINJA2 FORMAT =====\n")
jinja2_template = """
Write a one verse poem on the requested topic in the style of {{style}}.
Always state the requested style of the poem.
"""
await invoke_agent_with_template(template_str=jinja2_template, template_format="jinja2", default_style="haiku")
# 3) Using "handlebars" format
print("\n===== HANDLEBARS FORMAT =====\n")
handlebars_template = """
Write a one verse poem on the requested topic in the style of {{style}}.
Always state the requested style of the poem.
"""
await invoke_agent_with_template(
template_str=handlebars_template, template_format="handlebars", default_style="haiku"
)
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,112 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from typing import Annotated
from azure.identity import AzureCliCredential
from semantic_kernel.agents import ChatCompletionAgent, ChatHistoryAgentThread
from semantic_kernel.connectors.ai.completion_usage import CompletionUsage
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
from semantic_kernel.functions import kernel_function
"""
The following sample demonstrates how to create a chat completion agent
and use it with streaming responses. It also shows how to track token
usage during the streaming process.
"""
# 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"
async def main() -> None:
agent = ChatCompletionAgent(
service=AzureChatCompletion(credential=AzureCliCredential()),
name="Assistant",
instructions="Answer questions about the menu.",
plugins=[MenuPlugin()],
)
# Create a thread for the agent
# If no thread is provided, a new thread will be
# created and returned with the initial response
thread: ChatHistoryAgentThread = None
user_inputs = [
"Hello",
"What is the special soup?",
"How much does that cost?",
"Thank you",
]
completion_usage = CompletionUsage()
for user_input in user_inputs:
print(f"\n# User: '{user_input}'")
async for response in agent.invoke_stream(
messages=user_input,
thread=thread,
):
if response.content:
print(response.content, end="", flush=True)
if response.metadata.get("usage"):
completion_usage += response.metadata["usage"]
print(f"\nStreaming Usage: {response.metadata['usage']}")
thread = response.thread
print()
# Print the completion usage
print(f"\nStreaming Total Completion Usage: {completion_usage.model_dump_json(indent=4)}")
"""
Sample Output:
# User: 'Hello'
Hello! How can I help you with the menu today?
# User: 'What is the special soup?'
The special soup today is Clam Chowder. Would you like more details or are you interested in something else from
the menu?
# User: 'How much does that cost?'
The Clam Chowder special soup costs $9.99. Would you like to add it to your order or ask about something else?
# User: 'Thank you'
You're welcome! If you have any more questions or need help with the menu, just let me know. Enjoy your meal!
Streaming Total Completion Usage: {
"prompt_tokens": 1150,
"prompt_tokens_details": {
"audio_tokens": 0,
"cached_tokens": 0
},
"completion_tokens": 134,
"completion_tokens_details": {
"accepted_prediction_tokens": 0,
"audio_tokens": 0,
"reasoning_tokens": 0,
"rejected_prediction_tokens": 0
}
}
"""
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,90 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from azure.identity import AzureCliCredential
from semantic_kernel.agents import AgentGroupChat, ChatCompletionAgent
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
from semantic_kernel.contents import ChatHistorySummarizationReducer
"""
The following sample demonstrates how to implement a chat history
reducer as part of the Semantic Kernel Agent Framework. For this sample,
the ChatCompletionAgent with an AgentGroupChat is used. The Chat History
Reducer is a Summary Reducer. View the README for more information on
how to use the reducer and what each parameter does.
Note: This sample use the `AgentGroupChat` feature of Semantic Kernel, which is
no longer maintained. For a replacement, consider using the `GroupChatOrchestration`.
Read more about the `GroupChatOrchestration` here:
https://learn.microsoft.com/semantic-kernel/frameworks/agent/agent-orchestration/group-chat?pivots=programming-language-python
Here is a migration guide from `AgentGroupChat` to `GroupChatOrchestration`:
https://learn.microsoft.com/semantic-kernel/support/migration/group-chat-orchestration-migration-guide?pivots=programming-language-python
"""
async def main():
"""
Single-function approach that shows the same chat reducer behavior
while preserving all original logic and code lines (now commented).
"""
# Setup necessary parameters
reducer_msg_count = 10
reducer_threshold = 10
credential = AzureCliCredential()
# Create a summarization reducer and clear its history
history_summarization_reducer = ChatHistorySummarizationReducer(
service=AzureChatCompletion(credential=credential),
target_count=reducer_msg_count,
threshold_count=reducer_threshold,
)
history_summarization_reducer.clear()
# Create our agent
agent = ChatCompletionAgent(
name="NumeroTranslator",
instructions="Add one to the latest user number and spell it in Spanish without explanation.",
service=AzureChatCompletion(credential=credential),
)
# Create a group chat using the reducer
chat = AgentGroupChat(chat_history=history_summarization_reducer)
# Simulate user messages
message_count = 50 # Number of messages to simulate
for index in range(1, message_count, 2):
# Add user message to the chat
await chat.add_chat_message(message=str(index))
print(f"# User: '{index}'")
# Attempt to reduce history
is_reduced = await chat.reduce_history()
if is_reduced:
print(f"@ History reduced to {len(history_summarization_reducer.messages)} messages.")
# Invoke the agent and display responses
async for message in chat.invoke(agent):
print(f"# {message.role} - {message.name or '*'}: '{message.content}'")
# Retrieve messages
msgs = []
async for m in chat.get_chat_messages(agent):
msgs.append(m)
print(f"@ Message Count: {len(msgs)}\n")
# If a reduction happened and we use summarization, print the summary
if is_reduced:
for msg in msgs:
if msg.metadata and msg.metadata.get("__summary__"):
print(f"\tSummary: {msg.content}")
break
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,72 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import logging
from azure.identity import AzureCliCredential
from semantic_kernel.agents import ChatCompletionAgent, ChatHistoryAgentThread
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
from semantic_kernel.contents import ChatHistorySummarizationReducer
"""
The following sample demonstrates how to implement a truncation chat
history reducer as part of the Semantic Kernel Agent Framework. For
this sample, a single ChatCompletionAgent is used.
"""
# Initialize the logger for debugging and information messages
logger = logging.getLogger(__name__)
async def main():
# Setup necessary parameters
reducer_msg_count = 10
reducer_threshold = 10
credential = AzureCliCredential()
# Create a summarization reducer
history_summarization_reducer = ChatHistorySummarizationReducer(
service=AzureChatCompletion(credential=credential),
target_count=reducer_msg_count,
threshold_count=reducer_threshold,
)
thread: ChatHistoryAgentThread = ChatHistoryAgentThread(chat_history=history_summarization_reducer)
# Create our agent
agent = ChatCompletionAgent(
name="NumeroTranslator",
instructions="Add one to the latest user number and spell it in Spanish without explanation.",
service=AzureChatCompletion(credential=credential),
)
# Number of messages to simulate
message_count = 50
for index in range(1, message_count + 1, 2):
print(f"# User: '{index}'")
# Get agent response and store it
response = await agent.get_response(messages=str(index), thread=thread)
thread = response.thread
print(f"# Agent - {response.name}: '{response.content}'")
# Attempt reduction
is_reduced = await thread.reduce()
if is_reduced:
print(f"@ History reduced to {len(thread)} messages.")
print(f"@ Message Count: {len(thread)}\n")
# If reduced, print summary if present
if is_reduced:
async for msg in thread.get_messages():
if msg.metadata and msg.metadata.get("__summary__"):
print(f"\tSummary: {msg.content}")
break
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,113 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
from typing import Annotated
from azure.identity import AzureCliCredential
from semantic_kernel.agents import ChatCompletionAgent, ChatHistoryAgentThread
from semantic_kernel.connectors.ai.completion_usage import CompletionUsage
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
from semantic_kernel.functions import kernel_function
"""
The following sample demonstrates how to create a chat completion agent
and use it with non-streaming responses. It also shows how to track token
usage during agent invoke.
"""
# 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"
async def main() -> None:
agent = ChatCompletionAgent(
service=AzureChatCompletion(credential=AzureCliCredential()),
name="Assistant",
instructions="Answer questions about the menu.",
plugins=[MenuPlugin()],
)
# Create a thread for the agent
# If no thread is provided, a new thread will be
# created and returned with the initial response
thread: ChatHistoryAgentThread = None
user_inputs = [
"Hello",
"What is the special soup?",
"How much does that cost?",
"Thank you",
]
completion_usage = CompletionUsage()
for user_input in user_inputs:
print(f"\n# User: '{user_input}'")
async for response in agent.invoke(
messages=user_input,
thread=thread,
):
if response.content:
print(response.content)
if response.metadata.get("usage"):
completion_usage += response.metadata["usage"]
thread = response.thread
print()
# Print the completion usage
print(f"\nNon-Streaming Total Completion Usage: {completion_usage.model_dump_json(indent=4)}")
"""
Sample Output:
# User: 'Hello'
Hello! How can I help you with the menu today?
# User: 'What is the special soup?'
The special soup today is Clam Chowder. Would you like to know more about it or see the other specials?
# User: 'How much does that cost?'
The Clam Chowder special costs $9.99. Would you like to add that to your order or need more information?
# User: 'Thank you'
You're welcome! If you have any more questions or need help with the menu, just let me know. Enjoy your day!
Non-Streaming Total Completion Usage: {
"prompt_tokens": 772,
"prompt_tokens_details": {
"audio_tokens": 0,
"cached_tokens": 0
},
"completion_tokens": 92,
"completion_tokens_details": {
"accepted_prediction_tokens": 0,
"audio_tokens": 0,
"reasoning_tokens": 0,
"rejected_prediction_tokens": 0
}
}
"""
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,84 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import logging
from azure.identity import AzureCliCredential
from semantic_kernel.agents import AgentGroupChat, ChatCompletionAgent
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
from semantic_kernel.contents import ChatHistoryTruncationReducer
"""
The following sample demonstrates how to implement a chat history
reducer as part of the Semantic Kernel Agent Framework. For this sample,
the ChatCompletionAgent with an AgentGroupChat is used. The Chat History
Reducer is a Truncation Reducer. View the README for more information on
how to use the reducer and what each parameter does.
Note: This sample use the `AgentGroupChat` feature of Semantic Kernel, which is
no longer maintained. For a replacement, consider using the `GroupChatOrchestration`.
Read more about the `GroupChatOrchestration` here:
https://learn.microsoft.com/semantic-kernel/frameworks/agent/agent-orchestration/group-chat?pivots=programming-language-python
Here is a migration guide from `AgentGroupChat` to `GroupChatOrchestration`:
https://learn.microsoft.com/semantic-kernel/support/migration/group-chat-orchestration-migration-guide?pivots=programming-language-python
"""
# Initialize the logger for debugging and information messages
logger = logging.getLogger(__name__)
async def main():
"""
Single-function approach that shows the same chat reducer behavior
while preserving all original logic and code lines (now commented).
"""
# Setup necessary parameters
reducer_msg_count = 10
reducer_threshold = 10
# Create a truncation reducer and clear its history
history_truncation_reducer = ChatHistoryTruncationReducer(
target_count=reducer_msg_count, threshold_count=reducer_threshold
)
history_truncation_reducer.clear()
# Create our agent
agent = ChatCompletionAgent(
name="NumeroTranslator",
instructions="Add one to the latest user number and spell it in Spanish without explanation.",
service=AzureChatCompletion(credential=AzureCliCredential()),
)
# Create a group chat using the reducer
chat = AgentGroupChat(chat_history=history_truncation_reducer)
# Simulate user messages
message_count = 50 # Number of messages to simulate
for index in range(1, message_count, 2):
# Add user message to the chat
await chat.add_chat_message(message=str(index))
print(f"# User: '{index}'")
# Attempt to reduce history
is_reduced = await chat.reduce_history()
if is_reduced:
print(f"@ History reduced to {len(history_truncation_reducer.messages)} messages.")
# Invoke the agent and display responses
async for message in chat.invoke(agent):
print(f"# {message.role} - {message.name or '*'}: '{message.content}'")
# Retrieve messages
msgs = []
async for m in chat.get_chat_messages(agent):
msgs.append(m)
print(f"@ Message Count: {len(msgs)}\n")
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,67 @@
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import logging
from azure.identity import AzureCliCredential
from semantic_kernel.agents import (
ChatCompletionAgent,
)
from semantic_kernel.agents.chat_completion.chat_completion_agent import ChatHistoryAgentThread
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion
from semantic_kernel.contents import (
ChatHistoryTruncationReducer,
)
"""
The following sample demonstrates how to implement a truncation chat
history reducer as part of the Semantic Kernel Agent Framework. For
this sample, a single ChatCompletionAgent is used.
"""
# Initialize the logger for debugging and information messages
logger = logging.getLogger(__name__)
async def main():
# Setup necessary parameters
reducer_msg_count = 10
reducer_threshold = 10
# Create a truncation reducer
history_truncation_reducer = ChatHistoryTruncationReducer(
target_count=reducer_msg_count,
threshold_count=reducer_threshold,
)
thread: ChatHistoryAgentThread = ChatHistoryAgentThread(chat_history=history_truncation_reducer)
# Create our agent
agent = ChatCompletionAgent(
name="NumeroTranslator",
instructions="Add one to the latest user number and spell it in Spanish without explanation.",
service=AzureChatCompletion(credential=AzureCliCredential()),
)
# Number of messages to simulate
message_count = 50
for index in range(1, message_count + 1, 2):
print(f"# User: '{index}'")
# Get agent response and store it
response = await agent.get_response(messages=str(index), thread=thread)
thread = response.thread
print(f"# Agent - {response.name}: '{response.content}'")
# Attempt reduction
is_reduced = await thread.reduce()
if is_reduced:
print(f"@ History reduced to {len(thread)} messages.")
print(f"@ Message Count: {len(history_truncation_reducer.messages)}\n")
if __name__ == "__main__":
asyncio.run(main())