Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
O pacote beta agent-framework-tools Python fornece ferramentas de execução de shell e reconhecimento de ambiente por meio do agent_framework.tools namespace.
| Tool | Use-o quando |
|---|---|
LocalShellTool |
Os comandos são confiáveis ou aprovados individualmente e devem ser executados no ambiente de host do processo do agente. |
DockerShellTool |
Os comandos de shell gerados por modelo precisam de isolamento de contêiner OCI. |
ShellEnvironmentProvider |
O modelo precisa da família de shell ativa, do sistema operacional, do diretório de trabalho e das versões da CLI instalada. |
ShellPolicy |
Você deseja um pré-filtro de lista de permissões ou de lista de negação antes da aprovação ou execução. |
Warning
A execução do shell pode modificar arquivos, iniciar processos, acessar credenciais e se comunicar com sistemas externos. Use a camada de execução com menos privilégios que dá suporte à tarefa.
Instalar o pacote
dotnet add package Microsoft.Agents.AI.Tools.Shell --prerelease
Usar o shell local e a conscientização do ambiente
LocalShellExecutor dá suporte a modos sem estado e persistentes.
ShellEnvironmentProvider investiga o ambiente ativo e adiciona diretrizes de shell autoritativas ao contexto do 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 também está disponível para pré-filtragem de comando. Um exemplo de runnable DockerShellExecutor dedicado não está publicado no momento.
Instalar o pacote
pip install agent-framework-tools --pre
O pacote é instalado para encerrar árvores psutil de processo filho quando uma execução atinge o tempo limite.
Utilize LocalShellTool
LocalShellTool executa comandos diretamente no host. Ele usa como padrão um shell persistente, um tempo limite de 30 segundos, truncamento de saída de 64 KiB, confinamento de diretório de trabalho e aprovação para cada comando.
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())
Use mode="stateless" quando cada chamada deve ser executada em um novo processo. Use a AGENT_FRAMEWORK_SHELL variável de ambiente ou o argumento construtor shell para substituir o shell resolvido.
Importante
LocalShellTool não é uma área restrita. A aprovação é o limite de segurança principal. A desabilitação da aprovação requer acknowledge_unsafe=True.
Restringir comandos com ShellPolicy
ShellPolicy aplica listas de permissão e negação de expressão regular antes da execução. As regras de negação têm precedência.
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
Uma política de comando é um pré-filtro de usabilidade, não um limite de segurança. Sintaxe de shell, aliases, variáveis, interpretadores e cargas codificadas podem ignorar a correspondência de padrões simples.
Adicionar ShellEnvironmentProvider
ShellEnvironmentProvider investiga a família de shells, a versão, o sistema operacional, o diretório de trabalho e as versões da CLI selecionada e, em seguida, injeta essas informações antes que o agente seja executado. A lista de investigação padrão é git, nodee dockerpython.
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)
Utilize DockerShellTool
DockerShellTool requer Docker ou Podman em PATH. Os padrões desabilitam a rede, são executados como um usuário não raiz, usam um sistema de arquivos raiz somente leitura, descartam recursos, limitam a memória a 512 MiB e limitam o contêiner a 256 processos.
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)
A imagem padrão é mcr.microsoft.com/azurelinux/base/core:3.0. Passe docker_binary="podman" para usar o Podman. Um exemplo de runnable DockerShellTool dedicado não está publicado no momento.
Escolher uma camada de execução
| Scenario | Tool | Limite de isolamento |
|---|---|---|
| Comandos de desenvolvimento confiáveis | LocalShellTool |
Aprovação no processo de host |
| Comandos de shell não confiáveis | DockerShellTool |
Contêiner OCI com sinalizadores de isolamento padrão |
| Código gerado não confiável sem um shell | Hyperlight CodeAct | MicroVM do Hyperlight |
O Go fornece a execução do shell local e a investigação de ambiente por meio tool/shelltoolde . Consulte Usar a ferramenta de shell local.
DockerShellTool no momento, as diretrizes não estão disponíveis para o Go.
Usar ferramentas de shell com o Harness Agent
Agentes sem formatação e HarnessAgent usar a mesma configuração de shell de duas partes: registrar a função do executor como uma ferramenta e adicionar ShellEnvironmentProvider quando o modelo deve receber shell, sistema operacional, diretório de trabalho e contexto de versão da CLI.
HarnessAgent não cria ou possui um executor de 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 padrão para o nome run_shell e requireApproval: true.
LocalShellExecutor usa como padrão o modo persistente, um limite de 64 KiB por fluxo de saída e sem tempo limite; o exemplo usa explicitamente os 30 segundos LocalShellExecutor.DefaultTimeoutrecomendados.
ShellEnvironmentProviderOptions usa como padrão a investigação git, dotnet, node, pythone docker, com um tempo limite de cinco segundos por investigação.
Crie um executor persistente por sessão de usuário e descarte-o quando a sessão terminar. Não compartilhe entre usuários ou conversas simultâneas porque o diretório de trabalho, o ambiente, o histórico do shell, os trabalhos em segundo plano e a fila de comandos são compartilhados.
ShellPolicy é apenas um pré-filtro; mantenha a aprovação habilitada, use credenciais com menos privilégios e prefira DockerShellExecutor quando os comandos exigirem um limite de isolamento mais forte.
As ferramentas do Shell estão disponíveis no pacote de pré-lançamento Microsoft.Agents.AI.Tools.Shell .
HarnessAgent está disponível em Microsoft.Agents.AI.Harness.
Para um agente sem formatação, crie a função de shell com client.get_shell_tool(func=shell.as_function()) e adicione ShellEnvironmentProvider separadamente.
create_harness_agent executa as duas etapas quando você 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 é opt-in e deve expor as_function(). A fábrica adiciona a ferramenta shell e ShellEnvironmentProvider somente quando o cliente implementa SupportsShellTool; caso contrário, ele registra um aviso e ignora ambos.
shell_environment_provider_options é opcional e é usado apenas com shell_executor.
LocalShellTool usa como padrão o modo persistente, um tempo limite de 30 segundos, saída combinada de 64 KiB, ancoragem de diretório de trabalho e approval_mode="always_require". Como a aprovação da ferramenta Harness está habilitada por padrão, passe um AgentSession para run. O chamador é proprietário do ciclo de vida do executor; use async with ou chame close()e crie uma ferramenta persistente por sessão do usuário. Não compartilhe o estado mutável do shell entre usuários ou conversas simultâneas.
O shell do host não é uma área restrita. Mantenha a aprovação habilitada, use credenciais com menos privilégios e use DockerShellTool para isolamento de contêiner. Desabilitar a aprovação requer approval_mode="never_require" e acknowledge_unsafe=True; ShellPolicy sozinho, não é um limite de segurança.
create_harness_agent é liberado em agent-framework-core. A integração do Shell é fornecida pelo pacote de pré-lançamento agent-framework-tools e emite um ExperimentalWarning quando habilitado.
Um Go Harness empacotado não está disponível no momento. Redigir a ferramenta de shell local e o provedor de ambiente diretamente em um agente go simples.