Beendigung#
Im vorherigen Abschnitt haben wir untersucht, wie Agenten definiert und in Teams organisiert werden können, um Aufgaben zu lösen. Ein Lauf kann jedoch ewig dauern, und in vielen Fällen müssen wir wissen, *wann* wir ihn beenden müssen. Das ist die Aufgabe der Abbruchbedingung.
AgentChat unterstützt mehrere Abbruchbedingungen, indem es eine Basisklasse TerminationCondition und mehrere davon erblich implementierte Klassen bereitstellt.
Eine Abbruchbedingung ist ein aufrufbares Objekt, das eine Sequenz von BaseAgentEvent oder BaseChatMessage Objekten entgegennimmt, **seitdem die Bedingung das letzte Mal aufgerufen wurde**, und eine StopMessage zurückgibt, wenn die Konversation beendet werden soll, oder andernfalls None. Sobald eine Abbruchbedingung erreicht wurde, muss sie durch Aufrufen von reset() zurückgesetzt werden, bevor sie erneut verwendet werden kann.
Einige wichtige Anmerkungen zu Abbruchbedingungen
Sie sind zustandsbehaftet, werden aber nach jedem Beenden eines Laufs automatisch zurückgesetzt (
run()oderrun_stream()).Sie können mit den Operatoren UND und ODER kombiniert werden.
Hinweis
Bei Gruppenteams (d.h. RoundRobinGroupChat, SelectorGroupChat und Swarm) wird die Abbruchbedingung nach jeder Antwort eines Agenten aufgerufen. Während eine Antwort mehrere innere Nachrichten enthalten kann, ruft das Team seine Abbruchbedingung nur einmal für alle Nachrichten aus einer einzelnen Antwort auf. Die Bedingung wird also mit der "Delta-Sequenz" der Nachrichten seit dem letzten Aufruf aufgerufen.
Integrierte Abbruchbedingungen
MaxMessageTermination: Stoppt, nachdem eine angegebene Anzahl von Nachrichten erzeugt wurde, einschließlich Agenten- und Nachrichten.TextMentionTermination: Stoppt, wenn ein bestimmter Text oder eine Zeichenkette in einer Nachricht erwähnt wird (z.B. „TERMINATE“).TokenUsageTermination: Stoppt, wenn eine bestimmte Anzahl von Prompt- oder Completion-Tokens verwendet wird. Dies erfordert, dass die Agenten die Token-Nutzung in ihren Nachrichten melden.TimeoutTermination: Stoppt nach einer angegebenen Dauer in Sekunden.HandoffTermination: Stoppt, wenn ein Übergang zu einem bestimmten Ziel angefordert wird. Übergangsnachrichten können verwendet werden, um Muster wieSwarmzu erstellen. Dies ist nützlich, wenn Sie den Lauf unterbrechen und der Anwendung oder dem Benutzer die Eingabe ermöglichen möchten, wenn ein Agent an sie übergibt.SourceMatchTermination: Stoppt, nachdem ein bestimmter Agent antwortet.ExternalTermination: Ermöglicht die programmatische Steuerung der Beendigung von außerhalb des Laufs. Dies ist nützlich für UI-Integrationen (z.B. „Stop“-Schaltflächen in Chat-Oberflächen).StopMessageTermination: Stoppt, wenn eineStopMessagevon einem Agenten erzeugt wird.TextMessageTermination: Stoppt, wenn eineTextMessagevon einem Agenten erzeugt wird.FunctionCallTermination: Stoppt, wenn einToolCallExecutionEvent, das einFunctionExecutionResultmit einem übereinstimmenden Namen enthält, von einem Agenten erzeugt wird.FunctionalTermination: Stoppt, wenn ein Funktionsausdruck auf der letzten Delta-Sequenz von Nachrichten zuTrueevaluiert wird. Dies ist nützlich, um schnell benutzerdefinierte Abbruchbedingungen zu erstellen, die nicht von den integrierten abgedeckt werden.
Grundlegende Verwendung#
Um die Eigenschaften von Abbruchbedingungen zu demonstrieren, erstellen wir ein Team, das aus zwei Agenten besteht: einem primären Agenten, der für die Textgenerierung zuständig ist, und einem Kritiker-Agenten, der den generierten Text überprüft und Feedback gibt.
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.conditions import MaxMessageTermination, TextMentionTermination
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.ui import Console
from autogen_ext.models.openai import OpenAIChatCompletionClient
model_client = OpenAIChatCompletionClient(
model="gpt-4o",
temperature=1,
# api_key="sk-...", # Optional if you have an OPENAI_API_KEY env variable set.
)
# Create the primary agent.
primary_agent = AssistantAgent(
"primary",
model_client=model_client,
system_message="You are a helpful AI assistant.",
)
# Create the critic agent.
critic_agent = AssistantAgent(
"critic",
model_client=model_client,
system_message="Provide constructive feedback for every message. Respond with 'APPROVE' to when your feedbacks are addressed.",
)
Lassen Sie uns untersuchen, wie Abbruchbedingungen nach jedem Aufruf von run oder run_stream automatisch zurückgesetzt werden, sodass das Team seine Konversation von dort fortsetzen kann, wo es aufgehört hat.
max_msg_termination = MaxMessageTermination(max_messages=3)
round_robin_team = RoundRobinGroupChat([primary_agent, critic_agent], termination_condition=max_msg_termination)
# Use asyncio.run(...) if you are running this script as a standalone script.
await Console(round_robin_team.run_stream(task="Write a unique, Haiku about the weather in Paris"))
---------- user ----------
Write a unique, Haiku about the weather in Paris
---------- primary ----------
Gentle rain whispers,
Cobblestones glisten softly—
Paris dreams in gray.
[Prompt tokens: 30, Completion tokens: 19]
---------- critic ----------
The Haiku captures the essence of a rainy day in Paris beautifully, and the imagery is vivid. However, it's important to ensure the use of the traditional 5-7-5 syllable structure for Haikus. Your current Haiku lines are composed of 4-7-5 syllables, which slightly deviates from the form. Consider revising the first line to fit the structure.
For example:
Soft rain whispers down,
Cobblestones glisten softly —
Paris dreams in gray.
This revision maintains the essence of your original lines while adhering to the traditional Haiku structure.
[Prompt tokens: 70, Completion tokens: 120]
---------- Summary ----------
Number of messages: 3
Finish reason: Maximum number of messages 3 reached, current message count: 3
Total prompt tokens: 100
Total completion tokens: 139
Duration: 3.34 seconds
TaskResult(messages=[TextMessage(source='user', models_usage=None, content='Write a unique, Haiku about the weather in Paris'), TextMessage(source='primary', models_usage=RequestUsage(prompt_tokens=30, completion_tokens=19), content='Gentle rain whispers, \nCobblestones glisten softly— \nParis dreams in gray.'), TextMessage(source='critic', models_usage=RequestUsage(prompt_tokens=70, completion_tokens=120), content="The Haiku captures the essence of a rainy day in Paris beautifully, and the imagery is vivid. However, it's important to ensure the use of the traditional 5-7-5 syllable structure for Haikus. Your current Haiku lines are composed of 4-7-5 syllables, which slightly deviates from the form. Consider revising the first line to fit the structure.\n\nFor example:\nSoft rain whispers down, \nCobblestones glisten softly — \nParis dreams in gray.\n\nThis revision maintains the essence of your original lines while adhering to the traditional Haiku structure.")], stop_reason='Maximum number of messages 3 reached, current message count: 3')
Die Konversation wurde nach Erreichen des maximalen Nachrichtenlimits beendet. Da der primäre Agent nicht auf das Feedback antworten konnte, fahren wir mit der Konversation fort.
# Use asyncio.run(...) if you are running this script as a standalone script.
await Console(round_robin_team.run_stream())
---------- primary ----------
Thank you for your feedback. Here is the revised Haiku:
Soft rain whispers down,
Cobblestones glisten softly —
Paris dreams in gray.
[Prompt tokens: 181, Completion tokens: 32]
---------- critic ----------
The revised Haiku now follows the traditional 5-7-5 syllable pattern, and it still beautifully captures the atmospheric mood of Paris in the rain. The imagery and flow are both clear and evocative. Well done on making the adjustment!
APPROVE
[Prompt tokens: 234, Completion tokens: 54]
---------- primary ----------
Thank you for your kind words and approval. I'm glad the revision meets your expectations and captures the essence of Paris. If you have any more requests or need further assistance, feel free to ask!
[Prompt tokens: 279, Completion tokens: 39]
---------- Summary ----------
Number of messages: 3
Finish reason: Maximum number of messages 3 reached, current message count: 3
Total prompt tokens: 694
Total completion tokens: 125
Duration: 6.43 seconds
TaskResult(messages=[TextMessage(source='primary', models_usage=RequestUsage(prompt_tokens=181, completion_tokens=32), content='Thank you for your feedback. Here is the revised Haiku:\n\nSoft rain whispers down, \nCobblestones glisten softly — \nParis dreams in gray.'), TextMessage(source='critic', models_usage=RequestUsage(prompt_tokens=234, completion_tokens=54), content='The revised Haiku now follows the traditional 5-7-5 syllable pattern, and it still beautifully captures the atmospheric mood of Paris in the rain. The imagery and flow are both clear and evocative. Well done on making the adjustment! \n\nAPPROVE'), TextMessage(source='primary', models_usage=RequestUsage(prompt_tokens=279, completion_tokens=39), content="Thank you for your kind words and approval. I'm glad the revision meets your expectations and captures the essence of Paris. If you have any more requests or need further assistance, feel free to ask!")], stop_reason='Maximum number of messages 3 reached, current message count: 3')
Das Team setzte die Konversation von dort fort, wo es aufgehört hatte, sodass der primäre Agent auf das Feedback antworten konnte.
Kombinieren von Abbruchbedingungen#
Lassen Sie uns zeigen, wie Abbruchbedingungen mit den Operatoren UND (&) und ODER (|) kombiniert werden können, um komplexere Abbruchlogiken zu erstellen. Zum Beispiel erstellen wir ein Team, das entweder nach Erzeugung von 10 Nachrichten stoppt oder wenn der Kritiker-Agent eine Nachricht genehmigt.
max_msg_termination = MaxMessageTermination(max_messages=10)
text_termination = TextMentionTermination("APPROVE")
combined_termination = max_msg_termination | text_termination
round_robin_team = RoundRobinGroupChat([primary_agent, critic_agent], termination_condition=combined_termination)
# Use asyncio.run(...) if you are running this script as a standalone script.
await Console(round_robin_team.run_stream(task="Write a unique, Haiku about the weather in Paris"))
---------- user ----------
Write a unique, Haiku about the weather in Paris
---------- primary ----------
Spring breeze gently hums,
Cherry blossoms in full bloom—
Paris wakes to life.
[Prompt tokens: 467, Completion tokens: 19]
---------- critic ----------
The Haiku beautifully captures the awakening of Paris in the spring. The imagery of a gentle spring breeze and cherry blossoms in full bloom effectively conveys the rejuvenating feel of the season. The final line, "Paris wakes to life," encapsulates the renewed energy and vibrancy of the city. The Haiku adheres to the 5-7-5 syllable structure and portrays a vivid seasonal transformation in a concise and poetic manner. Excellent work!
APPROVE
[Prompt tokens: 746, Completion tokens: 93]
---------- Summary ----------
Number of messages: 3
Finish reason: Text 'APPROVE' mentioned
Total prompt tokens: 1213
Total completion tokens: 112
Duration: 2.75 seconds
TaskResult(messages=[TextMessage(source='user', models_usage=None, content='Write a unique, Haiku about the weather in Paris'), TextMessage(source='primary', models_usage=RequestUsage(prompt_tokens=467, completion_tokens=19), content='Spring breeze gently hums, \nCherry blossoms in full bloom— \nParis wakes to life.'), TextMessage(source='critic', models_usage=RequestUsage(prompt_tokens=746, completion_tokens=93), content='The Haiku beautifully captures the awakening of Paris in the spring. The imagery of a gentle spring breeze and cherry blossoms in full bloom effectively conveys the rejuvenating feel of the season. The final line, "Paris wakes to life," encapsulates the renewed energy and vibrancy of the city. The Haiku adheres to the 5-7-5 syllable structure and portrays a vivid seasonal transformation in a concise and poetic manner. Excellent work!\n\nAPPROVE')], stop_reason="Text 'APPROVE' mentioned")
Die Konversation stoppte, nachdem der Kritiker-Agent die Nachricht genehmigt hatte, obwohl sie auch gestoppt hätte, wenn 10 Nachrichten erzeugt worden wären.
Alternativ können wir, wenn wir den Lauf nur stoppen wollen, wenn beide Bedingungen erfüllt sind, den UND (&) Operator verwenden.
combined_termination = max_msg_termination & text_termination
Benutzerdefinierte Abbruchbedingung#
Die integrierten Abbruchbedingungen sind für die meisten Anwendungsfälle ausreichend. Es kann jedoch Fälle geben, in denen Sie eine benutzerdefinierte Abbruchbedingung implementieren müssen, die nicht in die bestehenden passt. Dies können Sie tun, indem Sie von der Klasse TerminationCondition erben.
In diesem Beispiel erstellen wir eine benutzerdefinierte Abbruchbedingung, die die Konversation stoppt, wenn ein bestimmter Funktionsaufruf erfolgt.
from typing import Sequence
from autogen_agentchat.base import TerminatedException, TerminationCondition
from autogen_agentchat.messages import BaseAgentEvent, BaseChatMessage, StopMessage, ToolCallExecutionEvent
from autogen_core import Component
from pydantic import BaseModel
from typing_extensions import Self
class FunctionCallTerminationConfig(BaseModel):
"""Configuration for the termination condition to allow for serialization
and deserialization of the component.
"""
function_name: str
class FunctionCallTermination(TerminationCondition, Component[FunctionCallTerminationConfig]):
"""Terminate the conversation if a FunctionExecutionResult with a specific name is received."""
component_config_schema = FunctionCallTerminationConfig
component_provider_override = "autogen_agentchat.conditions.FunctionCallTermination"
"""The schema for the component configuration."""
def __init__(self, function_name: str) -> None:
self._terminated = False
self._function_name = function_name
@property
def terminated(self) -> bool:
return self._terminated
async def __call__(self, messages: Sequence[BaseAgentEvent | BaseChatMessage]) -> StopMessage | None:
if self._terminated:
raise TerminatedException("Termination condition has already been reached")
for message in messages:
if isinstance(message, ToolCallExecutionEvent):
for execution in message.content:
if execution.name == self._function_name:
self._terminated = True
return StopMessage(
content=f"Function '{self._function_name}' was executed.",
source="FunctionCallTermination",
)
return None
async def reset(self) -> None:
self._terminated = False
def _to_config(self) -> FunctionCallTerminationConfig:
return FunctionCallTerminationConfig(
function_name=self._function_name,
)
@classmethod
def _from_config(cls, config: FunctionCallTerminationConfig) -> Self:
return cls(
function_name=config.function_name,
)
Lassen Sie uns diese neue Abbruchbedingung verwenden, um die Konversation zu stoppen, wenn der Kritiker-Agent eine Nachricht mit der Funktion approve genehmigt.
Zuerst erstellen wir eine einfache Funktion, die aufgerufen wird, wenn der Kritiker-Agent eine Nachricht genehmigt.
def approve() -> None:
"""Approve the message when all feedbacks have been addressed."""
pass
Dann erstellen wir die Agenten. Der Kritiker-Agent ist mit dem approve-Tool ausgestattet.
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.ui import Console
from autogen_ext.models.openai import OpenAIChatCompletionClient
model_client = OpenAIChatCompletionClient(
model="gpt-4o",
temperature=1,
# api_key="sk-...", # Optional if you have an OPENAI_API_KEY env variable set.
)
# Create the primary agent.
primary_agent = AssistantAgent(
"primary",
model_client=model_client,
system_message="You are a helpful AI assistant.",
)
# Create the critic agent with the approve function as a tool.
critic_agent = AssistantAgent(
"critic",
model_client=model_client,
tools=[approve], # Register the approve function as a tool.
system_message="Provide constructive feedback. Use the approve tool to approve when all feedbacks are addressed.",
)
Nun erstellen wir die Abbruchbedingung und das Team. Wir führen das Team mit der Aufgabe der Gedichteschreibung aus.
function_call_termination = FunctionCallTermination(function_name="approve")
round_robin_team = RoundRobinGroupChat([primary_agent, critic_agent], termination_condition=function_call_termination)
# Use asyncio.run(...) if you are running this script as a standalone script.
await Console(round_robin_team.run_stream(task="Write a unique, Haiku about the weather in Paris"))
await model_client.close()
---------- user ----------
Write a unique, Haiku about the weather in Paris
---------- primary ----------
Raindrops gently fall,
Cobblestones shine in dim light—
Paris dreams in grey.
---------- critic ----------
This Haiku beautifully captures a melancholic yet romantic image of Paris in the rain. The use of sensory imagery like "Raindrops gently fall" and "Cobblestones shine" effectively paints a vivid picture. It could be interesting to experiment with more distinct seasonal elements of Paris, such as incorporating the Seine River or iconic landmarks in the context of the weather. Overall, it successfully conveys the atmosphere of Paris in subtle, poetic imagery.
---------- primary ----------
Thank you for your feedback! I’m glad you enjoyed the imagery. Here’s another Haiku that incorporates iconic Parisian elements:
Eiffel stands in mist,
Seine's ripple mirrors the sky—
Spring whispers anew.
---------- critic ----------
[FunctionCall(id='call_QEWJZ873EG4UIEpsQHi1HsAu', arguments='{}', name='approve')]
---------- critic ----------
[FunctionExecutionResult(content='None', name='approve', call_id='call_QEWJZ873EG4UIEpsQHi1HsAu', is_error=False)]
---------- critic ----------
None
TaskResult(messages=[TextMessage(source='user', models_usage=None, metadata={}, content='Write a unique, Haiku about the weather in Paris', type='TextMessage'), TextMessage(source='primary', models_usage=RequestUsage(prompt_tokens=30, completion_tokens=23), metadata={}, content='Raindrops gently fall, \nCobblestones shine in dim light— \nParis dreams in grey. ', type='TextMessage'), TextMessage(source='critic', models_usage=RequestUsage(prompt_tokens=99, completion_tokens=90), metadata={}, content='This Haiku beautifully captures a melancholic yet romantic image of Paris in the rain. The use of sensory imagery like "Raindrops gently fall" and "Cobblestones shine" effectively paints a vivid picture. It could be interesting to experiment with more distinct seasonal elements of Paris, such as incorporating the Seine River or iconic landmarks in the context of the weather. Overall, it successfully conveys the atmosphere of Paris in subtle, poetic imagery.', type='TextMessage'), TextMessage(source='primary', models_usage=RequestUsage(prompt_tokens=152, completion_tokens=48), metadata={}, content="Thank you for your feedback! I’m glad you enjoyed the imagery. Here’s another Haiku that incorporates iconic Parisian elements:\n\nEiffel stands in mist, \nSeine's ripple mirrors the sky— \nSpring whispers anew. ", type='TextMessage'), ToolCallRequestEvent(source='critic', models_usage=RequestUsage(prompt_tokens=246, completion_tokens=11), metadata={}, content=[FunctionCall(id='call_QEWJZ873EG4UIEpsQHi1HsAu', arguments='{}', name='approve')], type='ToolCallRequestEvent'), ToolCallExecutionEvent(source='critic', models_usage=None, metadata={}, content=[FunctionExecutionResult(content='None', name='approve', call_id='call_QEWJZ873EG4UIEpsQHi1HsAu', is_error=False)], type='ToolCallExecutionEvent'), ToolCallSummaryMessage(source='critic', models_usage=None, metadata={}, content='None', type='ToolCallSummaryMessage')], stop_reason="Function 'approve' was executed.")
Sie sehen, dass die Konversation gestoppt wurde, als der Kritiker-Agent die Nachricht mit dem Funktionsaufruf approve genehmigt hat.