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
parent
fd2c26188f
commit
da4f013a5d
|
@ -177,14 +177,6 @@ class BaseAgent(Configurable[BaseAgentSettings], ABC):
|
|||
self.legacy_config = legacy_config
|
||||
"""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.prompt_strategy = prompt_strategy
|
||||
|
|
|
@ -18,6 +18,10 @@ class AgentTerminated(AgentException):
|
|||
"""The agent terminated or was terminated"""
|
||||
|
||||
|
||||
class AgentFinished(AgentTerminated):
|
||||
"""The agent self-terminated"""
|
||||
|
||||
|
||||
class ConfigurationError(AgentException):
|
||||
"""Error caused by invalid, incompatible or otherwise incorrect configuration"""
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ from sentry_sdk import set_user
|
|||
from autogpt.agent_factory.configurators import configure_agent_with_state
|
||||
from autogpt.agent_factory.generators import generate_agent_for_task
|
||||
from autogpt.agent_manager import AgentManager
|
||||
from autogpt.agents.utils.exceptions import AgentFinished
|
||||
from autogpt.commands.system import finish
|
||||
from autogpt.commands.user_interaction import ask_user
|
||||
from autogpt.config import Config
|
||||
|
@ -230,26 +231,6 @@ class AgentProtocolServer:
|
|||
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
|
||||
execute_result = ActionSuccessResult(outputs=user_input)
|
||||
agent.event_history.register_result(execute_result)
|
||||
|
@ -261,11 +242,31 @@ class AgentProtocolServer:
|
|||
step_id=step.step_id,
|
||||
status="running",
|
||||
)
|
||||
# Execute previously proposed action
|
||||
execute_result = await agent.execute(
|
||||
command_name=execute_command,
|
||||
command_args=execute_command_args,
|
||||
)
|
||||
|
||||
try:
|
||||
# Execute previously proposed action
|
||||
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:
|
||||
assert user_input
|
||||
execute_result = await agent.execute(
|
||||
|
|
|
@ -16,8 +16,6 @@ from typing import TYPE_CHECKING, Optional
|
|||
from colorama import Fore, Style
|
||||
from forge.sdk.db import AgentDB
|
||||
|
||||
from autogpt.file_storage import FileStorageBackendName, get_storage
|
||||
|
||||
if TYPE_CHECKING:
|
||||
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.agents import AgentThoughts, CommandArgs, CommandName
|
||||
from autogpt.agents.utils.exceptions import AgentTerminated, InvalidAgentResponseError
|
||||
from autogpt.commands.system import finish
|
||||
from autogpt.config import (
|
||||
AIDirectives,
|
||||
AIProfile,
|
||||
|
@ -35,8 +34,10 @@ from autogpt.config import (
|
|||
)
|
||||
from autogpt.core.resource.model_providers.openai import OpenAIProvider
|
||||
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.helpers import print_attribute, speak
|
||||
from autogpt.models.action_history import ActionInterruptedByHuman
|
||||
from autogpt.plugins import scan_plugins
|
||||
from scripts.install_plugin_deps import install_plugin_dependencies
|
||||
|
||||
|
@ -217,6 +218,21 @@ async def run_auto_gpt(
|
|||
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,
|
||||
# assume the user doesn't want to revise them
|
||||
if not any(
|
||||
|
|
|
@ -6,7 +6,7 @@ import logging
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
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.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
|
||||
improve the code.
|
||||
"""
|
||||
raise AgentTerminated(reason)
|
||||
raise AgentFinished(reason)
|
||||
|
||||
|
||||
@command(
|
||||
|
|
Loading…
Reference in New Issue