Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Le package Python en version bêta agent-framework-tools fournit des outils d’exécution de commandes shell et de prise en compte de l’environnement via l’espace de noms agent_framework.tools.
| Tool | Utilisez-le quand |
|---|---|
LocalShellTool |
Les commandes sont fiables ou approuvées individuellement et doivent être exécutées dans l’environnement hôte du processus agent. |
DockerShellTool |
Les commandes shell générées par un modèle nécessitent une isolation dans un conteneur OCI. |
ShellEnvironmentProvider |
Le modèle a besoin de la famille d’interpréteurs de commandes active, du système d’exploitation, du répertoire de travail et des versions cli installées. |
ShellPolicy |
Vous souhaitez un pré-filtre de liste d’autorisation ou de liste de refus avant l’approbation ou l’exécution. |
Avertissement
L’exécution de l’interpréteur de commandes peut modifier des fichiers, lancer des processus, accéder aux informations d’identification et communiquer avec des systèmes externes. Utilisez le niveau d’exécution le moins privilégié qui prend en charge la tâche.
Installer le package
dotnet add package Microsoft.Agents.AI.Tools.Shell --prerelease
Utiliser un interpréteur de commandes local et la reconnaissance de l’environnement
LocalShellExecutor prend en charge les modes sans état et persistants.
ShellEnvironmentProvider sonde l’environnement actif et ajoute des instructions d’interpréteur de commandes faisant autorité au contexte de l’assistant.
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Tools.Shell;
using Microsoft.Extensions.AI;
var endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-5.4-mini";
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
var aiProjectClient = new AIProjectClient(new Uri(endpoint), new DefaultAzureCredential());
const string Instructions = """
You are an agent with a single tool: run_shell. Use it to satisfy the
user's request. Do not describe what you would do — actually run the
commands. Reply with the final answer derived from real output.
""";
// --------------------------------------------------------------------
// 1. Stateless mode — each call gets a fresh shell.
// --------------------------------------------------------------------
Console.WriteLine("### Stateless mode\n");
await using (var statelessShell = new LocalShellExecutor(new() { Mode = ShellMode.Stateless, AcknowledgeUnsafe = true }))
{
var envProvider = new ShellEnvironmentProvider(statelessShell);
var statelessAgent = aiProjectClient.AsAIAgent(new ChatClientAgentOptions
{
ChatOptions = new()
{
ModelId = deploymentName,
Instructions = Instructions,
Tools = [statelessShell.AsAIFunction(requireApproval: false)],
},
AIContextProviders = [envProvider],
});
// --------------------------------------------------------------------
// 2. Persistent mode — one shell, reused across calls. State carries.
// --------------------------------------------------------------------
Console.WriteLine("\n### Persistent mode\n");
await using (var persistentShell = new LocalShellExecutor(new() { Mode = ShellMode.Persistent, AcknowledgeUnsafe = true }))
{
var envProvider = new ShellEnvironmentProvider(persistentShell);
var persistentAgent = aiProjectClient.AsAIAgent(new ChatClientAgentOptions
{
ChatOptions = new()
{
ModelId = deploymentName,
Instructions = Instructions,
Tools = [persistentShell.AsAIFunction(requireApproval: false)],
},
AIContextProviders = [envProvider],
});
var persistentSession = await persistentAgent.CreateSessionAsync();
// State carries across calls in persistent mode: cd into temp, then
// verify the next call sees the new CWD.
Console.WriteLine(await persistentAgent.RunAsync("Change directory into the system temp folder, then print the current working directory.", persistentSession));
Console.WriteLine();
Console.WriteLine(await persistentAgent.RunAsync("In a NEW shell call, print the current working directory again. Tell me whether it still matches the temp folder.", persistentSession));
Console.WriteLine();
// Same idea with an exported variable: set in one call, read in the next.
Console.WriteLine(await persistentAgent.RunAsync("Set the environment variable DEMO_TOKEN to the value 'hello-world'.", persistentSession));
Console.WriteLine();
Console.WriteLine(await persistentAgent.RunAsync("Print the current value of DEMO_TOKEN. Tell me exactly what value the shell reports.", persistentSession));
Console.WriteLine();
PrintSnapshot(envProvider.CurrentSnapshot!);
}
ShellPolicy est également disponible pour le pré-filtrage des commandes. Un exemple runnable DockerShellExecutor dédié n’est actuellement pas publié.
Installer le package
pip install agent-framework-tools --pre
Le package installe psutil pour mettre fin aux arborescences de processus enfants lorsqu’une exécution dépasse le délai imparti.
Utilisez LocalShellTool.
LocalShellTool exécute des commandes directement sur l’hôte. Par défaut, il utilise un shell persistant, un délai d’expiration de 30 secondes, une troncation de la sortie à 64 Kio et une redéfinition du répertoire de travail comme point d’ancrage. Les appels de l’agent via as_function() nécessitent une approbation par défaut. Les appels directs à run() ne nécessitent pas d’approbation.
import asyncio
from typing import Any
from agent_framework import Agent, Message
from agent_framework.openai import OpenAIChatClient
from agent_framework.tools import LocalShellTool
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
async def main() -> None:
print("=== OpenAI Agent with LocalShellTool Example ===")
print("NOTE: Commands will execute on your local machine.\n")
client = OpenAIChatClient(model="gpt-5.4-nano")
async with LocalShellTool() as shell:
agent = Agent(
client=client,
instructions="You are a helpful assistant that can run shell commands to help the user.",
tools=[client.get_shell_tool(func=shell.as_function())],
)
query = "Use the shell tool to execute `python --version` and show only the command output."
print(f"User: {query}")
result = await run_with_approvals(query, agent)
if isinstance(result, str):
print(f"Agent: {result}\n")
return
if result.text:
print(f"Agent: {result.text}\n")
else:
printed = False
for message in result.messages:
for content in message.contents:
if content.type == "function_result" and content.result:
print(f"Agent (tool output): {content.result}\n")
printed = True
if not printed:
print("Agent: (no text output returned)\n")
async def run_with_approvals(query: str, agent: Agent) -> Any:
"""Run the agent and handle shell approvals outside tool execution."""
current_input: str | list[Any] = query
while True:
result = await agent.run(current_input)
if not result.user_input_requests:
return result
next_input: list[Any] = [query]
rejected = False
for user_input_needed in result.user_input_requests:
if user_input_needed.function_call is None:
continue
print(
f"\nShell request: {user_input_needed.function_call.name}"
f"\nArguments: {user_input_needed.function_call.arguments}"
)
user_approval = await asyncio.to_thread(input, "\nApprove shell command? (y/n): ")
approved = user_approval.strip().lower() == "y"
next_input.append(Message("assistant", [user_input_needed]))
next_input.append(Message("user", [user_input_needed.to_function_approval_response(approved)]))
if not approved:
rejected = True
break
if rejected:
print("\nShell command rejected. Stopping without additional approval prompts.")
return "Shell command execution was rejected by user."
current_input = next_input
if __name__ == "__main__":
asyncio.run(main())
Les éléments de transcription d’interpréteur de commandes hébergés par un fournisseur OpenAI restent informationnels, même lorsqu’un exécuteur d’interpréteur de commandes local est configuré. Seul un appel explicite bien formé local_shell_call, ou un appel au shell marqué avec environment.type="local", emprunte le chemin de la fonction locale et de l’approbation. La configuration LocalShellTool seule n’entraîne pas l’exécution d’appels d’interpréteur de commandes hébergés par un fournisseur sur l’hôte.
Utilisez mode="stateless" quand chaque appel doit s’exécuter dans un nouveau processus. Utilisez la variable d’environnement AGENT_FRAMEWORK_SHELL ou l’argument de constructeur shell pour remplacer l’interpréteur de commandes résolu.
Important
LocalShellTool n’est pas un sandbox. L’approbation humaine ajoute une étape de validation, mais elle n’isole pas l’interpréteur de commandes. La désactivation de l’approbation pour les appels d’agent nécessite acknowledge_unsafe=True.
Restreindre les commandes avec ShellPolicy
ShellPolicy applique des listes d’autorisation et d’interdiction basées sur des expressions régulières au texte de la commande avant son exécution. Les règles de refus sont prioritaires. Il n’inspecte pas ce que l’interpréteur de commandes exécute ou limite l’accès aux fichiers.
import asyncio
from agent_framework import Agent
from agent_framework.openai import OpenAIChatClient
from agent_framework.tools import LocalShellTool, ShellPolicy
from dotenv import load_dotenv
load_dotenv()
async def main() -> None:
client = OpenAIChatClient(model="gpt-5.4-nano")
shell = LocalShellTool(
mode="stateless",
# Unsafe for production as shown: these filters do not replace human approval or isolation.
approval_mode="never_require",
acknowledge_unsafe=True,
policy=ShellPolicy(
allowlist=[
r"^ls(\s|$)",
r"^pwd$",
r"^cat\s[^|;&]+$",
r"^git\s+(status|log|diff)(\s|$)",
r"^python\s+--version$",
],
),
timeout=10,
)
agent = Agent(
client=client,
instructions=("Use only these shell commands: ls, pwd, cat, git status/log/diff, python --version."),
tools=[client.get_shell_tool(func=shell.as_function())],
)
Avertissement
L’exemple désactive la validation humaine et n’est adapté qu’à un environnement isolé et jetable, sans secrets ni données précieuses. Les substitutions de commandes, telles que $(...) et les guillemets inversés, peuvent correspondre à des motifs simples de liste d’autorisation. Les modèles qui correspondent uniquement au début d’une commande peuvent également autoriser des opérations supplémentaires. Utilisez l’isolation et les autorisations restreintes appliquées séparément pour la production. La vérification humaine peut ajouter un contrôle, mais elle n’isole pas l’interpréteur de commandes.
Privilégiez les motifs de chaîne. Python compile les chaînes à l’aide du moteur regex et applique une limite d’une seconde pour chaque correspondance. Un délai d’expiration sur la liste de blocage refuse la commande, et un délai d’expiration sur la liste d’autorisation n’accorde pas la permission. Un précompilé regex.Pattern utilise la même limite. Une bibliothèque re.Pattern standard précompilée conserve ses indicateurs, mais ne peut pas être interrompue. Évitez donc les expressions coûteuses ou ambiguës de cette forme.
Ajouter ShellEnvironmentProvider
ShellEnvironmentProvider sonde la famille d’interpréteurs de commandes, la version, le système d’exploitation, le répertoire de travail et les versions cli sélectionnées, puis injecte ces informations avant l’exécution de l’agent. La liste de sondes par défaut est git, node, pythonet docker.
import asyncio
from agent_framework import Agent
from agent_framework.openai import OpenAIChatClient
from agent_framework.tools import (
LocalShellTool,
ShellEnvironmentProvider,
ShellEnvironmentProviderOptions,
)
from dotenv import load_dotenv
load_dotenv()
def _print_snapshot(label: str, provider: ShellEnvironmentProvider) -> None:
snapshot = provider.current_snapshot
if snapshot is None:
print(f"[{label}] no snapshot captured")
return
print(f"\n[{label}] snapshot:")
print(f" family = {snapshot.family.value}")
print(f" os = {snapshot.os_description}")
print(f" shell_version = {snapshot.shell_version}")
print(f" working_directory = {snapshot.working_directory}")
for tool, version in snapshot.tool_versions.items():
print(f" {tool:<17} = {version}")
async def _ask(agent: Agent, query: str) -> None:
print(f"\nUser: {query}")
result = await agent.run(query)
if result.text:
print(f"Agent: {result.text}")
async def main() -> None:
client = OpenAIChatClient(model="gpt-5.4-nano")
options = ShellEnvironmentProviderOptions(
probe_tools=("git", "python", "uv", "node"),
)
print("=== stateless mode ===")
async with LocalShellTool(
mode="stateless",
approval_mode="never_require",
acknowledge_unsafe=True,
) as shell:
provider = ShellEnvironmentProvider(shell, options)
agent = Agent(
client=client,
instructions="Use the shell tool to answer the user's question.",
tools=[client.get_shell_tool(func=shell.as_function())],
context_providers=[provider],
)
await _ask(agent, "Show me the current working directory.")
await _ask(agent, "Now `cd ..` then show the working directory again.")
await _ask(agent, "Show the working directory once more — did `cd` persist?")
_print_snapshot("stateless", provider)
print("\n=== persistent mode ===")
async with LocalShellTool(
mode="persistent",
confine_workdir=False,
approval_mode="never_require",
acknowledge_unsafe=True,
) as shell:
provider = ShellEnvironmentProvider(shell, options)
agent = Agent(
client=client,
instructions="Use the shell tool to answer the user's question.",
tools=[client.get_shell_tool(func=shell.as_function())],
context_providers=[provider],
)
await _ask(agent, "Show me the current working directory.")
await _ask(agent, "Now `cd ..` then show the working directory again.")
await _ask(agent, "Show the working directory once more — did `cd` persist?")
_print_snapshot("persistent", provider)
Utilisez DockerShellTool.
DockerShellTool nécessite Docker ou Podman sur PATH. Les valeurs par défaut désactivent la mise en réseau, s’exécutent en tant qu’utilisateur non racine, utilisent un système de fichiers racine en lecture seule, suppriment des fonctionnalités, limitent la mémoire à 512 Mio et limitent le conteneur à 256 processus.
from agent_framework.tools import DockerShellTool
async with DockerShellTool(
image="mcr.microsoft.com/azurelinux/base/core:3.0",
approval_mode="never_require",
) as shell:
result = await shell.run("uname -a && id")
print(result.stdout)
L’image par défaut est mcr.microsoft.com/azurelinux/base/core:3.0. Passez docker_binary="podman" pour utiliser Podman. Un exemple runnable DockerShellTool dédié n’est actuellement pas publié.
Utilisez extra_run_args uniquement les options Docker qui n’affaiblissent pas l’isolation configurée ou les limites de ressources. La validation reconnaît les indicateurs longs, les indicateurs courts, les valeurs jointes et les indicateurs courts en cluster. Il rejette les remplacements tels que -u / --user, , -v--memory-m / --volume / , --network, et .--pids-limit Utilisez l’option de constructeur correspondante DockerShellTool à la place.
Choisir un niveau d’exécution
| Scénario | Tool | Limite d’isolation |
|---|---|---|
| Commandes de développement approuvées | LocalShellTool |
Aucun ; l’approbation humaine est activée par défaut |
| Commandes shell non fiables | DockerShellTool |
Conteneur OCI avec des indicateurs d’isolation par défaut |
| Code généré non fiable sans interpréteur de commandes | Hyperlight CodeAct | microVM Hyperlight |
Go permet l’exécution de commandes shell en local et l’inspection de l’environnement via tool/shelltool. Consultez Utiliser l’outil shell local.
DockerShellTool les conseils ne sont actuellement pas disponibles pour Go.
Utiliser des outils shell avec Harness Agent
Les agents bruts et HarnessAgent utilisent la même configuration d’interpréteur de commandes en deux parties : inscrivez la fonction de l’exécuteur en tant qu’outil et ajoutez ShellEnvironmentProvider quand le modèle doit recevoir un interpréteur de commandes, un système d’exploitation, un répertoire de travail et un contexte de version CLI.
HarnessAgent ne crée ni ne possède d’exécuteur shell :
using System.IO;
using Microsoft.Agents.AI;
using Microsoft.Agents.AI.Tools.Shell;
using Microsoft.Extensions.AI;
await using var shell = new LocalShellExecutor(new LocalShellExecutorOptions
{
WorkingDirectory = Directory.GetCurrentDirectory(),
Timeout = LocalShellExecutor.DefaultTimeout,
});
AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
AIContextProviders = [new ShellEnvironmentProvider(shell)],
ChatOptions = new ChatOptions
{
Tools = [shell.AsAIFunction(requireApproval: true)],
},
});
AsAIFunction est défini par défaut sur le nom run_shell et requireApproval: true.
LocalShellExecutor par défaut en mode persistant, une limite de 64 Kio par flux de sortie et aucun délai d’expiration ; l’exemple utilise explicitement les 30 secondes LocalShellExecutor.DefaultTimeoutrecommandées.
ShellEnvironmentProviderOptionseffectue par défaut une tentative de détection de git, dotnet, node, python et docker, avec un délai d’expiration de cinq secondes par tentative.
Créez un exécuteur persistant par session utilisateur et supprimez-le lorsque la session se termine. Ne le partagez pas entre les utilisateurs ou les conversations simultanées, car le répertoire de travail, l’environnement, l’historique de l’interpréteur de commandes, les travaux en arrière-plan et la file d’attente de commandes sont partagés.
ShellPolicy n’est qu’un pré-filtre ; conservez l’approbation activée, utilisez des informations d’identification avec privilèges minimum et préférez DockerShellExecutor quand les commandes nécessitent une limite d’isolation plus forte.
Les outils Shell sont disponibles à partir du package de préversion Microsoft.Agents.AI.Tools.Shell .
HarnessAgent est disponible à partir de Microsoft.Agents.AI.Harness.
Pour un agent brut, créez la fonction shell avec client.get_shell_tool(func=shell.as_function()) et ajoutez ShellEnvironmentProvider séparément.
create_harness_agent effectue les deux étapes lorsque vous passez shell_executor:
from agent_framework import create_harness_agent
from agent_framework.tools import LocalShellTool, ShellEnvironmentProviderOptions
async with LocalShellTool() as shell:
agent = create_harness_agent(
client=client,
shell_executor=shell,
shell_environment_provider_options=ShellEnvironmentProviderOptions(
probe_tools=("git", "python"),
),
)
session = agent.create_session()
response = await agent.run("Inspect the current repository.", session=session)
shell_executor est opt-in et doit exposer as_function(). La fabrique ajoute l’interpréteur de commandes et ShellEnvironmentProvider uniquement lorsque le client implémente SupportsShellTool. Sinon, il enregistre un avertissement et ignore les deux.
shell_environment_provider_options est facultatif et est utilisé uniquement avec shell_executor.
LocalShellTool est défini par défaut sur le mode persistant, un délai d’expiration de 30 secondes, une sortie combinée de 64 KiB, un ré-ancrage du répertoire de travail et approval_mode="always_require". Étant donné que l’approbation de l’outil Harness est activée par défaut, transmettez un AgentSession à run. L’appelant possède le cycle de vie de l’exécuteur ; utilisez ou appelez async withclose()et créez un outil persistant par session utilisateur. Ne partagez pas l’état modifiable de l’interpréteur de commandes entre plusieurs utilisateurs ou conversations concurrentes.
L’interpréteur de commandes hôte n’est pas un bac à sable. Conservez l’approbation activée, utilisez des informations d’identification avec privilèges minimum et utilisez-les DockerShellTool pour l’isolation du conteneur. La désactivation de l’approbation nécessite approval_mode="never_require" et acknowledge_unsafe=True; ShellPolicy seule n’est pas une limite de sécurité.
create_harness_agent est publié dans agent-framework-core. L’intégration au shell est fournie par le package en préversion agent-framework-tools et émet un ExperimentalWarning lorsqu’elle est activée.
Un Harnais Go empaqueté n’est actuellement pas disponible. Composez l’outil shell local et le fournisseur d’environnement directement sur un agent Go brut.