Shell-eszközök

A béta agent-framework-tools Python csomag rendszerhéj-végrehajtást és környezettudatos eszközöket biztosít a agent_framework.tools névtéren keresztül.

Eszköz Akkor használja, ha
LocalShellTool A parancsok megbízhatók vagy egyedileg jóváhagyottak, és az ügynökfolyamat gazdakörnyezetében kell futniuk.
DockerShellTool A modell által létrehozott rendszerhéjparancsok OCI-tároló elkülönítését igénylik.
ShellEnvironmentProvider A modellnek szüksége van az aktív shellcsalád, az operációs rendszer, a munkakönyvtár és a telepített CLI-eszközök verzióinak adataira.
ShellPolicy A jóváhagyás vagy a végrehajtás előtt egy engedélyezési vagy megtagadási listát szeretne előszűrni.

Warning

A rendszerhéj végrehajtása módosíthatja a fájlokat, elindíthatja a folyamatokat, hozzáférhet a hitelesítő adatokhoz, és kommunikálhat a külső rendszerekkel. Használja a feladatot támogató legkevésbé kiemelt végrehajtási szintet.

A csomag telepítése

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

A helyi parancsértelmező és a környezet ismeretének használata

LocalShellExecutor állapot nélküli és állandó üzemmódokat támogat. ShellEnvironmentProvider felméri az aktív környezetet, és irányadó, shellre vonatkozó útmutatást ad az ügynök kontextusához.

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 parancselőszűréshez is elérhető. Egy dedikált futtatható DockerShellExecutor minta jelenleg nincs közzétéve.

A csomag telepítése

pip install agent-framework-tools --pre

A csomag a psutil elemet telepíti a gyermekfolyamatok fájának leállításához, ha egy végrehajtás túllépi az időkorlátot.

Használja a LocalShellTool-t

A(z) LocalShellTool parancsok közvetlenül a gazdagépen futnak. Alapértelmezés szerint perzisztens rendszerhéjat, 30 másodperces időtúllépést, a kimenet 64 KiB-nál történő csonkolását és a munkakönyvtár újbóli rögzítését használja. A as_function() használatával kezdeményezett ügynökhívások alapértelmezés szerint jóváhagyást igényelnek. A(z) run() címre irányuló közvetlen hívások nem igényelnek jóváhagyást.

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

A stderr felé irányuló közvetlen hívások egy exit_code objektumot adnak vissza, különálló duration_ms, truncated, timed_out, ShellResult, stdout és run() mezőkkel. Amikor a rendszerhéj OpenAI-válaszokon vagy Foundry-üzemeltetésen fut, az Agent Framework megőrzi a standard kimenetet, a standard hibát, a kilépési állapotokat és az időtúllépési eredményeket a szolgáltatók közötti folytatás során. A nem nulla kilépési kódok továbbra is hibák maradnak, a standard error külön marad, és az időtúllépés nem jelenik meg sikeres kilépésként.

Az OpenAI szolgáltató által üzemeltetett rendszerhéj-átiratelemek tájékoztató jellegűek maradnak, még akkor is, ha egy helyi rendszerhéj-végrehajtó van konfigurálva. Csak egy jól formázott explicit local_shell_callvagy egy felületi environment.type="local"hívás jelöli meg a helyi függvényt és a jóváhagyási útvonalat. A LocalShellTool önmagában történő konfigurálása nem eredményezi, hogy a szolgáltató által üzemeltetett parancsértelmező-hívások a gazdagépen fussanak.

Akkor használható mode="stateless" , ha minden hívásnak egy új folyamatban kell futnia. A meghatározott shell felülbírálásához használja a AGENT_FRAMEWORK_SHELL környezeti változót vagy a shell konstruktorargumentumot.

Az állandó parancsok sorosítva vannak. A kimeneti túlcsordulás vagy egy olyan időtúllépés után, amely helyreállíthatatlanná teszi a munkamenetet, az üzenetsorba helyezett parancs egy friss rendszerhéjban indul el, és nem viszi tovább a megszakadt munkamenet rendszerhéj-helyi változóit, függvényeit és munkakönyvtár-módosításait. A szabályosan megszakított POSIX-időtúllépés megőrizheti a meglévő rendszerhéjat és annak állapotát. Windows rendszeren az időtúllépés mindig bezárja az aktuális perzisztens shellt, még akkor is, ha a megszakított parancs kibocsátja a befejezési jelölőt.

