베타 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 생성자 인수를 사용하여 결정된 셸을 재정의합니다.
중요합니다
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 샘플은 현재 게시되지 않습니다.
실행 계층 선택
| Scenario | 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명시적으로 사용합니다. 는 기본적으로 , , , 및 를 프로브하며, 각 프로브의 시간 제한은 5초입니다.
사용자 세션당 하나의 영구 실행기를 만들고 세션이 종료되면 삭제합니다. 작업 디렉터리, 환경, 셸 기록, 백그라운드 작업 및 명령 큐가 공유되므로 사용자 또는 동시 대화 간에 공유하지 마세요.
ShellPolicy 는 사전 필터일 뿐입니다. 승인을 사용하도록 설정하고, 최소 권한 자격 증명을 사용하며, 명령에 더 강력한 격리 경계가 필요한 경우를 선호 DockerShellExecutor 합니다.
셸 도구는 시험판 Microsoft.Agents.AI.Tools.Shell 패키지에서 사용할 수 있습니다.
HarnessAgent은(는) Microsoft.Agents.AI.Harness에서 사용할 수 있습니다.
기본 에이전트의 경우 client.get_shell_tool(func=shell.as_function())로 셸 함수를 만들고 ShellEnvironmentProvider를 별도로 추가합니다.
shell_executor를 전달하면 create_harness_agent가 두 단계를 모두 수행합니다:
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()를 노출해야 합니다. 팩토리는 클라이언트가 SupportsShellTool를 구현하는 경우에만 셸 도구와 ShellEnvironmentProvider를 추가하며, 그렇지 않으면 경고를 로그에 기록하고 둘 다 건너뜁니다.
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=True이 필요하며, ShellPolicy만으로는 보안 경계가 아닙니다.
create_harness_agent는 agent-framework-core에서 출시됩니다. 셸 통합은 사전 릴리스 agent-framework-tools 패키지에서 제공되며, 사용하도록 설정하면 ExperimentalWarning를 출력합니다.
패키지된 Go Harness는 현재 사용할 수 없습니다. 일반 Go 에이전트에서 직접 로컬 셸 도구 및 환경 공급자를 작성합니다.