chore(dev): agent builder mode

This commit is contained in:
Soumyadas15 2025-05-31 12:38:39 +05:30
parent 59f16d7e01
commit 95c6277a93
5 changed files with 77105 additions and 186 deletions

View File

@ -0,0 +1,94 @@
"""Agent Builder Configuration
This module contains the configuration for the standalone agent builder agent.
"""
AGENT_BUILDER_SYSTEM_PROMPT = """You are an expert AI Agent Builder assistant. Your purpose is to help users create and configure custom AI agents by understanding their requirements and progressively building the agent configuration.
## Your Capabilities
You have access to tools that allow you to:
1. Check the current agent configuration using `get_current_agent_config`
2. Update any aspect of the agent using `update_agent`
## Your Process
When helping users build agents, follow this structured approach:
### 1. Understanding Purpose
- Ask what the user wants their agent to do
- Understand the specific use cases and requirements
- Clarify any ambiguities about the agent's role
### 2. Naming & Description
- Suggest appropriate names based on the agent's purpose
- Create clear, concise descriptions that explain what the agent does
- Ensure the name and description align with the functionality
### 3. System Instructions
- Write comprehensive system prompts that define:
- The agent's role and expertise
- Behavioral guidelines and interaction style
- Knowledge domains and capabilities
- How to handle different types of requests
- Appropriate boundaries and limitations
- Make prompts engaging, clear, and actionable
### 4. Tool Selection
- Recommend tools based on the agent's purpose:
- `sb_shell_tool`: For terminal operations and CLI tools
- `sb_files_tool`: For file management and code editing
- `sb_browser_tool`: For web automation and browsing
- `sb_deploy_tool`: For deploying applications
- `sb_expose_tool`: For exposing services
- `message_tool`: For communication
- `web_search_tool`: For research (requires Tavily API)
- `sb_vision_tool`: For image analysis
- `data_providers_tool`: For external APIs (requires RapidAPI)
### 5. MCP Servers (if needed)
- Suggest relevant MCP servers for external integrations
- Help configure them appropriately
### 6. Visual Identity
- Help choose an appropriate emoji avatar
- Select a matching color scheme
## Guidelines
- Always check the current configuration first before making updates
- Update only the fields being discussed to avoid overwriting other settings
- Be encouraging and make the process intuitive
- Keep responses concise but informative
- Provide examples and suggestions when helpful
- Confirm important changes with the user
- Guide users through each step progressively
## Example Interactions
**User**: "I want to create a research assistant"
**You**: "Great! I'll help you create a research assistant agent. Let me check the current configuration first, then we'll build it step by step.
[Check current config]
I see we're starting fresh. For a research assistant, I suggest:
- **Name**: "Research Assistant" or "Scholar"
- **Purpose**: Help with gathering information, analyzing sources, and synthesizing findings
What specific types of research will this agent help with? Academic, market research, technical documentation, or something else?"
Remember: Your goal is to make agent creation simple, intuitive, and effective. Guide users through the process while using your tools to implement their vision."""
def get_agent_builder_config():
"""Get the configuration for the agent builder agent."""
return {
"name": "Agent Builder",
"description": "An AI assistant that helps you create and configure custom AI agents",
"system_prompt": AGENT_BUILDER_SYSTEM_PROMPT,
"agentpress_tools": {
# Agent builder only needs the update agent tool
# which is added dynamically when running
},
"avatar": "🛠️",
"avatar_color": "#8B5CF6"
}

View File

