# How the Strix Skills System Specializes Agent Behavior: A Technical Deep Dive

> Explore the Strix skills system technical deep dive. Learn how markdown knowledge packages transform LLM agents into specialized security testers. Elevate agent context and capabilities.

- Repository: [Strix/strix](https://github.com/usestrix/strix)
- Tags: deep-dive
- Published: 2026-03-26

---

**The Strix skills system transforms generic LLM agents into specialized security-testing specialists by injecting up to five markdown-based knowledge packages directly into the system prompt, enriching the model's context with domain-specific techniques, payloads, and validation steps.**

The Strix skills system equips each LLM-driven agent with curated, domain-specific knowledge packages that are dynamically injected into the agent's system prompt. By selectively loading specialized skills—capped at five per agent—the system converts a general-purpose language model into a focused penetration-testing specialist capable of sophisticated vulnerability assessment. This article examines the complete architectural flow based on the usestrix/strix open-source codebase.

## Skill Discovery and Categorization

The skills infrastructure begins with automated discovery in [`strix/skills/__init__.py`](https://github.com/usestrix/strix/blob/main/strix/skills/__init__.py). The `get_available_skills()` function scans the repository's `resources/skills` directory to build a mapping of categories to skill names. This process explicitly excludes internal categories such as `scan_modes` and `coordination` to ensure only user-facing, functional skills are exposed to agents.

Each skill is stored as a **markdown file** containing specialized security-testing knowledge, payload structures, and validation methodologies. The discovery mechanism ensures that the system maintains an up-to-date registry of available specializations without manual configuration updates.

## Agent Creation and Skill Assignment

When initializing a new agent, the `create_agent()` function defined in [`strix/tools/agents_graph/agents_graph_actions.py`](https://github.com/usestrix/strix/blob/main/strix/tools/agents_graph/agents_graph_actions.py) accepts an optional `skills` parameter containing a list of skill identifiers. This list is stored within the agent's `LLMConfig` and subsequently assigned to `LLM._active_skills` during instantiation.

This initial assignment establishes the agent's behavioral foundation. By specifying skills such as `authentication_jwt` or `business_logic` at creation time, developers pre-configure the agent with deep expertise in specific vulnerability classes before any conversation begins.

## System Prompt Injection and Composition

The core specialization mechanism occurs during system prompt construction in [`strix/llm/llm.py`](https://github.com/usestrix/strix/blob/main/strix/llm/llm.py). The `LLM._load_system_prompt` method orchestrates this process through several coordinated steps:

1. **Skill Selection**: The `_get_skills_to_load()` method appends the configured scan-mode skill and deduplicates the active skills list to prevent redundancy.

2. **Content Retrieval**: The `load_skills()` function reads the markdown files from `resources/skills`, strips any YAML front-matter, and returns a name-to-content dictionary.

3. **Template Integration**: The Jinja environment exposes this dictionary through a `get_skill` helper function, allowing the `system_prompt.jinja` template to inject raw skill content directly into the prompt context.

This injection effectively transforms the generic LLM into a domain expert by providing immediate access to specialized testing techniques and payload structures during inference.

## Runtime Skill Addition and Updates

Agents can dynamically expand their capabilities during execution through the `load_skill` tool implemented in [`strix/tools/load_skill/load_skill_actions.py`](https://github.com/usestrix/strix/blob/main/strix/tools/load_skill/load_skill_actions.py). When an agent identifies the need for additional expertise—such as network scanning or fuzzing capabilities—it can invoke this tool with a comma-separated list of skill names.

The tool validates the request format, resolves the current agent state, and invokes `LLM.add_skills()` (defined in [`strix/llm/llm.py`](https://github.com/usestrix/strix/blob/main/strix/llm/llm.py) lines 124-132) to extend the `_active_skills` list. This runtime extension updates the agent's context state immediately, allowing subsequent conversation turns to benefit from the newly loaded knowledge without restarting the session.

## Enforcement of the Five-Skill Limit

To maintain prompt efficiency and prevent context overflow, the system enforces strict limits through `validate_requested_skills()` in [`strix/skills/__init__.py`](https://github.com/usestrix/strix/blob/main/strix/skills/__init__.py) (lines 63-66). This function caps the total number of active skills at **five** per agent and returns a clear error message if an agent attempts to exceed this threshold.

This constraint ensures that the LLM receives high-fidelity, focused instructions rather than diluted, overlapping knowledge bases, maintaining the precision required for effective security testing.

## Practical Implementation Examples

### Creating an Agent with Predefined Skills

```python
from strix.tools.agents_graph.agents_graph_actions import create_agent

agent = create_agent(
    task="Test authentication mechanisms",
    skills=["authentication_jwt", "business_logic"]   # ≤ 5 items

)

```

The `create_agent` function stores this list in the agent's configuration. When the LLM initializes, these skills are automatically loaded into the system prompt via `LLM._load_system_prompt`.

### Loading Additional Skills at Runtime

```python

# During conversation, the LLM can invoke:

load_skill(agent_state, skills="nmap, ffuf")

```

This validates the comma-separated request, calls `LLM.add_skills()`, and updates the agent's context so subsequent messages incorporate `nmap` and `ffuf` scanning techniques from `resources/skills/<category>/nmap.md` and [`ffuf.md`](https://github.com/usestrix/strix/blob/main/ffuf.md).

### Inspecting the Generated System Prompt

```python
print(agent.llm.system_prompt[:500])   # first 500 chars

```

The output reveals injected skill sections such as:

```

... Advanced techniques ... 
{{ get_skill("nmap") }} 
{{ get_skill("ffuf") }}

```

These placeholders render as the raw markdown content from the respective skill files, making specialized knowledge immediately available to the model.

## Summary

- The **Strix skills system** specializes agents by injecting markdown-based knowledge packages directly into the system prompt, converting generic LLMs into domain-specific security testers.
- **Skill discovery** occurs automatically via `get_available_skills()` scanning the `resources/skills` directory, while `create_agent()` assigns initial capabilities through `LLM._active_skills`.
- **Dynamic composition** happens in `LLM._load_system_prompt`, where `load_skills()` processes markdown files and exposes them to the Jinja template via the `get_skill` helper.
- **Runtime flexibility** is provided by the `load_skill` tool, which invokes `LLM.add_skills()` to extend capabilities without session restart.
- A **hard limit of five skills** enforced by `validate_requested_skills()` ensures focused, high-quality context injection and prevents prompt bloat.

## Frequently Asked Questions

### How does the Strix skills system differ from traditional prompt engineering?

Unlike static prompt templates, the Strix skills system uses **dynamic markdown injection** that loads domain-specific content from external files into the Jinja-rendered system prompt. This modular approach allows runtime specialization where agents can acquire new capabilities (like `nmap` or `ffuf` expertise) mid-conversation without code changes, whereas traditional prompts require manual rewriting for each domain.

### What is the maximum number of skills an agent can load simultaneously?

According to the source code in [`strix/skills/__init__.py`](https://github.com/usestrix/strix/blob/main/strix/skills/__init__.py), the `validate_requested_skills()` function enforces a **maximum of five active skills** per agent. This limit prevents context window overflow and ensures the LLM receives focused, high-fidelity instructions rather than diluted knowledge from excessive specializations.

### Can skills be added to an agent after it has started processing a task?

Yes. The `load_skill` tool in [`strix/tools/load_skill/load_skill_actions.py`](https://github.com/usestrix/strix/blob/main/strix/tools/load_skill/load_skill_actions.py) enables **runtime skill addition** during active conversations. When invoked, it validates the comma-separated skill list, resolves the running agent, and calls `LLM.add_skills()` to update `_active_skills` and refresh the system prompt context for subsequent inference steps.

### Where are skill files stored and what format do they use?

Skill files are stored as **markdown documents** in the `resources/skills` directory of the repository. The `load_skills()` function processes these files by stripping YAML front-matter and exposing the raw content to the Jinja template environment, allowing the LLM to receive structured, formatted expertise directly in its context window.