Skip to content

Commit 58d407a

Browse files
initial scaffold and AF code for migration guide
1 parent 7e07b48 commit 58d407a

1 file changed

Lines changed: 285 additions & 2 deletions

File tree

  • agent-framework/migration-guide/from-semantic-kernel

agent-framework/migration-guide/from-semantic-kernel/index.md

Lines changed: 285 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ i.e: OpenAI Assistants Provider
121121
await thread.DeleteAsync();
122122
```
123123

124-
#### Agent Framework
124+
#### Agent Framework
125125

126126
> [!NOTE]
127127
> OpenAI Responses introduced a new conversation model that simplifies how conversations are handled. This simplifies hosted thread management compared to the now deprecated OpenAI Assistants model. For more information see the [OpenAI Assistants migration guide](https://platform.openai.com/docs/assistants/migration).
@@ -150,7 +150,7 @@ In semantic kernel to expose a function as a tool you must:
150150
KernelFunction function = KernelFunctionFactory.CreateFromMethod(GetWeather);
151151
KernelPlugin plugin = KernelPluginFactory.CreateFromFunctions("KernelPluginName", [function]);
152152
Kernel kernel = ... // Create kernel
153-
kernel.Plugins.Add(plugin);
153+
kernel.Plugins.Add(plugin);
154154

155155
ChatCompletionAgent agent = new() { Kernel = kernel, ... };
156156
```
@@ -304,4 +304,287 @@ The agent framework supports all the abovementioned services via a single agent
304304
::: zone-end
305305
::: zone pivot="programming-language-python"
306306

307+
## Key differences
308+
309+
Here is a summary of the key differences between the Semantic Kernel Agent Framework and the Microsoft Agent Framework to help you migrate your code.
310+
311+
### 1. Package and Import Updates
312+
313+
#### Semantic Kernel
314+
315+
Semantic Kernel packages are installed as `semantic-kernel` and imported as `semantic_kernel`. The package also has a number of `extras` that you can install to install the different dependencies for different AI providers and other features.
316+
317+
```python
318+
from semantic_kernel import Kernel
319+
from semantic_kernel.agents import ChatCompletionAgent
320+
```
321+
322+
#### Agent Framework
323+
324+
Agent Framework package is installed as `agent-framework` and imported as `agent_framework`.
325+
Agent Framework is built up differently, it has a core package `agent-framework-core` that contains the core functionality, and then there are multiple packages that rely on that core package, such as `agent-framework-azure-ai`, `agent-framework-mem0`, `agent-framework-copilotstudio`, etc. When you run `pip install agent-framework` it will install the core package and *all* packages, so that you can get started with all the features quickly. When you are ready to reduce the number of packages because you know what you need, you can install only the packages you need, so for instance if you only plan to use Azure AI Foundry and Mem0 you can install only those two packages: `pip install agent-framework-azure-ai agent-framework-mem0`, `agent-framework-core` is a dependency to those two, so will automatically be installed.
326+
327+
Even though the packages are split up, the imports are all from `agent_framework`, or it's modules. So for instance to import the client for Azure AI Foundry you would do:
328+
329+
```python
330+
from agent_framework.azure import AzureAIAgentClient
331+
```
332+
333+
Many of the most commonly used types are imported directly from `agent_framework`:
334+
335+
```python
336+
from agent_framework import ChatMessage, ChatAgent
337+
```
338+
339+
### 2. Agent Type Consolidation
340+
341+
#### Semantic Kernel
342+
Semantic Kernel provides specific agent classes for various services, e.g. ChatCompletionAgent, AzureAIAgent, OpenAIAssistantAgent, etc. See [Agent types in Semantic Kernel](/semantic-kernel/agents/agent-types).
343+
344+
#### Agent Framework
345+
In Agent Framework the majority of agents are built using the `ChatAgent` which can be used with all the `ChatClient` based services, such as Azure AI Foundry, OpenAI ChatCompletion and OpenAI Responses. We currently have two other agents, `CopilotStudioAgent` for use with Copilot Studio and `A2AAgent` for use with A2A.
346+
347+
All the built-in agents are based on the BaseAgent (`from agent_framework import BaseAgent`). And all agents are consistent with the `AgentProtocol` (`from agent_framework import AgentProtocol`) interface.
348+
349+
### 2. Agent Creation Simplification
350+
351+
#### Semantic Kernel
352+
353+
Every agent in Semantic Kernel depends on a `Kernel` instance and will have
354+
an empty `Kernel` if not provided.
355+
356+
```python
357+
# TODO: Add Semantic Kernel example
358+
```
359+
360+
361+
#### Agent Framework
362+
363+
Agent creation in Agent Framework can be done in two ways, directly:
364+
365+
```python
366+
from agent_framework.azure import AzureAIAgentClient
367+
from agent_framework import ChatMessage, ChatAgent
368+
369+
agent = ChatAgent(chat_client=AzureAIAgentClient(credential=AzureCliCredential()), instructions="You are a helpful assistant")
370+
```
371+
or, with the convenience methods provided by chat clients:
372+
373+
```python
374+
from agent_framework.azure import AzureOpenAIChatClient
375+
from azure.identity import AzureCliCredential
376+
agent = AzureOpenAIChatClient(credential=AzureCliCredential()).create_agent(instructions="You are a helpful assistant")
377+
```
378+
379+
The direct method, exposes all possible parameters you can set for your agent, while the convenience method has a subset, you can still pass in the same set of parameters, because internally we call the direct method.
380+
381+
### 3. Agent Thread Creation
382+
383+
#### Semantic Kernel
384+
385+
The caller has to know the thread type and create it manually.
386+
387+
```python
388+
# TODO: Add thread creation examples
389+
```
390+
391+
#### Agent Framework
392+
393+
The agent can be asked to create a new thread for you.
394+
395+
```python
396+
agent = ...
397+
thread = agent.get_new_thread()
398+
```
399+
400+
a thread is then created in one of three ways:
401+
1. if the agent has a thread_id (or conversation_id or something similar) set, it will create a thread in the underlying service with that id.
402+
Once a thread has a `service_thread_id`, you can no longer use it to store messages in memory.
403+
And this only applies to agents that have a service-side thread concept. such as Azure AI Foundry Agents and OpenAI Assistants.
404+
2. if the agent has a `chat_message_store_factory` set, it will use that factory to create a message store and use that to create an in-memory thread.
405+
It can then no longer be used with a agent with the `store` parameter set to `True`.
406+
3. if neither of the above is set, we consider it `uninitialized` and depending on how it is used, it will either become a in-memory thread or a service thread.
407+
408+
#### Agent Framework
409+
410+
> [!NOTE]
411+
> OpenAI Responses introduced a new conversation model that simplifies how conversations are handled. This simplifies hosted thread management compared to the now deprecated OpenAI Assistants model. For more information see the [OpenAI Assistants migration guide](https://platform.openai.com/docs/assistants/migration).
412+
413+
Agent Framework doesn't have a thread deletion API in the `AgentThread` type as not all providers support hosted threads or thread deletion and this will become more common as more providers shift to responses based architectures.
414+
415+
If you require thread deletion and the provider allows this, the caller **should** keep track of the created threads and delete them later when necessary via the provider's sdk.
416+
417+
i.e: OpenAI Assistants Provider
418+
```python
419+
# TODO: Add thread deletion via provider SDK example
420+
```
421+
422+
### 5. Tool Registration
423+
424+
#### Semantic Kernel
425+
426+
In semantic kernel to expose a function as a tool you must:
427+
428+
1. Decorate the function with a `@kernel_function` decorator.
429+
2. Have a `Plugin` class or use the kernel plugin factory to wrap the function.
430+
3. Have a `Kernel` to add your plugin to.
431+
4. Pass the `Kernel` to the agent.
432+
433+
```python
434+
# TODO: Add SK tool registration example
435+
```
436+
437+
#### Agent Framework
438+
439+
In agent framework in a single call you can register tools directly in the agent creation process. But we no longer have the concept of a plugin, to wrap multiple functions, but you can still do that if you want to.
440+
441+
The simplest way to create a tool is just to create a python function:
442+
443+
```python
444+
def get_weather(location: str) -> str:
445+
"""Get the weather for a given location."""
446+
return f"The weather in {location} is sunny."
447+
448+
agent = chat_client.create_agent(tools=get_weather)
449+
```
450+
> Note: the `tools` parameter is present on both the agent creation, the `run` and `run_stream` methods, as well as the `get_response` and `get_streaming_response` methods, it allows you to supply tools both as a list or a single function.
451+
452+
The name of the function will then become the name of the tool, and the docstring will become the description of the tool, you can also add a description to the parameters:
453+
454+
```python
455+
from typing import Annotated
456+
457+
def get_weather(location: Annotated[str, "The location to get the weather for."]) -> str:
458+
"""Get the weather for a given location."""
459+
return f"The weather in {location} is sunny."
460+
```
461+
462+
Finally, you can use the decorator to further customize the name and description of the tool:
463+
464+
```python
465+
from typing import Annotated
466+
from agent_framework import ai_function
467+
468+
@ai_function(name="weather_tool", description="Retrieves weather information for any location")
469+
def get_weather(location: Annotated[str, "The location to get the weather for."])
470+
"""Get the weather for a given location."""
471+
return f"The weather in {location} is sunny."
472+
```
473+
474+
This also works when you create a class with multiple tools as methods.
475+
476+
When creating the agent, we can now provide the function tool to the agent, by passing it to the `tools` parameter.
477+
478+
```python
479+
class Plugin:
480+
481+
def __init__(self, initial_state: str):
482+
self.state: list[str] = [initial_state]
483+
484+
def get_weather(self, location: Annotated[str, "The location to get the weather for."]) -> str:
485+
"""Get the weather for a given location."""
486+
self.state.append(f"Requested weather for {location}. ")
487+
return f"The weather in {location} is sunny."
488+
489+
def get_weather_details(self, location: Annotated[str, "The location to get the weather details for."]) -> str:
490+
"""Get detailed weather for a given location."""
491+
self.state.append(f"Requested detailed weather for {location}. ")
492+
return f"The weather in {location} is sunny with a high of 25°C and a low of 15°C."
493+
494+
plugin = Plugin("Initial state")
495+
agent = chat_client.create_agent(tools=[plugin.get_weather, plugin.get_weather_details])
496+
497+
... # use the agent
498+
499+
print("Plugin state:", plugin.state)
500+
```
501+
> Note: the functions within the class can also be decorated with `@ai_function` to customize the name and description of the tools.
502+
503+
This mechanism is also useful for tools that need additional input that cannot be supplied by the LLM, such as connections, secrets, etc.
504+
505+
### 6. Agent Non-Streaming Invocation
506+
507+
Key differences can be seen in the method names from `invoke` to `run`, return types (e.g. `AgentRunResponse`) and parameters.
508+
509+
#### Semantic Kernel
510+
511+
The Non-Streaming uses an async iterator pattern for returning multiple agent messages.
512+
513+
```python
514+
# TODO: Add SK non-streaming invocation example
515+
```
516+
517+
#### Agent Framework
518+
519+
The Non-Streaming returns a single `AgentRunResponse` with the agent response that can contain multiple messages.
520+
The text result of the run is available in `response.text` or `str(AgentRunResponse)`.
521+
All messages created as part of the response are returned in the `response.messages` list.
522+
This may include tool call messages, function results, reasoning updates and final results.
523+
524+
```python
525+
agent = ...
526+
527+
response = await agent.run(user_input, thread)
528+
print("Agent response:", response.text)
529+
530+
```
531+
532+
### 7. Agent Streaming Invocation
533+
534+
Key differences in the method names from `invoke` to `run_stream`, return types (`AgentRunResponseUpdate`) and parameters.
535+
536+
#### Semantic Kernel
537+
538+
```python
539+
# TODO: Add SK streaming invocation example
540+
```
541+
542+
#### Agent Framework
543+
544+
Similar streaming API pattern with the key difference being that it returns `AgentRunResponseUpdate` objects including more agent related information per update.
545+
546+
All contents produced by any service underlying the Agent are returned. The textual result of the agent is available by concatenating the `response.text` values. And you can gather the updates up to a full response if needed.
547+
548+
```python
549+
from agent_framework import AgentRunResponse
550+
agent = ...
551+
updates = []
552+
async for update in agent.run_stream(user_input, thread):
553+
updates.append(update)
554+
print(update.text) # Update is str() friendly
555+
556+
full_response = AgentRunResponse.from_agent_run_response_updates(updates)
557+
print("Full agent response:", full_response.text)
558+
```
559+
560+
You can even do that directly:
561+
562+
```python
563+
from agent_framework import AgentRunResponse
564+
agent = ...
565+
full_response = AgentRunResponse.from_agent_response_generator(agent.run_stream(user_input, thread))
566+
print("Full agent response:", full_response.text)
567+
```
568+
569+
570+
### 9. Options Configuration
571+
572+
**Problem**: Complex options setup in SK
573+
574+
```python
575+
# TODO: Add SK options configuration example
576+
```
577+
578+
**Solution**: Simplified options in AF
579+
580+
In agent framework, we allow the passing of all parameters directly to the relevant methods, so that you do not have to import anything extra, or create any options objects, unless you want to. Internally we use a `ChatOptions` object, that you can also create and pass in if you want to. This is also created in a `ChatAgent` to hold the options and can be overridden per call.
581+
582+
```python
583+
agent = ...
584+
585+
response = await agent.run(user_input, thread, max_tokens=1000, frequency_penalty=0.5)
586+
```
587+
588+
> Note: The above is specific to a `ChatAgent`, because other agents may have different options, they should all accepts `messages` as a parameter, since that is defined in the `AgentProtocol`.
589+
307590
::: zone-end

0 commit comments

Comments
 (0)