Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 79 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,86 @@
# skillos

Implementation of Google's [SkillOS](https://arxiv.org/abs/2605.06614).
Implementation of Google's [SkillOS](https://arxiv.org/abs/2605.06614) — a framework for self-evolving agents.

Basically, it is a framework for self-evolving agents.
## Architecture

## Packages
SkillOS is split into a backend-agnostic core and SDK-specific plugins:

- [`packages/skillos-core/`](packages/skillos-core/) — core abstractions
(`SkillRepo`, `Skill`, `Curator`, `AsyncCurator`). Backend-agnostic via fsspec.
- [`packages/skillos-strands/`](packages/skillos-strands/) — Strands Agents
analyzer for the Curator (Amazon Bedrock via `strands-agents`).
| Package | Install | Description |
|---------|---------|-------------|
| [`skillos-core`](packages/skillos-core/) | `pip install skillos-core` | Core abstractions: `SkillRepo`, `Skill`, `Curator`, `Changelog`. Backend-agnostic via fsspec. |
| [`skillos-strands`](packages/skillos-strands/) | `pip install skillos-strands` | Curator backed by [Strands Agents](https://github.com/strands-agents/sdk-python) (Amazon Bedrock). |
| [`skillos-adk`](packages/skillos-adk/) | `pip install skillos-adk` | Curator backed by [Google ADK](https://google.github.io/adk-docs/) (Gemini). |
| [`skillos-langchain`](packages/skillos-langchain/) | `pip install skillos-langchain` | Curator backed by [LangChain](https://python.langchain.com/) (any LLM). |

The core package defines the `Curator` interface and `SkillRepo` primitives. SDK packages implement the curator for their framework — and nothing else. You pick the one that matches your agent runtime.

## Quick start

Choose the package for your agent framework:

=== "Strands"

```bash
pip install skillos-strands
```

```python
from skillos_core import SkillRepo
from skillos_strands import StrandsCurator
from strands import Agent
from strands.models import BedrockModel

repo = SkillRepo("./my-skills")
model = BedrockModel(model_id="us.amazon.nova-pro-v1:0")
curator = StrandsCurator(repo, model=model)

agent = Agent(model=model, hooks=[curator.hook()])
agent("Summarize this document for me.")
# → skills are curated automatically after each invocation
```

=== "Google ADK"

```bash
pip install skillos-adk
```

```python
from skillos_core import SkillRepo
from skillos_adk import ADKCurator
from google.adk.agents import LlmAgent
from google.adk.runners import Runner

repo = SkillRepo("./my-skills")
curator = ADKCurator(repo, model="gemini-2.0-flash")

agent = LlmAgent(name="my-agent", model="gemini-2.0-flash")
runner = Runner(agent=agent, plugins=[curator.plugin()])
# → skills are curated automatically after each run
```

=== "LangChain"

```bash
pip install skillos-langchain
```

```python
from skillos_core import SkillRepo
from skillos_langchain import LangChainCurator
from langchain_aws import ChatBedrock

repo = SkillRepo("./my-skills")
llm = ChatBedrock(model_id="us.amazon.nova-pro-v1:0")
curator = LangChainCurator(repo, llm=llm)

await agent.ainvoke(
{"input": "Summarize this document."},
config={"callbacks": [curator.callback()]},
)
# → skills are curated automatically when the chain completes
```

## Development

Expand All @@ -22,7 +93,7 @@ uv sync # create .venv and install all workspace packages
uv run pytest # run the full test suite
```

To work on a single package, run pytest scoped to it:
To work on a single package:

```
uv run pytest packages/skillos-core
Expand Down
77 changes: 77 additions & 0 deletions docs/api/skillos-adk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# skillos-adk API Reference

`skillos-adk` wraps a [Google ADK](https://google.github.io/adk-docs/) `LlmAgent` as a [`Curator`](skillos-core.md#curator). It fires automatically after each runner invocation via an ADK plugin, or can be called manually.

```bash
pip install skillos-adk
```

---

## ADKCurator

```python
from skillos_adk import ADKCurator
```

A [`Curator`](skillos-core.md#curator) backed by a Google ADK `LlmAgent`.

### `ADKCurator(repo, *, model, system_prompt=...)`

| Parameter | Type | Description |
|-----------|------|-------------|
| `repo` | `SkillRepo` | The skill repository to curate. |
| `model` | `str \| BaseLlm` | Model name (e.g. `"gemini-2.0-flash"`) or an ADK `BaseLlm` instance. |
| `system_prompt` | `str` | Override the default curator system prompt. |

### `plugin() -> BasePlugin`

Return a `BasePlugin` for use in `Runner(plugins=[curator.plugin()])`. The plugin fires `curate()` automatically after every runner invocation via `after_run_callback`.

```python
from skillos_adk import ADKCurator
from google.adk.agents import LlmAgent
from google.adk.runners import InMemoryRunner

repo = SkillRepo("./my-skills")
curator = ADKCurator(repo, model="gemini-2.0-flash")

agent = LlmAgent(name="my-agent", model="gemini-2.0-flash")
runner = InMemoryRunner(agent=agent, app_name="my-app", plugins=[curator.plugin()])
```

### `curate(history: ConversationHistory) -> Changelog`

Analyse a [`ConversationHistory`](skillos-core.md#conversationhistory) and mutate the repo. Returns a [`Changelog`](skillos-core.md#changelog) describing what changed.

Use this directly when you want to trigger curation from a specific point in your runner loop:

```python
history = _events_to_history(session.events)
changelog = await curator.curate(history)
print(f"{len(changelog.applied)} skill(s) updated")
```

---

## create_skill_tools

```python
from skillos_adk import create_skill_tools
```

### `create_skill_tools(repo, *, changelog=None) -> list[FunctionTool]`

Create the five ADK `FunctionTool` wrappers for interacting with a `SkillRepo`. Returned tools are suitable for `LlmAgent(tools=...)`.

| Tool | Description |
|------|-------------|
| `list_skills` | Return all skill names. |
| `read_skill(name)` | Return a skill's name, description, body, metadata, and resource list. |
| `insert_skill(name, description, body, ...)` | Create a new skill. |
| `update_skill(name, ...)` | Partially update an existing skill. |
| `delete_skill(name)` | Delete a skill and its resources. |

When `changelog` is provided, every mutation is recorded as a [`Change`](skillos-core.md#change).

`ADKCurator` calls `create_skill_tools` internally; you only need this directly if you are building a custom curator or want skill tools in your own ADK agent.
66 changes: 66 additions & 0 deletions docs/api/skillos-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,72 @@ Read a bundled resource file by relative path.

---

## Curator

```python
from skillos_core import Curator
```

Abstract base class for SDK-specific curator implementations.

### `curate(history: ConversationHistory) -> Changelog`

Analyse the conversation and make changes to the `SkillRepo`. Returns a `Changelog` describing what was created, updated, or deleted. Concrete implementations are provided by `skillos-strands`, `skillos-adk`, and `skillos-langchain`.

---

## ConversationHistory

```python
from skillos_core import ConversationHistory, Message
```

`ConversationHistory = list[Message]` where `Message = dict[str, Any]`. SDK packages translate their native message types into this format before passing them to `Curator.curate()`.

---

## Changelog

```python
from skillos_core import Changelog, Change, ChangeKind
```

### `Changelog`

A record of what the curator did in one pass.

| Attribute / Property | Type | Description |
|----------------------|------|-------------|
| `changes` | `list[Change]` | All changes attempted, in order. |
| `applied` | `list[Change]` | Changes that succeeded. |
| `failed` | `list[Change]` | Changes that raised an exception. |

### `Change`

A single attempted skill mutation.

| Field | Type | Description |
|-------|------|-------------|
| `kind` | `ChangeKind` | `INSERT`, `UPDATE`, or `DELETE`. |
| `name` | `str` | Name of the skill. |
| `applied` | `bool` | `True` if the mutation succeeded. |
| `error` | `str \| None` | Error message if `applied` is `False`. |
| `description` | `str \| None` | New description, if supplied. |
| `body` | `str \| None` | New body, if supplied. |
| `license` | `str \| None` | New license, if supplied. |
| `allowed_tools` | `list[str] \| None` | New tool list, if supplied. |

### `ChangeKind`

```python
class ChangeKind(str, Enum):
INSERT = "insert"
UPDATE = "update"
DELETE = "delete"
```

---

## License

```python
Expand Down
78 changes: 78 additions & 0 deletions docs/api/skillos-strands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# skillos-strands API Reference

`skillos-strands` wraps a [Strands Agents](https://github.com/strands-agents/sdk-python) `Agent` as a [`Curator`](skillos-core.md#curator). It fires automatically after each invocation via the Strands hook system, or can be called manually.

```bash
pip install skillos-strands
```

---

## StrandsCurator

```python
from skillos_strands import StrandsCurator
```

A [`Curator`](skillos-core.md#curator) backed by a Strands `Agent`.

### `StrandsCurator(repo, *, model, system_prompt=...)`

| Parameter | Type | Description |
|-----------|------|-------------|
| `repo` | `SkillRepo` | The skill repository to curate. |
| `model` | `strands.models.Model` | The Strands model to use for the curator agent. |
| `system_prompt` | `str` | Override the default curator system prompt. |

### `hook() -> HookProvider`

Return a `HookProvider` for use in `Agent(hooks=[curator.hook()])`. The hook fires `curate()` automatically after every agent invocation.

```python
from skillos_strands import StrandsCurator
from strands import Agent
from strands.models import BedrockModel

repo = SkillRepo("./my-skills")
model = BedrockModel(model_id="us.amazon.nova-pro-v1:0")
curator = StrandsCurator(repo, model=model)

agent = Agent(model=model, hooks=[curator.hook()])
agent("Do something useful.")
```

### `curate(history: ConversationHistory) -> Changelog`

Analyse a [`ConversationHistory`](skillos-core.md#conversationhistory) and mutate the repo. Returns a [`Changelog`](skillos-core.md#changelog) describing what changed.

Use this directly when you manage the agent lifecycle yourself and want to trigger curation at a specific point:

```python
result = agent("Do something.")
changelog = await curator.curate(agent.messages)
print(f"{len(changelog.applied)} skill(s) updated")
```

---

## create_skill_tools

```python
from skillos_strands import create_skill_tools
```

### `create_skill_tools(repo, *, changelog=None) -> list[DecoratedFunctionTool]`

Create the five Strands `@tool` functions for interacting with a `SkillRepo`. Returned tools are suitable for `Agent(tools=...)`.

| Tool | Description |
|------|-------------|
| `list_skills` | Return all skill names. |
| `read_skill(name)` | Return a skill's name, description, body, metadata, and resource list. |
| `insert_skill(name, description, body, ...)` | Create a new skill. |
| `update_skill(name, ...)` | Partially update an existing skill. |
| `delete_skill(name)` | Delete a skill and its resources. |

When `changelog` is provided, every mutation is recorded as a [`Change`](skillos-core.md#change).

`StrandsCurator` calls `create_skill_tools` internally; you only need this directly if you are building a custom curator or want skill tools in your own agent.
Loading