A(z) close() meghívása a rendszerhéj újraindítása helyett a leállítás előtt vagy közben várólistára helyezett parancsokat utasítja el. Egy későbbi hívás új munkamenetet nyithat meg.

Important

LocalShellTool nem tesztkörnyezet. Az emberi jóváhagyás hozzáad egy felülvizsgálati kaput, de nem különíti el a héjat. Az ügynökhívások jóváhagyásának letiltásához a következőre van szükség acknowledge_unsafe=True: .

Parancsok korlátozása a(z) ShellPolicy használatával

ShellPolicy reguláriskifejezés-alapú engedélyezési és tiltási listákat alkalmaz a parancsszövegre a végrehajtás előtt. A megtagadási szabályok elsőbbséget élveznek. Nem vizsgálja meg, hogy a rendszerhéj végül mit hajt végre, és nem korlátozza a fájlhozzáférést.

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

Warning

A minta kikapcsolja az emberi jóváhagyást, és csak olyan elkülönített, eldobható környezetben használható, amely nem tartalmaz bizalmas vagy értékes adatokat. A parancshelyettesítések, például $(...) a backtickek egyszerű engedélyezési listamintákat adhatnak át. Azok a minták, amelyek csak a parancs elejének felelnek meg, további műveleteket is lehetővé tehetnek. Használjon külön-külön kikényszerített elkülönítést és korlátozott engedélyeket az éles környezetben. Az emberi felülvizsgálat hozzá tud adni egy ellenőrzést, de nem különíti el a héjat.

Sztringminták előnyben részesítése. A Python a karakterláncokat a regex motorral fordítja le, és minden egyes illesztéshez egy másodperces időkorlátot alkalmaz. A megtagadási listák időtúllépése megtagadja a parancsot, és az engedélyezési listák időtúllépése nem ad engedélyt. Az előre lefordított regex.Pattern ugyanazt a korlátot használja. Az előre összeállított standard kódtár re.Pattern megőrzi a jelzőit, de nem szakítható meg, ezért kerülje a drága vagy nem egyértelmű kifejezéseket ebben a formában.

Adj hozzá ShellEnvironmentProvider

ShellEnvironmentProvider felderíti a shellcsaládot, a verziót, az operációs rendszert, a munkakönyvtárat és a kiválasztott CLI-verziókat, majd még azelőtt beilleszti ezt az információt, hogy az ügynök elindulna. Az alapértelmezett mintavételi lista: git, nodepythonés 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)

Használja a DockerShellTool-t

DockerShellTool használatához Docker vagy Podman szükséges a következőn: PATH. Az alapértelmezett beállítások letiltják a hálózatkezelést, nem gyökérfelhasználóként futnak, írásvédett legfelső szintű fájlrendszert használnak, képességeket csökkentenek, 512 MiB-re korlátozzák a memóriát, és 256 folyamatra korlátozzák a tárolót.

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)

Az alapértelmezett kép a következő mcr.microsoft.com/azurelinux/base/core:3.0. A Podman használatához adja meg a(z) docker_binary="podman" értéket. Egy dedikált futtatható DockerShellTool minta jelenleg nincs közzétéve.

Csak olyan Docker-beállításokhoz használható extra_run_args , amelyek nem gyengítik a konfigurált elkülönítést vagy erőforráskorlátokat. Az ellenőrzés felismeri a hosszú jelzőket, a rövid jelölőket, a csatolt értékeket és a fürtözött rövid jelölőket. Elutasítja az olyan felülbírálásokat, mint a -u--user / , --memory-m / , / --volume-v, --network és --pids-limit. Használja inkább a megfelelő DockerShellTool konstruktort.

Végrehajtási szint kiválasztása

Scenario Eszköz Elkülönítési határ
Megbízható fejlesztési parancsok LocalShellTool Nincs; az emberi jóváhagyás alapértelmezés szerint engedélyezve van
Nem megbízható shellparancsok DockerShellTool OCI-tároló alapértelmezett elkülönítési jelzőkkel
Nem megbízható forrásból származó generált kód shell nélkül Hyperlight CodeAct Hyperlight microVM

