Back to recipes

Autonomous Agent

Agent

The Autonomous Agent workflow represents a highly flexible and adaptable approach to problem-solving. In this model, an LLM-based agent operates with a degree of independence, making decisions, taking actions, and learning from outcomes to achieve a given goal.

Autonomous Agent workflow diagram

Diagram Explanation

The diagram illustrates a cyclical process of planning, execution, and learning. The agent determines the next best action based on its current understanding, carries out this action, collects feedback, and updates its knowledge or strategy based on these observations.

Use Cases

  • Adaptive Customer Support: Handle diverse queries, learning from each interaction to improve responses.
  • Autonomous Research Assistant: Break down research tasks, search for information, and refine approaches.
  • Self-Improving Code Generator: Write, test, and refactor code based on specifications and performance metrics.

Implementation

from typing import Dict, List, Any, Optional
from pydantic import BaseModel
from datetime import datetime
from helpers import run_llm, JSON_llm, execute_tool

class Action(BaseModel):
    """Represents a planned action"""
    tool: str
    params: Dict[str, Any]
    reason: str
    expected_outcome: str

class Observation(BaseModel):
    """Represents an observation from executing an action"""
    action: Action
    result: Any
    success: bool
    timestamp: datetime

class AgentState(BaseModel):
    """Represents the agent's current state"""
    goal: str
    context: Dict[str, Any]
    observations: List[Observation]
    learned_patterns: Dict[str, Any]

class AutonomousAgent:
    def __init__(self, goal: str, available_tools: Dict[str, Any]):
        self.state = AgentState(
            goal=goal,
            context={},
            observations=[],
            learned_patterns={}
        )
        self.tools = available_tools

    async def plan_next_action(self) -> Optional[Action]:
        """Plan the next action based on current state"""
        PLANNING_PROMPT = """Given the current state and goal, determine the next best action.
        
        Goal: {goal}
        Context: {context}
        Recent Observations: {observations}
        Learned Patterns: {patterns}
        Available Tools: {tools}
        
        Return as JSON with:
        - tool: name of tool to use
        - params: parameters for the tool
        - reason: why this action was chosen
        - expected_outcome: what we expect to achieve
        """
        
        # Get last 5 observations for context
        recent_obs = self.state.observations[-5:] if self.state.observations else []
        
        action = await JSON_llm(
            PLANNING_PROMPT.format(
                goal=self.state.goal,
                context=self.state.context,
                observations=recent_obs,
                patterns=self.state.learned_patterns,
                tools=self.tools
            ),
            Action
        )
        
        return action

    async def execute_action(self, action: Action) -> Observation:
        """Execute an action and record the observation"""
        try:
            result = await execute_tool(action.tool, action.params)
            success = True
        except Exception as e:
            result = str(e)
            success = False
        
        observation = Observation(
            action=action,
            result=result,
            success=success,
            timestamp=datetime.now()
        )
        
        self.state.observations.append(observation)
        return observation

    async def learn_from_observation(self, observation: Observation) -> None:
        """Update learned patterns based on observation"""
        LEARNING_PROMPT = """Analyze this observation and extract patterns or insights:
        
        Action Taken: {action}
        Result: {result}
        Success: {success}
        
        Current Patterns: {patterns}
        
        Return as JSON with updated patterns dictionary.
        """
        
        new_patterns = await JSON_llm(
            LEARNING_PROMPT.format(
                action=observation.action,
                result=observation.result,
                success=observation.success,
                patterns=self.state.learned_patterns
            ),
            Dict[str, Any]
        )
        
        self.state.learned_patterns.update(new_patterns)

    async def update_context(self, observation: Observation) -> None:
        """Update context based on observation"""
        CONTEXT_PROMPT = """Update the context based on this observation:
        
        Current Context: {context}
        Observation: {observation}
        
        Return as JSON with updated context dictionary.
        """
        
        new_context = await JSON_llm(
            CONTEXT_PROMPT.format(
                context=self.state.context,
                observation=observation
            ),
            Dict[str, Any]
        )
        
        self.state.context.update(new_context)

    async def run(self, max_steps: int = 10) -> str:
        """Run the agent to achieve its goal"""
        step = 0
        
        while step < max_steps:
            print(f"\nStep {step + 1}")
            print("-" * 40)
            
            # Plan next action
            action = await self.plan_next_action()
            if not action:
                print("No further actions needed")
                break
            
            print(f"Planned Action: {action.tool}")
            print(f"Reason: {action.reason}")
            
            # Execute action and observe
            observation = await self.execute_action(action)
            print(f"Success: {observation.success}")
            
            # Learn from observation
            await self.learn_from_observation(observation)
            
            # Update context
            await self.update_context(observation)
            
            # Check if goal is achieved
            if await self.check_goal_achieved():
                print("\nGoal achieved!")
                break
            
            step += 1
        
        return self.summarize_results()

    async def check_goal_achieved(self) -> bool:
        """Check if the goal has been achieved"""
        CHECK_GOAL_PROMPT = """Given the current state and goal, has the goal been achieved?
        
        Goal: {goal}
        Context: {context}
        
        Return true or false.
        """
        
        return await JSON_llm(
            CHECK_GOAL_PROMPT.format(
                goal=self.state.goal,
                context=self.state.context
            ),
            bool
        )

    def summarize_results(self) -> str:
        """Summarize the agent's results"""
        return f"""
        Goal: {self.state.goal}
        Steps Taken: {len(self.state.observations)}
        Success Rate: {
            sum(1 for o in self.state.observations if o.success) / 
            len(self.state.observations) if self.state.observations else 0
        }
        Final Context: {self.state.context}
        """

# Example usage
async def main():
    # Define available tools
    tools = {
        "search": "Search for information online",
        "extract": "Extract specific information from text",
        "summarize": "Summarize lengthy content",
        "analyze": "Analyze data or text",
        "generate": "Generate new content"
    }
    
    # Create agent with a research goal
    agent = AutonomousAgent(
        goal="Research and summarize recent developments in quantum computing",
        available_tools=tools
    )
    
    # Run agent
    result = await agent.run()
    print("\nFinal Results:", result)

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())