mirror of https://github.com/kortix-ai/suna.git
chore(dev): agent builder mode
This commit is contained in:
parent
59f16d7e01
commit
95c6277a93
|
@ -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"
|
||||||
|
}
|
|
@ -802,6 +802,7 @@ async def initiate_agent_with_files(
|
||||||
enable_context_manager: Optional[bool] = Form(False),
|
enable_context_manager: Optional[bool] = Form(False),
|
||||||
agent_id: Optional[str] = Form(None), # Add agent_id parameter
|
agent_id: Optional[str] = Form(None), # Add agent_id parameter
|
||||||
files: List[UploadFile] = File(default=[]),
|
files: List[UploadFile] = File(default=[]),
|
||||||
|
is_agent_builder: Optional[bool] = Form(False),
|
||||||
user_id: str = Depends(get_current_user_id_from_jwt)
|
user_id: str = Depends(get_current_user_id_from_jwt)
|
||||||
):
|
):
|
||||||
"""Initiate a new agent session with optional file attachments."""
|
"""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
|
model_name=model_name, # Already resolved above
|
||||||
enable_thinking=enable_thinking, reasoning_effort=reasoning_effort,
|
enable_thinking=enable_thinking, reasoning_effort=reasoning_effort,
|
||||||
stream=stream, enable_context_manager=enable_context_manager,
|
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}
|
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:
|
except Exception as e:
|
||||||
logger.error(f"Error fetching user agent library: {str(e)}")
|
logger.error(f"Error fetching user agent library: {str(e)}")
|
||||||
raise HTTPException(status_code=500, detail="Internal server error")
|
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"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
|
@ -44,7 +44,8 @@ async def run_agent(
|
||||||
reasoning_effort: Optional[str] = 'low',
|
reasoning_effort: Optional[str] = 'low',
|
||||||
enable_context_manager: bool = True,
|
enable_context_manager: bool = True,
|
||||||
agent_config: Optional[dict] = None,
|
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."""
|
"""Run the development agent with specified configuration."""
|
||||||
logger.info(f"🚀 Starting agent with model: {model_name}")
|
logger.info(f"🚀 Starting agent with model: {model_name}")
|
||||||
|
@ -84,7 +85,14 @@ async def run_agent(
|
||||||
# Register tools based on configuration
|
# Register tools based on configuration
|
||||||
# If no agent config (enabled_tools is None), register ALL tools for full Suna capabilities
|
# 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 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:
|
if enabled_tools is None:
|
||||||
# No agent specified - register ALL tools for full Suna experience
|
# No agent specified - register ALL tools for full Suna experience
|
||||||
logger.info("No agent specified - registering all tools for full Suna capabilities")
|
logger.info("No agent specified - registering all tools for full Suna capabilities")
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -53,7 +53,9 @@ async def run_agent_background(
|
||||||
reasoning_effort: Optional[str],
|
reasoning_effort: Optional[str],
|
||||||
stream: bool,
|
stream: bool,
|
||||||
enable_context_manager: 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."""
|
"""Run the agent in the background using Redis for state."""
|
||||||
await initialize()
|
await initialize()
|
||||||
|
@ -122,7 +124,8 @@ async def run_agent_background(
|
||||||
enable_thinking=enable_thinking, reasoning_effort=reasoning_effort,
|
enable_thinking=enable_thinking, reasoning_effort=reasoning_effort,
|
||||||
enable_context_manager=enable_context_manager,
|
enable_context_manager=enable_context_manager,
|
||||||
agent_config=agent_config,
|
agent_config=agent_config,
|
||||||
trace=trace
|
trace=trace,
|
||||||
|
is_agent_builder=is_agent_builder
|
||||||
)
|
)
|
||||||
|
|
||||||
final_status = "running"
|
final_status = "running"
|
||||||
|
|
Loading…
Reference in New Issue