Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Бета-версия пакета Python agent-framework-tools предоставляет инструменты для выполнения команд в оболочке и работы с окружением в пространстве имён agent_framework.tools.
| инструмент | Используйте его, когда |
|---|---|
LocalShellTool |
Команды считаются доверенными или одобрены по отдельности и должны выполняться в хостовой среде процесса агента. |
DockerShellTool |
Для команд оболочки, созданных моделью, требуется изоляция OCI-container. |
ShellEnvironmentProvider |
Модель нуждается в активном семействе оболочки, операционной системе, рабочем каталоге и установленных версиях ИНТЕРФЕЙСА командной строки. |
ShellPolicy |
Перед утверждением или выполнением необходимо предварительно отфильтровать список разрешений или запретов. |
Предупреждение
Выполнение оболочки может изменять файлы, запускать процессы, получать доступ к учетным данным и взаимодействовать с внешними системами. Используйте наименее привилегированный уровень выполнения, поддерживающий задачу.
Установите пакет
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-секундный тайм-аут, усечение вывода до 64 КиБ и повторная привязка к рабочему каталогу. Вызовы агента через as_function() по умолчанию требуют одобрения. Прямые вызовы к run() не запрашивают подтверждение.
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())
Прямые вызовы run() возвращают ShellResult с отдельными полями stdout, stderr, exit_code, duration_ms, truncated и timed_out. При запуске оболочки через OpenAI Responses или хостинг Foundry Agent Framework сохраняет стандартный вывод, стандартную ошибку, результаты завершения и результаты тайм-аута при продолжении у провайдера. Ненулевое завершение остается неудачным, стандартный поток ошибок остается отдельным, и время ожидания не отображается как успешное завершение.
Элементы расшифровки оболочки, размещенные у поставщика OpenAI, остаются информационными, даже если настроен локальный исполнитель оболочки. Только корректно сформированный явный local_shell_call или вызов оболочки, помеченный символом environment.type="local", попадает в локальную функцию и процесс утверждения. Настройка LocalShellTool сама по себе не приводит к тому, что вызовы оболочки, размещённой у поставщика, выполняются на хосте.
Используйте mode="stateless" , когда каждый вызов должен выполняться в новом процессе.
AGENT_FRAMEWORK_SHELL Используйте переменную среды или shell аргумент конструктора, чтобы переопределить разрешенную оболочку.
Постоянные команды сериализуются. После переполнения выходных данных или после истечения времени ожидания, которое делает сеанс невосстанавливаемым, команда из очереди запускается в свежей оболочке и теряет локальные переменные оболочки, функции и изменения рабочего каталога из завершенного сеанса. Корректно прерванное время ожидания POSIX может сохранить существующую оболочку и её состояние. В Windows при тайм-ауте всегда закрывается текущая постоянная оболочка командной строки, даже если прерванная команда выдает маркер завершения.
Вызов close() отклоняет команды, поставленные в очередь до или во время завершения работы, вместо перезапуска оболочки. Последующий вызов может открыть новый сеанс.
Это важно
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",
# 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())],
)
Предупреждение
В этом примере отключено подтверждение со стороны человека, и он подходит только для изолированной одноразовой среды без конфиденциальных или ценных данных. Подстановки команд, такие как $(...) и обратные кавычки, могут передавать простые шаблоны из allow-list. Шаблоны, которые соответствуют только началу команды, также могут разрешать дополнительные операции. Используйте отдельно примененные разрешения изоляции и ограничения для рабочей среды. Проверка человеком может служить дополнительной проверкой, но не изолирует оболочку.
Используйте предпочтительно строковые шаблоны. Python компилирует строки с помощью механизма regex и применяет ограничение в одну секунду к каждому совпадению. Тайм-аут списка запрета отклоняет команду, а тайм-аут списка разрешений не дает разрешения. Предварительно скомпилированный regex.Pattern использует то же ограничение. Предварительно скомпилированная стандартная библиотека re.Pattern сохраняет свои флаги, но не может быть прервана, поэтому избегайте дорогостоящих или неоднозначных выражений в этой форме.
Добавить ShellEnvironmentProvider
ShellEnvironmentProvider проверяет семейство оболочки, версию, операционную систему, рабочий каталог и выбранные версии CLI, а затем внедряет эти сведения перед запуском агента. Список проб по умолчанию : git, nodepythonи 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
Для DockerShellTool требуется Docker или Podman на PATH. Параметры по умолчанию отключают сеть, запускают контейнер от имени непривилегированного пользователя, используют корневую файловую систему только для чтения, сбрасывают привилегии, ограничивают память до 512 МиБ и ограничивают количество процессов в контейнере до 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изображение. Передайте docker_binary="podman", чтобы использовать Podman. Отдельный готовый к запуску пример DockerShellTool в настоящее время не опубликован.
Используйте unsafe_extra_run_args только в том случае, если специальный параметр конструктора не существует.
Эти маркеры добавляются без изменений в команду Docker или Podman и могут ослаблять или обходить настроенные ограничения изоляции и ресурсов. Держите их под контролем разработчика и никогда не заполняйте их из выходных данных пользователя или модели. Используйте соответствующий DockerShellTool параметр конструктора вместо этого, если такой параметр доступен.
Выбор уровня выполнения
| Scenario | инструмент | Граница изоляции |
|---|---|---|
| Команды доверенной разработки | LocalShellTool |
Нет; Утверждение человека включено по умолчанию |
| Недоверенные команды оболочки | DockerShellTool |
Контейнер OCI с флагами изоляции по умолчанию |
| Недоверенный сгенерированный код без оболочки | Hyperlight CodeAct | Гиперлегкая микро-ВМ |
Go предоставляет локальный запуск команд оболочки и анализ окружения через tool/shelltool. См. Использование локального инструмента оболочки.
DockerShellTool В настоящее время руководство недоступно для Go.
Использование инструментов оболочки с Harness Agent
Обычные агенты и HarnessAgent используют одну и ту же двухкомпонентную настройку оболочки: зарегистрируйте функцию исполнителя как инструмент и добавьте ShellEnvironmentProvider, если модель должна получать контекст оболочки, операционной системы, рабочего каталога и версии CLI.
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 по умолчанию используются постоянный режим, ограничение в 64 KiB на каждый выходной поток и отсутствие тайм-аута; в примере явно используется рекомендуемый 30-секундный LocalShellExecutor.DefaultTimeout.
ShellEnvironmentProviderOptions по умолчанию проверяет git, dotnet, node, python и docker, с тайм-аутом в пять секунд для каждой проверки.
Создайте один постоянный исполнитель для каждого сеанса пользователя и удалите его при завершении сеанса. Не используйте это совместно для нескольких пользователей или одновременных диалогов, поскольку рабочий каталог, среда, история команд оболочки, фоновые задания и очередь команд являются общими.
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 требует явного согласия и должен предоставлять доступ к as_function(). Фабрика добавляет инструмент shell и ShellEnvironmentProvider только если клиент реализует SupportsShellTool; в противном случае она записывает предупреждение в журнал и пропускает их оба.
shell_environment_provider_options является необязательным и используется только с shell_executor.
Для LocalShellTool по умолчанию используются постоянный режим, 30-секундный тайм-аут, объединённый вывод размером 64 КиБ, повторная привязка рабочего каталога и 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 по умолчанию не добавляет инструмент оболочки. Добавьте локальное средство оболочки в agent.Config.Tools, а его поставщик среды — в agent.Config.ContextProviders перед передачей конфигурации в harness.AgentConfig. Вспомогательный компонент сохраняет эти компоненты, предоставляемые вызывающим пользователем, и добавляет значения по умолчанию обвязки.
См. статью «Использование локального средства оболочки» для требований к настройке и утверждению.