Skip to content

Commit 7b4250b

Browse files
neubigopenhands-agentenystxingyaoww
authored
Rename "microagents" to "skills" throughout the repository (#820)
Co-authored-by: openhands <openhands@all-hands.dev> Co-authored-by: Engel Nyst <enyst@users.noreply.github.com> Co-authored-by: Xingyao Wang <xingyao@all-hands.dev>
1 parent 6554de0 commit 7b4250b

35 files changed

+1182
-1218
lines changed

README.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -193,28 +193,31 @@ The context system manages agent state, environment, and conversation history.
193193

194194
Context is automatically managed but you can customize your context with:
195195

196-
1. [Repo Microagents](https://docs.all-hands.dev/usage/prompting/microagents-repo) that provide agent with context of your repository.
197-
2. [Knowledge Microagents](https://docs.all-hands.dev/usage/prompting/microagents-keyword) that provide agent with context when user mentioned certain keywords
196+
1. [Repo Skills](https://docs.all-hands.dev/usage/prompting/skills-repo) that provide agent with context of your repository.
197+
2. [Knowledge Skills](https://docs.all-hands.dev/usage/prompting/skills-keyword) that provide agent with context when user mentioned certain keywords
198198
3. Providing custom suffix for system and user prompt.
199199

200200
```python
201201
from openhands.sdk import AgentContext
202-
from openhands.sdk.context import RepoMicroagent, KnowledgeMicroagent
202+
from openhands.sdk.context import KeywordTrigger, Skill
203203

204204
context = AgentContext(
205-
microagents=[
206-
RepoMicroagent(
205+
skills=[
206+
Skill(
207207
name="repo.md",
208208
content="When you see this message, you should reply like "
209209
"you are a grumpy cat forced to use the internet.",
210+
source="repo.md",
211+
trigger=None, # Always-active skill
210212
),
211-
KnowledgeMicroagent(
213+
Skill(
212214
name="flarglebargle",
213215
content=(
214216
'IMPORTANT! The user has said the magic word "flarglebargle". '
215217
"You must only respond with a message telling them how smart they are"
216218
),
217-
triggers=["flarglebargle"],
219+
source="flarglebargle.md",
220+
trigger=KeywordTrigger(keywords=["flarglebargle"]),
218221
),
219222
],
220223
system_message_suffix="Always finish your response with the word 'yay!'",

examples/01_standalone_sdk/03_activate_microagent.py renamed to examples/01_standalone_sdk/03_activate_skill.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
get_logger,
1313
)
1414
from openhands.sdk.context import (
15-
KnowledgeMicroagent,
16-
RepoMicroagent,
15+
KeywordTrigger,
16+
Skill,
1717
)
1818
from openhands.sdk.tool import Tool, register_tool
1919
from openhands.tools.execute_bash import BashTool
@@ -46,19 +46,27 @@
4646
]
4747

4848
agent_context = AgentContext(
49-
microagents=[
50-
RepoMicroagent(
49+
skills=[
50+
Skill(
5151
name="repo.md",
5252
content="When you see this message, you should reply like "
5353
"you are a grumpy cat forced to use the internet.",
54+
# source is optional - identifies where the skill came from
55+
# You can set it to be the path of a file that contains the skill content
56+
source=None,
57+
# trigger determines when the skill is active
58+
# trigger=None means always active
59+
trigger=None,
5460
),
55-
KnowledgeMicroagent(
61+
Skill(
5662
name="flarglebargle",
5763
content=(
5864
'IMPORTANT! The user has said the magic word "flarglebargle". '
5965
"You must only respond with a message telling them how smart they are"
6066
),
61-
triggers=["flarglebargle"],
67+
source=None,
68+
# KeywordTrigger = activated when keywords appear in user messages
69+
trigger=KeywordTrigger(keywords=["flarglebargle"]),
6270
),
6371
],
6472
system_message_suffix="Always finish your response with the word 'yay!'",
@@ -83,12 +91,12 @@ def conversation_callback(event: Event):
8391
)
8492

8593
print("=" * 100)
86-
print("Checking if the repo microagent is activated.")
94+
print("Checking if the repo skill is activated.")
8795
conversation.send_message("Hey are you a grumpy cat?")
8896
conversation.run()
8997

9098
print("=" * 100)
91-
print("Now sending flarglebargle to trigger the knowledge microagent!")
99+
print("Now sending flarglebargle to trigger the knowledge skill!")
92100
conversation.send_message("flarglebargle!")
93101
conversation.run()
94102

openhands-sdk/openhands/sdk/agent/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ class AgentBase(DiscriminatedUnionMixin, ABC):
8080
"the agent with specific context.",
8181
examples=[
8282
{
83-
"microagents": [
83+
"skills": [
8484
{
8585
"name": "repo.md",
8686
"content": "When you see this message, you should reply like "

openhands-sdk/openhands/sdk/context/README.md

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,38 @@
11
---
22
title: Context
3-
description: Microagents and knowledge that agents can rely on during conversations. Provides repository context and structured knowledge.
3+
description: Skills and knowledge that agents can rely on during conversations. Provides repository context and structured knowledge.
44
---
55

66
# Context
77

8-
Context provides microagents and knowledge the agent can rely on during a conversation.
8+
Context provides skills and knowledge the agent can rely on during a conversation.
99

1010
## Key Components
1111

12-
- **AgentContext**: Composes microagents; pass to Agent to condition behavior
13-
- **RepoMicroagent**: Pulls knowledge from `.openhands/microagents/repo.md` or explicit content
14-
- **KnowledgeMicroagent**: Embeds structured knowledge with optional triggers
12+
- **AgentContext**: Composes skills; pass to Agent to condition behavior
13+
- **Skill**: Embeds structured knowledge with different trigger types:
14+
- **trigger=None**: Activates for all conversations (repository-wide context)
15+
- **KeywordTrigger**: Activates when specific keywords appear in user messages
16+
- **TaskTrigger**: Activates based on task-specific conditions
1517

1618
## Quick Example
1719

1820
```python
19-
from openhands.sdk.context import AgentContext, KnowledgeMicroagent
21+
from openhands.sdk.context import AgentContext, KeywordTrigger, Skill
2022

2123
agent_context = AgentContext(
22-
microagents=[
23-
KnowledgeMicroagent(
24+
skills=[
25+
Skill(
26+
name="repo-guidelines",
27+
content="Repository-wide coding standards and best practices.",
28+
source="repo.md",
29+
trigger=None, # Always-active skill
30+
),
31+
Skill(
2432
name="flarglebargle",
2533
content="If the user says flarglebargle, compliment them.",
26-
triggers=["flarglebargle"],
34+
source="flarglebargle.md",
35+
trigger=KeywordTrigger(keywords=["flarglebargle"]),
2736
),
2837
]
2938
)
Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
1-
from openhands.sdk.context.agent_context import (
2-
AgentContext,
3-
)
4-
from openhands.sdk.context.microagents import (
5-
BaseMicroagent,
6-
KnowledgeMicroagent,
7-
MicroagentKnowledge,
8-
MicroagentValidationError,
9-
RepoMicroagent,
10-
load_microagents_from_dir,
11-
)
1+
from openhands.sdk.context.agent_context import AgentContext
122
from openhands.sdk.context.prompts import render_template
3+
from openhands.sdk.context.skills import (
4+
BaseTrigger,
5+
KeywordTrigger,
6+
Skill,
7+
SkillKnowledge,
8+
SkillValidationError,
9+
TaskTrigger,
10+
load_skills_from_dir,
11+
)
1312

1413

1514
__all__ = [
1615
"AgentContext",
17-
"BaseMicroagent",
18-
"KnowledgeMicroagent",
19-
"RepoMicroagent",
20-
"MicroagentKnowledge",
21-
"load_microagents_from_dir",
16+
"Skill",
17+
"BaseTrigger",
18+
"KeywordTrigger",
19+
"TaskTrigger",
20+
"SkillKnowledge",
21+
"load_skills_from_dir",
2222
"render_template",
23-
"MicroagentValidationError",
23+
"SkillValidationError",
2424
]

openhands-sdk/openhands/sdk/context/agent_context.py

Lines changed: 42 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@
22

33
from pydantic import BaseModel, Field, field_validator
44

5-
from openhands.sdk.context.microagents import (
6-
BaseMicroagent,
7-
KnowledgeMicroagent,
8-
MicroagentKnowledge,
9-
RepoMicroagent,
10-
)
115
from openhands.sdk.context.prompts import render_template
6+
from openhands.sdk.context.skills import (
7+
Skill,
8+
SkillKnowledge,
9+
)
1210
from openhands.sdk.llm import Message, TextContent
1311
from openhands.sdk.logger import get_logger
1412

@@ -23,26 +21,26 @@ class AgentContext(BaseModel):
2321
2422
AgentContext unifies all the contextual inputs that shape how the system
2523
extends and interprets user prompts. It combines both static environment
26-
details and dynamic, user-activated extensions from microagents.
24+
details and dynamic, user-activated extensions from skills.
2725
2826
Specifically, it provides:
29-
- **Repository context / Repo Microagents**: Information about the active codebase,
30-
branches, and repo-specific instructions contributed by repo microagents.
27+
- **Repository context / Repo Skills**: Information about the active codebase,
28+
branches, and repo-specific instructions contributed by repo skills.
3129
- **Runtime context**: Current execution environment (hosts, working
3230
directory, secrets, date, etc.).
3331
- **Conversation instructions**: Optional task- or channel-specific rules
3432
that constrain or guide the agent’s behavior across the session.
35-
- **Knowledge Microagents**: Extensible components that can be triggered by user input
33+
- **Knowledge Skills**: Extensible components that can be triggered by user input
3634
to inject knowledge or domain-specific guidance.
3735
3836
Together, these elements make AgentContext the primary container responsible
3937
for assembling, formatting, and injecting all prompt-relevant context into
4038
LLM interactions.
4139
""" # noqa: E501
4240

43-
microagents: list[BaseMicroagent] = Field(
41+
skills: list[Skill] = Field(
4442
default_factory=list,
45-
description="List of available microagents that can extend the user's input.",
43+
description="List of available skills that can extend the user's input.",
4644
)
4745
system_message_suffix: str | None = Field(
4846
default=None, description="Optional suffix to append to the system prompt."
@@ -51,42 +49,37 @@ class AgentContext(BaseModel):
5149
default=None, description="Optional suffix to append to the user's message."
5250
)
5351

54-
@field_validator("microagents")
52+
@field_validator("skills")
5553
@classmethod
56-
def _validate_microagents(cls, v: list[BaseMicroagent], _info):
54+
def _validate_skills(cls, v: list[Skill], _info):
5755
if not v:
5856
return v
59-
# Check for duplicate microagent names
57+
# Check for duplicate skill names
6058
seen_names = set()
61-
for microagent in v:
62-
if microagent.name in seen_names:
63-
raise ValueError(f"Duplicate microagent name found: {microagent.name}")
64-
seen_names.add(microagent.name)
59+
for skill in v:
60+
if skill.name in seen_names:
61+
raise ValueError(f"Duplicate skill name found: {skill.name}")
62+
seen_names.add(skill.name)
6563
return v
6664

6765
def get_system_message_suffix(self) -> str | None:
68-
"""Get the system message with repo microagent content and custom suffix.
66+
"""Get the system message with repo skill content and custom suffix.
6967
7068
Custom suffix can typically includes:
7169
- Repository information (repo name, branch name, PR number, etc.)
7270
- Runtime information (e.g., available hosts, current date)
7371
- Conversation instructions (e.g., user preferences, task details)
74-
- Repository-specific instructions (collected from repo microagents)
72+
- Repository-specific instructions (collected from repo skills)
7573
"""
76-
repo_microagents = [
77-
m for m in self.microagents if isinstance(m, RepoMicroagent)
78-
]
79-
logger.debug(
80-
f"Triggered {len(repo_microagents)} repository "
81-
f"microagents: {repo_microagents}"
82-
)
74+
repo_skills = [s for s in self.skills if s.trigger is None]
75+
logger.debug(f"Triggered {len(repo_skills)} repository skills: {repo_skills}")
8376
# Build the workspace context information
84-
if repo_microagents:
77+
if repo_skills:
8578
# TODO(test): add a test for this rendering to make sure they work
8679
formatted_text = render_template(
8780
prompt_dir=str(PROMPT_DIR),
8881
template_name="system_message_suffix.j2",
89-
repo_microagents=repo_microagents,
82+
repo_skills=repo_skills,
9083
system_message_suffix=self.system_message_suffix or "",
9184
).strip()
9285
return formatted_text
@@ -95,14 +88,14 @@ def get_system_message_suffix(self) -> str | None:
9588
return None
9689

9790
def get_user_message_suffix(
98-
self, user_message: Message, skip_microagent_names: list[str]
91+
self, user_message: Message, skip_skill_names: list[str]
9992
) -> tuple[TextContent, list[str]] | None:
100-
"""Augment the user’s message with knowledge recalled from microagents.
93+
"""Augment the user’s message with knowledge recalled from skills.
10194
10295
This works by:
10396
- Extracting the text content of the user message
104-
- Matching microagent triggers against the query
105-
- Returning formatted knowledge and triggered microagent names if relevant microagents were triggered
97+
- Matching skill triggers against the query
98+
- Returning formatted knowledge and triggered skill names if relevant skills were triggered
10699
""" # noqa: E501
107100

108101
user_message_suffix = None
@@ -112,39 +105,39 @@ def get_user_message_suffix(
112105
query = "\n".join(
113106
c.text for c in user_message.content if isinstance(c, TextContent)
114107
).strip()
115-
recalled_knowledge: list[MicroagentKnowledge] = []
108+
recalled_knowledge: list[SkillKnowledge] = []
116109
# skip empty queries, but still return user_message_suffix if it exists
117110
if not query:
118111
if user_message_suffix:
119112
return TextContent(text=user_message_suffix), []
120113
return None
121-
# Search for microagent triggers in the query
122-
for microagent in self.microagents:
123-
if not isinstance(microagent, KnowledgeMicroagent):
114+
# Search for skill triggers in the query
115+
for skill in self.skills:
116+
if not isinstance(skill, Skill):
124117
continue
125-
trigger = microagent.match_trigger(query)
126-
if trigger and microagent.name not in skip_microagent_names:
118+
trigger = skill.match_trigger(query)
119+
if trigger and skill.name not in skip_skill_names:
127120
logger.info(
128-
"Microagent '%s' triggered by keyword '%s'",
129-
microagent.name,
121+
"Skill '%s' triggered by keyword '%s'",
122+
skill.name,
130123
trigger,
131124
)
132125
recalled_knowledge.append(
133-
MicroagentKnowledge(
134-
name=microagent.name,
126+
SkillKnowledge(
127+
name=skill.name,
135128
trigger=trigger,
136-
content=microagent.content,
129+
content=skill.content,
137130
)
138131
)
139132
if recalled_knowledge:
140-
formatted_microagent_text = render_template(
133+
formatted_skill_text = render_template(
141134
prompt_dir=str(PROMPT_DIR),
142-
template_name="microagent_knowledge_info.j2",
135+
template_name="skill_knowledge_info.j2",
143136
triggered_agents=recalled_knowledge,
144137
)
145138
if user_message_suffix:
146-
formatted_microagent_text += "\n" + user_message_suffix
147-
return TextContent(text=formatted_microagent_text), [
139+
formatted_skill_text += "\n" + user_message_suffix
140+
return TextContent(text=formatted_skill_text), [
148141
k.name for k in recalled_knowledge
149142
]
150143

openhands-sdk/openhands/sdk/context/microagents/__init__.py

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)