From 00f733cf5e387121d1271951df40d29d46a23ae3 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Wed, 3 Dec 2025 19:52:50 -0800 Subject: [PATCH 01/11] ADR for create/get agent API --- docs/decisions/0011-create-get-agent-api.md | 91 +++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 docs/decisions/0011-create-get-agent-api.md diff --git a/docs/decisions/0011-create-get-agent-api.md b/docs/decisions/0011-create-get-agent-api.md new file mode 100644 index 0000000000..c633a54425 --- /dev/null +++ b/docs/decisions/0011-create-get-agent-api.md @@ -0,0 +1,91 @@ +--- +status: proposed +contact: dmytrostruk +date: 2025-12-03 +deciders: dmytrostruk, markwallace-microsoft, eavanvalkenburg, giles17 +--- + +# Create/Get Agent API + +## Context and Problem Statement + +There is a misalignment between the create/get agent API in the .NET and Python implementations. + +In .NET, the `CreateAIAgent` method can create either a local instance of an agent or a remote instance if the backend provider supports it. For remote agents, once the agent is created, you can retrieve an existing remote agent by using the `GetAIAgent` method. If a backend provider doesn't support remote agents, `CreateAIAgent` just initializes a new local agent instance and `GetAIAgent` is not available. There is also a `BuildAIAgent` method, which is an extension for the `ChatClientBuilder` class from `Microsoft.Extensions.AI`. It builds pipelines of `IChatClient` instances with an `IServiceProvider`. This functionality does not exist in Python, so `BuildAIAgent` is out of scope. + +In Python, there is only one `create_agent` method, which always creates a local instance of the agent. If the backend provider supports remote agents, the remote agent is created only on the first `agent.run()` invocation. + +Below is a short summary of different providers and their APIs in .NET: + +| Package | Method | Behavior | Python support | +|---|---|---|---| +| Microsoft.Agents.AI | `CreateAIAgent` (based on `IChatClient`) | Creates a local instance of `ChatClientAgent`. | Yes (`create_agent` in `BaseChatClient`). | +| Microsoft.Agents.AI.Anthropic | `CreateAIAgent` (based on `IBetaService` and `IAnthropicClient`) | Creates a local instance of `ChatClientAgent`. | Yes (`AnthropicClient` inherits `BaseChatClient`, which exposes `create_agent`). | +| Microsoft.Agents.AI.AzureAI (V2) | `GetAIAgent` (based on `AIProjectClient` with `AgentReference`) | Creates a local instance of `ChatClientAgent`. | Partial (Python uses `create_agent` from `BaseChatClient`). | +| Microsoft.Agents.AI.AzureAI (V2) | `GetAIAgent`/`GetAIAgentAsync` (with `Name`/`ChatClientAgentOptions`) | Fetches `AgentRecord` via HTTP, then creates a local `ChatClientAgent` instance. | No | +| Microsoft.Agents.AI.AzureAI (V2) | `CreateAIAgent`/`CreateAIAgentAsync` (based on `AIProjectClient`) | Creates a remote agent first, then wraps it into a local `ChatClientAgent` instance. | No | +| Microsoft.Agents.AI.AzureAI.Persistent (V1) | `GetAIAgent` (based on `PersistentAgentsClient` with `PersistentAgent`) | Creates a local instance of `ChatClientAgent`. | Partial (Python uses `create_agent` from `BaseChatClient`). | +| Microsoft.Agents.AI.AzureAI.Persistent (V1) | `GetAIAgent`/`GetAIAgentAsync` (with `AgentId`) | Fetches `PersistentAgent` via HTTP, then creates a local `ChatClientAgent` instance. | No | +| Microsoft.Agents.AI.AzureAI.Persistent (V1) | `CreateAIAgent`/`CreateAIAgentAsync` | Creates a remote agent first, then wraps it into a local `ChatClientAgent` instance. | No | +| Microsoft.Agents.AI.OpenAI | `GetAIAgent` (based on `AssistantClient` with `Assistant`) | Creates a local instance of `ChatClientAgent`. | Partial (Python uses `create_agent` from `BaseChatClient`). | +| Microsoft.Agents.AI.OpenAI | `GetAIAgent`/`GetAIAgentAsync` (with `AgentId`) | Fetches `Assistant` via HTTP, then creates a local `ChatClientAgent` instance. | No | +| Microsoft.Agents.AI.OpenAI | `CreateAIAgent`/`CreateAIAgentAsync` (based on `AssistantClient`) | Creates a remote agent first, then wraps it into a local `ChatClientAgent` instance. | No | +| Microsoft.Agents.AI.OpenAI | `CreateAIAgent` (based on `ChatClient`) | Creates a local instance of `ChatClientAgent`. | Yes (`create_agent` in `BaseChatClient`). | +| Microsoft.Agents.AI.OpenAI | `CreateAIAgent` (based on `OpenAIResponseClient`) | Creates a local instance of `ChatClientAgent`. | Yes (`create_agent` in `BaseChatClient`). | + +## Decision Drivers + +- API should be aligned between .NET and Python. +- API should be intuitive and consistent between backend providers in .NET and Python. + +## Considered Options + +Add missing implementations on the Python side. This should include the following: + +### agent-framework-azure-ai (both V1 and V2) + +- Add a `get_agent` method that accepts an underlying SDK agent instance and creates a local instance of `ChatAgent`. +- Add a `get_agent` method that accepts an agent identifier, performs an additional HTTP request to fetch agent data, and then creates a local instance of `ChatAgent`. +- Override the `create_agent` method from `BaseChatClient` to create a remote agent instance and wrap it into a local `ChatAgent`. + +.NET: + +```csharp +var agent1 = new AIProjectClient(...).GetAIAgent(agentSdkInstance); // Creates a local ChatClientAgent instance +var agent2 = new AIProjectClient(...).GetAIAgent(agentName); // Fetches agent data, creates a local ChatClientAgent instance +var agent3 = new AIProjectClient(...).CreateAIAgent(...); // Creates a remote agent, returns a local ChatClientAgent instance +``` + +Python: + +```python +agent1 = AIProjectClient(...).get_agent(agent_sdk_instance) # Creates a local ChatAgent instance +agent2 = AIProjectClient(...).get_agent(agent_name) # Fetches agent data, creates a local ChatAgent instance +agent3 = AIProjectClient(...).create_agent(...) # Creates a remote agent, returns a local ChatAgent instance +``` + +### agent-framework-core (OpenAI Assistants) + +- Add a `get_agent` method that accepts an underlying SDK agent instance and creates a local instance of `ChatAgent`. +- Add a `get_agent` method that accepts an agent name, performs an additional HTTP request to fetch agent data, and then creates a local instance of `ChatAgent`. +- Override the `create_agent` method from `BaseChatClient` to create a remote agent instance and wrap it into a local `ChatAgent`. + +.NET: + +```csharp +var agent1 = new AssistantClient(...).GetAIAgent(agentSdkInstance); // Creates a local ChatClientAgent instance +var agent2 = new AssistantClient(...).GetAIAgent(agentId); // Fetches agent data, creates a local ChatClientAgent instance +var agent3 = new AssistantClient(...).CreateAIAgent(...); // Creates a remote agent, returns a local ChatClientAgent instance +``` + +Python: + +```python +agent1 = OpenAIAssistantsClient(...).get_agent(agent_sdk_instance) # Creates a local ChatAgent instance +agent2 = OpenAIAssistantsClient(...).get_agent(agent_name) # Fetches agent data, creates a local ChatAgent instance +agent3 = OpenAIAssistantsClient(...).create_agent(...) # Creates a remote agent, returns a local ChatAgent instance +``` + +## Decision Outcome + +TBD From 5cada6edbc05f480e5b7bc31c2463f66945fbd5e Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Fri, 12 Dec 2025 10:38:21 -0800 Subject: [PATCH 02/11] Updated ADR with implementation options --- docs/decisions/0011-create-get-agent-api.md | 174 ++++++++++++++++++-- 1 file changed, 159 insertions(+), 15 deletions(-) diff --git a/docs/decisions/0011-create-get-agent-api.md b/docs/decisions/0011-create-get-agent-api.md index c633a54425..7e07973264 100644 --- a/docs/decisions/0011-create-get-agent-api.md +++ b/docs/decisions/0011-create-get-agent-api.md @@ -1,7 +1,7 @@ --- status: proposed contact: dmytrostruk -date: 2025-12-03 +date: 2025-12-12 deciders: dmytrostruk, markwallace-microsoft, eavanvalkenburg, giles17 --- @@ -33,6 +33,41 @@ Below is a short summary of different providers and their APIs in .NET: | Microsoft.Agents.AI.OpenAI | `CreateAIAgent` (based on `ChatClient`) | Creates a local instance of `ChatClientAgent`. | Yes (`create_agent` in `BaseChatClient`). | | Microsoft.Agents.AI.OpenAI | `CreateAIAgent` (based on `OpenAIResponseClient`) | Creates a local instance of `ChatClientAgent`. | Yes (`create_agent` in `BaseChatClient`). | +Another difference between Python and .NET implementation is that in .NET `CreateAIAgent`/`GetAIAgent` methods are implemented as extension methods based on underlying SDK client, like `AIProjectClient` from Azure AI or `AssistantClient` from OpenAI: + +```csharp +// Definition +public static ChatClientAgent CreateAIAgent( + this AIProjectClient aiProjectClient, + string name, + string model, + string instructions, + string? description = null, + IList? tools = null, + Func? clientFactory = null, + IServiceProvider? services = null, + CancellationToken cancellationToken = default) +{ } + +// Usage +AIProjectClient aiProjectClient = new(new Uri(endpoint), new AzureCliCredential()); // Initialization of underlying SDK client + +var newAgent = await aiProjectClient.CreateAIAgentAsync(name: AgentName, model: deploymentName, instructions: AgentInstructions, tools: [tool]); // ChatClientAgent creation from underlying SDK client + +// Alternative usage (same as extension method, just explicit syntax) +var newAgent = await AzureAIProjectChatClientExtensions.CreateAIAgentAsync( + aiProjectClient, + name: AgentName, + model: deploymentName, + instructions: AgentInstructions, + tools: [tool]); +``` + +Python doesn't support extension methods. Currently `create_agent` method is defined on `BaseChatClient`, but this method only creates a local instance of `ChatAgent` and it can't create remote agents for providers that support it for a couple of reasons: + +- It's defined as non-async. +- `BaseChatClient` implementation is stateful for providers like Azure AI or OpenAI Assistants. The implementation stores agent/assistant metadata like `AgentId` and `AgentName`, so currently it's not possible to create different instances of `ChatAgent` from a single `BaseChatClient` in case if the implementation is stateful. + ## Decision Drivers - API should be aligned between .NET and Python. @@ -51,19 +86,11 @@ Add missing implementations on the Python side. This should include the followin .NET: ```csharp -var agent1 = new AIProjectClient(...).GetAIAgent(agentSdkInstance); // Creates a local ChatClientAgent instance +var agent1 = new AIProjectClient(...).GetAIAgent(agentInstanceFromSdkType); // Creates a local ChatClientAgent instance from Azure.AI.Projects.OpenAI.AgentReference var agent2 = new AIProjectClient(...).GetAIAgent(agentName); // Fetches agent data, creates a local ChatClientAgent instance var agent3 = new AIProjectClient(...).CreateAIAgent(...); // Creates a remote agent, returns a local ChatClientAgent instance ``` -Python: - -```python -agent1 = AIProjectClient(...).get_agent(agent_sdk_instance) # Creates a local ChatAgent instance -agent2 = AIProjectClient(...).get_agent(agent_name) # Fetches agent data, creates a local ChatAgent instance -agent3 = AIProjectClient(...).create_agent(...) # Creates a remote agent, returns a local ChatAgent instance -``` - ### agent-framework-core (OpenAI Assistants) - Add a `get_agent` method that accepts an underlying SDK agent instance and creates a local instance of `ChatAgent`. @@ -73,19 +100,136 @@ agent3 = AIProjectClient(...).create_agent(...) # Creates a remote agent, return .NET: ```csharp -var agent1 = new AssistantClient(...).GetAIAgent(agentSdkInstance); // Creates a local ChatClientAgent instance +var agent1 = new AssistantClient(...).GetAIAgent(agentInstanceFromSdkType); // Creates a local ChatClientAgent instance from OpenAI.Assistants.Assistant var agent2 = new AssistantClient(...).GetAIAgent(agentId); // Fetches agent data, creates a local ChatClientAgent instance var agent3 = new AssistantClient(...).CreateAIAgent(...); // Creates a remote agent, returns a local ChatClientAgent instance ``` -Python: +### Possible Python implementations + +Methods like `create_agent` and `get_agent` should be implemented separately or defined on some stateless component that will allow to create multiple agents from the same instance/place. + +Possible options: + +#### Option 1: Module-level functions + +Implement free functions in the provider package that accept the underlying SDK client as the first argument (similar to .NET extension methods, but expressed in Python). + +Example: ```python -agent1 = OpenAIAssistantsClient(...).get_agent(agent_sdk_instance) # Creates a local ChatAgent instance -agent2 = OpenAIAssistantsClient(...).get_agent(agent_name) # Fetches agent data, creates a local ChatAgent instance -agent3 = OpenAIAssistantsClient(...).create_agent(...) # Creates a remote agent, returns a local ChatAgent instance +from agent_framework.azure import agents as azure_agents + +ai_project_client = AIProjectClient(...) + +# Creates a remote agent first, then returns a local ChatAgent wrapper +agent = await azure_agents.create_agent( + ai_project_client, + name="", + instructions="", + tools=[tool], +) + +# Gets an existing remote agent and returns a local ChatAgent wrapper +agent = await azure_agents.get_agent(ai_project_client, agent_id=agent_id) + +# Wraps an SDK agent instance (no extra HTTP call) +agent1 = azure_agents.get_agent(ai_project_client, agent_reference) ``` +Pros: + +- Naturally supports async `create_agent` / `get_agent`. +- Supports multiple agents per SDK client. +- Closest conceptual match to .NET extension methods while staying Pythonic. + +Cons: + +- Discoverability is lower (users need to know where the functions live). + +#### Option 2: Factory object + +Introduce a dedicated factory type that is constructed from the underlying SDK client, and exposes async `create_agent` / `get_agent` methods. + +Example: + +```python +from agent_framework.azure import AgentFactory + +ai_project_client = AIProjectClient(...) +factory = AgentFactory(ai_project_client) + +agent = await factory.create_agent( + name="", + instructions="", + tools=[tool], +) + +agent = await factory.get_agent(agent_id=agent_id) +agent = factory.get_agent(agent_reference=agent_reference) +``` + +Pros: + +- High discoverability and clear grouping of related behavior. +- Keeps SDK clients unchanged and supports multiple agents per SDK client. + +Cons: + +- Adds a new public concept/type for users to learn. + +#### Option 3: Inheritance (SDK client subclass) + +Create a subclass of the underlying SDK client and add `create_agent` / `get_agent` methods. + +Example: + +```python +class ExtendedAIProjectClient(AIProjectClient): + async def create_agent(self, *, name: str, model: str, instructions: str, **kwargs) -> ChatAgent: + ... + + async def get_agent(self, *, agent_id: str | None = None, sdk_agent=None, **kwargs) -> ChatAgent: + ... + +client = ExtendedAIProjectClient(...) +agent = await client.create_agent(name="", instructions="") +``` + +Pros: + +- Discoverable and ergonomic call sites. +- Mirrors the .NET “methods on the client” feeling. + +Cons: + +- Many SDK clients are not designed for inheritance; SDK upgrades can break subclasses. +- Users must opt into subclass everywhere. +- Typing/initialization can be tricky if the SDK client has non-trivial constructors. + +#### Option 4: Monkey patching + +Attach `create_agent` / `get_agent` methods to an SDK client class (or instance) at runtime. + +Example: + +```python +def _create_agent(self, *, name: str, model: str, instructions: str, **kwargs) -> ChatAgent: + ... + +AIProjectClient.create_agent = _create_agent # monkey patch +``` + +Pros: + +- Produces “extension method-like” call sites without wrappers or subclasses. + +Cons: + +- Fragile across SDK updates and difficult to type-check. +- Surprising behavior (global side effects), potential conflicts across packages. +- Harder to support/debug, especially in larger apps and test suites. + ## Decision Outcome TBD From bda731fdb596517e04d6e06e32c6ee55d406acc3 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Tue, 30 Dec 2025 11:21:15 -0800 Subject: [PATCH 03/11] Small updates --- docs/decisions/0011-create-get-agent-api.md | 27 +++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/docs/decisions/0011-create-get-agent-api.md b/docs/decisions/0011-create-get-agent-api.md index 7e07973264..66b0897ae7 100644 --- a/docs/decisions/0011-create-get-agent-api.md +++ b/docs/decisions/0011-create-get-agent-api.md @@ -146,33 +146,46 @@ Pros: Cons: - Discoverability is lower (users need to know where the functions live). +- Verbose when creating multiple agents (client must be passed every time): -#### Option 2: Factory object + ```python + agent1 = await azure_agents.create_agent(client, name="Agent1", ...) + agent2 = await azure_agents.create_agent(client, name="Agent2", ...) + ``` -Introduce a dedicated factory type that is constructed from the underlying SDK client, and exposes async `create_agent` / `get_agent` methods. +#### Option 2: Provider object + +Introduce a dedicated provider type that is constructed from the underlying SDK client, and exposes async `create_agent` / `get_agent` methods. Example: ```python -from agent_framework.azure import AgentFactory +from agent_framework.azure import AzureAIAgentProvider ai_project_client = AIProjectClient(...) -factory = AgentFactory(ai_project_client) +provider = AzureAIAgentProvider(ai_project_client) -agent = await factory.create_agent( +agent = await provider.create_agent( name="", instructions="", tools=[tool], ) -agent = await factory.get_agent(agent_id=agent_id) -agent = factory.get_agent(agent_reference=agent_reference) +agent = await provider.get_agent(agent_id=agent_id) +agent = provider.get_agent(agent_reference=agent_reference) ``` Pros: - High discoverability and clear grouping of related behavior. - Keeps SDK clients unchanged and supports multiple agents per SDK client. +- Concise when creating multiple agents (client passed once): + + ```python + provider = AzureAIAgentProvider(ai_project_client) + agent1 = await provider.create_agent(name="Agent1", ...) + agent2 = await provider.create_agent(name="Agent2", ...) + ``` Cons: From 950fc377cddb457f7cb557c312000c0e90587151 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Mon, 5 Jan 2026 12:23:49 -0800 Subject: [PATCH 04/11] Updated decision outcome section --- docs/decisions/0011-create-get-agent-api.md | 30 +++++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/docs/decisions/0011-create-get-agent-api.md b/docs/decisions/0011-create-get-agent-api.md index 66b0897ae7..ad3f0b9a2f 100644 --- a/docs/decisions/0011-create-get-agent-api.md +++ b/docs/decisions/0011-create-get-agent-api.md @@ -118,12 +118,12 @@ Implement free functions in the provider package that accept the underlying SDK Example: ```python -from agent_framework.azure import agents as azure_agents +from agent_framework.azure import create_agent, get_agent ai_project_client = AIProjectClient(...) # Creates a remote agent first, then returns a local ChatAgent wrapper -agent = await azure_agents.create_agent( +created_agent = await create_agent( ai_project_client, name="", instructions="", @@ -131,10 +131,10 @@ agent = await azure_agents.create_agent( ) # Gets an existing remote agent and returns a local ChatAgent wrapper -agent = await azure_agents.get_agent(ai_project_client, agent_id=agent_id) +first_agent = await get_agent(ai_project_client, agent_id=agent_id) # Wraps an SDK agent instance (no extra HTTP call) -agent1 = azure_agents.get_agent(ai_project_client, agent_reference) +second_agent = get_agent(ai_project_client, agent_reference) ``` Pros: @@ -245,4 +245,24 @@ Cons: ## Decision Outcome -TBD +Implement `get_agent`/`create_agent` API via `Option 1: Module-level functions`: + +```python +from agent_framework.azure import create_agent, get_agent + +ai_project_client = AIProjectClient(...) + +# Creates a remote agent first, then returns a local ChatAgent wrapper +created_agent = await create_agent( + ai_project_client, + name="", + instructions="", + tools=[tool], +) + +# Gets an existing remote agent and returns a local ChatAgent wrapper +first_agent = await get_agent(ai_project_client, agent_id=agent_id) + +# Wraps an SDK agent instance (no extra HTTP call) +second_agent = get_agent(ai_project_client, agent_reference) +``` From f87fcc65493fa1e04793036bb875cc08c5050ba6 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Mon, 5 Jan 2026 12:33:14 -0800 Subject: [PATCH 05/11] Updated broken links --- docs/decisions/0001-agent-run-response.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/decisions/0001-agent-run-response.md b/docs/decisions/0001-agent-run-response.md index b60878adff..cc903332f0 100644 --- a/docs/decisions/0001-agent-run-response.md +++ b/docs/decisions/0001-agent-run-response.md @@ -64,7 +64,7 @@ Approaches observed from the compared SDKs: | AutoGen | **Approach 1** Separates messages into Agent-Agent (maps to Primary) and Internal (maps to Secondary) and these are returned as separate properties on the agent response object. See [types of messages](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/tutorial/messages.html#types-of-messages) and [Response](https://microsoft.github.io/autogen/stable/reference/python/autogen_agentchat.base.html#autogen_agentchat.base.Response) | **Approach 2** Returns a stream of internal events and the last item is a Response object. See [ChatAgent.on_messages_stream](https://microsoft.github.io/autogen/stable/reference/python/autogen_agentchat.base.html#autogen_agentchat.base.ChatAgent.on_messages_stream) | | OpenAI Agent SDK | **Approach 1** Separates new_items (Primary+Secondary) from final output (Primary) as separate properties on the [RunResult](https://github.com/openai/openai-agents-python/blob/main/src/agents/result.py#L39) | **Approach 1** Similar to non-streaming, has a way of streaming updates via a method on the response object which includes all data, and then a separate final output property on the response object which is populated only when the run is complete. See [RunResultStreaming](https://github.com/openai/openai-agents-python/blob/main/src/agents/result.py#L136) | | Google ADK | **Approach 2** [Emits events](https://google.github.io/adk-docs/runtime/#step-by-step-breakdown) with [FinalResponse](https://github.com/google/adk-java/blob/main/core/src/main/java/com/google/adk/events/Event.java#L232) true (Primary) / false (Secondary) and callers have to filter out those with false to get just the final response message | **Approach 2** Similar to non-streaming except [events](https://google.github.io/adk-docs/runtime/#streaming-vs-non-streaming-output-partialtrue) are emitted with [Partial](https://github.com/google/adk-java/blob/main/core/src/main/java/com/google/adk/events/Event.java#L133) true to indicate that they are streaming messages. A final non partial event is also emitted. | -| AWS (Strands) | **Approach 3** Returns an [AgentResult](https://strandsagents.com/latest/api-reference/agent/#strands.agent.agent_result.AgentResult) (Primary) with messages and a reason for the run's completion. | **Approach 2** [Streams events](https://strandsagents.com/latest/api-reference/agent/#strands.agent.agent.Agent.stream_async) (Primary+Secondary) including, response text, current_tool_use, even data from "callbacks" (strands plugins) | +| AWS (Strands) | **Approach 3** Returns an [AgentResult](https://strandsagents.com/latest/documentation/docs/api-reference/python/agent/agent_result/#strands.agent.agent_result) (Primary) with messages and a reason for the run's completion. | **Approach 2** [Streams events](https://strandsagents.com/latest/documentation/docs/api-reference/python/agent/agent/#strands.agent.agent.Agent.stream_async) (Primary+Secondary) including, response text, current_tool_use, even data from "callbacks" (strands plugins) | | LangGraph | **Approach 2** A mixed list of all [messages](https://langchain-ai.github.io/langgraph/agents/run_agents/#output-format) | **Approach 2** A mixed list of all [messages](https://langchain-ai.github.io/langgraph/agents/run_agents/#output-format) | | Agno | **Combination of various approaches** Returns a [RunResponse](https://docs.agno.com/reference/agents/run-response) object with text content, messages (essentially chat history including inputs and instructions), reasoning and thinking text properties. Secondary events could potentially be extracted from messages. | **Approach 2** Returns [RunResponseEvent](https://docs.agno.com/reference/agents/run-response#runresponseevent-types-and-attributes) objects including tool call, memory update, etc, information, where the [RunResponseCompletedEvent](https://docs.agno.com/reference/agents/run-response#runresponsecompletedevent) has similar properties to RunResponse| | A2A | **Approach 3** Returns a [Task or Message](https://a2aproject.github.io/A2A/latest/specification/#71-messagesend) where the message is the final result (Primary) and task is a reference to a long running process. | **Approach 2** Returns a [stream](https://a2aproject.github.io/A2A/latest/specification/#72-messagestream) that contains task updates (Secondary) and a final message (Primary) | @@ -496,7 +496,7 @@ We need to decide what AIContent types, each agent response type will be mapped |-|-| | AutoGen | **Approach 1** Supports [configuring an agent](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/tutorial/agents.html#structured-output) at agent creation. | | Google ADK | **Approach 1** Both [input and output shemas can be specified for LLM Agents](https://google.github.io/adk-docs/agents/llm-agents/#structuring-data-input_schema-output_schema-output_key) at construction time. This option is specific to this agent type and other agent types do not necessarily support | -| AWS (Strands) | **Approach 2** Supports a special invocation method called [structured_output](https://strandsagents.com/latest/api-reference/agent/#strands.agent.agent.Agent.structured_output) | +| AWS (Strands) | **Approach 2** Supports a special invocation method called [structured_output](https://strandsagents.com/latest/documentation/docs/api-reference/python/agent/agent/#strands.agent.agent.Agent.structured_output) | | LangGraph | **Approach 1** Supports [configuring an agent](https://langchain-ai.github.io/langgraph/agents/agents/?h=structured#6-configure-structured-output) at agent construction time, and a [structured response](https://langchain-ai.github.io/langgraph/agents/run_agents/#output-format) can be retrieved as a special property on the agent response | | Agno | **Approach 1** Supports [configuring an agent](https://docs.agno.com/examples/getting-started/structured-output) at agent construction time | | A2A | **Informal Approach 2** Doesn't formally support schema negotiation, but [hints can be provided via metadata](https://a2a-protocol.org/latest/specification/#97-structured-data-exchange-requesting-and-providing-json) at invocation time | @@ -508,7 +508,7 @@ We need to decide what AIContent types, each agent response type will be mapped |-|-| | AutoGen | Supports a [stop reason](https://microsoft.github.io/autogen/stable/reference/python/autogen_agentchat.base.html#autogen_agentchat.base.TaskResult.stop_reason) which is a freeform text string | | Google ADK | [No equivalent present](https://github.com/google/adk-python/blob/main/src/google/adk/events/event.py) | -| AWS (Strands) | Exposes a [stop_reason](https://strandsagents.com/latest/api-reference/types/#strands.types.event_loop.StopReason) property on the [AgentResult](https://strandsagents.com/latest/api-reference/agent/#strands.agent.agent_result.AgentResult) class with options that are tied closely to LLM operations. | +| AWS (Strands) | Exposes a [stop_reason](https://strandsagents.com/latest/documentation/docs/api-reference/python/types/streaming/#strands.types.streaming.StopReason) property on the [AgentResult](https://strandsagents.com/latest/documentation/docs/api-reference/python/agent/agent_result/#strands.agent.agent_result) class with options that are tied closely to LLM operations. | | LangGraph | No equivalent present, output contains only [messages](https://langchain-ai.github.io/langgraph/agents/run_agents/#output-format) | | Agno | [No equivalent present](https://docs.agno.com/reference/agents/run-response) | | A2A | No equivalent present, response only contains a [message](https://a2a-protocol.org/latest/specification/#64-message-object) or [task](https://a2a-protocol.org/latest/specification/#61-task-object). | From 2a784247bc73f966fe46494918c638943aa68f54 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Wed, 7 Jan 2026 12:50:09 -0800 Subject: [PATCH 06/11] Small updates --- docs/decisions/0011-create-get-agent-api.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/decisions/0011-create-get-agent-api.md b/docs/decisions/0011-create-get-agent-api.md index ad3f0b9a2f..9a0066e3d6 100644 --- a/docs/decisions/0011-create-get-agent-api.md +++ b/docs/decisions/0011-create-get-agent-api.md @@ -245,15 +245,15 @@ Cons: ## Decision Outcome -Implement `get_agent`/`create_agent` API via `Option 1: Module-level functions`: +Implement `get_hosted_agent`/`create_hosted_agent` API via `Option 1: Module-level functions`: ```python -from agent_framework.azure import create_agent, get_agent +from agent_framework.azure import create_hosted_agent, get_hosted_agent ai_project_client = AIProjectClient(...) # Creates a remote agent first, then returns a local ChatAgent wrapper -created_agent = await create_agent( +created_agent = await create_hosted_agent( ai_project_client, name="", instructions="", @@ -261,8 +261,8 @@ created_agent = await create_agent( ) # Gets an existing remote agent and returns a local ChatAgent wrapper -first_agent = await get_agent(ai_project_client, agent_id=agent_id) +first_agent = await get_hosted_agent(ai_project_client, agent_id=agent_id) # Wraps an SDK agent instance (no extra HTTP call) -second_agent = get_agent(ai_project_client, agent_reference) +second_agent = get_hosted_agent(ai_project_client, agent_reference) ``` From ee9d4d118de2f7dc94347803a2d469eac19f8c48 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Wed, 7 Jan 2026 13:11:07 -0800 Subject: [PATCH 07/11] Fixed merge conflicts --- docs/decisions/0001-agent-run-response.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/decisions/0001-agent-run-response.md b/docs/decisions/0001-agent-run-response.md index b60878adff..fd18758d2d 100644 --- a/docs/decisions/0001-agent-run-response.md +++ b/docs/decisions/0001-agent-run-response.md @@ -64,7 +64,7 @@ Approaches observed from the compared SDKs: | AutoGen | **Approach 1** Separates messages into Agent-Agent (maps to Primary) and Internal (maps to Secondary) and these are returned as separate properties on the agent response object. See [types of messages](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/tutorial/messages.html#types-of-messages) and [Response](https://microsoft.github.io/autogen/stable/reference/python/autogen_agentchat.base.html#autogen_agentchat.base.Response) | **Approach 2** Returns a stream of internal events and the last item is a Response object. See [ChatAgent.on_messages_stream](https://microsoft.github.io/autogen/stable/reference/python/autogen_agentchat.base.html#autogen_agentchat.base.ChatAgent.on_messages_stream) | | OpenAI Agent SDK | **Approach 1** Separates new_items (Primary+Secondary) from final output (Primary) as separate properties on the [RunResult](https://github.com/openai/openai-agents-python/blob/main/src/agents/result.py#L39) | **Approach 1** Similar to non-streaming, has a way of streaming updates via a method on the response object which includes all data, and then a separate final output property on the response object which is populated only when the run is complete. See [RunResultStreaming](https://github.com/openai/openai-agents-python/blob/main/src/agents/result.py#L136) | | Google ADK | **Approach 2** [Emits events](https://google.github.io/adk-docs/runtime/#step-by-step-breakdown) with [FinalResponse](https://github.com/google/adk-java/blob/main/core/src/main/java/com/google/adk/events/Event.java#L232) true (Primary) / false (Secondary) and callers have to filter out those with false to get just the final response message | **Approach 2** Similar to non-streaming except [events](https://google.github.io/adk-docs/runtime/#streaming-vs-non-streaming-output-partialtrue) are emitted with [Partial](https://github.com/google/adk-java/blob/main/core/src/main/java/com/google/adk/events/Event.java#L133) true to indicate that they are streaming messages. A final non partial event is also emitted. | -| AWS (Strands) | **Approach 3** Returns an [AgentResult](https://strandsagents.com/latest/api-reference/agent/#strands.agent.agent_result.AgentResult) (Primary) with messages and a reason for the run's completion. | **Approach 2** [Streams events](https://strandsagents.com/latest/api-reference/agent/#strands.agent.agent.Agent.stream_async) (Primary+Secondary) including, response text, current_tool_use, even data from "callbacks" (strands plugins) | +| AWS (Strands) | **Approach 3** Returns an [AgentResult](https://strandsagents.com/latest/documentation/docs/api-reference/python/agent/agent_result/) (Primary) with messages and a reason for the run's completion. | **Approach 2** [Streams events](https://strandsagents.com/latest/documentation/docs/api-reference/python/agent/agent/#strands.agent.agent.Agent.stream_async) (Primary+Secondary) including, response text, current_tool_use, even data from "callbacks" (strands plugins) | | LangGraph | **Approach 2** A mixed list of all [messages](https://langchain-ai.github.io/langgraph/agents/run_agents/#output-format) | **Approach 2** A mixed list of all [messages](https://langchain-ai.github.io/langgraph/agents/run_agents/#output-format) | | Agno | **Combination of various approaches** Returns a [RunResponse](https://docs.agno.com/reference/agents/run-response) object with text content, messages (essentially chat history including inputs and instructions), reasoning and thinking text properties. Secondary events could potentially be extracted from messages. | **Approach 2** Returns [RunResponseEvent](https://docs.agno.com/reference/agents/run-response#runresponseevent-types-and-attributes) objects including tool call, memory update, etc, information, where the [RunResponseCompletedEvent](https://docs.agno.com/reference/agents/run-response#runresponsecompletedevent) has similar properties to RunResponse| | A2A | **Approach 3** Returns a [Task or Message](https://a2aproject.github.io/A2A/latest/specification/#71-messagesend) where the message is the final result (Primary) and task is a reference to a long running process. | **Approach 2** Returns a [stream](https://a2aproject.github.io/A2A/latest/specification/#72-messagestream) that contains task updates (Secondary) and a final message (Primary) | @@ -495,8 +495,8 @@ We need to decide what AIContent types, each agent response type will be mapped | SDK | Structured Outputs support | |-|-| | AutoGen | **Approach 1** Supports [configuring an agent](https://microsoft.github.io/autogen/stable/user-guide/agentchat-user-guide/tutorial/agents.html#structured-output) at agent creation. | -| Google ADK | **Approach 1** Both [input and output shemas can be specified for LLM Agents](https://google.github.io/adk-docs/agents/llm-agents/#structuring-data-input_schema-output_schema-output_key) at construction time. This option is specific to this agent type and other agent types do not necessarily support | -| AWS (Strands) | **Approach 2** Supports a special invocation method called [structured_output](https://strandsagents.com/latest/api-reference/agent/#strands.agent.agent.Agent.structured_output) | +| Google ADK | **Approach 1** Both [input and output schemas can be specified for LLM Agents](https://google.github.io/adk-docs/agents/llm-agents/#structuring-data-input_schema-output_schema-output_key) at construction time. This option is specific to this agent type and other agent types do not necessarily support | +| AWS (Strands) | **Approach 2** Supports a special invocation method called [structured_output](https://strandsagents.com/latest/documentation/docs/api-reference/python/agent/agent/#strands.agent.agent.Agent.structured_output) | | LangGraph | **Approach 1** Supports [configuring an agent](https://langchain-ai.github.io/langgraph/agents/agents/?h=structured#6-configure-structured-output) at agent construction time, and a [structured response](https://langchain-ai.github.io/langgraph/agents/run_agents/#output-format) can be retrieved as a special property on the agent response | | Agno | **Approach 1** Supports [configuring an agent](https://docs.agno.com/examples/getting-started/structured-output) at agent construction time | | A2A | **Informal Approach 2** Doesn't formally support schema negotiation, but [hints can be provided via metadata](https://a2a-protocol.org/latest/specification/#97-structured-data-exchange-requesting-and-providing-json) at invocation time | @@ -508,8 +508,8 @@ We need to decide what AIContent types, each agent response type will be mapped |-|-| | AutoGen | Supports a [stop reason](https://microsoft.github.io/autogen/stable/reference/python/autogen_agentchat.base.html#autogen_agentchat.base.TaskResult.stop_reason) which is a freeform text string | | Google ADK | [No equivalent present](https://github.com/google/adk-python/blob/main/src/google/adk/events/event.py) | -| AWS (Strands) | Exposes a [stop_reason](https://strandsagents.com/latest/api-reference/types/#strands.types.event_loop.StopReason) property on the [AgentResult](https://strandsagents.com/latest/api-reference/agent/#strands.agent.agent_result.AgentResult) class with options that are tied closely to LLM operations. | +| AWS (Strands) | Exposes a [stop_reason](https://strandsagents.com/latest/documentation/docs/api-reference/python/types/event_loop/#strands.types.event_loop.StopReason) property on the [AgentResult](https://strandsagents.com/latest/documentation/docs/api-reference/python/agent/agent_result/) class with options that are tied closely to LLM operations. | | LangGraph | No equivalent present, output contains only [messages](https://langchain-ai.github.io/langgraph/agents/run_agents/#output-format) | | Agno | [No equivalent present](https://docs.agno.com/reference/agents/run-response) | | A2A | No equivalent present, response only contains a [message](https://a2a-protocol.org/latest/specification/#64-message-object) or [task](https://a2a-protocol.org/latest/specification/#61-task-object). | -| Protocol Activity | [No equivalent present.](https://github.com/microsoft/Agents/blob/main/specs/activity/protocol-activity.md) | +| Protocol Activity | [No equivalent present.](https://github.com/microsoft/Agents/blob/main/specs/activity/protocol-activity.md) | \ No newline at end of file From 02ae9d69b9e9dbf4c62cf5039914c2c89e52f3f0 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Wed, 7 Jan 2026 13:11:57 -0800 Subject: [PATCH 08/11] Small fix --- docs/decisions/0001-agent-run-response.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/decisions/0001-agent-run-response.md b/docs/decisions/0001-agent-run-response.md index fd18758d2d..9f13af787c 100644 --- a/docs/decisions/0001-agent-run-response.md +++ b/docs/decisions/0001-agent-run-response.md @@ -512,4 +512,4 @@ We need to decide what AIContent types, each agent response type will be mapped | LangGraph | No equivalent present, output contains only [messages](https://langchain-ai.github.io/langgraph/agents/run_agents/#output-format) | | Agno | [No equivalent present](https://docs.agno.com/reference/agents/run-response) | | A2A | No equivalent present, response only contains a [message](https://a2a-protocol.org/latest/specification/#64-message-object) or [task](https://a2a-protocol.org/latest/specification/#61-task-object). | -| Protocol Activity | [No equivalent present.](https://github.com/microsoft/Agents/blob/main/specs/activity/protocol-activity.md) | \ No newline at end of file +| Protocol Activity | [No equivalent present.](https://github.com/microsoft/Agents/blob/main/specs/activity/protocol-activity.md) | From de31d116aeb6d6a5cb3c66ef799c8607d50b3c4d Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:35:14 -0800 Subject: [PATCH 09/11] Updated decision outcome section --- docs/decisions/0011-create-get-agent-api.md | 128 +++++++++++++++++--- 1 file changed, 114 insertions(+), 14 deletions(-) diff --git a/docs/decisions/0011-create-get-agent-api.md b/docs/decisions/0011-create-get-agent-api.md index 9a0066e3d6..96d992fbff 100644 --- a/docs/decisions/0011-create-get-agent-api.md +++ b/docs/decisions/0011-create-get-agent-api.md @@ -245,24 +245,124 @@ Cons: ## Decision Outcome -Implement `get_hosted_agent`/`create_hosted_agent` API via `Option 1: Module-level functions`: +Implement `create_agent`/`get_agent`/`as_agent` API via **Option 2: Provider object**. +### Rationale + +| Aspect | Option 1 (Functions) | Option 2 (Provider) | +|--------|----------------------|---------------------| +| Multiple implementations | One package may contain V1, V2, and other agent types. Function names like `create_agent` become ambiguous - which agent type does it create? | Each provider class is explicit: `AzureAIAgentProvider` vs `AzureAIAgentResponsesProvider` | +| Discoverability | Users must know to import specific functions from the package | IDE autocomplete on provider instance shows all available methods | +| Client reuse | SDK client must be passed to every function call: `create_agent(client, ...)`, `get_agent(client, ...)` | SDK client passed once at construction: `provider = Provider(client)` | + +**Option 1 example:** ```python -from agent_framework.azure import create_hosted_agent, get_hosted_agent +from agent_framework.azure import create_agent, get_agent +agent1 = await create_agent(client, name="Agent1", ...) # Which agent type, V1 or V2? +agent2 = await create_agent(client, name="Agent2", ...) # Repetitive client passing +``` -ai_project_client = AIProjectClient(...) +**Option 2 example:** +```python +from agent_framework.azure import AzureAIAgentProvider +provider = AzureAIAgentProvider(client) # Clear which service, client passed once +agent1 = await provider.create_agent(name="Agent1", ...) +agent2 = await provider.create_agent(name="Agent2", ...) +``` -# Creates a remote agent first, then returns a local ChatAgent wrapper -created_agent = await create_hosted_agent( - ai_project_client, - name="", - instructions="", - tools=[tool], -) +### Method Naming -# Gets an existing remote agent and returns a local ChatAgent wrapper -first_agent = await get_hosted_agent(ai_project_client, agent_id=agent_id) +| Operation | Python | .NET | Async | +|-----------|--------|------|-------| +| Create on service | `create_agent()` | `CreateAIAgent()` | Yes | +| Get from service | `get_agent(id=...)` | `GetAIAgent(agentId)` | Yes | +| Wrap SDK object | `as_agent(reference)` | `AsAIAgent(agentInstance)` | No | -# Wraps an SDK agent instance (no extra HTTP call) -second_agent = get_hosted_agent(ai_project_client, agent_reference) +The method names (`create_agent`, `get_agent`) do not explicitly mention "service" or "remote" because: +- In Python, the provider class name explicitly identifies the service (`AzureAIAgentProvider`, `OpenAIAssistantProvider`), making additional qualifiers in method names redundant. +- In .NET, these are extension methods on `AIProjectClient` or `AssistantClient`, which already imply service operations. + +### Provider Class Naming + +| Package | Provider Class | Service | +|---------|---------------|---------| +| `agent_framework.azure` | `AzureAIAgentResponsesProvider` | Azure AI Agent Service (V2) | +| `agent_framework.azure` | `AzureAIAgentProvider` | Azure AI Persistent Agents (V1) | +| `agent_framework.openai` | `OpenAIAssistantProvider` | OpenAI Assistants API | + +> **Note:** Azure AI naming is temporary. Final naming will be updated according to Azure AI / Microsoft Foundry renaming decisions. + +### Usage Examples + +#### Azure AI Agent Service V2 (based on Responses API) + +```python +from agent_framework.azure import AzureAIAgentResponsesProvider +from azure.ai.projects import AIProjectClient + +client = AIProjectClient(endpoint, credential) +provider = AzureAIAgentResponsesProvider(client) + +# Create new agent on service +agent = await provider.create_agent(name="MyAgent", model="gpt-4", instructions="...") + +# Get existing agent by ID +agent = await provider.get_agent(agent_id="agent-123") + +# Wrap already-fetched SDK object (no HTTP calls) +agent_ref = await client.agents.get_agent("agent-123") +agent = provider.as_agent(agent_ref) +``` + +#### Azure AI Persistent Agents V1 + +```python +from agent_framework.azure import AzureAIAgentProvider +from azure.ai.agents import AgentsClient + +client = AgentsClient(endpoint, credential) +provider = AzureAIAgentProvider(client) + +agent = await provider.create_agent(name="MyAgent", model="gpt-4", instructions="...") +agent = await provider.get_agent(agent_id="persistent-agent-456") +agent = provider.as_agent(persistent_agent) +``` + +#### OpenAI Assistants + +```python +from agent_framework.openai import OpenAIAssistantProvider +from openai import OpenAI + +client = OpenAI() +provider = OpenAIAssistantProvider(client) + +agent = await provider.create_agent(name="MyAssistant", model="gpt-4", instructions="...") +agent = await provider.get_agent(assistant_id="asst_123") +agent = provider.as_agent(assistant) +``` + +#### Local-Only Agents (No Provider) + +Current method `create_agent` (python) / `CreateAIAgent` (.NET) can be renamed to `as_agent` (python) / `AsAIAgent` (.NET) to emphasize the conversion logic rather than creation/initialization logic and to avoid collision with `create_agent` method for remote calls. + +```python +from agent_framework import ChatAgent +from agent_framework.openai import OpenAIChatClient + +# Convert chat client to ChatAgent (no remote service involved) +client = OpenAIChatClient(model="gpt-4") +agent = client.as_agent(name="LocalAgent", instructions="...") # instead of create_agent ``` + +### Adding New Agent Types + +Python: + +1. Create provider class in appropriate package. +2. Implement `create_agent`, `get_agent`, `as_agent` as applicable. + +.NET: + +1. Create static class for extension methods. +2. Implement `CreateAIAgentAsync`, `GetAIAgentAsync`, `AsAIAgent` as applicable. From d1fa6f66b1410b588bc907d9fad607debd5373f7 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Sun, 11 Jan 2026 18:55:15 -0800 Subject: [PATCH 10/11] Small fixes --- docs/decisions/0011-create-get-agent-api.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/decisions/0011-create-get-agent-api.md b/docs/decisions/0011-create-get-agent-api.md index 96d992fbff..1e6e0f369f 100644 --- a/docs/decisions/0011-create-get-agent-api.md +++ b/docs/decisions/0011-create-get-agent-api.md @@ -286,8 +286,8 @@ The method names (`create_agent`, `get_agent`) do not explicitly mention "servic | Package | Provider Class | Service | |---------|---------------|---------| -| `agent_framework.azure` | `AzureAIAgentResponsesProvider` | Azure AI Agent Service (V2) | -| `agent_framework.azure` | `AzureAIAgentProvider` | Azure AI Persistent Agents (V1) | +| `agent_framework.azure` | `AzureAIAgentResponsesProvider` | Azure AI Agent Service, based on Responses API (V2) | +| `agent_framework.azure` | `AzureAIAgentProvider` | Azure AI Agent Service (V1) | | `agent_framework.openai` | `OpenAIAssistantProvider` | OpenAI Assistants API | > **Note:** Azure AI naming is temporary. Final naming will be updated according to Azure AI / Microsoft Foundry renaming decisions. @@ -306,11 +306,11 @@ provider = AzureAIAgentResponsesProvider(client) # Create new agent on service agent = await provider.create_agent(name="MyAgent", model="gpt-4", instructions="...") -# Get existing agent by ID -agent = await provider.get_agent(agent_id="agent-123") +# Get existing agent by name +agent = await provider.get_agent(agent_name="MyAgent") # Wrap already-fetched SDK object (no HTTP calls) -agent_ref = await client.agents.get_agent("agent-123") +agent_ref = await client.agents.get("MyAgent") agent = provider.as_agent(agent_ref) ``` From c020b78a55c291f44d2dbcbc8ce6d7e295d4d1a0 Mon Sep 17 00:00:00 2001 From: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com> Date: Mon, 12 Jan 2026 10:02:02 -0800 Subject: [PATCH 11/11] Updated provider naming based on client SDK --- docs/decisions/0011-create-get-agent-api.md | 26 ++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/decisions/0011-create-get-agent-api.md b/docs/decisions/0011-create-get-agent-api.md index 1e6e0f369f..4703c1271d 100644 --- a/docs/decisions/0011-create-get-agent-api.md +++ b/docs/decisions/0011-create-get-agent-api.md @@ -251,7 +251,7 @@ Implement `create_agent`/`get_agent`/`as_agent` API via **Option 2: Provider obj | Aspect | Option 1 (Functions) | Option 2 (Provider) | |--------|----------------------|---------------------| -| Multiple implementations | One package may contain V1, V2, and other agent types. Function names like `create_agent` become ambiguous - which agent type does it create? | Each provider class is explicit: `AzureAIAgentProvider` vs `AzureAIAgentResponsesProvider` | +| Multiple implementations | One package may contain V1, V2, and other agent types. Function names like `create_agent` become ambiguous - which agent type does it create? | Each provider class is explicit: `AzureAIAgentsProvider` vs `AzureAIProjectAgentProvider` | | Discoverability | Users must know to import specific functions from the package | IDE autocomplete on provider instance shows all available methods | | Client reuse | SDK client must be passed to every function call: `create_agent(client, ...)`, `get_agent(client, ...)` | SDK client passed once at construction: `provider = Provider(client)` | @@ -264,8 +264,8 @@ agent2 = await create_agent(client, name="Agent2", ...) # Repetitive client pas **Option 2 example:** ```python -from agent_framework.azure import AzureAIAgentProvider -provider = AzureAIAgentProvider(client) # Clear which service, client passed once +from agent_framework.azure import AzureAIProjectAgentProvider +provider = AzureAIProjectAgentProvider(client) # Clear which service, client passed once agent1 = await provider.create_agent(name="Agent1", ...) agent2 = await provider.create_agent(name="Agent2", ...) ``` @@ -279,16 +279,16 @@ agent2 = await provider.create_agent(name="Agent2", ...) | Wrap SDK object | `as_agent(reference)` | `AsAIAgent(agentInstance)` | No | The method names (`create_agent`, `get_agent`) do not explicitly mention "service" or "remote" because: -- In Python, the provider class name explicitly identifies the service (`AzureAIAgentProvider`, `OpenAIAssistantProvider`), making additional qualifiers in method names redundant. +- In Python, the provider class name explicitly identifies the service (`AzureAIAgentsProvider`, `OpenAIAssistantProvider`), making additional qualifiers in method names redundant. - In .NET, these are extension methods on `AIProjectClient` or `AssistantClient`, which already imply service operations. ### Provider Class Naming -| Package | Provider Class | Service | -|---------|---------------|---------| -| `agent_framework.azure` | `AzureAIAgentResponsesProvider` | Azure AI Agent Service, based on Responses API (V2) | -| `agent_framework.azure` | `AzureAIAgentProvider` | Azure AI Agent Service (V1) | -| `agent_framework.openai` | `OpenAIAssistantProvider` | OpenAI Assistants API | +| Package | Provider Class | SDK Client | Service | +|---------|---------------|------------|---------| +| `agent_framework.azure` | `AzureAIProjectAgentProvider` | `AIProjectClient` | Azure AI Agent Service, based on Responses API (V2) | +| `agent_framework.azure` | `AzureAIAgentsProvider` | `AgentsClient` | Azure AI Agent Service (V1) | +| `agent_framework.openai` | `OpenAIAssistantProvider` | `AsyncOpenAI` | OpenAI Assistants API | > **Note:** Azure AI naming is temporary. Final naming will be updated according to Azure AI / Microsoft Foundry renaming decisions. @@ -297,11 +297,11 @@ The method names (`create_agent`, `get_agent`) do not explicitly mention "servic #### Azure AI Agent Service V2 (based on Responses API) ```python -from agent_framework.azure import AzureAIAgentResponsesProvider +from agent_framework.azure import AzureAIProjectAgentProvider from azure.ai.projects import AIProjectClient client = AIProjectClient(endpoint, credential) -provider = AzureAIAgentResponsesProvider(client) +provider = AzureAIProjectAgentProvider(client) # Create new agent on service agent = await provider.create_agent(name="MyAgent", model="gpt-4", instructions="...") @@ -317,11 +317,11 @@ agent = provider.as_agent(agent_ref) #### Azure AI Persistent Agents V1 ```python -from agent_framework.azure import AzureAIAgentProvider +from agent_framework.azure import AzureAIAgentsProvider from azure.ai.agents import AgentsClient client = AgentsClient(endpoint, credential) -provider = AzureAIAgentProvider(client) +provider = AzureAIAgentsProvider(client) agent = await provider.create_agent(name="MyAgent", model="gpt-4", instructions="...") agent = await provider.get_agent(agent_id="persistent-agent-456")