fix(agent): Fix & improve agent self-termination and resumption mechanism

- Add `AgentFinished` exception (subclass of `AgentTerminated`)
- Raise `AgentFinished` instead of `AgentTerminated` in `finish` method
- Remove resumption patch from PR #6990 in `BaseAgent`
- Clean up implementation of `finish` in `AgentProtocolServer`
- Add resumption mechanism in `run_auto_gpt` (main.py)
pull/7014/head
Reinier van der Leer 2024-03-13 20:42:12 +01:00
parent fd2c26188f
commit da4f013a5d
No known key found for this signature in database
GPG Key ID: CDC1180FDAE06193
5 changed files with 50 additions and 37 deletions

View File

@ -177,14 +177,6 @@ class BaseAgent(Configurable[BaseAgentSettings], ABC):
self.legacy_config = legacy_config self.legacy_config = legacy_config
"""LEGACY: Monolithic application configuration.""" """LEGACY: Monolithic application configuration."""
# In case the agent is resumed, cursor is set to the last episode
if self.event_history:
# To prevent errors, when the last action is "finish", we register a result
# And move cursor to the next action
if self.event_history.current_episode.action.name == "finish":
self.event_history.register_result(ActionSuccessResult())
self.event_history.cursor = len(self.event_history)
self.llm_provider = llm_provider self.llm_provider = llm_provider
self.prompt_strategy = prompt_strategy self.prompt_strategy = prompt_strategy

View File

@ -18,6 +18,10 @@ class AgentTerminated(AgentException):
"""The agent terminated or was terminated""" """The agent terminated or was terminated"""
class AgentFinished(AgentTerminated):
"""The agent self-terminated"""
class ConfigurationError(AgentException): class ConfigurationError(AgentException):
"""Error caused by invalid, incompatible or otherwise incorrect configuration""" """Error caused by invalid, incompatible or otherwise incorrect configuration"""

View File

