Agent Skills: AgentSkillsToolset¶
AgentSkillsToolset loads
Agent Skills – SKILL.md bundles (instructions,
and optionally scripts and resources) that the model discovers and loads on
demand. Only a compact catalog of skill names and descriptions sits in the
prompt until the model decides it needs one, so a large skill library costs few
tokens until used (progressive disclosure).
It is backed by the community pydantic-ai-skills package (MIT); native progressive disclosure is in flight upstream in pydantic/pydantic-ai#5230. Install the optional extra to use it:
pip install "apache-airflow-providers-common-ai[skills]"
Each source is a local directory or a connection-resolved
GitSkills. Sources are resolved when
the agent enters the toolset, on the worker – never while the Dag processor
parses the file – so a Git token is never baked into the serialized Dag, and
cloned repositories are removed when the run ends.
A local directory of SKILL.md bundles:
if SQLToolset is not None:
@dag(tags=["example"])
def example_agent_skills_local():
AgentOperator(
task_id="reporter",
prompt="How many orders did our top 5 customers place last month?",
llm_conn_id="pydanticai_default",
system_prompt="You are a data analyst. Consult your skills before writing SQL.",
toolsets=[
AgentSkillsToolset(sources=[str(SKILLS_DIR)]),
SQLToolset(
db_conn_id="postgres_default",
allowed_tables=["customers", "orders"],
max_rows=50,
),
],
)
A Git repository, with credentials from an Airflow connection:
@dag(tags=["example"])
def example_agent_skills_git():
AgentOperator(
task_id="support_agent",
prompt="Summarize our refund policy and apply it to order 12345.",
llm_conn_id="pydanticai_default",
system_prompt="You are a support agent. Load the relevant skill before answering.",
toolsets=[
AgentSkillsToolset(
sources=[
GitSkills(
repo_url="https://github.com/my-org/agent-skills",
conn_id="github_skills",
path="skills",
),
],
),
],
)
For a private repository, point conn_id at a
git connection; credentials
are resolved through the Git provider’s GitHook (an HTTPS token in the
connection password, or an SSH key in the connection’s extra). A plain http://
URL with conn_id is rejected so a credential is never sent in cleartext, and a
repo_url that embeds a username/password is rejected (use conn_id). After
cloning, the credential is stripped from the checkout’s .git/config. As with
any git clone, the worker’s own git configuration (credential helpers, SSH
agent) may still apply, so run workers without ambient git credentials if you
need strict isolation.
Warning
Skill bundles can contain scripts that the agent may run on the worker via
the run_skill_script tool. For a remote source, anyone who can modify the
repository can introduce code that executes on your worker, outside Dag
review and versioning. Point GitSkills at a trusted repository, pin
branch to a trusted ref, and treat skill contents as code that runs in
your environment.
Parameters¶
sources: List of skill sources – local directory paths and/orGitSkills.exclude_tools: Optional set of skill tool names to hide from the agent (e.g.{"run_skill_script"}to disable on-worker script execution).exclude_resources: Optional glob patterns to exclude from resource discovery, added on top of the built-in defaults (__pycache__,*.pyc,*.pyo,.DS_Store,.git). A skill exposes every readable text file it contains as a resource; these patterns keep matched files out of the resource list and theread_skill_resourcetool (e.g.["*.env", "secrets/*"]). Each pattern matches the full skill-relative path or any single path component. This hides files from resource discovery only – it does not stop a skill’srun_skill_scriptfrom reading them off disk, so pair it withexclude_tools={"run_skill_script"}when the files are genuinely sensitive.
Using Agent Skills with other frameworks¶
AgentSkillsToolset is a standard pydantic-ai toolset, so it also works with a
plain pydantic_ai.Agent you build yourself, not just AgentOperator.
Because Agent Skills is a cross-framework format, the connection handling is also
reusable through resolve_skills(), which
resolves sources to local SKILL.md directories that any loader accepts:
from airflow.providers.common.ai.skills import GitSkills, resolve_skills
sources = ["./skills", GitSkills(repo_url="https://github.com/org/skills", conn_id="github_skills")]
with resolve_skills(sources) as dirs:
# LangChain DeepAgents
agent = create_deep_agent(model="openai:gpt-5.4", skills=dirs)
# ...or Strands
agent = Agent(plugins=[AgentSkills(skills=dirs)])
resolve_skills needs the Git provider (for GitSkills) but not pydantic-ai,
and removes any cloned directories when the with block exits.
When to choose it¶
Choose it when what the agent is missing is procedural knowledge rather than
an endpoint: how this team writes a report, which checks run before a release,
what the house conventions are. A skill is a directory of instructions and
optional scripts, and
AgentSkillsToolset makes it
discoverable. See Agent Skills: AgentSkillsToolset for the layout.
What it cannot do
exclude_resourcesdoes not hide a file from the skill’s own scripts. It keeps matches out of resource discovery and out ofread_skill_resource, and the parameter’s documentation says plainly that it does not stoprun_skill_scriptfrom reading them off disk. For genuinely sensitive files, pair it withexclude_tools={"run_skill_script"}. (The parameter needspydantic-ai-skills>=1.2.0, which theskillsextra already pins.)It does not move script execution anywhere safer. The toolset’s own wording for
exclude_toolscallsrun_skill_script“on-worker script execution”, and nothing in this toolset routes those scripts into a sandbox, so unless you exclude the tool, a skill’s scripts run in the worker process with the worker’s reach. If that is not acceptable, exclude the tool or put the work behindSandboxToolsetinstead.It re-fetches a Git source on every run.
GitSkillsis resolved and shallow-cloned on the worker when the run starts, and the checkout is deleted when it ends; nothing is kept between runs, so a large or slow repository pays that clone once per run. A local directory is read in place and costs nothing.
A real example. example_agent_skills.py loads skills from a local
directory:
if SQLToolset is not None:
@dag(tags=["example"])
def example_agent_skills_local():
AgentOperator(
task_id="reporter",
prompt="How many orders did our top 5 customers place last month?",
llm_conn_id="pydanticai_default",
system_prompt="You are a data analyst. Consult your skills before writing SQL.",
toolsets=[
AgentSkillsToolset(sources=[str(SKILLS_DIR)]),
SQLToolset(
db_conn_id="postgres_default",
allowed_tables=["customers", "orders"],
max_rows=50,
),
],
)
The two skills it ships, aip-tracker and sql-reporting, are procedural by
nature: neither adds an endpoint the agent could not already reach. That is the
signal you are on the right route.
Credentials and where it runs. A local directory needs no credential. A
private repository goes through
GitSkills and its conn_id,
resolved by the Git provider’s GitHook; plain http:// is refused when a
conn_id is set, so a credential is never sent in the clear. Cloning, reading
and any script execution happen on the worker.