A Go helyi parancsértelmező-végrehajtást és környezetvizsgálatot tesz lehetővé a(z) tool/shelltool révén. Lásd: A helyi parancsértelmező eszköz használata.

DockerShellTool Az útmutató jelenleg nem érhető el a Go számára.

Shelleszközök használata a Harness Agenttel

Az egyszerű ügynökök és a HarnessAgent ugyanazt a kétrészes rendszerhéj-beállítást használják: regisztrálja a végrehajtó függvényét eszközként, és adja hozzá a ShellEnvironmentProvider elemet, amikor a modellnek meg kell kapnia a rendszerhéjjal, az operációs rendszerrel, a munkakönyvtárral és a CLI-verzióval kapcsolatos környezetet. HarnessAgent nem hoz létre shellvégrehajtót, és nem is rendelkezik vele:

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 alapértelmezés szerint a run_shell és requireApproval: true nevet használja. LocalShellExecutor alapértelmezés szerint állandó mód, kimeneti streamenként 64 KiB-korlát, időtúllépés nélkül; a példa kifejezetten az ajánlott 30 másodpercet LocalShellExecutor.DefaultTimeouthasználja. ShellEnvironmentProviderOptions alapértelmezés szerint a következőket vizsgálja: git, dotnet, node, python és docker, vizsgálatonként öt másodperces időtúllépéssel.

Hozzon létre egy állandó végrehajtót felhasználói munkamenetenként, és helyezze el a munkamenet végén. Ne ossza meg felhasználók vagy párhuzamos beszélgetések között, mert a munkakönyvtár, a környezet, a shell-előzmények, a háttérfeladatok és a parancsvárólista közösek. ShellPolicy csak előszűrés; hagyja engedélyezve a jóváhagyást, használja a legkevésbé kiemelt hitelesítő adatokat, és előnyben részesítse DockerShellExecutor , ha a parancsok erősebb elkülönítési határt igényelnek.

A parancsértelmező eszközök az előzetes kiadású Microsoft.Agents.AI.Tools.Shell csomagban érhetők el. HarnessAgent elérhető innen: Microsoft.Agents.AI.Harness.

Egyszerű ügynök esetén hozza létre a shellfüggvényt a(z) client.get_shell_tool(func=shell.as_function()) használatával, és a(z) ShellEnvironmentProvider elemet külön adja hozzá. create_harness_agent mindkét lépést végrehajtja, ha átadja a(z) shell_executor paramétert:

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 igény szerinti, és elérhetővé kell tennie as_function(). A gyártó csak akkor adja hozzá a shelleszközt és a(z) ShellEnvironmentProvider elemet, ha a kliens megvalósítja a(z) SupportsShellTool elemet; ellenkező esetben figyelmeztetést naplóz, és mindkettőt kihagyja. A(z) shell_environment_provider_options nem kötelező, és csak a(z) shell_executor elemmel együtt használható.

LocalShellTool alapértelmezés szerint perzisztens módot, 30 másodperces időtúllépést, 64 KiB-os egyesített kimenetet, a munkakönyvtár újbóli rögzítését és approval_mode="always_require" használ. Mivel a Harness eszköz jóváhagyása alapértelmezés szerint engedélyezve van, adjon át egy AgentSession elemet a következőnek: run. A hívóé a végrehajtó életciklusa; használjon async with vagy hívjon close(), és hozzon létre egy állandó eszközt felhasználói munkamenetenként. Ne ossza meg a módosítható shellállapotot a felhasználók vagy a párhuzamos beszélgetések között.

A gazdarendszer parancsértelmezője nem elkülönített környezet. Hagyja engedélyezve a jóváhagyást, használja a legkevésbé kiemelt hitelesítő adatokat, és használja DockerShellTool a tárolóelkülönítéshez. A jóváhagyás letiltása megköveteli approval_mode="never_require" , és acknowledge_unsafe=Truea ; ShellPolicy önmagában nem biztonsági határ.

create_harness_agent a következőben jelenik meg: agent-framework-core A héjintegrációt az előzetes kiadású agent-framework-tools csomag biztosítja, és engedélyezéskor egy ExperimentalWarning jelet bocsát ki.

A csomagolt Go Harness jelenleg nem érhető el. Állítsa össze a helyi shelleszközt és a környezeti szolgáltatót közvetlenül egy egyszerű Go-ügynökre.