Compare commits

..
Author SHA1 Message Date
Shawn HenryandGitHub b59a22d4d7 Revise important notes on third-party system usage
Updated important notes regarding third-party systems and responsibilities when using the Microsoft Agent Framework.
2026-03-31 14:46:08 -07:00
15 changed files with 90 additions and 102 deletions
+6 -2
View File
@@ -206,5 +206,9 @@ The samples typically read configuration from environment variables. Common requ
- [Architectural Decision Records](./docs/decisions)
## Important Notes
If you use the Microsoft Agent Framework to build applications that operate with third-party servers or agents, you do so at your own risk. We recommend reviewing all data being shared with third-party servers or agents and being cognizant of third-party practices for retention and location of data. It is your responsibility to manage whether your data will flow outside of your organization's Azure compliance and geographic boundaries and any related implications.
> [!IMPORTANT]
> If you use Microsoft Agent Framework to build applications that operate with any third-party servers, agents, code, or non-Azure Direct models (“Third-Party Systems”), you do so at your own risk. Third-Party Systems are Non-Microsoft Products under the Microsoft Product Terms and are governed by their own third-party license terms. You are responsible for any usage and associated costs.
>
> We recommend reviewing all data being shared with and received from Third-Party Systems and being cognizant of third-party practices for handling, sharing, retention and location of data. It is your responsibility to manage whether your data will flow outside of your organization’s Azure compliance and geographic boundaries and any related implications, and that appropriate permissions, boundaries and approvals are provisioned.
>
> You are responsible for carefully reviewing and testing applications you build using Microsoft Agent Framework in the context of your specific use cases, and making all appropriate decisions and customizations. This includes implementing your own responsible AI mitigations such as metaprompt, content filters, or other safety systems, and ensuring your applications meet appropriate quality, reliability, security, and trustworthiness standards. See also: [Transparency FAQ](./TRANSPARENCY_FAQ.md.md)
+4 -4
View File
@@ -6,9 +6,9 @@ This gallery helps AutoGen developers move to the Microsoft Agent Framework (AF)
### Single-Agent Parity
- [01_basic_agent.py](single_agent/01_basic_agent.py) — Minimal AutoGen `AssistantAgent` and AF `Agent` comparison.
- [02_agent_with_tool.py](single_agent/02_agent_with_tool.py) — Function tool integration in both SDKs.
- [03_agent_thread_and_stream.py](single_agent/03_agent_thread_and_stream.py) — Session management and streaming responses.
- [01_basic_assistant_agent.py](single_agent/01_basic_assistant_agent.py) — Minimal AutoGen `AssistantAgent` and AF `Agent` comparison.
- [02_assistant_agent_with_tool.py](single_agent/02_assistant_agent_with_tool.py) — Function tool integration in both SDKs.
- [03_assistant_agent_thread_and_stream.py](single_agent/03_assistant_agent_thread_and_stream.py) — Session management and streaming responses.
- [04_agent_as_tool.py](single_agent/04_agent_as_tool.py) — Using agents as tools (hierarchical agent pattern) and streaming with tools.
### Multi-Agent Orchestration
@@ -35,7 +35,7 @@ Each script is fully async and the `main()` routine runs both implementations ba
From the repository root:
```bash
python samples/autogen-migration/single_agent/01_basic_agent.py
python samples/autogen-migration/single_agent/01_basic_assistant_agent.py
```
Every script accepts no CLI arguments and will first call the AutoGen implementation, followed by the AF version. Adjust the prompt or credentials inside the file as necessary before running.
@@ -65,7 +65,7 @@ async def run_agent_framework() -> None:
from agent_framework.openai import OpenAIChatClient
from agent_framework.orchestrations import SequentialBuilder
client = OpenAIChatClient(model="gpt-4.1-mini")
client = OpenAIChatClient(model_id="gpt-4.1-mini")
# Create specialized agents
researcher = Agent(
@@ -112,7 +112,7 @@ async def run_agent_framework_with_cycle() -> None:
)
from agent_framework.openai import OpenAIChatClient
client = OpenAIChatClient(model="gpt-4.1-mini")
client = OpenAIChatClient(model_id="gpt-4.1-mini")
# Create specialized agents
researcher = Agent(
@@ -76,7 +76,7 @@ async def run_agent_framework() -> None:
from agent_framework.openai import OpenAIChatClient
from agent_framework.orchestrations import MagenticBuilder
client = OpenAIChatClient(model="gpt-4.1-mini")
client = OpenAIChatClient(model_id="gpt-4.1-mini")
# Create specialized agents
researcher = Agent(
@@ -23,7 +23,7 @@ load_dotenv()
async def run_semantic_kernel() -> None:
from semantic_kernel.agents import ChatCompletionAgent
from semantic_kernel.agents import ChatCompletionAgent, ChatHistoryAgentThread
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from semantic_kernel.functions import kernel_function
@@ -39,7 +39,11 @@ async def run_semantic_kernel() -> None:
instructions="Answer menu questions accurately.",
plugins=[SpecialsPlugin()],
)
response = await agent.get_response("What soup can I order today?")
thread = ChatHistoryAgentThread()
response = await agent.get_response(
messages="What soup can I order today?",
thread=thread,
)
print("[SK]", response.message.content)
@@ -58,7 +62,12 @@ async def run_agent_framework() -> None:
instructions="Answer menu questions accurately.",
tools=[specials],
)
reply = await chat_agent.run("What soup can I order today?")
session = chat_agent.create_session()
reply = await chat_agent.run(
"What soup can I order today?",
session=session,
tool_choice="auto",
)
print("[AF]", reply.text)
@@ -22,13 +22,10 @@ async def run_semantic_kernel() -> None:
from semantic_kernel.agents import OpenAIResponsesAgent
from semantic_kernel.connectors.ai.open_ai import OpenAISettings
openai_settings = OpenAISettings()
assert openai_settings.responses_model_id is not None, "Responses model ID must be set in OpenAISettings"
client = OpenAIResponsesAgent.create_client()
# SK response agents wrap OpenAI's hosted Responses API.
agent = OpenAIResponsesAgent(
ai_model_id=openai_settings.responses_model_id,
ai_model=OpenAISettings().responses_model_id,
client=client,
instructions="Answer in one concise sentence.",
name="Expert",
@@ -28,13 +28,10 @@ async def run_semantic_kernel() -> None:
def add(self, a: float, b: float) -> float:
return a + b
openai_settings = OpenAISettings()
assert openai_settings.responses_model_id is not None, "Responses model ID must be set in OpenAISettings"
client = OpenAIResponsesAgent.create_client()
# Plugins advertise callable tools to the Responses agent.
agent = OpenAIResponsesAgent(
ai_model_id=openai_settings.responses_model_id,
ai_model=OpenAISettings().responses_model_id,
client=client,
instructions="Use the add tool when math is required.",
name="MathExpert",
@@ -29,17 +29,14 @@ async def run_semantic_kernel() -> None:
from semantic_kernel.agents import OpenAIResponsesAgent
from semantic_kernel.connectors.ai.open_ai import OpenAISettings
openai_settings = OpenAISettings()
assert openai_settings.responses_model_id is not None, "Responses model ID must be set in OpenAISettings"
client = OpenAIResponsesAgent.create_client()
# response_format requests schema-constrained output from the model.
agent = OpenAIResponsesAgent(
ai_model_id=openai_settings.responses_model_id,
ai_model=OpenAISettings().responses_model_id,
client=client,
instructions="Return launch briefs as structured JSON.",
name="ProductMarketer",
text=OpenAIResponsesAgent.configure_response_format(ReleaseBrief), # type: ignore
text=OpenAIResponsesAgent.configure_response_format(ReleaseBrief),
)
response = await agent.get_response(
"Draft a launch brief for the Contoso Note app.",
@@ -55,7 +55,7 @@ def build_semantic_kernel_agents() -> list[ChatCompletionAgent]:
async def run_semantic_kernel_example(prompt: str) -> Sequence[ChatMessageContent]:
concurrent_orchestration = ConcurrentOrchestration(members=build_semantic_kernel_agents()) # type: ignore
concurrent_orchestration = ConcurrentOrchestration(members=build_semantic_kernel_agents())
runtime = InProcessRuntime()
runtime.start()
@@ -91,14 +91,12 @@ def _print_semantic_kernel_outputs(outputs: Sequence[ChatMessageContent]) -> Non
async def run_agent_framework_example(prompt: str) -> Sequence[list[Message]]:
client = OpenAIChatCompletionClient(credential=AzureCliCredential())
physics = Agent(
client=client,
physics = Agent(client=client,
instructions=("You are an expert in physics. Answer questions from a physics perspective."),
name="physics",
)
chemistry = Agent(
client=client,
chemistry = Agent(client=client,
instructions=("You are an expert in chemistry. Answer questions from a chemistry perspective."),
name="chemistry",
)
@@ -16,8 +16,8 @@ import sys
from collections.abc import Sequence
from typing import Any, cast
from agent_framework import Agent, AgentResponseUpdate, Message
from agent_framework.openai import OpenAIChatCompletionClient
from agent_framework import Agent, Message
from agent_framework.foundry import FoundryChatClient
from agent_framework.orchestrations import GroupChatBuilder
from azure.identity import AzureCliCredential
from dotenv import load_dotenv
@@ -82,6 +82,9 @@ def build_semantic_kernel_agents() -> list[ChatCompletionAgent]:
class ChatCompletionGroupChatManager(GroupChatManager):
"""Group chat manager that delegates orchestration decisions to an Azure OpenAI deployment."""
service: ChatCompletionClientBase
topic: str
termination_prompt: str = (
"You are coordinating a conversation about '{{$topic}}'. "
"Decide if the discussion has produced a solid answer. "
@@ -101,11 +104,8 @@ class ChatCompletionGroupChatManager(GroupChatManager):
)
def __init__(self, *, topic: str, service: ChatCompletionClientBase, max_rounds: int | None = None) -> None:
super().__init__(max_rounds=max_rounds)
super().__init__(topic=topic, service=service, max_rounds=max_rounds)
self._round_robin_index = 0
self._topic = topic
self._service = service
async def _render_prompt(self, template: str, **kwargs: Any) -> str:
prompt_template = KernelPromptTemplate(prompt_template_config=PromptTemplateConfig(template=template))
@@ -117,7 +117,7 @@ class ChatCompletionGroupChatManager(GroupChatManager):
@override
async def should_terminate(self, chat_history: ChatHistory) -> BooleanResult:
rendered_prompt = await self._render_prompt(self.termination_prompt, topic=self._topic)
rendered_prompt = await self._render_prompt(self.termination_prompt, topic=self.topic)
chat_history.messages.insert(
0,
ChatMessageContent(role=AuthorRole.SYSTEM, content=rendered_prompt),
@@ -126,11 +126,11 @@ class ChatCompletionGroupChatManager(GroupChatManager):
ChatMessageContent(role=AuthorRole.USER, content="Decide if the discussion is complete."),
)
response = await self._service.get_chat_message_content(
response = await self.service.get_chat_message_content(
chat_history,
settings=PromptExecutionSettings(response_format=BooleanResult),
)
return BooleanResult.model_validate_json(response.content) # type: ignore
return BooleanResult.model_validate_json(response.content)
@override
async def select_next_agent(
@@ -140,7 +140,7 @@ class ChatCompletionGroupChatManager(GroupChatManager):
) -> StringResult:
rendered_prompt = await self._render_prompt(
self.selection_prompt,
topic=self._topic,
topic=self.topic,
participants=", ".join(participant_descriptions.keys()),
)
chat_history.messages.insert(
@@ -151,18 +151,18 @@ class ChatCompletionGroupChatManager(GroupChatManager):
ChatMessageContent(role=AuthorRole.USER, content="Pick the next participant to speak."),
)
response = await self._service.get_chat_message_content(
response = await self.service.get_chat_message_content(
chat_history,
settings=PromptExecutionSettings(response_format=StringResult),
)
result = StringResult.model_validate_json(response.content) # type: ignore
result = StringResult.model_validate_json(response.content)
if result.result not in participant_descriptions:
raise RuntimeError(f"Unknown participant selected: {result.result}")
return result
@override
async def filter_results(self, chat_history: ChatHistory) -> MessageResult:
rendered_prompt = await self._render_prompt(self.summary_prompt, topic=self._topic)
rendered_prompt = await self._render_prompt(self.summary_prompt, topic=self.topic)
chat_history.messages.insert(
0,
ChatMessageContent(role=AuthorRole.SYSTEM, content=rendered_prompt),
@@ -171,11 +171,11 @@ class ChatCompletionGroupChatManager(GroupChatManager):
ChatMessageContent(role=AuthorRole.USER, content="Summarize the plan."),
)
response = await self._service.get_chat_message_content(
response = await self.service.get_chat_message_content(
chat_history,
settings=PromptExecutionSettings(response_format=StringResult),
)
string_result = StringResult.model_validate_json(response.content) # type: ignore
string_result = StringResult.model_validate_json(response.content)
return MessageResult(
result=ChatMessageContent(role=AuthorRole.ASSISTANT, content=string_result.result),
reason=string_result.reason,
@@ -197,7 +197,7 @@ async def sk_agent_response_callback(message: ChatMessageContent | Sequence[Chat
async def run_semantic_kernel_example(task: str) -> str:
credential = AzureCliCredential()
orchestration = GroupChatOrchestration(
members=build_semantic_kernel_agents(), # type: ignore
members=build_semantic_kernel_agents(),
manager=ChatCompletionGroupChatManager(
topic=DISCUSSION_TOPIC,
service=AzureChatCompletion(credential=credential),
@@ -225,7 +225,7 @@ async def run_semantic_kernel_example(task: str) -> str:
async def run_agent_framework_example(task: str) -> str:
client = OpenAIChatCompletionClient(credential=AzureCliCredential())
credential = AzureCliCredential()
researcher = Agent(
name="Researcher",
@@ -234,42 +234,32 @@ async def run_agent_framework_example(task: str) -> str:
"Gather concise facts or considerations that help plan a community hackathon. "
"Keep your responses factual and scannable."
),
client=client,
client=FoundryChatClient(credential=credential),
)
planner = Agent(
name="Planner",
description="Turns the collected notes into a concrete action plan.",
instructions=("Propose a structured action plan that accounts for logistics, roles, and timeline."),
client=client,
client=FoundryChatClient(credential=credential),
)
workflow = GroupChatBuilder(
participants=[researcher, planner],
orchestrator_agent=Agent(client=client),
max_rounds=8,
intermediate_outputs=True,
orchestrator_agent=Agent(client=FoundryChatClient(credential=credential)),
).build()
output_messages: list[Message] = []
last_message_id: str | None = None
final_response = ""
async for event in workflow.run(task, stream=True):
if event.type == "output":
if isinstance(event.data, AgentResponseUpdate):
if event.data.message_id != last_message_id:
last_message_id = event.data.message_id
print(f"{event.data.author_name}: {event.data.text}", end="")
else:
print(event.data.text, end="")
data = event.data
if isinstance(data, list) and len(data) > 0:
# Get the final message from the conversation
final_message = data[-1]
final_response = final_message.text or "" if isinstance(final_message, Message) else str(data)
else:
output_messages.extend(cast(list[Message], event.data))
for message in output_messages:
print(f"[{message.author_name}] {message.text}")
if output_messages:
return output_messages[-1].text
return ""
final_response = str(data)
return final_response
async def main() -> None:
@@ -11,14 +11,15 @@
"""Side-by-side handoff orchestrations for Semantic Kernel and Agent Framework."""
import asyncio
from collections.abc import AsyncIterable, Callable, Iterator, Sequence
import sys
from collections.abc import AsyncIterable, Iterator, Sequence
from agent_framework import (
Agent,
Message,
WorkflowEvent,
)
from agent_framework.openai import OpenAIChatCompletionClient
from agent_framework.foundry import FoundryChatClient
from agent_framework.orchestrations import HandoffAgentUserRequest, HandoffBuilder
from azure.identity import AzureCliCredential
from dotenv import load_dotenv
@@ -35,6 +36,11 @@ from semantic_kernel.contents import (
)
from semantic_kernel.functions import kernel_function
if sys.version_info >= (3, 12):
pass # pragma: no cover
else:
pass # pragma: no cover
# Load environment variables from .env file
load_dotenv()
@@ -143,7 +149,7 @@ def _sk_streaming_callback(message: StreamingChatMessageContent, is_final: bool)
_sk_new_message = True
def _make_sk_human_responder(script: Iterator[str]) -> Callable[[], ChatMessageContent]:
def _make_sk_human_responder(script: Iterator[str]) -> callable:
def _responder() -> ChatMessageContent:
try:
user_text = next(script)
@@ -184,7 +190,7 @@ async def run_semantic_kernel_example(initial_task: str, scripted_responses: Seq
######################################################################
def _create_af_agents(client: OpenAIChatCompletionClient):
def _create_af_agents(client: FoundryChatClient):
triage = Agent(
client=client,
name="triage_agent",
@@ -239,7 +245,7 @@ def _extract_final_conversation(events: list[WorkflowEvent]) -> list[Message]:
async def run_agent_framework_example(initial_task: str, scripted_responses: Sequence[str]) -> str:
client = OpenAIChatCompletionClient(credential=AzureCliCredential())
client = FoundryChatClient(credential=AzureCliCredential())
triage, refund, status, returns = _create_af_agents(client)
workflow = (
@@ -275,7 +281,7 @@ async def run_agent_framework_example(initial_task: str, scripted_responses: Seq
return ""
# Render final transcript succinctly.
lines: list[str] = []
lines = []
for message in conversation:
text = message.text or ""
if not text.strip():
@@ -13,9 +13,8 @@
import asyncio
from collections.abc import Sequence
from typing import cast
from agent_framework import Agent, AgentResponseUpdate, Message
from agent_framework import Agent
from agent_framework.openai import OpenAIChatClient
from agent_framework.orchestrations import MagenticBuilder
from dotenv import load_dotenv
@@ -47,21 +46,21 @@ PROMPT = (
######################################################################
async def build_semantic_kernel_agents() -> list[ChatCompletionAgent | OpenAIAssistantAgent]:
async def build_semantic_kernel_agents() -> list:
research_agent = ChatCompletionAgent(
name="ResearchAgent",
description="A helpful assistant with access to web search. Ask it to perform web searches.",
instructions=(
"You are a Researcher. You find information without additional computation or quantitative analysis."
),
service=OpenAIChatCompletion(ai_model_id="gpt-4o-mini-search-preview"),
service=OpenAIChatCompletion(ai_model_id="gpt-4o-search-preview"),
)
client = OpenAIAssistantAgent.create_client()
code_interpreter_tool, code_interpreter_tool_resources = OpenAIAssistantAgent.configure_code_interpreter_tool()
openai_settings = OpenAISettings()
model_id = openai_settings.chat_model_id if openai_settings.chat_model_id else "gpt-5"
definition = await client.beta.assistants.create( # pyright: ignore[reportDeprecated]
definition = await client.beta.assistants.create(
model=model_id,
name="CoderAgent",
description="A helpful assistant that writes and executes code to process and analyze data.",
@@ -95,7 +94,7 @@ def sk_agent_response_callback(
async def run_semantic_kernel_example(prompt: str) -> Sequence[ChatMessageContent]:
agents = await build_semantic_kernel_agents()
magentic_orchestration = MagenticOrchestration(
members=agents, # type: ignore
members=agents,
manager=StandardMagenticManager(chat_completion_service=OpenAIChatCompletion()),
agent_response_callback=sk_agent_response_callback,
)
@@ -138,7 +137,7 @@ async def run_agent_framework_example(prompt: str) -> str | None:
instructions=(
"You are a Researcher. You find information without additional computation or quantitative analysis."
),
client=OpenAIChatClient(model="gpt-4o-mini-search-preview"),
client=OpenAIChatClient(model="gpt-4o-search-preview"),
)
# Create code interpreter tool using static method
@@ -161,31 +160,22 @@ async def run_agent_framework_example(prompt: str) -> str | None:
client=OpenAIChatClient(),
)
workflow = MagenticBuilder(
participants=[researcher, coder],
manager_agent=manager_agent, # type: ignore
intermediate_outputs=True,
).build()
workflow = MagenticBuilder(participants=[researcher, coder], manager_agent=manager_agent).build()
output_messages: list[Message] = []
last_message_id: str | None = None
final_text: str | None = None
async for event in workflow.run(prompt, stream=True):
if event.type == "output":
if isinstance(event.data, AgentResponseUpdate):
if event.data.message_id != last_message_id:
last_message_id = event.data.message_id
print(f"{event.data.author_name}: {event.data.text}", end="")
else:
print(event.data.text, end="")
else:
output_messages.extend(cast(list[Message], event.data))
for message in output_messages:
print(f"[{message.author_name}] {message.text}")
data = event.data
if isinstance(data, str):
final_text = data
elif isinstance(data, list):
# Extract text from the last assistant message
for msg in reversed(data):
if hasattr(msg, "text") and msg.text:
final_text = msg.text
break
if output_messages:
return output_messages[-1].text
return None
return final_text
def _print_agent_framework_output(result: str | None) -> None: