Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Microsoft Agent Framework unterstützt das Erstellen von benutzerdefinierten Agents, indem sie von der AIAgent Klasse erben und die erforderlichen Methoden implementieren.
In diesem Artikel wird erläutert, wie Sie einen einfachen benutzerdefinierten Agent erstellen, der die Benutzereingaben in Großbuchstaben zurückgibt. In den meisten Fällen umfasst das Erstellen Ihres eigenen Agents komplexere Logik und Integration in einen KI-Dienst.
Erste Schritte
Fügen Sie dem Projekt die erforderlichen NuGet-Pakete hinzu.
dotnet add package Microsoft.Agents.AI.Abstractions --prerelease
Erstellen eines benutzerdefinierten Agents
Die Agentsitzung
Zum Erstellen eines benutzerdefinierten Agents benötigen Sie auch eine Sitzung, die zum Nachverfolgen des Status einer einzelnen Unterhaltung verwendet wird, einschließlich des Nachrichtenverlaufs und aller anderen Status, den der Agent verwalten muss.
Um den Einstieg zu erleichtern, können Sie von verschiedenen Basisklassen erben, die allgemeine Sitzungsspeichermechanismen implementieren.
-
InMemoryAgentSession– speichert den Chatverlauf im Arbeitsspeicher und kann in JSON serialisiert werden. -
ServiceIdAgentSession– speichert keinen Chatverlauf, ermöglicht ihnen jedoch die Zuordnung einer ID zur Sitzung, unter der der Chatverlauf extern gespeichert werden kann.
In diesem Beispiel verwenden Sie die InMemoryAgentSession Basisklasse für die benutzerdefinierte Sitzung.
internal sealed class CustomAgentSession : InMemoryAgentSession
{
internal CustomAgentSession() : base() { }
internal CustomAgentSession(JsonElement serializedSessionState, JsonSerializerOptions? jsonSerializerOptions = null)
: base(serializedSessionState, jsonSerializerOptions) { }
}
Die Agent-Klasse
Erstellen Sie als Nächstes die Agentklasse selbst, indem Sie von der AIAgent Klasse erben.
internal sealed class UpperCaseParrotAgent : AIAgent
{
}
Erstellen von Sitzungen
Sitzungen werden immer über zwei Factorymethoden für die Agentklasse erstellt. Dadurch kann der Agent steuern, wie Sitzungen erstellt und deserialisiert werden. Agents können daher alle zusätzlichen Zustände oder Verhaltensweisen anfügen, die beim Erstellen der Sitzung erforderlich sind.
Es sind zwei Methoden erforderlich, die implementiert werden müssen:
public override Task<AgentSession> GetNewSessionAsync(CancellationToken cancellationToken = default)
=> Task.FromResult<AgentSession>(new CustomAgentSession());
public override Task<AgentSession> DeserializeSessionAsync(JsonElement serializedSession, JsonSerializerOptions? jsonSerializerOptions = null, CancellationToken cancellationToken = default)
=> Task.FromResult<AgentSession>(new CustomAgentSession(serializedSession, jsonSerializerOptions));
Kern-Agent-Logik
Die Kernlogik des Agents besteht darin, eingabemeldungen zu übernehmen, den Text in Großbuchstaben zu konvertieren und als Antwortnachrichten zurückzugeben.
Fügen Sie die folgende Methode hinzu, um diese Logik zu enthalten.
Die Eingabemeldungen werden geklont, da verschiedene Aspekte der Eingabemeldungen geändert werden müssen, um gültige Antwortnachrichten zu sein. Zum Beispiel muss die Rolle in Assistant geändert werden.
private static IEnumerable<ChatMessage> CloneAndToUpperCase(IEnumerable<ChatMessage> messages, string agentName) => messages.Select(x =>
{
var messageClone = x.Clone();
messageClone.Role = ChatRole.Assistant;
messageClone.MessageId = Guid.NewGuid().ToString();
messageClone.AuthorName = agentName;
messageClone.Contents = x.Contents.Select(c => c is TextContent tc ? new TextContent(tc.Text.ToUpperInvariant())
{
AdditionalProperties = tc.AdditionalProperties,
Annotations = tc.Annotations,
RawRepresentation = tc.RawRepresentation
} : c).ToList();
return messageClone;
});
Agent-Ausführungsmethoden
Schließlich müssen Sie die beiden Kernmethoden implementieren, die verwendet werden, um den Agenten auszuführen: eine für Nicht-Streaming und eine für Streaming.
Für beide Methoden müssen Sie sicherstellen, dass eine Sitzung bereitgestellt wird, und falls nicht, eine neue Sitzung erstellen.
Nachrichten können abgerufen und an die ChatHistoryProvider Sitzung übergeben werden.
Wenn Sie dies nicht tun, kann der Benutzer keine Multi-Turn-Unterhaltung mit einem Agenten führen, und jede Ausführung wird als eine neue Interaktion betrachtet.
public override async Task<AgentResponse> RunAsync(IEnumerable<ChatMessage> messages, AgentSession? session = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default)
{
session ??= await this.GetNewSessionAsync(cancellationToken);
// Get existing messages from the store
var invokingContext = new ChatHistoryProvider.InvokingContext(messages);
var storeMessages = await typedSession.ChatHistoryProvider.InvokingAsync(invokingContext, cancellationToken);
List<ChatMessage> responseMessages = CloneAndToUpperCase(messages, this.DisplayName).ToList();
// Notify the session of the input and output messages.
var invokedContext = new ChatHistoryProvider.InvokedContext(messages, storeMessages)
{
ResponseMessages = responseMessages
};
await typedSession.ChatHistoryProvider.InvokedAsync(invokedContext, cancellationToken);
return new AgentResponse
{
AgentId = this.Id,
ResponseId = Guid.NewGuid().ToString(),
Messages = responseMessages
};
}
public override async IAsyncEnumerable<AgentResponseUpdate> RunStreamingAsync(IEnumerable<ChatMessage> messages, AgentSession? session = null, AgentRunOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
session ??= await this.GetNewSessionAsync(cancellationToken);
// Get existing messages from the store
var invokingContext = new ChatHistoryProvider.InvokingContext(messages);
var storeMessages = await typedSession.ChatHistoryProvider.InvokingAsync(invokingContext, cancellationToken);
List<ChatMessage> responseMessages = CloneAndToUpperCase(messages, this.DisplayName).ToList();
// Notify the session of the input and output messages.
var invokedContext = new ChatHistoryProvider.InvokedContext(messages, storeMessages)
{
ResponseMessages = responseMessages
};
await typedSession.ChatHistoryProvider.InvokedAsync(invokedContext, cancellationToken);
foreach (var message in responseMessages)
{
yield return new AgentResponseUpdate
{
AgentId = this.Id,
AuthorName = this.DisplayName,
Role = ChatRole.Assistant,
Contents = message.Contents,
ResponseId = Guid.NewGuid().ToString(),
MessageId = Guid.NewGuid().ToString()
};
}
}
Den Agent verwenden
Wenn die AIAgent-Methoden alle ordnungsgemäß implementiert sind, wäre der Agent ein Standardagent und würde Standardbetriebsfunktionen eines Agents unterstützen.
Weitere Informationen zum Ausführen und Interagieren mit Agenten finden Sie in den Agenten-Einführungstutorials.
Microsoft Agent Framework unterstützt das Erstellen von benutzerdefinierten Agents, indem sie von der BaseAgent Klasse erben und die erforderlichen Methoden implementieren.
Dieses Dokument zeigt, wie Sie einen einfachen benutzerdefinierten Agent erstellen, der die Benutzereingabe mit einem Präfix wiedergibt. In den meisten Fällen umfasst das Erstellen Ihres eigenen Agents komplexere Logik und Integration in einen KI-Dienst.
Erste Schritte
Fügen Sie ihrem Projekt die erforderlichen Python-Pakete hinzu.
pip install agent-framework-core --pre
Erstellen eines benutzerdefinierten Agents
Das Agent-Protokoll
Das Framework stellt das AgentProtocol Protokoll bereit, das die Schnittstelle definiert, die alle Agents implementieren müssen. Benutzerdefinierte Agents können dieses Protokoll entweder direkt implementieren oder die BaseAgent Klasse zur Vereinfachung erweitern.
from agent_framework import AgentProtocol, AgentResponse, AgentResponseUpdate, AgentThread, ChatMessage
from collections.abc import AsyncIterable
from typing import Any
class MyCustomAgent(AgentProtocol):
"""A custom agent that implements the AgentProtocol directly."""
@property
def id(self) -> str:
"""Returns the ID of the agent."""
...
async def run(
self,
messages: str | ChatMessage | list[str] | list[ChatMessage] | None = None,
*,
thread: AgentThread | None = None,
**kwargs: Any,
) -> AgentResponse:
"""Execute the agent and return a complete response."""
...
def run_stream(
self,
messages: str | ChatMessage | list[str] | list[ChatMessage] | None = None,
*,
thread: AgentThread | None = None,
**kwargs: Any,
) -> AsyncIterable[AgentResponseUpdate]:
"""Execute the agent and yield streaming response updates."""
...
Verwenden von BaseAgent
Der empfohlene Ansatz besteht darin, die BaseAgent Klasse zu erweitern, die allgemeine Funktionen bereitstellt und die Implementierung vereinfacht:
from agent_framework import (
BaseAgent,
AgentResponse,
AgentResponseUpdate,
AgentThread,
ChatMessage,
Role,
TextContent,
)
from collections.abc import AsyncIterable
from typing import Any
class EchoAgent(BaseAgent):
"""A simple custom agent that echoes user messages with a prefix."""
echo_prefix: str = "Echo: "
def __init__(
self,
*,
name: str | None = None,
description: str | None = None,
echo_prefix: str = "Echo: ",
**kwargs: Any,
) -> None:
"""Initialize the EchoAgent.
Args:
name: The name of the agent.
description: The description of the agent.
echo_prefix: The prefix to add to echoed messages.
**kwargs: Additional keyword arguments passed to BaseAgent.
"""
super().__init__(
name=name,
description=description,
echo_prefix=echo_prefix,
**kwargs,
)
async def run(
self,
messages: str | ChatMessage | list[str] | list[ChatMessage] | None = None,
*,
thread: AgentThread | None = None,
**kwargs: Any,
) -> AgentResponse:
"""Execute the agent and return a complete response.
Args:
messages: The message(s) to process.
thread: The conversation thread (optional).
**kwargs: Additional keyword arguments.
Returns:
An AgentResponse containing the agent's reply.
"""
# Normalize input messages to a list
normalized_messages = self._normalize_messages(messages)
if not normalized_messages:
response_message = ChatMessage(
role=Role.ASSISTANT,
contents=[TextContent(text="Hello! I'm a custom echo agent. Send me a message and I'll echo it back.")],
)
else:
# For simplicity, echo the last user message
last_message = normalized_messages[-1]
if last_message.text:
echo_text = f"{self.echo_prefix}{last_message.text}"
else:
echo_text = f"{self.echo_prefix}[Non-text message received]"
response_message = ChatMessage(role=Role.ASSISTANT, contents=[TextContent(text=echo_text)])
# Notify the thread of new messages if provided
if thread is not None:
await self._notify_thread_of_new_messages(thread, normalized_messages, response_message)
return AgentResponse(messages=[response_message])
async def run_stream(
self,
messages: str | ChatMessage | list[str] | list[ChatMessage] | None = None,
*,
thread: AgentThread | None = None,
**kwargs: Any,
) -> AsyncIterable[AgentResponseUpdate]:
"""Execute the agent and yield streaming response updates.
Args:
messages: The message(s) to process.
thread: The conversation thread (optional).
**kwargs: Additional keyword arguments.
Yields:
AgentResponseUpdate objects containing chunks of the response.
"""
# Normalize input messages to a list
normalized_messages = self._normalize_messages(messages)
if not normalized_messages:
response_text = "Hello! I'm a custom echo agent. Send me a message and I'll echo it back."
else:
# For simplicity, echo the last user message
last_message = normalized_messages[-1]
if last_message.text:
response_text = f"{self.echo_prefix}{last_message.text}"
else:
response_text = f"{self.echo_prefix}[Non-text message received]"
# Simulate streaming by yielding the response word by word
words = response_text.split()
for i, word in enumerate(words):
# Add space before word except for the first one
chunk_text = f" {word}" if i > 0 else word
yield AgentResponseUpdate(
contents=[TextContent(text=chunk_text)],
role=Role.ASSISTANT,
)
# Small delay to simulate streaming
await asyncio.sleep(0.1)
# Notify the thread of the complete response if provided
if thread is not None:
complete_response = ChatMessage(role=Role.ASSISTANT, contents=[TextContent(text=response_text)])
await self._notify_thread_of_new_messages(thread, normalized_messages, complete_response)
Den Agent verwenden
Wenn agent-Methoden alle ordnungsgemäß implementiert sind, würde der Agent alle Standard-Agent-Vorgänge unterstützen.
Weitere Informationen zum Ausführen und Interagieren mit Agenten finden Sie in den Agenten-Einführungstutorials.