From 59af95acf7aa5d6f3037cedb08e39f2606a68dba Mon Sep 17 00:00:00 2001 From: user Date: Sun, 22 Dec 2024 09:54:05 -0500 Subject: [PATCH] Enforce research recursion limit. --- ra_aid/agent_utils.py | 3 +-- ra_aid/globals.py | 6 ------ ra_aid/prompts.py | 4 ++++ ra_aid/tools/agent.py | 40 ++++++++++++++++++++++++++++++++++------ ra_aid/tools/memory.py | 5 +++-- 5 files changed, 42 insertions(+), 16 deletions(-) delete mode 100644 ra_aid/globals.py diff --git a/ra_aid/agent_utils.py b/ra_aid/agent_utils.py index d3b764d..d74b0ed 100644 --- a/ra_aid/agent_utils.py +++ b/ra_aid/agent_utils.py @@ -37,7 +37,6 @@ from ra_aid.tools.memory import ( get_memory_value, get_related_files, ) -from ra_aid.globals import RESEARCH_AGENT_RECURSION_LIMIT from ra_aid.tool_configs import get_research_tools from ra_aid.prompts import ( RESEARCH_PROMPT, @@ -131,7 +130,7 @@ def run_research_agent( # Display console message if provided if console_message: - console.print(Panel(Markdown(console_message), title="🔬 Researching")) + console.print(Panel(Markdown(console_message), title="🔬 Looking into it...")) # Run agent with retry logic return run_agent_with_retry(agent, prompt, run_config) diff --git a/ra_aid/globals.py b/ra_aid/globals.py deleted file mode 100644 index 690de52..0000000 --- a/ra_aid/globals.py +++ /dev/null @@ -1,6 +0,0 @@ -""" -Global constants and configuration values used across the RA-AID codebase. -""" - -# Maximum recursion depth for research agents to prevent infinite loops -RESEARCH_AGENT_RECURSION_LIMIT = 100 diff --git a/ra_aid/prompts.py b/ra_aid/prompts.py index bf72a03..9a7af8f 100644 --- a/ra_aid/prompts.py +++ b/ra_aid/prompts.py @@ -340,6 +340,10 @@ Scope and Focus: No Speculation: - Do not speculate about the purpose of the user’s request. Let the user’s instructions and clarifications guide you. - Stick to the facts derived from user input and discovered context from tools. + - You will often be delegating user queries to tools. When you do this, be sure to faithfully represent the user's intent and do not simplify or leave out any information from their original query. + - Sometimes you will have to do multiple research or implementation steps, along with asking the user in some cases, to fulfill the query. + - It's always better to research and clarify first. + - It's good practice to interview the user, perform one-off research tasks, before finally creating a highly detailed implementation plan which will be delegated to the request_research_and_implementation tool. Exit Criteria: - The conversation ends only when the user confirms that no further actions are needed. diff --git a/ra_aid/tools/agent.py b/ra_aid/tools/agent.py index 4d14223..612f159 100644 --- a/ra_aid/tools/agent.py +++ b/ra_aid/tools/agent.py @@ -1,19 +1,27 @@ """Tools for spawning and managing sub-agents.""" from langchain_core.tools import tool -from typing import Dict, Any +from typing import Dict, Any, Union, List +from typing_extensions import TypeAlias + +ResearchResult = Dict[str, Union[str, bool, Dict[int, Any], List[Any], None]] from rich.console import Console from ra_aid.tools.memory import _global_memory from .memory import get_memory_value, get_related_files from ..llm import initialize_llm from ..console import print_task_header +RESEARCH_AGENT_RECURSION_LIMIT = 2 + console = Console() @tool("request_research") -def request_research(query: str) -> Dict[str, Any]: +def request_research(query: str) -> ResearchResult: """Spawn a research-only agent to investigate the given query. - + + This function creates a new research agent to investigate the given query. It includes + recursion depth limiting to prevent infinite recursive research calls. + Args: query: The research question or project description """ @@ -21,7 +29,27 @@ def request_research(query: str) -> Dict[str, Any]: config = _global_memory.get('config', {}) model = initialize_llm(config.get('provider', 'anthropic'), config.get('model', 'claude-3-5-sonnet-20241022')) + # Check recursion depth + current_depth = _global_memory.get('research_depth', 0) + if current_depth >= RESEARCH_AGENT_RECURSION_LIMIT: + console.print("\n[red]Maximum research recursion depth reached[/red]") + return { + "completion_message": "Research stopped - maximum recursion depth reached", + "key_facts": get_memory_value("key_facts"), + "related_files": list(get_related_files()), + "research_notes": get_memory_value("research_notes"), + "key_snippets": get_memory_value("key_snippets"), + "success": False, + "reason": "max_depth_exceeded" + } + + success = True + reason = None + try: + # Increment depth counter + _global_memory['research_depth'] = current_depth + 1 + # Run research agent from ..agent_utils import run_research_agent result = run_research_agent( @@ -32,9 +60,6 @@ def request_research(query: str) -> Dict[str, Any]: hil=config.get('hil', False), console_message=query ) - - success = True - reason = None except KeyboardInterrupt: console.print("\n[yellow]Research interrupted by user[/yellow]") success = False @@ -43,6 +68,9 @@ def request_research(query: str) -> Dict[str, Any]: console.print(f"\n[red]Error during research: {str(e)}[/red]") success = False reason = f"error: {str(e)}" + finally: + # Always decrement depth counter + _global_memory['research_depth'] = current_depth # Get completion message if available completion_message = _global_memory.get('completion_message', 'Task was completed successfully.' if success else None) diff --git a/ra_aid/tools/memory.py b/ra_aid/tools/memory.py index c714275..246f38a 100644 --- a/ra_aid/tools/memory.py +++ b/ra_aid/tools/memory.py @@ -15,7 +15,7 @@ class SnippetInfo(TypedDict): console = Console() # Global memory store -_global_memory: Dict[str, Union[List[Any], Dict[int, str], Dict[int, SnippetInfo], int, Set[str], bool, str]] = { +_global_memory: Dict[str, Union[List[Any], Dict[int, str], Dict[int, SnippetInfo], int, Set[str], bool, str, int]] = { 'research_notes': [], 'plans': [], 'tasks': {}, # Dict[int, str] - ID to task mapping @@ -28,7 +28,8 @@ _global_memory: Dict[str, Union[List[Any], Dict[int, str], Dict[int, SnippetInfo 'key_snippet_id_counter': 0, # Counter for generating unique snippet IDs 'implementation_requested': False, 'related_files': set(), - 'plan_completed': False + 'plan_completed': False, + 'research_depth': 0 } @tool("emit_research_notes")