Files
agent-framework/python/samples/02-agents/skills/mixed_skills/mixed_skills.py
T
Eduard van Valkenburg 55b6e7a9f4 Python: Add Python feature lifecycle decorators for released APIs (#4975)
* Add Python feature lifecycle decorators

Introduce reusable experimental and release-candidate decorators for released packages, migrate the Skills APIs to the new staged metadata and warning system, and add lifecycle guidance plus samples.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Fix Python CI follow-ups

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Address PR review feedback

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* Preserve protocol runtime checks

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-31 19:40:08 +00:00

165 lines
5.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Copyright (c) Microsoft. All rights reserved.
import asyncio
import json
import os
import sys
# Uncomment this filter to suppress the experimental Skills warning before
# using the sample's Skills APIs.
# import warnings
# warnings.filterwarnings("ignore", message=r"\[SKILLS\].*", category=FutureWarning)
from pathlib import Path
from textwrap import dedent
from typing import Any
from agent_framework import (
Agent,
Skill,
SkillsProvider,
)
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential
from dotenv import load_dotenv
# Add the skills folder root to sys.path so the shared subprocess_script_runner can be imported
_SKILLS_ROOT = str(Path(__file__).resolve().parent.parent)
if _SKILLS_ROOT not in sys.path:
sys.path.insert(0, _SKILLS_ROOT)
from subprocess_script_runner import subprocess_script_runner # noqa: E402
"""
Mixed Skills — Code skills and file skills in a single agent
This sample demonstrates how to combine **code-defined skills** (with
``@skill.script`` and ``@skill.resource`` decorators) and **file-based skills**
(discovered from ``SKILL.md`` files on disk) in a single agent using
``SkillsProvider`` and a ``SkillScriptRunner`` callable.
Key concepts shown:
- Code skills with ``@skill.script``: executable Python functions the agent
can invoke directly in-process.
- Code skills with ``@skill.resource``: dynamic content the agent can read
on demand.
- File skills from disk: ``SKILL.md`` files with reference documents and
executable script files.
- ``script_runner``: routes **file-based** script execution
through a callback, enabling custom handling (e.g. subprocess calls).
Code-defined scripts (``@skill.script``) run in-process automatically.
The sample registers two skills:
1. **volume-converter** (code skill) — converts between gallons and liters using
``@skill.script`` for conversion and ``@skill.resource`` for the factor table.
2. **unit-converter** (file skill) — converts between common units (miles↔km,
pounds↔kg) via a subprocess-executed Python script discovered from
``skills/unit-converter/SKILL.md``.
"""
# Load environment variables from .env file
load_dotenv()
# ---------------------------------------------------------------------------
# 1. Define a code skill with @skill.script and @skill.resource decorators
# ---------------------------------------------------------------------------
volume_converter_skill = Skill(
name="volume-converter",
description="Convert between gallons and liters using a conversion factor",
content=dedent("""\
Use this skill when the user asks to convert between gallons and liters.
1. Review the conversion-table resource to find the correct factor.
2. Use the convert script, passing the value and factor.
"""),
)
@volume_converter_skill.resource(name="conversion-table", description="Volume conversion factors")
def volume_table() -> Any:
"""Return the volume conversion factor table."""
return dedent("""\
# Volume Conversion Table
Formula: **result = value × factor**
| From | To | Factor |
|---------|--------|---------|
| gallons | liters | 3.78541 |
| liters | gallons| 0.264172|
""")
@volume_converter_skill.script(name="convert", description="Convert a value: result = value × factor")
def convert_volume(value: float, factor: float) -> str:
"""Convert a value using a multiplication factor.
Args:
value: The numeric value to convert.
factor: Conversion factor from the table.
Returns:
JSON string with the conversion result.
"""
result = round(value * factor, 4)
return json.dumps({"value": value, "factor": factor, "result": result})
# ---------------------------------------------------------------------------
# 2. Wire everything together and run the agent
# ---------------------------------------------------------------------------
async def main() -> None:
"""Run the combined skills demo."""
endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"]
deployment = os.environ.get("FOUNDRY_MODEL", "gpt-4o-mini")
# Create the chat client
client = FoundryChatClient(
project_endpoint=endpoint,
model=deployment,
credential=AzureCliCredential(),
)
# Create the SkillsProvider with both code and file skills.
# The script_runner handles file-based scripts; code-defined scripts
# (@skill.script) run in-process automatically.
skills_dir = Path(__file__).parent / "skills"
skills_provider = SkillsProvider(
skill_paths=str(skills_dir),
skills=[volume_converter_skill],
script_runner=subprocess_script_runner,
)
# Run the agent
async with Agent(
client=client,
instructions="You are a helpful assistant that can convert units.",
context_providers=[skills_provider],
) as agent:
# Ask the agent to use both skills
print("Converting units")
print("-" * 60)
response = await agent.run(
"How many kilometers is a marathon (26.2 miles)? And how many liters is a 5-gallon bucket?"
)
print(f"Agent: {response}\n")
if __name__ == "__main__":
asyncio.run(main())
"""
Sample output:
Converting units
------------------------------------------------------------
Agent: Here are your conversions:
1. **26.2 miles → 42.16 km** (a marathon distance)
2. **5 gallons → 18.93 liters**
I used the conversion factors from each skill's reference table.
"""