Shell-hulpprogramma's

Het bèta-Python-pakket agent-framework-tools biedt shell-uitvoering en hulpprogramma's voor omgevingsbewustzijn via de agent_framework.tools naamruimte.

Tool Gebruik deze wanneer
LocalShellTool Opdrachten worden vertrouwd of afzonderlijk goedgekeurd en moeten worden uitgevoerd in de hostomgeving van het agentproces.
DockerShellTool Door model gegenereerde shell-opdrachten hebben OCI-containerisolatie nodig.
ShellEnvironmentProvider Het model heeft de actieve shell-familie, het besturingssysteem, de werkmap en de geïnstalleerde CLI-versies nodig.
ShellPolicy U wilt een vooraf filter voor toestaan of weigeren voordat goedkeuring of uitvoering wordt uitgevoerd.

Warning

Shell-uitvoering kan bestanden wijzigen, processen starten, referenties openen en communiceren met externe systemen. Gebruik de laag met minimale bevoegdheden die ondersteuning biedt voor de taak.

Installeer het pakket

dotnet add package Microsoft.Agents.AI.Tools.Shell --prerelease

Lokale shell en omgevingsbewustzijn gebruiken

LocalShellExecutor ondersteunt stateless en permanente modi. ShellEnvironmentProvider test de actieve omgeving en voegt gezaghebbende shellrichtlijnen toe aan de agentcontext.

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 is ook beschikbaar voor het vooraf filteren van opdrachten. Er is momenteel geen speciaal voorbeeld gepubliceerd dat kan DockerShellExecutor worden uitgevoerd.

Installeer het pakket

pip install agent-framework-tools --pre

Het pakket wordt geïnstalleerd om onderliggende processtructuren psutil te beëindigen wanneer er een time-out optreedt voor een uitvoering.

Gebruik LocalShellTool

LocalShellTool voert opdrachten rechtstreeks op de host uit. De standaardinstelling is een permanente shell, een time-out van 30 seconden, afkapping van 64 KiB-uitvoer, insluiting van werkmappen en goedkeuring voor elke opdracht.

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())

Gebruik mode="stateless" wanneer elke aanroep in een nieuw proces moet worden uitgevoerd. Gebruik de AGENT_FRAMEWORK_SHELL omgevingsvariabele of het shell constructorargument om de opgeloste shell te overschrijven.

Important

LocalShellTool is geen sandbox. Goedkeuring is de primaire beveiligingsgrens. Voor het uitschakelen van goedkeuring is vereist acknowledge_unsafe=True.

Opdrachten beperken met ShellPolicy

ShellPolicy past lijsten voor toestaan en weigeren van reguliere expressies toe voordat deze worden uitgevoerd. Regels voor weigeren hebben voorrang.

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",
        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=(
            "You can run a narrow set of read-only shell commands (ls, pwd, cat, "
            "git status/log/diff, python --version). Anything else will be rejected."
        ),
        tools=[client.get_shell_tool(func=shell.as_function())],
    )

    query = "Summarise the current directory and print the Python version."
    print(f"User: {query}")
    result = await agent.run(query)
    print(f"Agent: {result.text}")

Warning

Een opdrachtbeleid is een prefilter voor bruikbaarheid, niet een beveiligingsgrens. Shell-syntaxis, aliassen, variabelen, interpreters en gecodeerde nettoladingen kunnen eenvoudige patroonkoppeling omzeilen.

ShellEnvironmentProvider toevoegen

ShellEnvironmentProvider test de shell-familie, versie, besturingssysteem, werkmap en geselecteerde CLI-versies, en injecteert die informatie voordat de agent wordt uitgevoerd. De standaardtestlijst is git, node, en pythondocker.

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)

Gebruik DockerShellTool

DockerShellTool Vereist Docker of Podman op PATH. De standaardinstellingen schakelen netwerken uit, worden uitgevoerd als een niet-hoofdgebruiker, gebruiken een alleen-lezen hoofdbestandssysteem, drop-mogelijkheden, beperken geheugen tot 512 MiB en de container bij 256 processen beperken.

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)

De standaardafbeelding is mcr.microsoft.com/azurelinux/base/core:3.0. Geef docker_binary="podman" door om Podman te gebruiken. Er is momenteel geen speciaal voorbeeld gepubliceerd dat kan DockerShellTool worden uitgevoerd.

