Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Il pacchetto Python beta agent-framework-tools fornisce strumenti per l'esecuzione della shell e per il rilevamento dell'ambiente tramite il namespace agent_framework.tools.
| Tool | Usarlo quando |
|---|---|
LocalShellTool |
I comandi sono attendibili o approvati singolarmente e devono essere eseguiti nell'ambiente host del processo dell'agente. |
DockerShellTool |
I comandi shell generati dal modello richiedono l'isolamento dei container OCI. |
ShellEnvironmentProvider |
Il modello richiede la famiglia di shell attiva, il sistema operativo, la directory di lavoro e le versioni dell'interfaccia della riga di comando installate. |
ShellPolicy |
Si desidera un prefiltro basato su una lista di elementi consentiti o bloccati prima dell'approvazione o dell'esecuzione. |
Avvertimento
L'esecuzione della shell può modificare file, avviare processi, credenziali di accesso e comunicare con sistemi esterni. Usare il livello di esecuzione con il minor livello di privilegi che consenta di eseguire l'attività.
Installare il pacchetto
dotnet add package Microsoft.Agents.AI.Tools.Shell --prerelease
Usare la shell locale e il riconoscimento dell'ambiente
LocalShellExecutor supporta le modalità senza stato e persistenti.
ShellEnvironmentProvider analizza l'ambiente attivo e aggiunge istruzioni affidabili sulla shell al contesto dell'agente.
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 è disponibile anche per il pre-filtraggio dei comandi. Un esempio eseguibile DockerShellExecutor specifico non è al momento disponibile.
Installare il pacchetto
pip install agent-framework-tools --pre
Il pacchetto installa psutil per terminare gli alberi di processi figlio quando un'esecuzione restituisce un timeout.
Utilizzare LocalShellTool.
LocalShellTool esegue i comandi direttamente nell'host. Per impostazione predefinita, prevede una shell persistente, un timeout di 30 secondi, un troncamento dell'output a 64 KiB e il riancoraggio della directory di lavoro. Per impostazione predefinita, le chiamate dell'agente tramite as_function() richiedono l'approvazione. Le chiamate dirette a run() non richiedono l'approvazione.
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())
Le chiamate dirette a run() restituiscono un oggetto ShellResult con campi separati stdout, stderr, exit_code, duration_ms, truncated e timed_out. Quando la shell viene eseguita tramite l'hosting OpenAI Responses o Foundry, Agent Framework mantiene l'output standard, l'errore standard, i risultati di uscita e i risultati di timeout tra le continuazioni del provider. Le uscite diverse da zero rimangono errori, l'errore standard rimane separato e un timeout non viene visualizzato come uscita riuscita.
Gli elementi di trascrizione della shell ospitata dal provider OpenAI rimangono informativi, anche quando è configurato un executor della shell locale. Solo una chiamata esplicita e ben formata local_shell_call, o una chiamata shell contrassegnata con environment.type="local", accede alla funzione locale e al flusso di approvazione. Configurare LocalShellTool da solo non causa l'esecuzione sull'host delle chiamate della shell ospitata dal provider.
Usare mode="stateless" quando ogni chiamata deve essere eseguita in un nuovo processo. Usare la variabile di ambiente AGENT_FRAMEWORK_SHELL o l'argomento del costruttore shell per eseguire l'override della shell risolta.
Importante
LocalShellTool non è una sandbox. L'approvazione umana aggiunge una fase di revisione, ma non isola la shell. La disabilitazione dell'approvazione per le chiamate dell'agente richiede acknowledge_unsafe=True.
Limitare i comandi con ShellPolicy
ShellPolicy applica al testo del comando liste di autorizzazione e di blocco basate su espressioni regolari prima di eseguirlo. Le regole di negazione hanno la precedenza. Non verifica ciò che la shell esegue effettivamente né limita l'accesso ai file.
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())],
)
Avvertimento
L'esempio disabilita l'approvazione umana ed è adatto solo per un ambiente isolato e eliminabile senza segreti o dati preziosi. Le sostituzioni dei comandi, ad esempio $(...) e backtick, possono passare semplici modelli di elenco elementi consentiti. I modelli che corrispondono solo all'inizio di un comando possono anche consentire operazioni aggiuntive. Usa l'isolamento applicato in modo indipendente e autorizzazioni limitate per la produzione. La revisione umana può aggiungere un controllo, ma non isola la shell.
Preferire i modelli di stringa. Python compila le stringhe tramite il motore regex e applica un limite di un secondo a ogni corrispondenza. Un timeout dell'elenco di rifiuto nega il comando e un timeout dell'elenco di consenso non autorizza il comando. Un precompilato regex.Pattern utilizza lo stesso limite. Una libreria standard precompilata re.Pattern mantiene i flag, ma non può essere interrotta, quindi evita espressioni costose o ambigue in tale forma.
Aggiungere ShellEnvironmentProvider
ShellEnvironmentProvider rileva la famiglia della shell, la versione, il sistema operativo, la directory di lavoro e le versioni selezionate degli strumenti CLI, quindi inietta queste informazioni prima che l'agente venga eseguito. L'elenco di probe predefinito è git, nodepython, e 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)
Utilizzare DockerShellTool.
DockerShellTool richiede Docker o Podman in PATH. Le impostazioni predefinite disabilitano la rete, vengono eseguite come utente non radice, usano un file system radice di sola lettura, eliminano funzionalità, limitano la memoria a 512 MiB e limitano il contenitore a 256 processi.
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'immagine predefinita è mcr.microsoft.com/azurelinux/base/core:3.0. Specificare docker_binary="podman" per usare Podman. Un esempio eseguibile DockerShellTool specifico non è al momento disponibile.
Usare extra_run_args solo per le opzioni Docker che non indebolino l'isolamento o i limiti delle risorse configurati. La validazione riconosce flag lunghi, flag brevi, valori allegati e flag brevi raggruppati. Rifiuta gli override, ad esempio -u / --user, -m--memory / , -v--volume / , --networke .--pids-limit Usare invece l'opzione del costruttore corrispondente DockerShellTool .
Scegliere un livello di esecuzione
| Scenario | Tool | Limite di isolamento |
|---|---|---|
| Comandi di sviluppo attendibili | LocalShellTool |
Nessuno; l'approvazione umana è abilitata per impostazione predefinita |
| Comandi della shell non attendibili | DockerShellTool |
Contenitore OCI con i flag di isolamento predefiniti |
| Codice generato non attendibile senza una shell | Hyperlight CodeAct | Hyperlight microVM |
Go fornisce l'esecuzione di comandi nella shell locale e il rilevamento dell'ambiente tramite tool/shelltool. Vedere Usare lo strumento shell locale.
DockerShellTool le indicazioni non sono attualmente disponibili per Go.
Usare gli strumenti shell con Harness Agent
Gli agenti semplici e HarnessAgent usano la stessa configurazione della shell in due fasi: registrare la funzione dell'executor come strumento e aggiungere ShellEnvironmentProvider quando il modello deve ricevere il contesto della shell, del sistema operativo, della directory di lavoro e della versione dell'interfaccia della riga di comando.
HarnessAgent non crea né possiede un esecutore di 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 ha come valore predefinito il nome run_shell e requireApproval: true.
LocalShellExecutor è impostato per impostazione predefinita sulla modalità persistente, su un limite massimo di 64 KiB per ciascun flusso di output e su nessun timeout; l'esempio usa esplicitamente il valore consigliato di 30 secondi LocalShellExecutor.DefaultTimeout.
ShellEnvironmentProviderOptionsPer impostazione predefinita, verifica git, dotnet, node, python e docker, con un timeout di cinque secondi per ogni verifica.
Creare un executor permanente per sessione utente ed eliminarlo al termine della sessione. Non condividere tra più utenti o conversazioni simultanee perché la directory di lavoro, l'ambiente, la cronologia della shell, i processi in background e la coda dei comandi sono condivisi.
ShellPolicy è solo un filtro preliminare; mantenere abilitata l'approvazione, usare le credenziali con privilegi minimi e preferire DockerShellExecutor quando i comandi richiedono un limite di isolamento più forte.
Gli strumenti della shell sono disponibili nel pacchetto prerelease Microsoft.Agents.AI.Tools.Shell.
HarnessAgent è disponibile da Microsoft.Agents.AI.Harness.
Per un agente normale, creare la funzione shell con client.get_shell_tool(func=shell.as_function()) e aggiungere ShellEnvironmentProvider separatamente.
create_harness_agent esegue entrambi i passaggi quando si passa 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 è facoltativo e deve esporre as_function(). La factory aggiunge lo strumento shell e ShellEnvironmentProvider solo quando il client implementa SupportsShellTool. In caso contrario, registra un avviso e ignora entrambi.
shell_environment_provider_options è facoltativo e viene usato solo con shell_executor.
LocalShellTool usa per impostazione predefinita la modalità persistente, un timeout di 30 secondi, un output combinato di 64 KiB, il riancoraggio della directory di lavoro e approval_mode="always_require". Poiché l'approvazione dello strumento Infrastruttura è abilitata per impostazione predefinita, passare AgentSession a run. Il chiamante gestisce il ciclo di vita dell'executor; usa async with o chiama close(), e crea uno strumento permanente per ogni sessione utente. Non condividere lo stato mutabile della shell tra utenti o conversazioni concorrenti.
La shell host non è una sandbox. Mantieni abilitata l'approvazione, usa credenziali con privilegi minimi necessari e usa DockerShellTool per l'isolamento dei contenitori. La disabilitazione dell'approvazione richiede approval_mode="never_require" e acknowledge_unsafe=True; ShellPolicy da solo non è un limite di sicurezza.
create_harness_agent viene rilasciato in versione agent-framework-core. L'integrazione della shell è fornita dal pacchetto prerelease agent-framework-tools ed emette un ExperimentalWarning quando è abilitata.
Un Go Harness in pacchetto non è attualmente disponibile. Componi lo strumento di shell locale e il provider dell'ambiente direttamente su un semplice agente Go.