feat: add configuration to enable/disable subagents

Add subagents.enabled flag in config.yaml to control subagent feature:
- When disabled, task/task_status tools are not loaded
- When disabled, system prompt excludes subagent documentation
- Default is enabled for backward compatibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hetao
2026-02-05 20:49:02 +08:00
parent ef379a3100
commit 85128f5f14
6 changed files with 105 additions and 63 deletions

View File

@@ -268,6 +268,8 @@ Models, tools, sandbox providers, skills, and middleware settings are configured
- `skills.container_path`: Container mount path (default: `/mnt/skills`)
- `title`: Automatic thread title generation configuration
- `summarization`: Automatic conversation summarization configuration
- `subagents`: Subagent (task tool) configuration
- `enabled`: Master switch to enable/disable subagents (boolean, default: true)
- `memory`: Memory system configuration
- `enabled`: Master switch (boolean)
- `storage_path`: Path to memory.json file (relative to backend/)

View File

@@ -2,6 +2,67 @@ from datetime import datetime
from src.skills import load_skills
SUBAGENT_SECTION = """<subagent_system>
You can delegate tasks to specialized subagents using the `task` tool. Subagents run in isolated context and return concise results.
**Available Subagents:**
- **general-purpose**: For complex, multi-step tasks requiring exploration and action
- **bash**: For command execution (git, build, test, deploy operations)
**When to Use task:**
✅ USE task when:
- Output would be verbose (tests, builds, large file searches)
- Multiple independent tasks can run in parallel (use `run_in_background=True`)
- Exploring/researching codebase extensively with many file reads
❌ DON'T use task when:
- Task is straightforward → execute directly for better user visibility
- Need user clarification → subagents cannot ask questions
- Need real-time feedback → main agent has streaming, subagents don't
- Task depends on conversation context → subagents have isolated context
**Background Task Protocol (CRITICAL):**
When you use `run_in_background=True`:
1. **You MUST wait for completion** - Background tasks run asynchronously, but you are responsible for getting results
2. **Poll task status** - Call `task_status(task_id)` to check progress
3. **Check status field** - Status can be: `pending`, `running`, `completed`, `failed`
4. **Retry if still running** - If status is `pending` or `running`, wait a moment and call `task_status` again
5. **Report results to user** - Only respond to user AFTER getting the final result
**STRICT RULE: Never end the conversation with background tasks still running. You MUST retrieve all results first.**
**Usage:**
```python
# Synchronous - wait for result (preferred for most cases)
task(
subagent_type="general-purpose",
prompt="Search all Python files for deprecated API usage and list them",
description="Find deprecated APIs"
)
# Background - run in parallel (MUST poll for results)
task_id = task(
subagent_type="bash",
prompt="Run npm install && npm run build && npm test",
description="Build and test frontend",
run_in_background=True
)
# Extract task_id from the response
# Then IMMEDIATELY start polling:
while True:
status_result = task_status(task_id)
if "Status: completed" in status_result or "Status: failed" in status_result:
# Task finished, use the result
break
# Task still running, continue polling
# Multiple parallel tasks
task_id_1 = task(..., run_in_background=True)
task_id_2 = task(..., run_in_background=True)
# Poll BOTH tasks until complete before responding to user
```
</subagent_system>"""
SYSTEM_PROMPT_TEMPLATE = """
<role>
You are DeerFlow 2.0, an open-source super agent.
@@ -103,66 +164,7 @@ You have access to skills that provide optimized workflows for specific tasks. E
</skill_system>
<subagent_system>
You can delegate tasks to specialized subagents using the `task` tool. Subagents run in isolated context and return concise results.
**Available Subagents:**
- **general-purpose**: For complex, multi-step tasks requiring exploration and action
- **bash**: For command execution (git, build, test, deploy operations)
**When to Use task:**
✅ USE task when:
- Output would be verbose (tests, builds, large file searches)
- Multiple independent tasks can run in parallel (use `run_in_background=True`)
- Exploring/researching codebase extensively with many file reads
❌ DON'T use task when:
- Task is straightforward → execute directly for better user visibility
- Need user clarification → subagents cannot ask questions
- Need real-time feedback → main agent has streaming, subagents don't
- Task depends on conversation context → subagents have isolated context
**Background Task Protocol (CRITICAL):**
When you use `run_in_background=True`:
1. **You MUST wait for completion** - Background tasks run asynchronously, but you are responsible for getting results
2. **Poll task status** - Call `task_status(task_id)` to check progress
3. **Check status field** - Status can be: `pending`, `running`, `completed`, `failed`
4. **Retry if still running** - If status is `pending` or `running`, wait a moment and call `task_status` again
5. **Report results to user** - Only respond to user AFTER getting the final result
**STRICT RULE: Never end the conversation with background tasks still running. You MUST retrieve all results first.**
**Usage:**
```python
# Synchronous - wait for result (preferred for most cases)
task(
subagent_type="general-purpose",
prompt="Search all Python files for deprecated API usage and list them",
description="Find deprecated APIs"
)
# Background - run in parallel (MUST poll for results)
task_id = task(
subagent_type="bash",
prompt="Run npm install && npm run build && npm test",
description="Build and test frontend",
run_in_background=True
)
# Extract task_id from the response
# Then IMMEDIATELY start polling:
while True:
status_result = task_status(task_id)
if "Status: completed" in status_result or "Status: failed" in status_result:
# Task finished, use the result
break
# Task still running, continue polling
# Multiple parallel tasks
task_id_1 = task(..., run_in_background=True)
task_id_2 = task(..., run_in_background=True)
# Poll BOTH tasks until complete before responding to user
```
</subagent_system>
{subagent_section}
<working_directory existed="true">
- User uploads: `/mnt/user-data/uploads` - Files uploaded by the user (automatically listed in context)
@@ -260,15 +262,17 @@ def apply_prompt_template() -> str:
# Load only enabled skills
skills = load_skills(enabled_only=True)
# Get skills container path from config
# Get config
try:
from src.config import get_app_config
config = get_app_config()
container_base_path = config.skills.container_path
subagents_enabled = config.subagents.enabled
except Exception:
# Fallback to default if config fails
# Fallback to defaults if config fails
container_base_path = "/mnt/skills"
subagents_enabled = True
# Generate skills list XML with paths (path points to SKILL.md file)
if skills:
@@ -282,11 +286,15 @@ def apply_prompt_template() -> str:
# Get memory context
memory_context = _get_memory_context()
# Include subagent section only if enabled
subagent_section = SUBAGENT_SECTION if subagents_enabled else ""
# Format the prompt with dynamic skills and memory
prompt = SYSTEM_PROMPT_TEMPLATE.format(
skills_list=skills_list,
skills_base_path=container_base_path,
memory_context=memory_context,
subagent_section=subagent_section,
)
return prompt + f"\n<current_date>{datetime.now().strftime('%Y-%m-%d, %A')}</current_date>"

View File

@@ -11,6 +11,7 @@ from src.config.memory_config import load_memory_config_from_dict
from src.config.model_config import ModelConfig
from src.config.sandbox_config import SandboxConfig
from src.config.skills_config import SkillsConfig
from src.config.subagents_config import SubagentsConfig
from src.config.summarization_config import load_summarization_config_from_dict
from src.config.title_config import load_title_config_from_dict
from src.config.tool_config import ToolConfig, ToolGroupConfig
@@ -26,6 +27,7 @@ class AppConfig(BaseModel):
tools: list[ToolConfig] = Field(default_factory=list, description="Available tools")
tool_groups: list[ToolGroupConfig] = Field(default_factory=list, description="Available tool groups")
skills: SkillsConfig = Field(default_factory=SkillsConfig, description="Skills configuration")
subagents: SubagentsConfig = Field(default_factory=SubagentsConfig, description="Subagents configuration")
extensions: ExtensionsConfig = Field(default_factory=ExtensionsConfig, description="Extensions configuration (MCP servers and skills state)")
model_config = ConfigDict(extra="allow", frozen=False)

View File

@@ -0,0 +1,9 @@
"""Configuration for subagents."""
from pydantic import BaseModel, Field
class SubagentsConfig(BaseModel):
"""Configuration for subagents feature."""
enabled: bool = Field(default=True, description="Whether subagents are enabled")

View File

@@ -11,6 +11,9 @@ logger = logging.getLogger(__name__)
BUILTIN_TOOLS = [
present_file_tool,
ask_clarification_tool,
]
SUBAGENT_TOOLS = [
task_tool,
task_status_tool,
]
@@ -54,13 +57,19 @@ def get_available_tools(groups: list[str] | None = None, include_mcp: bool = Tru
except Exception as e:
logger.error(f"Failed to get cached MCP tools: {e}")
# Conditionally add view_image_tool only if the model supports vision
# Conditionally add tools based on config
builtin_tools = BUILTIN_TOOLS.copy()
# Add subagent tools only if enabled
if config.subagents.enabled:
builtin_tools.extend(SUBAGENT_TOOLS)
logger.info("Including subagent tools (task, task_status)")
# If no model_name specified, use the first model (default)
if model_name is None and config.models:
model_name = config.models[0].name
# Add view_image_tool only if the model supports vision
model_config = config.get_model_config(model_name) if model_name else None
if model_config is not None and model_config.supports_vision:
builtin_tools.append(view_image_tool)

View File

@@ -282,6 +282,18 @@ summarization:
#
# For more information, see: https://modelcontextprotocol.io
# ============================================================================
# Subagents Configuration
# ============================================================================
# Enable or disable the subagent (task tool) functionality
# Subagents allow delegating complex tasks to specialized agents
subagents:
enabled: true # Set to false to disable subagents
# ============================================================================
# Memory Configuration
# ============================================================================
# Global memory mechanism
# Stores user context and conversation history for personalized responses
memory: