베타 agent-framework-tools Python 패키지는 네임스페이스를 통해 agent_framework.tools 셸 실행 및 환경 인식 도구를 제공합니다.
| Tool | 사용해야 하는 경우 |
|---|---|
LocalShellTool |
명령은 신뢰할 수 있거나 개별적으로 승인되며 에이전트 프로세스의 호스트 환경에서 실행되어야 합니다. |
DockerShellTool |
모델 생성 셸 명령에는 OCI 컨테이너 격리가 필요합니다. |
ShellEnvironmentProvider |
모델에는 활성 셸 패밀리, 운영 체제, 작업 디렉터리 및 설치된 CLI 버전이 필요합니다. |
ShellPolicy |
승인 또는 실행 전에 허용 목록 또는 거부 목록 미리 필터링을 원합니다. |
Warning
셸 실행은 파일을 수정하고, 프로세스를 시작하고, 자격 증명에 액세스하고, 외부 시스템과 통신할 수 있습니다. 작업을 지원하는 최소 권한의 실행 계층을 사용합니다.
패키지 설치
dotnet add package Microsoft.Agents.AI.Tools.Shell --prerelease
로컬 셸 및 환경 인식 사용
LocalShellExecutor 는 상태 비저장 및 영구 모드를 지원합니다.
ShellEnvironmentProvider 는 활성 환경을 검색하고 에이전트 컨텍스트에 신뢰할 수 있는 셸 지침을 추가합니다.
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 는 명령 사전 필터링에도 사용할 수 있습니다. 전용 실행 가능 DockerShellExecutor 샘플은 현재 게시되지 않습니다.
패키지 설치
pip install agent-framework-tools --pre
실행 시간이 초과되면 자식 프로세스 트리를 종료하도록 패키지가 설치 psutil 됩니다.
LocalShellTool을(를) 사용하세요.
LocalShellTool 는 호스트에서 직접 명령을 실행합니다. 기본값은 영구 셸, 30초 시간 제한, 64KiB 출력 잘림, 작업 디렉터리 제한 및 모든 명령에 대한 승인입니다.
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())
각 호출이 새 프로세스에서 실행되어야 하는 경우에 사용합니다 mode="stateless" .
AGENT_FRAMEWORK_SHELL 환경 변수 또는 생성자 인수를 shell 사용하여 확인된 셸을 재정의합니다.
Important
LocalShellTool 는 샌드박스가 아닙니다. 승인은 기본 보안 경계입니다. 승인을 사용하지 않도록 설정하려면 .acknowledge_unsafe=True
다음을 사용하여 명령 제한 ShellPolicy
ShellPolicy 는 실행 전에 정규식 허용 및 거부 목록을 적용합니다. 거부 규칙이 우선합니다.
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
명령 정책은 보안 경계가 아닌 사용 가능성 사전 필터입니다. 셸 구문, 별칭, 변수, 인터프리터 및 인코딩된 페이로드는 간단한 패턴 일치를 무시할 수 있습니다.
ShellEnvironmentProvider 추가
ShellEnvironmentProvider 셸 패밀리, 버전, 운영 체제, 작업 디렉터리 및 선택한 CLI 버전을 검색한 다음 에이전트가 실행되기 전에 해당 정보를 삽입합니다. 기본 프로브 목록은 git, node및 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)
DockerShellTool을(를) 사용하세요.
DockerShellTool 에는 Docker 또는 Podman이 PATH필요합니다. 기본값은 네트워킹을 사용하지 않도록 설정하고, 루트가 아닌 사용자로 실행하고, 읽기 전용 루트 파일 시스템을 사용하고, 기능을 삭제하고, 메모리를 512MiB로 제한하고, 컨테이너를 256개 프로세스로 제한합니다.
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)
기본 이미지는 .입니다 mcr.microsoft.com/azurelinux/base/core:3.0. Podman을 사용하도록 전달 docker_binary="podman" 합니다. 전용 실행 가능 DockerShellTool 샘플은 현재 게시되지 않습니다.
실행 계층 선택
| 시나리오 | Tool | 격리 경계 |
|---|---|---|
| 신뢰할 수 있는 개발 명령 | LocalShellTool |
호스트 프로세스의 승인 |
| 신뢰할 수 없는 셸 명령 | DockerShellTool |
기본 격리 플래그가 있는 OCI 컨테이너 |
| 셸 없이 신뢰할 수 없는 생성된 코드 | 하이퍼라이트 CodeAct | 하이퍼라이트 마이크로VM |
Go는 로컬 셸 실행 및 환경 검색을 tool/shelltool제공합니다.
로컬 셸 도구 사용을 참조하세요.
DockerShellTool 지침은 현재 Go에 사용할 수 없습니다.
Harness 에이전트에서 셸 도구 사용
일반 에이전트와 HarnessAgent 동일한 두 부분으로 구성된 셸 설정을 사용합니다. 실행기 함수를 도구로 등록하고 모델이 셸, 운영 체제, 작업 디렉터리 및 CLI 버전 컨텍스트를 수신해야 하는 경우를 추가 ShellEnvironmentProvider 합니다.
HarnessAgent 는 셸 실행기를 만들거나 소유하지 않습니다.
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 기본값은 이름 run_shell 및 .로 설정됩니다 requireApproval: true.
LocalShellExecutor 기본값은 영구 모드, 출력 스트림당 64KiB 상한, 시간 제한 없음 이 예제에서는 권장되는 30초를 LocalShellExecutor.DefaultTimeout명시적으로 사용합니다.
사용자 세션당 하나의 영구 실행기를 만들고 세션이 종료되면 삭제합니다. 작업 디렉터리, 환경, 셸 기록, 백그라운드 작업 및 명령 큐가 공유되므로 사용자 또는 동시 대화 간에 공유하지 마세요.
ShellPolicy 는 사전 필터일 뿐입니다. 승인을 사용하도록 설정하고, 최소 권한 자격 증명을 사용하며, 명령에 더 강력한 격리 경계가 필요한 경우를 선호 DockerShellExecutor 합니다.
셸 도구는 시험판 Microsoft.Agents.AI.Tools.Shell 패키지에서 사용할 수 있습니다.
HarnessAgent에서 사용할 수 있습니다.Microsoft.Agents.AI.Harness
일반 에이전트의 경우 셸 함수 client.get_shell_tool(func=shell.as_function()) 를 만들고 별도로 추가 ShellEnvironmentProvider 합니다.
create_harness_agent 는 다음을 전달할 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)이며 노출 as_function()해야 합니다. 팩터리는 셸 도구를 ShellEnvironmentProvider 추가하고 클라이언트가 구현할 때만 추가합니다 SupportsShellTool. 그렇지 않으면 경고를 기록하고 둘 다 건너뜁니다.
shell_environment_provider_options 는 선택 사항이며 .와 함께 shell_executor만 사용됩니다.
LocalShellTool 기본값은 영구 모드, 30초 시간 제한, 64KiB 결합 출력, 작업 디렉터리 다시 고정 및 approval_mode="always_require". Harness 도구 승인은 기본적으로 사용하도록 설정되어 있으므로 다음을 AgentSession 전달합니다 run. 호출자는 실행기 수명 주기를 소유합니다. 사용자 세션당 하나의 영구 도구를 사용 async with 하거나 호출 close()하고 만듭니다. 사용자 또는 동시 대화 간에 변경 가능한 셸 상태를 공유하지 마세요.
호스트 셸은 샌드박스가 아닙니다. 승인을 사용하도록 설정하고, 최소 권한 자격 증명을 사용하고, 컨테이너 격리에 사용합니다 DockerShellTool . 승인을 사용하지 않도록 설정하려면 보안 approval_mode="never_require"acknowledge_unsafe=TrueShellPolicy 경계가 아닙니다.
create_harness_agent 는 .에서 릴리스됩니다 agent-framework-core. 셸 통합은 시험판 agent-framework-tools 패키지에서 제공되며 사용하도록 설정된 경우 내보내집니다 ExperimentalWarning .
패키지된 Go Harness는 현재 사용할 수 없습니다. 일반 Go 에이전트에서 직접 로컬 셸 도구 및 환경 공급자를 작성합니다.