diff --git a/user-documentation-python/user-guide/README.md b/user-documentation-python/user-guide/README.md index 7222338d16..4cb6bf7af8 100644 --- a/user-documentation-python/user-guide/README.md +++ b/user-documentation-python/user-guide/README.md @@ -1,4 +1,4 @@ -# Microsoft Agent Framework for .NET Concepts +# Microsoft Agent Framework for Python Concepts - [Agent Types](./agent-types.md) - [Multi-turn conversations and Threading](./multi-turn-conversations.md) diff --git a/user-documentation-python/user-guide/agent-types.md b/user-documentation-python/user-guide/agent-types.md index b3442b121a..b114a54acc 100644 --- a/user-documentation-python/user-guide/agent-types.md +++ b/user-documentation-python/user-guide/agent-types.md @@ -2,14 +2,14 @@ The Microsoft Agent Framework provides support for several types of agents to accommodate different use cases and requirements. -All agents are derived from a common base class, `AIAgent`, which provides a consistent interface for all agent types. This allows for building common, agent agnostic, higher level functionality such as multi-agent orchestrations. +All agents implement a common protocol, `AIAgent`, which provides a consistent interface for all agent types. This allows for building common, agent agnostic, higher level functionality such as multi-agent orchestrations. Let's dive into each agent type in more detail. ## Simple agents based on inference services The agent framework makes it easy to create simple agents based on many different inference services. -Any inference service that provides a ChatClient implementation can be used to build these agents. +Any inference service that provides a chat client implementation can be used to build these agents. These agents support a wide range of functionality out of the box: @@ -17,21 +17,136 @@ These agents support a wide range of functionality out of the box: 1. Multi-turn conversations with local chat history management or service provided chat history management 1. Custom service provided tools (e.g. MCP, Code Execution) 1. Structured output +1. Streaming responses -To create one of these agents, simply construct a `ChatClientAgent` using the ChatClient implementation of your choice. +To create one of these agents, simply construct a `ChatClientAgent` using the chat client implementation of your choice. +```python +from agent_framework import ChatClientAgent +from agent_framework.foundry import FoundryChatClient +from azure.identity.aio import AzureCliCredential -## Complex custom agents +async with ( + AzureCliCredential() as credential, + ChatClientAgent( + chat_client=FoundryChatClient(async_credential=credential), + instructions="You are a helpful assistant" + ) as agent +): + response = await agent.run("Hello!") +``` -It is also possible to create fully custom agents, that are not just wrappers around a ChatClient. -The agent framework provides the `AIAgent` base type, which when subclassed allows for complete control over the agent's behavior and capabilities. +Alternatively, you can use the convenience method on the chat client: -## Remote agents +```python +from agent_framework.foundry import FoundryChatClient +from azure.identity.aio import AzureCliCredential -The agent framework provides out of the box `AIAgent` subclasses for common service hosted agent protocols, -such as A2A. +async with AzureCliCredential() as credential: + agent = FoundryChatClient(async_credential=credential).create_agent( + instructions="You are a helpful assistant" + ) +``` -## Pre-built agents +For detailed examples, see: +- [Basic Foundry agent](../../../python/samples/getting_started/agents/foundry/foundry_basic.py) +- [Foundry with explicit settings](../../../python/samples/getting_started/agents/foundry/foundry_with_explicit_settings.py) +- [Using existing Foundry agent](../../../python/samples/getting_started/agents/foundry/foundry_with_existing_agent.py) -To be added. +### Function Tools +You can provide function tools to agents for enhanced capabilities: + +```python +from typing import Annotated +from pydantic import Field +from azure.identity.aio import AzureCliCredential + +def get_weather(location: Annotated[str, Field(description="The location to get the weather for.")]) -> str: + """Get the weather for a given location.""" + return f"The weather in {location} is sunny with a high of 25°C." + +async with ( + AzureCliCredential() as credential, + FoundryChatClient(async_credential=credential).create_agent( + instructions="You are a helpful weather assistant.", + tools=get_weather + ) as agent +): + response = await agent.run("What's the weather in Seattle?") +``` + +For complete examples with function tools, see: +- [Foundry with function tools](../../../python/samples/getting_started/agents/foundry/foundry_with_function_tools.py) + +### Streaming Responses + +Agents support both regular and streaming responses: + +```python +# Regular response (wait for complete result) +response = await agent.run("What's the weather like in Seattle?") +print(response.text) + +# Streaming response (get results as they are generated) +async for chunk in agent.run_streaming("What's the weather like in Portland?"): + if chunk.text: + print(chunk.text, end="", flush=True) +``` + +For streaming examples, see: +- [Foundry streaming examples](../../../python/samples/getting_started/agents/foundry/foundry_basic.py) + +### Code Interpreter Tools + +Foundry agents support hosted code interpreter tools for executing Python code: + +```python +from agent_framework import ChatClientAgent, HostedCodeInterpreterTool +from agent_framework.foundry import FoundryChatClient +from azure.identity.aio import AzureCliCredential + +async with ( + AzureCliCredential() as credential, + ChatClientAgent( + chat_client=FoundryChatClient(async_credential=credential), + instructions="You are a helpful assistant that can execute Python code.", + tools=HostedCodeInterpreterTool() + ) as agent +): + response = await agent.run("Calculate the factorial of 100 using Python") +``` + +For code interpreter examples, see: +- [Foundry with code interpreter](../../../python/samples/getting_started/agents/foundry/foundry_with_code_interpreter.py) + +## Custom agents + +It is also possible to create fully custom agents that are not just wrappers around a chat client. +Agent Framework provides the `AIAgent` protocol and `AgentBase` base class, which when implemented/subclassed allows for complete control over the agent's behavior and capabilities. + +```python +from agent_framework import AgentBase, AgentRunResponse, AgentRunResponseUpdate, AgentThread, ChatMessage +from collections.abc import AsyncIterable + +class CustomAgent(AgentBase): + async def run( + self, + messages: str | ChatMessage | list[str] | list[ChatMessage] | None = None, + *, + thread: AgentThread | None = None, + **kwargs: Any, + ) -> AgentRunResponse: + # Custom agent implementation + pass + + def run_streaming( + self, + messages: str | ChatMessage | list[str] | list[ChatMessage] | None = None, + *, + thread: AgentThread | None = None, + **kwargs: Any, + ) -> AsyncIterable[AgentRunResponseUpdate]: + # Custom streaming implementation + pass +``` diff --git a/user-documentation-python/user-guide/multi-turn-conversations.md b/user-documentation-python/user-guide/multi-turn-conversations.md index 3971c205a7..07b5e5891b 100644 --- a/user-documentation-python/user-guide/multi-turn-conversations.md +++ b/user-documentation-python/user-guide/multi-turn-conversations.md @@ -1,8 +1,8 @@ # Microsoft Agent Framework Multi-Turn Conversations and Threading -The Microsoft Agent Framework provides built-in support for managing multi-turn conversations with AI agents. This includes maintaining context across multiple interactions. Different agent types and underlying services that are used to build agents may support different threading types, and the agent framework abstracts these differences away, providing a consistent interface for developers. +The Microsoft Agent Framework provides built-in support for managing multi-turn conversations with AI agents. This includes maintaining context across multiple interactions. Different agent types and underlying services that are used to build agents may support different threading types, and the Agent Framework abstracts these differences away, providing a consistent interface for developers. -For example, when using a ChatClientAgent based on a foundry agent, the conversation history is persisted in the service. While, when using a ChatClientAgent based on chat completion with gpt-4.1 the conversation history is in-memory and managed by the agent. +For example, when using a `ChatClientAgent` based on a Foundry agent, the conversation history is persisted in the service. While when using a `ChatClientAgent` based on chat completion with gpt-4, the conversation history is in-memory and managed by the agent. The differences between the underlying threading models are abstracted away via the `AgentThread` type. @@ -12,11 +12,20 @@ The differences between the underlying threading models are abstracted away via `AgentThread` instances can be created in two ways: -1. By calling `GetNewThread` on the agent. +1. By calling `get_new_thread()` on the agent. 1. By running the agent and not providing an `AgentThread`. In this case the agent will create a throwaway `AgentThread` with an underlying thread which will only be used for the duration of the run. Some underlying threads may be persistently created in an underlying service, where the service requires this, e.g. Foundry Agents or OpenAI Responses. Any cleanup or deletion of these threads is the responsibility of the user. +```python +# Create a new thread. +thread = agent.get_new_thread() +# Run the agent with the thread. +response = await agent.run("Hello, how are you?", thread=thread) + +# Run an agent with a temporary thread. +response = await agent.run("Hello, how are you?") +``` ### AgentThread Storage @@ -27,6 +36,47 @@ id of the thread in the service. For cases where the conversation history is managed in-memory, the serialized `AgentThread` will contain the messages themselves. +```python +# Create a new thread. +thread = agent.get_new_thread() +# Run the agent with the thread. +response = await agent.run("Hello, how are you?", thread=thread) + +# Serialize the thread for storage. +serialized_thread = await thread.serialize() +# Deserialize the thread state after loading from storage. +resumed_thread = await agent.deserialize_thread(serialized_thread) + +# Run the agent with the resumed thread. +response = await agent.run("Hello, how are you?", thread=resumed_thread) +``` + +### Custom Message Stores + +For in-memory threads, you can provide a custom message store implementation to control how messages are stored and retrieved: + +```python +from agent_framework import AgentThread, ChatMessageList, ChatClientAgent +from agent_framework.foundry import FoundryChatClient +from azure.identity.aio import AzureCliCredential + +# Using the default in-memory message store +thread = AgentThread(message_store=ChatMessageList()) + +# Or let the agent create one automatically +thread = agent.get_new_thread() + +# You can also provide a custom message store factory when creating the agent +def custom_message_store_factory(): + return ChatMessageList() # or your custom implementation + +async with AzureCliCredential() as credential: + agent = ChatClientAgent( + chat_client=FoundryChatClient(async_credential=credential), + instructions="You are a helpful assistant", + chat_message_store_factory=custom_message_store_factory + ) +``` ## Agent/AgentThread relationship @@ -38,6 +88,48 @@ only has a reference to this service managed thread. It is therefore considered unsafe to use an `AgentThread` instance that was created by one agent with a different agent instance, unless you are aware of the underlying threading model and its implications. +## Practical Multi-Turn Example + +Here's a complete example showing how to maintain context across multiple interactions: + +```python +from agent_framework import ChatClientAgent, AgentThread +from agent_framework.foundry import FoundryChatClient +from azure.identity.aio import AzureCliCredential + +async def foundry_multi_turn_example(): + async with ( + AzureCliCredential() as credential, + ChatClientAgent( + chat_client=FoundryChatClient(async_credential=credential), + instructions="You are a helpful assistant" + ) as agent + ): + # Create a thread for persistent conversation + thread = agent.get_new_thread() + + # First interaction + response1 = await agent.run("My name is Alice", thread=thread) + print(f"Agent: {response1.text}") + + # Second interaction - agent remembers the name + response2 = await agent.run("What's my name?", thread=thread) + print(f"Agent: {response2.text}") # Should mention "Alice" + + # Serialize thread for storage + serialized = await thread.serialize() + + # Later, deserialize and continue conversation + new_thread = await agent.deserialize_thread(serialized) + response3 = await agent.run("What did we talk about?", thread=new_thread) + print(f"Agent: {response3.text}") # Should remember previous context +``` + +For complete threading examples, see: +- [Foundry with threads](../../../python/samples/getting_started/agents/foundry/foundry_with_thread.py) +- [Custom message store](../../../python/samples/getting_started/threads/custom_chat_message_store_thread.py) +- [Suspend and resume threads](../../../python/samples/getting_started/threads/suspend_resume_thread.py) + ## Threading support by service / protocol | Service | Threading Support | @@ -46,5 +138,3 @@ It is therefore considered unsafe to use an `AgentThread` instance that was crea | OpenAI Responses | Service managed persistent threads OR in-memory threads | | OpenAI ChatCompletion | In-memory threads | | OpenAI Assistants | Service managed threads | -| A2A | Service managed threads | -