Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Das Beta-Python-Paket agent-framework-tools bietet Shellausführungs- und Umgebungsbewusstseinstools über den agent_framework.tools Namespace.
| Tool | Verwenden Sie ihn, wenn |
|---|---|
LocalShellTool |
Befehle gelten als vertrauenswürdig oder werden einzeln freigegeben und sollten in der Hostumgebung des Agentenprozesses ausgeführt werden. |
DockerShellTool |
Modellgenerierte Shellbefehle benötigen OCI-Containerisolation. |
ShellEnvironmentProvider |
Das Modell benötigt die aktive Shellfamilie, das Betriebssystem, das Arbeitsverzeichnis und die installierten CLI-Versionen. |
ShellPolicy |
Sie benötigen vor der Genehmigung oder Ausführung einen Vorabfilter in Form einer Positivliste oder Negativliste. |
Warnung
Die Shellausführung kann Dateien ändern, Prozesse starten, Anmeldeinformationen zugreifen und mit externen Systemen kommunizieren. Verwenden Sie die Ausführungsebene mit den geringsten Rechten, die die Aufgabe unterstützt.
Installiere das Paket
dotnet add package Microsoft.Agents.AI.Tools.Shell --prerelease
Lokale Shell und Umgebungserkennung verwenden
LocalShellExecutor unterstützt zustandslose und persistente Modi.
ShellEnvironmentProvider untersucht die aktive Umgebung und fügt dem Agentkontext autoritative Shell-Anleitungen hinzu.
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 ist auch für die Vorabfilterung von Befehlen verfügbar. Ein eigenes ausführbares Beispiel DockerShellExecutor ist derzeit nicht veröffentlicht.
Installiere das Paket
pip install agent-framework-tools --pre
Das Paket installiert psutil, um Kindprozessbäume zu beenden, wenn eine Ausführung das Zeitlimit überschreitet.
LocalShellTool verwenden
LocalShellTool führt Befehle direkt auf dem Host aus. Standardmäßig verwendet es eine persistente Shell, ein 30-Sekunden-Timeout, eine Ausgabebegrenzung auf 64 KiB, eine Beschränkung auf das Arbeitsverzeichnis und eine Genehmigung für jeden Befehl.
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())
Verwenden Sie mode="stateless", wenn jeder Aufruf in einem neuen Prozess ausgeführt werden soll. Verwenden Sie die Umgebungsvariable AGENT_FRAMEWORK_SHELL oder das shell Konstruktorargument, um die aufgelöste Shell außer Kraft zu setzen.
Important
LocalShellTool ist kein Sandkasten. Die Genehmigung ist die primäre Sicherheitsgrenze. Zum Deaktivieren der Freigabe ist acknowledge_unsafe=True erforderlich.
Einschränken von Befehlen mit ShellPolicy
ShellPolicy wendet vor der Ausführung Zulassungs- und Sperrlisten für reguläre Ausdrücke an. Verweigerungsregeln haben Vorrang.
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}")
Warnung
Eine Befehlsrichtlinie ist ein Usability-Vorfilter, keine Sicherheitsbarriere. Shellsyntax, Aliase, Variablen, Dolmetscher und codierte Nutzlasten können den einfachen Musterabgleich umgehen.
Fügen Sie ShellEnvironmentProvider hinzu.
ShellEnvironmentProvider prüft die Shellfamilie, Version, Betriebssystem, Arbeitsverzeichnis und ausgewählte CLI-Versionen und fügt diese Informationen ein, bevor der Agent ausgeführt wird. Die Standardsondenliste ist git, node, , pythonund 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)
DockerShellTool verwenden
DockerShellTool erfordert Docker oder Podman auf PATH. Standardmäßig werden Netzwerke deaktiviert, als Nicht-Stammbenutzer ausgeführt, ein schreibgeschütztes Stammdateisystem, Drop-Funktionen, Speicher auf 512 MiB beschränkt und der Container auf 256 Prozesse begrenzt.
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)
Das Standardbild ist mcr.microsoft.com/azurelinux/base/core:3.0. Geben Sie docker_binary="podman" an, um Podman zu verwenden. Ein eigenständiges ausführbares Beispiel DockerShellTool ist derzeit nicht veröffentlicht.
Auswählen einer Ausführungsstufe
| Scenario | Tool | Isolationsgrenze |
|---|---|---|
| Vertrauenswürdige Entwicklungsbefehle | LocalShellTool |
Genehmigung im Hostprozess |
| Nicht vertrauenswürdige Shellbefehle | DockerShellTool |
OCI-Container mit Standardisolationskennzeichnungen |
| Nicht vertrauenswürdiger generierter Code ohne Shell | Hyperlight CodeAct | Hyperlight microVM |
Go bietet lokale Ausführung von Shell-Befehlen und die Untersuchung der Umgebung über tool/shelltool. Weitere Informationen finden Sie unter Verwenden des lokalen Shelltools.
DockerShellTool Anleitungen sind derzeit nicht für Go verfügbar.
Shell-Tools mit Harness Agent verwenden
Einfache Agents und HarnessAgent verwenden dieselbe zweiteilige Shell-Konfiguration: die Funktion des Executors als Tool registrieren und ShellEnvironmentProvider hinzufügen, wenn das Modell Kontext zur Shell, zum Betriebssystem, zum Arbeitsverzeichnis und zur CLI-Version erhalten soll.
HarnessAgent erstellt keinen Shell-Executor und besitzt auch keinen:
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 wird standardmäßig auf den Namen run_shell und requireApproval: true festgelegt.
LocalShellExecutor ist standardmäßig auf den persistenten Modus, eine Obergrenze von 64 KiB pro Ausgabestream und kein Timeout eingestellt; im Beispiel wird ausdrücklich das empfohlene LocalShellExecutor.DefaultTimeout mit 30 Sekunden verwendet.
ShellEnvironmentProviderOptions prüft standardmäßig git, dotnet, node, python und docker, mit einem Timeout von fünf Sekunden pro Prüfvorgang.
Erstellen Sie einen dauerhaften Executor pro Benutzersitzung, und löschen Sie ihn, wenn die Sitzung endet. Geben Sie sie nicht für mehrere Benutzer oder gleichzeitige Konversationen frei, da sich das Arbeitsverzeichnis, die Umgebung, der Shell-Verlauf, Hintergrundaufträge und die Befehlswarteschlange gemeinsam genutzt werden.
ShellPolicy ist nur ein Vorfilter; lassen Sie die Genehmigung aktiviert, verwenden Sie Anmeldeinformationen mit den geringstmöglichen Rechten und bevorzugen Sie DockerShellExecutor, wenn Befehle eine stärkere Isolationsgrenze erfordern.
Shelltools sind im Vorabversionspaket Microsoft.Agents.AI.Tools.Shell verfügbar.
HarnessAgent ist verfügbar von Microsoft.Agents.AI.Harness.
Erstellen Sie für einen einfachen Agenten die Shell-Funktion mit client.get_shell_tool(func=shell.as_function()) und fügen Sie ShellEnvironmentProvider separat hinzu.
create_harness_agent führt beide Schritte aus, wenn Sie shell_executor übergeben:
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 ist optional und muss as_function() verfügbar machen. Die Factory fügt das Shell-Tool und ShellEnvironmentProvider nur dann hinzu, wenn der Client SupportsShellTool implementiert; andernfalls protokolliert sie eine Warnung und überspringt beide.
shell_environment_provider_options ist optional und wird nur mit shell_executor verwendet.
LocalShellTool verwendet standardmäßig den persistenten Modus, ein 30-Sekunden-Timeout, eine kombinierte 64-KiB-Ausgabe, die Neuverankerung des Arbeitsverzeichnisses und approval_mode="always_require". Da die Toolgenehmigung für Harness standardmäßig aktiviert ist, übergeben Sie AgentSession an run. Der Aufrufer ist für den Lebenszyklus des Executors verantwortlich; verwenden Sie async with oder rufen Sie close() auf, und erstellen Sie pro Benutzersitzung ein dauerhaftes Tool. Geben Sie keinen veränderlichen Shell-Zustand zwischen Benutzern oder gleichzeitigen Unterhaltungen frei.
Die Hostshell ist kein Sandkasten. Lassen Sie Genehmigungen aktiviert, verwenden Sie Anmeldeinformationen nach dem Prinzip der geringsten Rechte und verwenden Sie DockerShellTool zur Containerisolation. Das Deaktivieren der Genehmigung erfordert approval_mode="never_require" und acknowledge_unsafe=True; ShellPolicy allein ist keine Sicherheitsgrenze.
create_harness_agent wird in agent-framework-core veröffentlicht. Die Shell-Integration wird durch das Paket in der Vorabversion agent-framework-tools bereitgestellt und gibt beim Aktivieren ein ExperimentalWarning aus.
Ein verpacktes Go Harness ist derzeit nicht verfügbar. Verfassen Sie das lokale Shelltool und den Umgebungsanbieter direkt auf einem einfachen Go-Agent.