mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Support skill scripts execution (#4558)
* support skill scripts execution * fix mixed line endings * address comments and fix syntax issues * use few try/except instead of one * change samples * validate either script path or script resource is set not both * fix: separate LLM args from runtime kwargs in skill script execution * address pr review comments * address PR review comments * Update python/packages/core/agent_framework/_skills.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update python/packages/core/agent_framework/_skills.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update python/packages/core/agent_framework/_skills.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * 1. Fixing the caching bug where parameters_schema would re-inspect on every call when the result was None 2. Updating the arguments tool description to be more generic (not CLI-specific) * fix failing tests * address pr review comments * address pr review comments * allow resource function returning any instead of sting * address PR review comments --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot
parent
2f8fd5f82f
commit
23ebfbc937
@@ -0,0 +1,69 @@
|
||||
# File-Based Agent Skills
|
||||
|
||||
This sample demonstrates how to use **file-based Agent Skills** with a `SkillsProvider` in the Microsoft Agent Framework. File-based skills are discovered from `SKILL.md` files on disk and can include reference documents and executable scripts.
|
||||
|
||||
## What are Agent Skills?
|
||||
|
||||
Agent Skills are modular packages of instructions and resources that enable AI agents to perform specialized tasks. They follow the [Agent Skills specification](https://agentskills.io/) and implement progressive disclosure:
|
||||
|
||||
1. **Advertise**: Skills are advertised with name + description (~100 tokens per skill)
|
||||
2. **Load**: Full instructions are loaded on-demand via `load_skill` tool
|
||||
3. **Resources**: References and other files loaded via `read_skill_resource` tool
|
||||
4. **Scripts**: Executable scripts run via `run_skill_script` tool
|
||||
|
||||
## Skills Included
|
||||
|
||||
### unit-converter
|
||||
Converts between common units (miles↔km, pounds↔kg) using a multiplication factor following [agentskills.io guidelines](https://agentskills.io/skill-creation/using-scripts).
|
||||
- `references/CONVERSION_TABLES.md` — Supported conversions and their factors
|
||||
- `scripts/convert.py` — Executable script with `--value` and `--factor` flags, JSON output, and `--help` support
|
||||
|
||||
## Key Components
|
||||
|
||||
- **`SkillsProvider`** — Discovers skills from `SKILL.md` files in a directory and registers tools for the agent
|
||||
- **`subprocess_script_runner`** — A `SkillScriptRunner` callback that runs scripts as local Python subprocesses, enabling the `run_skill_script` tool. Converts argument dicts to CLI flags (e.g. `{"value": 26.2, "factor": 1.60934}` → `--value 26.2 --factor 1.60934`). Shared across samples in [`../subprocess_script_runner.py`](../subprocess_script_runner.py).
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
file_based_skill/
|
||||
├── file_based_skill.py
|
||||
├── README.md
|
||||
└── skills/
|
||||
└── unit-converter/
|
||||
├── SKILL.md
|
||||
├── references/
|
||||
│ └── CONVERSION_TABLES.md
|
||||
└── scripts/
|
||||
└── convert.py
|
||||
```
|
||||
|
||||
## Running the Sample
|
||||
|
||||
### Prerequisites
|
||||
- An [Azure AI Foundry](https://ai.azure.com/) project with a deployed model (e.g. `gpt-4o-mini`)
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Set the required environment variables in a `.env` file (see `python/.env.example`):
|
||||
|
||||
- `AZURE_AI_PROJECT_ENDPOINT`: Your Azure AI Foundry project endpoint
|
||||
- `AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME`: The name of your model deployment (defaults to `gpt-4o-mini`)
|
||||
|
||||
### Authentication
|
||||
|
||||
This sample uses `AzureCliCredential` for authentication. Run `az login` in your terminal before running the sample.
|
||||
|
||||
### Run
|
||||
|
||||
```bash
|
||||
cd python
|
||||
uv run samples/02-agents/skills/file_based_skill/file_based_skill.py
|
||||
```
|
||||
|
||||
## Learn More
|
||||
|
||||
- [Agent Skills Specification](https://agentskills.io/)
|
||||
- [Code-Defined Skills Sample](../code_defined_skill/)
|
||||
- [Mixed Skills Sample](../mixed_skills/)
|
||||
- [Microsoft Agent Framework Documentation](../../../../../docs/)
|
||||
@@ -0,0 +1,94 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from agent_framework import Agent, SkillsProvider
|
||||
from agent_framework.azure import AzureOpenAIResponsesClient
|
||||
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
|
||||
|
||||
"""
|
||||
File-Based Agent Skills
|
||||
|
||||
This sample demonstrates how to use file-based Agent Skills with a SkillsProvider.
|
||||
Agent Skills are modular packages of instructions and resources that extend an agent's
|
||||
capabilities. They follow progressive disclosure:
|
||||
|
||||
1. Advertise — skill names and descriptions are injected into the system prompt
|
||||
2. Load — full instructions are loaded on-demand via the load_skill tool
|
||||
3. Read resources — supplementary files are read via the read_skill_resource tool
|
||||
4. Run scripts — skill scripts are run via the run_skill_script tool
|
||||
|
||||
This sample includes the unit-converter skill which demonstrates all three
|
||||
file-based capabilities: instructions (SKILL.md), resources (CONVERSION_TABLES.md),
|
||||
and scripts (convert.py).
|
||||
"""
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""Run the file-based skills demo."""
|
||||
endpoint = os.environ["AZURE_AI_PROJECT_ENDPOINT"]
|
||||
deployment = os.environ.get("AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME", "gpt-4o-mini")
|
||||
|
||||
# Create the chat client
|
||||
client = AzureOpenAIResponsesClient(
|
||||
project_endpoint=endpoint,
|
||||
deployment_name=deployment,
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
|
||||
# Create the skills provider
|
||||
# Discovers skills from the 'skills' directory and configures the
|
||||
# subprocess_script_runner to run file-based scripts.
|
||||
skills_dir = Path(__file__).parent / "skills"
|
||||
skills_provider = SkillsProvider(
|
||||
skill_paths=str(skills_dir),
|
||||
script_runner=subprocess_script_runner,
|
||||
)
|
||||
|
||||
# Create the agent with skills
|
||||
async with Agent(
|
||||
client=client,
|
||||
instructions="You are a helpful assistant.",
|
||||
context_providers=[skills_provider],
|
||||
) as agent:
|
||||
# The agent will: load the unit-converter skill, read the conversion
|
||||
# tables resource, then execute the convert.py script.
|
||||
print("Converting units")
|
||||
print("-" * 60)
|
||||
response = await agent.run(
|
||||
"How many kilometers is a marathon (26.2 miles)? "
|
||||
"And how many pounds is 75 kilograms?"
|
||||
)
|
||||
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. **75 kg → 165.35 lbs**
|
||||
|
||||
I used the conversion factors from the reference table:
|
||||
miles × 1.60934 and kilograms × 2.20462.
|
||||
"""
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
name: unit-converter
|
||||
description: Convert between common units using a multiplication factor. Use when asked to convert miles, kilometers, pounds, or kilograms.
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
When the user requests a unit conversion:
|
||||
1. First, review `references/CONVERSION_TABLES.md` to find the correct factor
|
||||
2. Run the `scripts/convert.py` script with `--value <number> --factor <factor>` (e.g. `--value 26.2 --factor 1.60934`)
|
||||
3. Present the converted value clearly with both units
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
# Conversion Tables
|
||||
|
||||
Formula: **result = value × factor**
|
||||
|
||||
| From | To | Factor |
|
||||
|-------------|-------------|----------|
|
||||
| miles | kilometers | 1.60934 |
|
||||
| kilometers | miles | 0.621371 |
|
||||
| pounds | kilograms | 0.453592 |
|
||||
| kilograms | pounds | 2.20462 |
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
# Unit conversion script
|
||||
# Converts a value using a multiplication factor: result = value × factor
|
||||
#
|
||||
# Usage:
|
||||
# python scripts/convert.py --value 26.2 --factor 1.60934
|
||||
# python scripts/convert.py --value 75 --factor 2.20462
|
||||
|
||||
import argparse
|
||||
import json
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Convert a value using a multiplication factor.",
|
||||
epilog="Examples:\n"
|
||||
" python scripts/convert.py --value 26.2 --factor 1.60934\n"
|
||||
" python scripts/convert.py --value 75 --factor 2.20462",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
parser.add_argument("--value", type=float, required=True, help="The numeric value to convert.")
|
||||
parser.add_argument("--factor", type=float, required=True, help="The conversion factor from the table.")
|
||||
args = parser.parse_args()
|
||||
|
||||
result = round(args.value * args.factor, 4)
|
||||
print(json.dumps({"value": args.value, "factor": args.factor, "result": result}))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user