@ -30,6 +30,7 @@ from sentry_sdk import set_user
from autogpt.agent_factory.configurators import configure_agent_with_state from autogpt.agent_factory.configurators import configure_agent_with_state
from autogpt.agent_factory.generators import generate_agent_for_task from autogpt.agent_factory.generators import generate_agent_for_task
from autogpt.agent_manager import AgentManager from autogpt.agent_manager import AgentManager
from autogpt.agents.utils.exceptions import AgentFinished
from autogpt.commands.system import finish from autogpt.commands.system import finish
from autogpt.commands.user_interaction import ask_user from autogpt.commands.user_interaction import ask_user
from autogpt.config import Config from autogpt.config import Config
@ -230,26 +231,6 @@ class AgentProtocolServer:
task=task, step=step, relative_path=path task=task, step=step, relative_path=path
) )
if step.is_last and execute_command == finish.__name__:
assert execute_command_args
additional_output = {}
task_total_cost = agent.llm_provider.get_incurred_cost()
if task_total_cost > 0:
additional_output["task_total_cost"] = task_total_cost
logger.info(
f"Total LLM cost for task {task_id}: "
f"${round(task_total_cost, 2)}"
)
step = await self.db.update_step(
task_id=task_id,
step_id=step.step_id,
output=execute_command_args["reason"],
additional_output=additional_output,
)
return step
if execute_command == ask_user.__name__: # HACK if execute_command == ask_user.__name__: # HACK
execute_result = ActionSuccessResult(outputs=user_input) execute_result = ActionSuccessResult(outputs=user_input)
agent.event_history.register_result(execute_result) agent.event_history.register_result(execute_result)
@ -261,11 +242,31 @@ class AgentProtocolServer:
step_id=step.step_id, step_id=step.step_id,
status="running", status="running",
) )
# Execute previously proposed action
execute_result = await agent.execute( try:
command_name=execute_command, # Execute previously proposed action
command_args=execute_command_args, execute_result = await agent.execute(
) command_name=execute_command,
command_args=execute_command_args,
)
except AgentFinished:
additional_output = {}
task_total_cost = agent.llm_provider.get_incurred_cost()
if task_total_cost > 0:
additional_output["task_total_cost"] = task_total_cost
logger.info(
f"Total LLM cost for task {task_id}: "
f"${round(task_total_cost, 2)}"
)
step = await self.db.update_step(
task_id=task_id,
step_id=step.step_id,
output=execute_command_args["reason"],
additional_output=additional_output,
)
await agent.save_state()
return step
else: else:
assert user_input assert user_input
execute_result = await agent.execute( execute_result = await agent.execute(

View File

@ -16,8 +16,6 @@ from typing import TYPE_CHECKING, Optional
from colorama import Fore, Style from colorama import Fore, Style
from forge.sdk.db import AgentDB from forge.sdk.db import AgentDB
from autogpt.file_storage import FileStorageBackendName, get_storage
if TYPE_CHECKING: if TYPE_CHECKING:
from autogpt.agents.agent import Agent from autogpt.agents.agent import Agent
@ -26,6 +24,7 @@ from autogpt.agent_factory.profile_generator import generate_agent_profile_for_t
from autogpt.agent_manager import AgentManager from autogpt.agent_manager import AgentManager
from autogpt.agents import AgentThoughts, CommandArgs, CommandName from autogpt.agents import AgentThoughts, CommandArgs, CommandName
from autogpt.agents.utils.exceptions import AgentTerminated, InvalidAgentResponseError from autogpt.agents.utils.exceptions import AgentTerminated, InvalidAgentResponseError
from autogpt.commands.system import finish
from autogpt.config import ( from autogpt.config import (
AIDirectives, AIDirectives,
AIProfile, AIProfile,
@ -35,8 +34,10 @@ from autogpt.config import (
) )
from autogpt.core.resource.model_providers.openai import OpenAIProvider from autogpt.core.resource.model_providers.openai import OpenAIProvider
from autogpt.core.runner.client_lib.utils import coroutine from autogpt.core.runner.client_lib.utils import coroutine
from autogpt.file_storage import FileStorageBackendName, get_storage
from autogpt.logs.config import configure_chat_plugins, configure_logging from autogpt.logs.config import configure_chat_plugins, configure_logging
from autogpt.logs.helpers import print_attribute, speak from autogpt.logs.helpers import print_attribute, speak
from autogpt.models.action_history import ActionInterruptedByHuman
from autogpt.plugins import scan_plugins from autogpt.plugins import scan_plugins
from scripts.install_plugin_deps import install_plugin_dependencies from scripts.install_plugin_deps import install_plugin_dependencies
@ -217,6 +218,21 @@ async def run_auto_gpt(
replace_directives=override_directives, replace_directives=override_directives,
) )
if (
agent.event_history.current_episode
and agent.event_history.current_episode.action.name == finish.__name__
and not agent.event_history.current_episode.result
):
# Agent was resumed after `finish` -> rewrite result of `finish` action
finish_reason = agent.event_history.current_episode.action.args["reason"]
print(f"Agent previously self-terminated; reason: '{finish_reason}'")
new_assignment = await clean_input(
config, "Please give a follow-up question or assignment:"
)
agent.event_history.register_result(
ActionInterruptedByHuman(feedback=new_assignment)
)
# If any of these are specified as arguments, # If any of these are specified as arguments,
# assume the user doesn't want to revise them # assume the user doesn't want to revise them
if not any( if not any(

View File

@ -6,7 +6,7 @@ import logging
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from autogpt.agents.features.context import get_agent_context from autogpt.agents.features.context import get_agent_context
from autogpt.agents.utils.exceptions import AgentTerminated, InvalidArgumentError from autogpt.agents.utils.exceptions import AgentFinished, InvalidArgumentError
from autogpt.command_decorator import command from autogpt.command_decorator import command
from autogpt.core.utils.json_schema import JSONSchema from autogpt.core.utils.json_schema import JSONSchema
@ -44,7 +44,7 @@ def finish(reason: str, agent: Agent) -> None:
A result string from create chat completion. A list of suggestions to A result string from create chat completion. A list of suggestions to
improve the code. improve the code.
""" """
raise AgentTerminated(reason) raise AgentFinished(reason)
@command( @command(