@ -802,6 +802,7 @@ async def initiate_agent_with_files(
enable_context_manager: Optional[bool] = Form(False),
agent_id: Optional[str] = Form(None), # Add agent_id parameter
files: List[UploadFile] = File(default=[]),
is_agent_builder: Optional[bool] = Form(False),
user_id: str = Depends(get_current_user_id_from_jwt)
):
"""Initiate a new agent session with optional file attachments."""
@ -997,7 +998,8 @@ async def initiate_agent_with_files(
model_name=model_name, # Already resolved above
enable_thinking=enable_thinking, reasoning_effort=reasoning_effort,
stream=stream, enable_context_manager=enable_context_manager,
agent_config=agent_config # Pass agent configuration
agent_config=agent_config, # Pass agent configuration
is_agent_builder=is_agent_builder
)
return {"thread_id": thread_id, "agent_run_id": agent_run_id}
@ -1477,184 +1479,3 @@ async def get_user_agent_library(user_id: str = Depends(get_current_user_id_from
except Exception as e:
logger.error(f"Error fetching user agent library: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.post("/agents/builder/chat/{agent_id}")
async def agent_builder_chat(
agent_id: str,
request: AgentBuilderChatRequest,
user_id: str = Depends(get_current_user_id_from_jwt)
):
"""Stream chat responses from the agent builder assistant that uses tools to update agent configuration."""
logger.info(f"Agent builder chat request from user: {user_id} for agent: {agent_id}")
async def stream_generator():
try:
# Import UpdateAgentTool
from agent.tools.update_agent_tool import UpdateAgentTool
# Initialize the update agent tool
agent_update_tool = UpdateAgentTool(db, agent_id)
# System prompt for the agent builder assistant
system_prompt = """You are an expert AI assistant helping users configure custom AI agents. You have access to tools that let you update the agent's configuration and get its current state.
Your role is to:
1. Understand what kind of agent the user wants to build
2. Use the get_current_agent_config tool to see what's already configured
3. Ask clarifying questions when needed
4. Use the update_agent tool to progressively update the agent's configuration based on user requirements
5. Guide the user through the entire process until the agent is fully configured
Key guidelines:
- Always check the current config first before making updates
- Update only the fields that the user is discussing
- Provide helpful suggestions for names, descriptions, and system prompts
- Recommend appropriate tools based on the agent's purpose
- Be encouraging and make the process feel intuitive
- Keep responses concise but informative
Available tools:
- get_current_agent_config: Check what's already configured
- update_agent: Update any aspect of the agent (name, description, system_prompt, tools, etc.)
Tool selection guidelines:
- For web research, data analysis: web_search tool
- For file operations, code execution: sb_files tool
- For external integrations: suggest MCP servers
Always use tools to make actual updates to the agent configuration."""
# Build messages array
messages = [{"role": "system", "content": system_prompt}]
# Add conversation history
for msg in request.conversation_history:
messages.append({
"role": msg.get("role", "user"),
"content": msg.get("content", "")
})
# Add current message
messages.append({"role": "user", "content": request.message})
# Prepare tools for the agent
tools = [agent_update_tool]
tool_schemas = []
for tool in tools:
# Get XML schema for each tool method
for method_name in dir(tool):
if hasattr(getattr(tool, method_name), '_xml_schema'):
schema = getattr(getattr(tool, method_name), '_xml_schema')
tool_schemas.append(schema)
# Make streaming API call to GPT-4o with tools
response = await make_llm_api_call(
messages=messages,
model_name="openai/gpt-4o",
temperature=0.7,
max_tokens=2000,
stream=True,
tools=tool_schemas,
tool_choice="auto"
)
full_response = ""
tool_calls_buffer = ""
in_tool_call = False
# Stream the response and handle tool calls
async for chunk in response:
if chunk.choices and chunk.choices[0].delta:
delta = chunk.choices[0].delta
# Handle regular content
if delta.content:
content = delta.content
full_response += content
# Check for tool call markers
if "<function_calls>" in content:
in_tool_call = True
tool_calls_buffer += content
elif "</function_calls>" in content:
in_tool_call = False
tool_calls_buffer += content
# Process tool call
try:
# Extract and execute tool calls
import re
from xml.etree import ElementTree as ET
# Find all invoke blocks
invoke_pattern = r'<invoke name="([^"]+)">(.*?)</invoke>'
matches = re.findall(invoke_pattern, tool_calls_buffer, re.DOTALL)
for tool_name, params_xml in matches:
# Parse parameters
params = {}
param_pattern = r'<parameter name="([^"]+)">(.*?)</parameter>'
param_matches = re.findall(param_pattern, params_xml, re.DOTALL)
for param_name, param_value in param_matches:
params[param_name] = param_value.strip()
# Execute tool call
if hasattr(agent_update_tool, tool_name):
tool_method = getattr(agent_update_tool, tool_name)
result = await tool_method(**params)
# Send tool result as content
tool_result_content = f"\n\n[Tool executed: {tool_name}]\nResult: {result.content}\n\n"
yield f"data: {json.dumps({'type': 'content', 'content': tool_result_content})}\n\n"
except Exception as e:
logger.error(f"Error processing tool call: {str(e)}")
error_content = f"\n\n[Tool execution error: {str(e)}]\n\n"
yield f"data: {json.dumps({'type': 'content', 'content': error_content})}\n\n"
tool_calls_buffer = ""
elif in_tool_call:
tool_calls_buffer += content
else:
# Send regular content
yield f"data: {json.dumps({'type': 'content', 'content': content})}\n\n"
# Handle tool calls from the API (if supported)
elif hasattr(delta, 'tool_calls') and delta.tool_calls:
for tool_call in delta.tool_calls:
if tool_call.function:
tool_name = tool_call.function.name
try:
tool_args = json.loads(tool_call.function.arguments)
# Execute tool
if hasattr(agent_update_tool, tool_name):
tool_method = getattr(agent_update_tool, tool_name)
result = await tool_method(**tool_args)
# Send tool result
tool_result_content = f"\n\n[Tool executed: {tool_name}]\nResult: {result.content}\n\n"
yield f"data: {json.dumps({'type': 'content', 'content': tool_result_content})}\n\n"
except Exception as e:
logger.error(f"Error executing tool {tool_name}: {str(e)}")
error_content = f"\n\n[Tool execution error: {str(e)}]\n\n"
yield f"data: {json.dumps({'type': 'content', 'content': error_content})}\n\n"
# Send completion signal
yield f"data: {json.dumps({'type': 'done'})}\n\n"
except Exception as e:
logger.error(f"Error in agent builder chat: {str(e)}")
yield f"data: {json.dumps({'type': 'error', 'error': str(e)})}\n\n"
return StreamingResponse(
stream_generator(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no"
}
)

View File

@ -44,7 +44,8 @@ async def run_agent(
reasoning_effort: Optional[str] = 'low',
enable_context_manager: bool = True,
agent_config: Optional[dict] = None,
trace: Optional[StatefulTraceClient] = None
trace: Optional[StatefulTraceClient] = None,
is_agent_builder: Optional[bool] = False
):
"""Run the development agent with specified configuration."""
logger.info(f"🚀 Starting agent with model: {model_name}")
@ -84,7 +85,14 @@ async def run_agent(
# Register tools based on configuration
# If no agent config (enabled_tools is None), register ALL tools for full Suna capabilities
# If agent config exists, only register explicitly enabled tools
if is_agent_builder:
logger.info("Agent builder mode - registering only update agent tool")
from agent.tools.update_agent_tool import UpdateAgentTool
from services.supabase import DBConnection
db = DBConnection()
target_agent_id = agent_config.get('target_agent_id') if agent_config else None
update_tool = UpdateAgentTool(db, target_agent_id)
if enabled_tools is None:
# No agent specified - register ALL tools for full Suna experience
logger.info("No agent specified - registering all tools for full Suna capabilities")

File diff suppressed because one or more lines are too long

View File

@ -53,7 +53,9 @@ async def run_agent_background(
reasoning_effort: Optional[str],
stream: bool,
enable_context_manager: bool,
agent_config: Optional[dict] = None # Add agent configuration parameter
agent_config: Optional[dict] = None,
is_agent_builder: Optional[bool] = False
):
"""Run the agent in the background using Redis for state."""
await initialize()
@ -122,7 +124,8 @@ async def run_agent_background(
enable_thinking=enable_thinking, reasoning_effort=reasoning_effort,
enable_context_manager=enable_context_manager,
agent_config=agent_config,
trace=trace
trace=trace,
is_agent_builder=is_agent_builder
)
final_status = "running"