Skip to main content
A ready-to-run example is available here!
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.

Agent File Format

An agent is a single .md file with YAML frontmatter and a Markdown body:
---
name: code-reviewer
description: >
  Reviews code for quality, bugs, and best practices.
  <example>Review this pull request for issues</example>
  <example>Check this code for bugs</example>
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

FieldRequiredDefaultDescription
nameYes-Agent identifier (e.g., code-reviewer)
descriptionNo""What this agent does. Shown to the orchestrator
toolsNo[]List of tools the agent can use
modelNo"inherit"LLM model ("inherit" uses the parent agent’s model)
colorNoNoneRich color name (e.g., "blue", "green") used by visualizers to style this agent’s output in terminal panels

<example> Tags

Add <example> tags inside the description to help the orchestrating agent know when to delegate to this agent:
description: >
  Writes and improves technical documentation.
  <example>Write docs for this module</example>
  <example>Improve the README</example>
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

Place agent files in these directories, scanned in priority order (first match wins):
PriorityLocationScope
1{project}/.agents/agents/*.mdProject-level (primary)
2{project}/.openhands/agents/*.mdProject-level (secondary)
3~/.agents/agents/*.mdUser-level (primary)
4~/.openhands/agents/*.mdUser-level (secondary)
my-project/
.agents
agents
code-reviewer.md
tech-writer.md
security-auditor.md
src/
...
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/.

Built-in Agents

The SDK ships with built-in agents that are automatically loaded at the beginning of each conversation and are available to the user. By default, agents include FinishTool and ThinkTool; they are appended after tool filtering. The table below summarizes all available built-in agents:
AgentToolsDescription
defaultterminal, file_editor, task_tracker, browser_tool_setgeneral purpose agent

Overall Priority

When the same agent name is defined in multiple places, the highest-priority source wins. Registration is first-come first-win.
PrioritySourceDescription
1 (highest)Programmatic register_agent()Registered first, never overwritten
2Plugin agents (Plugin.agents)Loaded from plugin agents/ directories
3Project-level file-based agents.agents/agents/*.md or .openhands/agents/*.md
4User-level file-based agents~/.agents/agents/*.md or ~/.openhands/agents/*.md
5 (lowest)Built-in agentsSDK built-in 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:
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}")
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:
from pathlib import Path

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:
from pathlib import Path

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:
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
  • 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()

Load agents from project-level or user-level directories respectively:
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. Once registered, the orchestrating agent can spawn and delegate tasks to them by name:
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"),
)
To learn more about agent delegation, follow our comprehensive guide.

Example Agent Files

Code Reviewer

---
name: code-reviewer
description: >
  Reviews code for quality, bugs, and best practices.
  <example>Review this pull request for issues</example>
  <example>Check this code for bugs</example>
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

---
name: tech-writer
description: >
  Writes and improves technical documentation.
  <example>Write docs for this module</example>
  <example>Improve the README</example>
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

Plugins bundle agents, tools, skills, and MCP servers into reusable packages. Learn more about plugins here.
File-based agents can also be bundled inside plugins. Place them in the agents/ directory of your plugin:
my-plugin/
.plugin
plugin.json
agents
code-reviewer.md
tech-writer.md
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/42_file_based_subagents.py
This example uses AgentDefinition directly. File-based agents are loaded into the same AgentDefinition objects (from Markdown) and registered the same way.
examples/01_standalone_sdk/42_file_based_subagents.py
"""Example: Defining a sub-agent inline with AgentDefinition.

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
from pathlib import Path

from openhands.sdk import (
    LLM,
    Agent,
    Conversation,
    Tool,
    agent_definition_to_factory,
    register_agent,
)
from openhands.sdk.subagent import AgentDefinition
from openhands.sdk.tool import register_tool
from openhands.tools.delegate import DelegateTool, DelegationVisualizer


# 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.",
)

# 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,
)

# 3. Set up the orchestrator agent with the DelegateTool
llm = LLM(
    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",
)

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

# 4. Ask the orchestrator to delegate to our agent
task = (
    "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."
)
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}")
You can run the example code as-is.
The model name should follow the LiteLLM convention: provider/model_name (e.g., anthropic/claude-sonnet-4-5-20250929, openai/gpt-4o). The LLM_API_KEY should be the API key for your chosen provider.
ChatGPT Plus/Pro subscribers: You can use LLM.subscription_login() to authenticate with your ChatGPT account and access Codex models without consuming API credits. See the LLM Subscriptions guide for details.

Next Steps

  • Sub-Agent Delegation - Learn about the DelegateTool and delegation patterns
  • Skills - Add specialized knowledge and triggers to agents
  • Plugins - Bundle agents, skills, hooks, and MCP servers together
  • Custom Agent - Create agents programmatically for more control