Een uitvoeringslaag kiezen

Scenario Tool Isolatiegrens
Vertrouwde ontwikkelopdrachten LocalShellTool Goedkeuring in het hostproces
Niet-vertrouwde shell-opdrachten DockerShellTool OCI-container met standaardisolatievlagmen
Niet-vertrouwde gegenereerde code zonder shell Hyperlight CodeAct Hyperlight microVM

Go biedt lokale shell-uitvoering en omgeving door.tool/shelltool Zie Het lokale shell-hulpprogramma gebruiken.

DockerShellTool richtlijnen zijn momenteel niet beschikbaar voor Go.

Shell-hulpprogramma's gebruiken met Harness Agent

Gewone agents en HarnessAgent gebruiken dezelfde tweedelige shellinstallatie: registreer de functie van de uitvoerder als een hulpprogramma en voeg toe ShellEnvironmentProvider wanneer het model shell, besturingssysteem, werkmap en CLI-versiecontext moet ontvangen. HarnessAgent maakt of bezit geen shell-uitvoerprogramma:

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 is standaard ingesteld op de naam run_shell en requireApproval: true. LocalShellExecutor standaard ingesteld op permanente modus, een limiet van 64 KiB per uitvoerstroom en geen time-out; in het voorbeeld wordt expliciet de aanbevolen 30 seconden LocalShellExecutor.DefaultTimeoutgebruikt. ShellEnvironmentProviderOptionsstandaard ingesteld op testengit, dotnet, node, en pythondocker, met een time-out van vijf seconden per test.

Maak één permanente uitvoerder per gebruikerssessie en verwijder deze wanneer de sessie afloopt. Deel deze niet tussen gebruikers of gelijktijdige gesprekken omdat werkmap, omgeving, shellgeschiedenis, achtergrondtaken en de opdrachtwachtrij worden gedeeld. ShellPolicy is alleen een voorfilter; goedkeuring ingeschakeld houden, referenties met minimale bevoegdheden gebruiken en de voorkeur geven DockerShellExecutor aan wanneer opdrachten een sterkere isolatiegrens vereisen.

Shell-hulpprogramma's zijn beschikbaar via het voorlopige Microsoft.Agents.AI.Tools.Shell pakket. HarnessAgent is beschikbaar vanaf Microsoft.Agents.AI.Harness.

Voor een gewone agent maakt u de shell-functie met client.get_shell_tool(func=shell.as_function()) en voegt u deze afzonderlijk toe ShellEnvironmentProvider . create_harness_agent voert beide stappen uit wanneer u het volgende doorgeeft 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 is opt-in en moet beschikbaar worden as_function()gesteld. De factory voegt het shell-hulpprogramma toe en ShellEnvironmentProvider alleen wanneer de client wordt geïmplementeerd SupportsShellTool; anders wordt er een waarschuwing en worden beide overgeslagen. shell_environment_provider_options is optioneel en wordt alleen gebruikt met shell_executor.

LocalShellTool standaard ingesteld op permanente modus, een time-out van 30 seconden, gecombineerde uitvoer van 64 KiB, herankering van approval_mode="always_require"werkmap en . Omdat De goedkeuring van harness-hulpprogramma's standaard is ingeschakeld, geeft u een AgentSession door aan run. De beller is eigenaar van de levenscyclus van de uitvoerder; gebruiken async with of aanroepen close()en één permanent hulpprogramma per gebruikerssessie maken. Deel de onveranderbare shellstatus niet voor gebruikers of gelijktijdige gesprekken.

De hostshell is geen sandbox. Goedkeuring ingeschakeld houden, referenties met minimale bevoegdheden gebruiken en gebruiken DockerShellTool voor containerisolatie. Het uitschakelen van goedkeuring vereist approval_mode="never_require" en acknowledge_unsafe=True; ShellPolicy alleen is geen beveiligingsgrens.

create_harness_agent wordt vrijgegeven in agent-framework-core. Shell-integratie wordt geleverd door het pre-releasepakket agent-framework-tools en verzendt een ExperimentalWarning wanneer deze optie is ingeschakeld.

Een verpakte Go Harness is momenteel niet beschikbaar. Stel het lokale shell-hulpprogramma en de omgevingsprovider rechtstreeks samen op een gewone Go-agent.