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.
An agent is a single .md file with YAML frontmatter and a Markdown body:
Copy
Ask AI
---name: code-reviewerdescription: > 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 - terminalmodel: inherit---# Code ReviewerYou 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.
Add <example> tags inside the description to help the orchestrating agent know when to delegate to this agent:
Copy
Ask AI
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.
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:
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:
Copy
Ask AI
from openhands.sdk.subagent import register_file_agentsagent_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.
For more control, load and register agents explicitly:
Copy
Ask AI
from pathlib import Pathfrom openhands.sdk import load_agents_from_dir, register_agent, agent_definition_to_factory# Load from a specific directoryagents_dir = Path("agents")agent_definitions = load_agents_from_dir(agents_dir)# Register each agentfor agent_def in agent_definitions: register_agent( name=agent_def.name, factory_func=agent_definition_to_factory(agent_def), description=agent_def.description, )
Scans a directory for .md files and returns a list of AgentDefinition objects:
Copy
Ask AI
from pathlib import Pathfrom openhands.sdk import load_agents_from_dirdefinitions = load_agents_from_dir(Path(".agents/agents"))for d in definitions: print(f"{d.name}: {d.tools}, model={d.model}")
Converts an AgentDefinition into a factory function (LLM) -> Agent:
Copy
Ask AI
from openhands.sdk import agent_definition_to_factoryfactory = 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)
---name: code-reviewerdescription: > 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 ReviewerYou 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.
---name: tech-writerdescription: > Writes and improves technical documentation. <example>Write docs for this module</example> <example>Improve the README</example>tools: - file_editor---# Technical WriterYou 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.
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.
This example uses AgentDefinition directly. File-based agents are loaded into the same AgentDefinition objects (from Markdown) and registered the same way.
"""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 thenasks the builtin default agent to judge the results."""import osfrom pathlib import Pathfrom openhands.sdk import ( LLM, Agent, Conversation, Tool, agent_definition_to_factory, register_agent,)from openhands.sdk.subagent import AgentDefinitionfrom openhands.sdk.tool import register_toolfrom openhands.tools.delegate import DelegateTool, DelegationVisualizer# 1. Define a sub-agent using AgentDefinitiongrammar_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 registryregister_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 DelegateToolllm = 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 agenttask = ( "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_costprint(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.