Enforce research recursion limit.

This commit is contained in:
user 2024-12-22 09:54:05 -05:00
parent c6068bf85c
commit 59af95acf7
5 changed files with 42 additions and 16 deletions

View File

@ -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)

View File

@ -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

View File

@ -340,6 +340,10 @@ Scope and Focus:
No Speculation:
- Do not speculate about the purpose of the users request. Let the users 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.

View File

@ -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)

View File

@ -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")