From 72e845a18c22a4b537e4c9c67654e065798abe37 Mon Sep 17 00:00:00 2001 From: VascoSch92 Date: Tue, 24 Feb 2026 14:40:15 +0100 Subject: [PATCH 1/8] add docs for file-based agents --- docs.json | 1 + sdk/guides/agent-file-based.mdx | 426 ++++++++++++++++++++++++++++++++ 2 files changed, 427 insertions(+) create mode 100644 sdk/guides/agent-file-based.mdx diff --git a/docs.json b/docs.json index 68d214ff..cbe2a850 100644 --- a/docs.json +++ b/docs.json @@ -313,6 +313,7 @@ "sdk/guides/agent-interactive-terminal", "sdk/guides/agent-browser-use", "sdk/guides/agent-custom", + "sdk/guides/agent-file-based", "sdk/guides/agent-stuck-detector", "sdk/guides/agent-tom-agent", "sdk/guides/critic" diff --git a/sdk/guides/agent-file-based.mdx b/sdk/guides/agent-file-based.mdx new file mode 100644 index 00000000..2fe29c58 --- /dev/null +++ b/sdk/guides/agent-file-based.mdx @@ -0,0 +1,426 @@ +--- +title: File-Based Agents +description: Define specialized sub-agents as simple Markdown files with YAML frontmatter — no Python code required. +--- + +import RunExampleCode from "/sdk/shared-snippets/how-to-run-example.mdx"; + +File-based agents let you define specialized sub-agents using Markdown files. Each file declares the agent's name, description, tools, and system prompt — the same things you'd pass to `register_agent()` in code, but without writing any Python. + +This is the fastest way to create reusable, domain-specific agents that can be invoked via [delegation](/sdk/guides/agent-delegation). + +## Agent File Format + +An agent is a single `.md` file with YAML frontmatter and a Markdown body: + +```markdown icon="markdown" +--- +name: code-reviewer +description: > + Reviews code for quality, bugs, and best practices. + Review this pull request for issues + Check this code for bugs +tools: + - file_editor + - terminal +model: inherit +--- + +# Code Reviewer + +You are a meticulous code reviewer. When reviewing code: + +1. **Correctness** - Look for bugs, off-by-one errors, and race conditions. +2. **Style** - Check for consistent naming and idiomatic usage. +3. **Performance** - Identify unnecessary allocations or algorithmic issues. +4. **Security** - Flag injection vulnerabilities or hardcoded secrets. + +Keep feedback concise and actionable. For each issue, suggest a fix. +``` + +The YAML frontmatter configures the agent. The Markdown body becomes the agent's system prompt. + +### Frontmatter Fields + +| Field | Required | Default | Description | +|-------|----------|---------|-------------| +| `name` | Yes | - | Agent identifier (e.g., `code-reviewer`) | +| `description` | No | `""` | What this agent does. Shown to the orchestrator | +| `tools` | No | `[]` | List of tools the agent can use | +| `model` | No | `"inherit"` | LLM model (`"inherit"` uses the parent agent's model) | +| `color` | No | `None` | Display color for the agent | + +### `` Tags + +Add `` tags inside the description to help the orchestrating agent know **when** to delegate to this agent: + +```markdown icon="markdown" +description: > + Writes and improves technical documentation. + Write docs for this module + Improve the README +``` + +These examples are extracted and stored as `when_to_use_examples` on the `AgentDefinition` object. The delegation system uses them to match user requests to the right sub-agent. + +## Directory Conventions + +Place agent files in these directories, scanned in **priority order** (first match wins): + +| Priority | Location | Scope | +|----------|----------|-------| +| 1 | `{project}/.agents/agents/*.md` | Project-level (primary) | +| 2 | `{project}/.openhands/agents/*.md` | Project-level (legacy) | +| 3 | `~/.agents/agents/*.md` | User-level (primary) | +| 4 | `~/.openhands/agents/*.md` | User-level (legacy) | + + + + + + + + + + + + + + + +**Rules:** +- Only top-level `.md` files are loaded (subdirectories are skipped) +- `README.md` files are automatically skipped +- Project-level agents take priority over user-level agents with the same name + + +Put agents shared across all your projects in `~/.agents/agents/`. Put project-specific agents in `{project}/.agents/agents/`. + + +## Overall Priority + +When the same agent name is defined in multiple places, the highest-priority source wins: + +1. **Programmatic** `register_agent()` calls (never overwritten) +2. **Plugin agents** (`Plugin.agents`) +3. **Project-level** file-based agents +4. **User-level** file-based agents + +## Auto-Registration + +The simplest way to use file-based agents is auto-registration. Call `register_file_agents()` with your project directory, and all discovered agents are registered into the delegation system: + +```python icon="python" focus={3} +from openhands.sdk.subagent import register_file_agents + +registered = register_file_agents("/path/to/project") +print(f"Registered agents: {registered}") +``` + +This scans both project-level and user-level directories, deduplicates by name, and registers each agent as a delegate that can be spawned by the orchestrator. + +## Manual Loading + +For more control, load and register agents explicitly: + +```python icon="python" focus={3-5, 7-13} +from openhands.sdk import load_agents_from_dir, register_agent, agent_definition_to_factory + +# Load from a specific directory +agents_dir = Path("agents") +agent_definitions = load_agents_from_dir(agents_dir) + +# Register each agent +for agent_def in agent_definitions: + register_agent( + name=agent_def.name, + factory_func=agent_definition_to_factory(agent_def), + description=agent_def.description, + ) +``` + +### Key Functions + +#### `load_agents_from_dir()` + +Scans a directory for `.md` files and returns a list of `AgentDefinition` objects: + +```python icon="python" focus={3} +from openhands.sdk import load_agents_from_dir + +definitions = load_agents_from_dir(Path(".agents/agents")) +for d in definitions: + print(f"{d.name}: {d.tools}, model={d.model}") +``` + +#### `agent_definition_to_factory()` + +Converts an `AgentDefinition` into a factory function `(LLM) -> Agent`: + +```python icon="python" +from openhands.sdk import agent_definition_to_factory + +factory = agent_definition_to_factory(agent_def) +# The factory is called by the delegation system with the parent's LLM +``` + +The factory: +- Maps tool names from the frontmatter to `Tool` objects +- Converts the Markdown body into an always-active `Skill` +- Respects the `model` field (`"inherit"` keeps the parent LLM; an explicit model name creates a copy) + +#### `load_project_agents()` / `load_user_agents()` + +Load agents from project-level or user-level directories respectively: + +```python icon="python" focus={3, 4} +from openhands.sdk.subagent import load_project_agents, load_user_agents + +project_agents = load_project_agents("/path/to/project") +user_agents = load_user_agents() # scans ~/.agents/agents/ and ~/.openhands/agents/ +``` + +## Using with Delegation + +File-based agents are designed to work with the [DelegateTool](/sdk/guides/agent-delegation). Once registered, the orchestrating agent can spawn and delegate tasks to them by name: + +```python icon="python" focus={6, 9-12, 15-19} +from openhands.sdk import Agent, Conversation, Tool +from openhands.sdk.subagent import register_file_agents +from openhands.sdk.tool import register_tool +from openhands.tools.delegate import DelegateTool, DelegationVisualizer + +register_file_agents("/path/to/project") # Register .agents/agents/*.md + +# Set up the orchestrator with DelegateTool +register_tool("DelegateTool", DelegateTool) +main_agent = Agent( + llm=llm, + tools=[Tool(name="DelegateTool")], +) + +conversation = Conversation( + agent=main_agent, + workspace="/path/to/project", + visualizer=DelegationVisualizer(name="Orchestrator"), +) +``` + +The orchestrator sees the registered agents and their descriptions, and can delegate work like: + +```json +{ + "command": "spawn", + "ids": ["code-reviewer", "tech-writer"] +} +``` + +## Example Agent Files + +### Code Reviewer + +```markdown icon="markdown" +--- +name: code-reviewer +description: > + Reviews code for quality, bugs, and best practices. + Review this pull request for issues + Check this code for bugs +tools: + - file_editor + - terminal +--- + +# Code Reviewer + +You are a meticulous code reviewer. When reviewing code: + +1. **Correctness** - Look for bugs, off-by-one errors, null pointer issues, and race conditions. +2. **Style** - Check for consistent naming, formatting, and idiomatic usage. +3. **Performance** - Identify unnecessary allocations, N+1 queries, or algorithmic inefficiencies. +4. **Security** - Flag potential injection vulnerabilities, hardcoded secrets, or unsafe deserialization. + +Keep feedback concise and actionable. For each issue found, suggest a concrete fix. +``` + +### Technical Writer + +```markdown icon="markdown" +--- +name: tech-writer +description: > + Writes and improves technical documentation. + Write docs for this module + Improve the README +tools: + - file_editor +--- + +# Technical Writer + +You are a skilled technical writer. When creating or improving documentation: + +1. **Audience** - Write for developers who are new to the project. +2. **Structure** - Use clear headings, code examples, and step-by-step instructions. +3. **Accuracy** - Read the source code before documenting behavior. Never guess. +4. **Brevity** - Prefer short, concrete sentences over long explanations. + +Always include a usage example with expected output when documenting functions or APIs. +``` + +## Agents in Plugins + +File-based agents can also be bundled inside [plugins](/sdk/guides/plugins). Place them in the `agents/` directory of your plugin: + + + + + + + + + + + + + +Plugin agents use the same `.md` format and are registered automatically when the plugin is loaded. They have higher priority than file-based agents but lower than programmatic `register_agent()` calls. + +## Ready-to-run Example + + +This example is available on GitHub: [examples/01_standalone_sdk/41_file_based_subagents](https://github.com/OpenHands/software-agent-sdk/tree/main/examples/01_standalone_sdk/41_file_based_subagents) + + +```python icon="python" expandable examples/01_standalone_sdk/41_file_based_subagents/41_file_based_subagents.py +"""Example: Loading Sub-Agents from Markdown Files + +This example demonstrates how to define sub-agents using Markdown files with +YAML frontmatter, load them with AgentDefinition, and register them for +delegation. + +Agent Markdown files follow a simple format: +- YAML frontmatter with: name, description, tools, model (optional), color (optional) +- The Markdown body becomes the agent's system prompt +- tags in the description help the main agent know when to delegate + +The example_agents/ directory contains two agents: +- code-reviewer: Reviews code for quality, bugs, and best practices +- tech-writer: Writes and improves technical documentation +""" + +import os +from pathlib import Path + +from openhands.sdk import ( + LLM, + Agent, + Conversation, + Tool, + agent_definition_to_factory, + get_logger, + load_agents_from_dir, + register_agent, +) +from openhands.sdk.subagent import AgentDefinition +from openhands.sdk.tool import register_tool +from openhands.tools.delegate import DelegateTool, DelegationVisualizer + + +logger = get_logger(__name__) + +script_dir = Path(__file__).parent +agents_dir = script_dir / "agents" + +print("Part 1: Loading Agent Definitions from Markdown") +print() + +# Load each agent definition and inspect its fields +for md_file in sorted(agents_dir.glob("*.md")): + agent_def = AgentDefinition.load(md_file) + print(f"\nAgent: {agent_def.name}") + print(f" Description: {agent_def.description[:80]}...") + print(f" Model: {agent_def.model}") + print(f" Tools: {agent_def.tools}") + print(f" When-to-use examples: {agent_def.when_to_use_examples}") + print(f" System prompt length: {len(agent_def.system_prompt)} chars") +print() + +print("Part 2: Registering and Using Agents with Delegation") +print() + +LLM_API_KEY = os.getenv("LLM_API_KEY") +assert LLM_API_KEY is not None, "LLM API key not set" + +model = os.getenv("LLM_MODEL", "anthropic/claude-sonnet-4-5-20250929") +llm = LLM( + model=model, + api_key=LLM_API_KEY, + base_url=os.getenv("LLM_BASE_URL"), + usage_id="file-agents-demo", +) + + +agents_definition = load_agents_from_dir(agents_dir) + +for agent_def in agents_definition: + register_agent( + name=agent_def.name, + factory_func=agent_definition_to_factory(agent_def), + description=agent_def.description, + ) + print(f"Registered agent: {agent_def.name}") + +# NOTE: In a real project, you can skip the manual loading above and instead +# place your .md files in .agents/agents/ at the project root, then call: +# +# from openhands.sdk.subagent import register_file_agents +# register_file_agents(project_dir) +# +# This automatically discovers and registers all agent definitions. + +print( + "Part 3: Set up the main (orchestrator) agent with " + "the DelegateTool and start the conversation" +) +print() + +# Set up the main (orchestrator) agent with the DelegateTool +register_tool("DelegateTool", DelegateTool) +main_agent = Agent( + llm=llm, + tools=[Tool(name="DelegateTool")], +) +conversation = Conversation( + agent=main_agent, + workspace=Path.cwd(), + visualizer=DelegationVisualizer(name="Orchestrator"), +) + +# Ask the main agent to delegate work to our file-based agents +task = ( + f"I have a Python file at {agents_dir}/code-reviewer.md. " + "Please delegate to the code-reviewer agent and ask it to review that file " + "for any issues. Then delegate to the tech-writer agent and ask it to " + "suggest a short README paragraph describing what is the code-reviewer.md " + "file doing. " + "Finally, combine both results into a summary." +) + +print("\nSending task to orchestrator...") +conversation.send_message(task) +conversation.run() + +cost = conversation.conversation_stats.get_combined_metrics().accumulated_cost +print(f"\nTotal cost: ${cost:.4f}") +print(f"EXAMPLE_COST: {cost:.4f}") +``` + + + +## Next Steps + +- **[Sub-Agent Delegation](/sdk/guides/agent-delegation)** - Learn about the DelegateTool and delegation patterns +- **[Skills](/sdk/guides/skill)** - Add specialized knowledge and triggers to agents +- **[Plugins](/sdk/guides/plugins)** - Bundle agents, skills, hooks, and MCP servers together +- **[Custom Agent](/sdk/guides/agent-custom)** - Create agents programmatically for more control From 22507a352679148534d44f8d3d8d7cdd5bdf76f7 Mon Sep 17 00:00:00 2001 From: VascoSch92 Date: Tue, 24 Feb 2026 15:35:28 +0100 Subject: [PATCH 2/8] fix --- sdk/guides/agent-file-based.mdx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/sdk/guides/agent-file-based.mdx b/sdk/guides/agent-file-based.mdx index 2fe29c58..843bd8d4 100644 --- a/sdk/guides/agent-file-based.mdx +++ b/sdk/guides/agent-file-based.mdx @@ -5,6 +5,8 @@ description: Define specialized sub-agents as simple Markdown files with YAML fr import RunExampleCode from "/sdk/shared-snippets/how-to-run-example.mdx"; +> A ready-to-run example is available [here](#ready-to-run-example)! + File-based agents let you define specialized sub-agents using Markdown files. Each file declares the agent's name, description, tools, and system prompt — the same things you'd pass to `register_agent()` in code, but without writing any Python. This is the fastest way to create reusable, domain-specific agents that can be invoked via [delegation](/sdk/guides/agent-delegation). @@ -48,7 +50,7 @@ The YAML frontmatter configures the agent. The Markdown body becomes the agent's | `description` | No | `""` | What this agent does. Shown to the orchestrator | | `tools` | No | `[]` | List of tools the agent can use | | `model` | No | `"inherit"` | LLM model (`"inherit"` uses the parent agent's model) | -| `color` | No | `None` | Display color for the agent | +| `color` | No | `None` | [Rich color name](https://rich.readthedocs.io/en/stable/appendix/colors.html) (e.g., `"blue"`, `"green"`) used by visualizers to style this agent's output in terminal panels | ### `` Tags @@ -70,9 +72,9 @@ Place agent files in these directories, scanned in **priority order** (first mat | Priority | Location | Scope | |----------|----------|-------| | 1 | `{project}/.agents/agents/*.md` | Project-level (primary) | -| 2 | `{project}/.openhands/agents/*.md` | Project-level (legacy) | +| 2 | `{project}/.openhands/agents/*.md` | Project-level (secondary) | | 3 | `~/.agents/agents/*.md` | User-level (primary) | -| 4 | `~/.openhands/agents/*.md` | User-level (legacy) | +| 4 | `~/.openhands/agents/*.md` | User-level (secondary) | @@ -270,7 +272,10 @@ Always include a usage example with expected output when documenting functions o ## Agents in Plugins -File-based agents can also be bundled inside [plugins](/sdk/guides/plugins). Place them in the `agents/` directory of your plugin: +> Plugins bundle agents, tools, skills, and MCP servers into reusable packages. +Learn more about plugins [here](/sdk/guides/plugins). + +File-based agents can also be bundled inside plugins. Place them in the `agents/` directory of your plugin: From a39faf9c467f7f8a6157ed459b14b01a82b73a27 Mon Sep 17 00:00:00 2001 From: VascoSch92 Date: Wed, 25 Feb 2026 16:29:09 +0100 Subject: [PATCH 3/8] add builtin agents --- sdk/guides/agent-file-based.mdx | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/sdk/guides/agent-file-based.mdx b/sdk/guides/agent-file-based.mdx index 843bd8d4..0e9cafc6 100644 --- a/sdk/guides/agent-file-based.mdx +++ b/sdk/guides/agent-file-based.mdx @@ -99,14 +99,27 @@ Place agent files in these directories, scanned in **priority order** (first mat Put agents shared across all your projects in `~/.agents/agents/`. Put project-specific agents in `{project}/.agents/agents/`. +## Built-in Agents + +The SDK ships with one builtin subagent. + +All presets also include `FinishTool` and `ThinkTool` as built-in tools (always appended, cannot be filtered). + +| Preset | Tools | Description | +|--------|-------|-------| +| **default** | `terminal`, `file_editor`, `task_tracker`, `browser_tool_set` | Default agent | + ## Overall Priority -When the same agent name is defined in multiple places, the highest-priority source wins: +When the same agent name is defined in multiple places, the highest-priority source wins. Registration is first-come first-win. -1. **Programmatic** `register_agent()` calls (never overwritten) -2. **Plugin agents** (`Plugin.agents`) -3. **Project-level** file-based agents -4. **User-level** file-based agents +| Priority | Source | Description | +|----------|--------|-------------| +| 1 (highest) | **Programmatic** `register_agent()` | Registered first, never overwritten | +| 2 | **Plugin agents** (`Plugin.agents`) | Loaded from plugin `agents/` directories | +| 3 | **Project-level** file-based agents | `.agents/agents/*.md` or `.openhands/agents/*.md` | +| 4 | **User-level** file-based agents | `~/.agents/agents/*.md` or `~/.openhands/agents/*.md` | +| 5 (lowest) | **Built-in agents** | SDK built-in agents | ## Auto-Registration From d48022938cd8221d23da263431a8f2205fc3738d Mon Sep 17 00:00:00 2001 From: Vasco Schiavo <115561717+VascoSch92@users.noreply.github.com> Date: Fri, 27 Feb 2026 09:31:05 +0100 Subject: [PATCH 4/8] Update sdk/guides/agent-file-based.mdx Co-authored-by: OpenHands Bot --- sdk/guides/agent-file-based.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdk/guides/agent-file-based.mdx b/sdk/guides/agent-file-based.mdx index 0e9cafc6..7cbcb860 100644 --- a/sdk/guides/agent-file-based.mdx +++ b/sdk/guides/agent-file-based.mdx @@ -128,7 +128,8 @@ The simplest way to use file-based agents is auto-registration. Call `register_f ```python icon="python" focus={3} from openhands.sdk.subagent import register_file_agents -registered = register_file_agents("/path/to/project") +agent_names = register_file_agents("/path/to/project") +print(f"Registered {len(agent_names)} agents: {agent_names}") print(f"Registered agents: {registered}") ``` From b0138c391debba06d4c0de5c124cfb97a4b79019 Mon Sep 17 00:00:00 2001 From: VascoSch92 Date: Fri, 27 Feb 2026 10:24:34 +0100 Subject: [PATCH 5/8] feedback --- sdk/guides/agent-file-based.mdx | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/sdk/guides/agent-file-based.mdx b/sdk/guides/agent-file-based.mdx index 7cbcb860..1eaafb96 100644 --- a/sdk/guides/agent-file-based.mdx +++ b/sdk/guides/agent-file-based.mdx @@ -101,13 +101,15 @@ Put agents shared across all your projects in `~/.agents/agents/`. Put project-s ## Built-in Agents -The SDK ships with one builtin subagent. +The SDK ships with built-in agents that are automatically loaded at the beginning of each conversation and are available to the user. -All presets also include `FinishTool` and `ThinkTool` as built-in tools (always appended, cannot be filtered). +All built-in agents include `FinishTool` and `ThinkTool` (these are always appended and cannot be filtered). -| Preset | Tools | Description | +The table below summarizes all available built-in agents: + +| Agent | Tools | Description | |--------|-------|-------| -| **default** | `terminal`, `file_editor`, `task_tracker`, `browser_tool_set` | Default agent | +| **default** | `terminal`, `file_editor`, `task_tracker`, `browser_tool_set` | general purpose agent | ## Overall Priority @@ -222,14 +224,7 @@ conversation = Conversation( ) ``` -The orchestrator sees the registered agents and their descriptions, and can delegate work like: - -```json -{ - "command": "spawn", - "ids": ["code-reviewer", "tech-writer"] -} -``` +To learn more about agent delegation, follow our [comprehensive guide](/sdk/guides/agent-file-based.mdx). ## Example Agent Files From 88571e15b392ca7d2e636a9cc83facd6a96c938f Mon Sep 17 00:00:00 2001 From: VascoSch92 Date: Fri, 27 Feb 2026 10:29:09 +0100 Subject: [PATCH 6/8] feedback --- sdk/guides/agent-file-based.mdx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sdk/guides/agent-file-based.mdx b/sdk/guides/agent-file-based.mdx index 1eaafb96..ae7fcd88 100644 --- a/sdk/guides/agent-file-based.mdx +++ b/sdk/guides/agent-file-based.mdx @@ -132,7 +132,6 @@ from openhands.sdk.subagent import register_file_agents agent_names = register_file_agents("/path/to/project") print(f"Registered {len(agent_names)} agents: {agent_names}") -print(f"Registered agents: {registered}") ``` This scans both project-level and user-level directories, deduplicates by name, and registers each agent as a delegate that can be spawned by the orchestrator. @@ -224,7 +223,7 @@ conversation = Conversation( ) ``` -To learn more about agent delegation, follow our [comprehensive guide](/sdk/guides/agent-file-based.mdx). +To learn more about agent delegation, follow our [comprehensive guide](/sdk/guides/agent-delegation.mdx). ## Example Agent Files From a4fe0358c9598c97c155f6e6504a56b9e4308c35 Mon Sep 17 00:00:00 2001 From: VascoSch92 Date: Fri, 27 Feb 2026 10:32:32 +0100 Subject: [PATCH 7/8] feedback --- sdk/guides/agent-file-based.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/guides/agent-file-based.mdx b/sdk/guides/agent-file-based.mdx index ae7fcd88..2d5e57f4 100644 --- a/sdk/guides/agent-file-based.mdx +++ b/sdk/guides/agent-file-based.mdx @@ -223,7 +223,7 @@ conversation = Conversation( ) ``` -To learn more about agent delegation, follow our [comprehensive guide](/sdk/guides/agent-delegation.mdx). +To learn more about agent delegation, follow our [comprehensive guide](/sdk/guides/agent-delegation). ## Example Agent Files From c931c1398d60f3d7ed0c523490d735ec4442299c Mon Sep 17 00:00:00 2001 From: openhands Date: Mon, 2 Mar 2026 06:55:51 +0000 Subject: [PATCH 8/8] docs(sdk): update file-based agents example to 42_file_based_subagents Co-authored-by: openhands --- sdk/guides/agent-file-based.mdx | 120 ++++++++++---------------------- 1 file changed, 37 insertions(+), 83 deletions(-) diff --git a/sdk/guides/agent-file-based.mdx b/sdk/guides/agent-file-based.mdx index 2d5e57f4..627e44a4 100644 --- a/sdk/guides/agent-file-based.mdx +++ b/sdk/guides/agent-file-based.mdx @@ -63,7 +63,7 @@ description: > Improve the README ``` -These examples are extracted and stored as `when_to_use_examples` on the `AgentDefinition` object. The delegation system uses them to match user requests to the right sub-agent. +These examples are extracted and stored as `when_to_use_examples` on the `AgentDefinition` object. They can be used by routing logic (or prompt-building) to help decide when to delegate to the right sub-agent. ## Directory Conventions @@ -103,7 +103,7 @@ Put agents shared across all your projects in `~/.agents/agents/`. Put project-s The SDK ships with built-in agents that are automatically loaded at the beginning of each conversation and are available to the user. -All built-in agents include `FinishTool` and `ThinkTool` (these are always appended and cannot be filtered). +By default, agents include `FinishTool` and `ThinkTool`; they are appended after tool filtering. The table below summarizes all available built-in agents: @@ -140,7 +140,9 @@ This scans both project-level and user-level directories, deduplicates by name, For more control, load and register agents explicitly: -```python icon="python" focus={3-5, 7-13} +```python icon="python" focus={3-6, 8-14} +from pathlib import Path + from openhands.sdk import load_agents_from_dir, register_agent, agent_definition_to_factory # Load from a specific directory @@ -162,7 +164,9 @@ for agent_def in agent_definitions: Scans a directory for `.md` files and returns a list of `AgentDefinition` objects: -```python icon="python" focus={3} +```python icon="python" focus={3-4} +from pathlib import Path + from openhands.sdk import load_agents_from_dir definitions = load_agents_from_dir(Path(".agents/agents")) @@ -183,7 +187,7 @@ factory = agent_definition_to_factory(agent_def) The factory: - Maps tool names from the frontmatter to `Tool` objects -- Converts the Markdown body into an always-active `Skill` +- Appends the Markdown body to the parent system message via `AgentContext(system_message_suffix=...)` - Respects the `model` field (`"inherit"` keeps the parent LLM; an explicit model name creates a copy) #### `load_project_agents()` / `load_user_agents()` @@ -302,24 +306,17 @@ Plugin agents use the same `.md` format and are registered automatically when th ## Ready-to-run Example -This example is available on GitHub: [examples/01_standalone_sdk/41_file_based_subagents](https://github.com/OpenHands/software-agent-sdk/tree/main/examples/01_standalone_sdk/41_file_based_subagents) +This example is available on GitHub: [examples/01_standalone_sdk/42_file_based_subagents.py](https://github.com/OpenHands/software-agent-sdk/blob/main/examples/01_standalone_sdk/42_file_based_subagents.py) -```python icon="python" expandable examples/01_standalone_sdk/41_file_based_subagents/41_file_based_subagents.py -"""Example: Loading Sub-Agents from Markdown Files - -This example demonstrates how to define sub-agents using Markdown files with -YAML frontmatter, load them with AgentDefinition, and register them for -delegation. +This example uses `AgentDefinition` directly. File-based agents are loaded into the same `AgentDefinition` objects (from Markdown) and registered the same way. -Agent Markdown files follow a simple format: -- YAML frontmatter with: name, description, tools, model (optional), color (optional) -- The Markdown body becomes the agent's system prompt -- tags in the description help the main agent know when to delegate +```python icon="python" expandable examples/01_standalone_sdk/42_file_based_subagents.py +"""Example: Defining a sub-agent inline with AgentDefinition. -The example_agents/ directory contains two agents: -- code-reviewer: Reviews code for quality, bugs, and best practices -- tech-writer: Writes and improves technical documentation +Defines a grammar-checker sub-agent using AgentDefinition, registers it, +and delegates work to it from an orchestrator agent. The orchestrator then +asks the builtin default agent to judge the results. """ import os @@ -331,8 +328,6 @@ from openhands.sdk import ( Conversation, Tool, agent_definition_to_factory, - get_logger, - load_agents_from_dir, register_agent, ) from openhands.sdk.subagent import AgentDefinition @@ -340,65 +335,29 @@ from openhands.sdk.tool import register_tool from openhands.tools.delegate import DelegateTool, DelegationVisualizer -logger = get_logger(__name__) - -script_dir = Path(__file__).parent -agents_dir = script_dir / "agents" - -print("Part 1: Loading Agent Definitions from Markdown") -print() - -# Load each agent definition and inspect its fields -for md_file in sorted(agents_dir.glob("*.md")): - agent_def = AgentDefinition.load(md_file) - print(f"\nAgent: {agent_def.name}") - print(f" Description: {agent_def.description[:80]}...") - print(f" Model: {agent_def.model}") - print(f" Tools: {agent_def.tools}") - print(f" When-to-use examples: {agent_def.when_to_use_examples}") - print(f" System prompt length: {len(agent_def.system_prompt)} chars") -print() - -print("Part 2: Registering and Using Agents with Delegation") -print() +# 1. Define a sub-agent using AgentDefinition +grammar_checker = AgentDefinition( + name="grammar-checker", + description="Checks documents for grammatical errors.", + tools=["file_editor"], + system_prompt="You are a grammar expert. Find and list grammatical errors.", +) -LLM_API_KEY = os.getenv("LLM_API_KEY") -assert LLM_API_KEY is not None, "LLM API key not set" +# 2. Register it in the delegate registry +register_agent( + name=grammar_checker.name, + factory_func=agent_definition_to_factory(grammar_checker), + description=grammar_checker.description, +) -model = os.getenv("LLM_MODEL", "anthropic/claude-sonnet-4-5-20250929") +# 3. Set up the orchestrator agent with the DelegateTool llm = LLM( - model=model, - api_key=LLM_API_KEY, + model=os.getenv("LLM_MODEL", "anthropic/claude-sonnet-4-5-20250929"), + api_key=os.getenv("LLM_API_KEY"), base_url=os.getenv("LLM_BASE_URL"), usage_id="file-agents-demo", ) - -agents_definition = load_agents_from_dir(agents_dir) - -for agent_def in agents_definition: - register_agent( - name=agent_def.name, - factory_func=agent_definition_to_factory(agent_def), - description=agent_def.description, - ) - print(f"Registered agent: {agent_def.name}") - -# NOTE: In a real project, you can skip the manual loading above and instead -# place your .md files in .agents/agents/ at the project root, then call: -# -# from openhands.sdk.subagent import register_file_agents -# register_file_agents(project_dir) -# -# This automatically discovers and registers all agent definitions. - -print( - "Part 3: Set up the main (orchestrator) agent with " - "the DelegateTool and start the conversation" -) -print() - -# Set up the main (orchestrator) agent with the DelegateTool register_tool("DelegateTool", DelegateTool) main_agent = Agent( llm=llm, @@ -410,17 +369,12 @@ conversation = Conversation( visualizer=DelegationVisualizer(name="Orchestrator"), ) -# Ask the main agent to delegate work to our file-based agents +# 4. Ask the orchestrator to delegate to our agent task = ( - f"I have a Python file at {agents_dir}/code-reviewer.md. " - "Please delegate to the code-reviewer agent and ask it to review that file " - "for any issues. Then delegate to the tech-writer agent and ask it to " - "suggest a short README paragraph describing what is the code-reviewer.md " - "file doing. " - "Finally, combine both results into a summary." + "Please delegate to the grammar-checker agent and ask it to review " + "the README.md file in search of grammatical errors.\n" + "Then ask the default agent to judge the errors." ) - -print("\nSending task to orchestrator...") conversation.send_message(task) conversation.run() @@ -429,7 +383,7 @@ print(f"\nTotal cost: ${cost:.4f}") print(f"EXAMPLE_COST: {cost:.4f}") ``` - + ## Next Steps