Com a ferramenta de shell, os modelos podem trabalhar em um ambiente de terminal completo. A API Responses oferece suporte à execução de comandos de shell tanto localmente quanto em um ambiente hospedado.
Com essa ferramenta, os modelos podem executar comandos em um dos seguintes ambientes:
A ferramenta de shell está disponível na API Responses. A API chat completions não oferece essa ferramenta.
Executar comandos arbitrários de shell pode ser perigoso. Sempre execute os comandos em um ambiente isolado e registre a atividade da ferramenta para fins de auditoria. Sempre que possível, permita ou bloqueie comandos específicos.
O Shell hospedado é uma opção nativa e fácil de usar para tarefas que exigem processamento mais elaborado e determinístico, desde cálculos até o processamento de conteúdo multimídia.
Use container_auto para que a OpenAI provisione e gerencie um contêiner para a solicitação.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19curl -L 'https://api.openai.com/v1/responses' \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "gpt-6-astra",
"tools": [
{ "type": "shell", "environment": { "type": "container_auto" } }
],
"input": [
{
"type": "message",
"role": "user",
"content": [
{ "type": "input_text", "text": "Execute: ls -lah /mnt/data && python --version && node --version" }
]
}
],
"tool_choice": "auto"
}'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23import OpenAI from "openai";
const client = new OpenAI();
const response = await client.responses.create({
model: "gpt-6-astra",
tools: [{ type: "shell", environment: { type: "container_auto" } }],
input: [
{
type: "message",
role: "user",
content: [
{
type: "input_text",
text: "Execute: ls -lah /mnt/data && python --version && node --version",
},
],
},
],
tool_choice: "auto",
});
console.log(response.output_text);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
tools=[{"type": "shell", "environment": {"type": "container_auto"}}],
input=[
{
"type": "message",
"role": "user",
"content": [
{
"type": "input_text",
"text": "Execute: ls -lah /mnt/data && python --version && node --version",
}
],
}
],
tool_choice="auto",
)
print(response.output_text)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
tool := responses.ToolUnionParam{OfShell: &responses.FunctionShellToolParam{
Environment: responses.FunctionShellToolEnvironmentUnionParam{OfContainerAuto: &responses.ContainerAutoParam{}},
}}
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Tools: []responses.ToolUnionParam{tool},
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Execute: ls -lah /mnt/data && python --version && node --version")},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.responses.ContainerAuto;
import com.openai.models.responses.FunctionShellTool;
import com.openai.models.responses.ResponseCreateParams;
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("Run ls -lah /mnt/data, then show the Python and Node.js versions.")
.addTool(
FunctionShellTool.builder().environment(ContainerAuto.builder().build()).build())
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
input: "Run ls -lah /mnt/data, then show the Python and Node.js versions.",
tools: [
{
type: :shell,
environment: { type: :container_auto }
}
]
)
puts(response.output_text)
- O ambiente de execução atualmente usa
Debian 12 como base, mas isso pode mudar.
- O diretório de trabalho padrão é
/mnt/data.
/mnt/data está sempre disponível e é o caminho com suporte para armazenar artefatos que os usuários podem baixar.
- O Shell hospedado não oferece suporte a sessões TTY interativas.
- Os comandos do Shell hospedado são executados sem
sudo.
- Você pode executar serviços dentro do contêiner quando o fluxo de trabalho exigir.
Atualmente, as linguagens pré-instaladas incluem:
- Python
3.11
- Node.js
22.16
- Java
17.0
- PHP
8.2
- Ruby
3.1
- Go
1.23
Se precisar de um ambiente que permaneça ativo durante um fluxo de trabalho iterativo, crie um contêiner e faça referência a ele nas chamadas seguintes à API Responses.
1
2
3
4
5
6
7
8curl -L 'https://api.openai.com/v1/containers' \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"name": "analysis-container",
"memory_limit": "1g",
"expires_after": { "anchor": "last_active_at", "minutes": 20 }
}'
1
2
3
4
5
6
7
8
9
10
11import OpenAI from "openai";
const client = new OpenAI();
const container = await client.containers.create({
name: "analysis-container",
memory_limit: "1g",
expires_after: { anchor: "last_active_at", minutes: 20 },
});
console.log(container.id);
1
2
3
4
5
6
7
8
9
10
11from openai import OpenAI
client = OpenAI()
container = client.containers.create(
name="analysis-container",
memory_limit="1g",
expires_after={"anchor": "last_active_at", "minutes": 20},
)
print(container.id)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
)
func main() {
client := openai.NewClient()
container, err := client.Containers.New(context.Background(), openai.ContainerNewParams{
Name: "analysis-container",
MemoryLimit: openai.ContainerNewParamsMemoryLimit1g,
ExpiresAfter: openai.ContainerNewParamsExpiresAfter{
Anchor: "last_active_at",
Minutes: 20,
},
})
if err != nil {
panic(err)
}
fmt.Println(container.ID)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.containers.ContainerCreateParams;
var container =
client
.containers()
.create(
ContainerCreateParams.builder()
.name("analysis")
.expiresAfter(
ContainerCreateParams.ExpiresAfter.builder()
.anchor(ContainerCreateParams.ExpiresAfter.Anchor.LAST_ACTIVE_AT)
.minutes(20)
.build())
.build());
System.out.println(container.id());
1
2
3
4
5
6
7
8
9
10require "openai"
client = OpenAI::Client.new
container = client.containers.create(
name: "analysis", expires_after: {
anchor: :last_active_at,
minutes: 20
}
)
puts(container.id)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16curl -L 'https://api.openai.com/v1/responses' \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "gpt-6-astra",
"tools": [
{
"type": "shell",
"environment": {
"type": "container_reference",
"container_id": "cntr_08f3d96c87a585390069118b594f7481a088b16cda7d9415fe"
}
}
],
"input": "List files in the container and show disk usage."
}'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import OpenAI from "openai";
const client = new OpenAI();
const response = await client.responses.create({
model: "gpt-6-astra",
tools: [
{
type: "shell",
environment: {
type: "container_reference",
container_id: "cntr_08f3d96c87a585390069118b594f7481a088b16cda7d9415fe",
},
},
],
input: "List files in the container and show disk usage.",
});
console.log(response.output_text);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15response = client.responses.create(
model="gpt-6-astra",
tools=[
{
"type": "shell",
"environment": {
"type": "container_reference",
"container_id": container.id,
},
}
],
input="List files in the container and show disk usage.",
)
print(response.output_text)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
tool := responses.ToolUnionParam{OfShell: &responses.FunctionShellToolParam{
Environment: responses.FunctionShellToolEnvironmentUnionParam{OfContainerReference: &responses.ContainerReferenceParam{ContainerID: "cntr_08f3d96c87a585390069118b594f7481a088b16cda7d9415fe"}},
}}
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Tools: []responses.ToolUnionParam{tool},
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("List files in the container and show disk usage.")},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.responses.FunctionShellTool;
import com.openai.models.responses.ResponseCreateParams;
String containerId = "cntr_08f3d96c87a585390069118b594f7481a088b16cda7d9415fe";
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("List files in the container and show disk usage.")
.addTool(FunctionShellTool.builder().containerReferenceEnvironment(containerId).build())
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
input: "List files in the container and show disk usage.",
tools: [
{
type: :shell,
environment: {
type: :container_reference,
container_id: "cntr_08f3d96c87a585390069118b594f7481a088b16cda7d9415fe"
}
}
]
)
puts(response.output_text)
Habilidades são pacotes reutilizáveis e versionados que você pode montar em ambientes de Shell hospedado. A montagem define quais habilidades ficam disponíveis. Ao executar o shell, o modelo decide se deve invocá-las.
Consulte o guia de habilidades para saber como enviar e versionar esses pacotes.
1
2
3
4
5
6
7
8
9
10curl -L 'https://api.openai.com/v1/containers' \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"name": "skill-container",
"skills": [
{ "type": "skill_reference", "skill_id": "skill_4db6f1a2c9e73508b41f9da06e2c7b5f" },
{ "type": "skill_reference", "skill_id": "openai-spreadsheets", "version": "latest" }
]
}'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20import OpenAI from "openai";
const client = new OpenAI();
const container = await client.containers.create({
name: "skill-container",
skills: [
{
type: "skill_reference",
skill_id: "skill_4db6f1a2c9e73508b41f9da06e2c7b5f",
},
{
type: "skill_reference",
skill_id: "openai-spreadsheets",
version: "latest",
},
],
});
console.log(container.id);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22# Replace the illustrative IDs and URLs below with your own resource values.
from openai import OpenAI
client = OpenAI()
skill_id = "skill_123"
container = client.containers.create(
name="skill-container",
skills=[
{
"type": "skill_reference",
"skill_id": skill_id,
},
{
"type": "skill_reference",
"skill_id": "openai-spreadsheets",
"version": "latest",
},
],
)
print(container.id)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
container, err := client.Containers.New(context.Background(), openai.ContainerNewParams{
Name: "skill-container",
Skills: []openai.ContainerNewParamsSkillUnion{
{OfSkillReference: &responses.SkillReferenceParam{SkillID: "skill_4db6f1a2c9e73508b41f9da06e2c7b5f"}},
{OfSkillReference: &responses.SkillReferenceParam{SkillID: "openai-spreadsheets", Version: openai.String("latest")}},
},
})
if err != nil {
panic(err)
}
fmt.Println(container.ID)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.containers.ContainerCreateParams;
import com.openai.models.responses.SkillReference;
String skillId = "skill_4db6f1a2c9e73508b41f9da06e2c7b5f";
var container =
client
.containers()
.create(
ContainerCreateParams.builder()
.name("skill-container")
.addSkill(SkillReference.builder().skillId(skillId).build())
.addSkill(
SkillReference.builder()
.skillId("openai-spreadsheets")
.version("latest")
.build())
.build());
System.out.println(container.id());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19require "openai"
client = OpenAI::Client.new
container = client.containers.create(
name: "skill-container",
skills: [
{
type: :skill_reference,
skill_id: "skill_4db6f1a2c9e73508b41f9da06e2c7b5f"
},
{
type: :skill_reference,
skill_id: "openai-spreadsheets",
version: "latest"
}
]
)
puts(container.id)
Por padrão, os contêineres hospedados não têm acesso de saída à rede.
Para habilitar esse acesso:
- Um administrador deve configurar a lista de permissão da sua organização no painel.
- Você deve definir explicitamente
network_policy no ambiente do contêiner ao fazer a solicitação.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25curl -L 'https://api.openai.com/v1/responses' \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-6-astra",
"tool_choice": "required",
"tools": [
{
"type": "shell",
"environment": {
"type": "container_auto",
"network_policy": {
"type": "allowlist",
"allowed_domains": ["pypi.org", "files.pythonhosted.org", "github.com"]
}
}
}
],
"input": [
{
"role": "user",
"content": "In the container, pip install httpx beautifulsoup4, fetch release pages, and write /mnt/data/release_digest.md."
}
]
}'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29import OpenAI from "openai";
const client = new OpenAI();
const response = await client.responses.create({
model: "gpt-6-astra",
tool_choice: "required",
tools: [
{
type: "shell",
environment: {
type: "container_auto",
network_policy: {
type: "allowlist",
allowed_domains: ["pypi.org", "files.pythonhosted.org", "github.com"],
},
},
},
],
input: [
{
role: "user",
content:
"In the container, pip install httpx beautifulsoup4, fetch release pages, and write /mnt/data/release_digest.md.",
},
],
});
console.log(response.output_text);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
tool_choice="required",
tools=[
{
"type": "shell",
"environment": {
"type": "container_auto",
"network_policy": {
"type": "allowlist",
"allowed_domains": [
"pypi.org",
"files.pythonhosted.org",
"github.com",
],
},
},
}
],
input=[
{
"role": "user",
"content": "In the container, pip install httpx beautifulsoup4, fetch release pages, and write /mnt/data/release_digest.md.",
}
],
)
print(response.output_text)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
tool := responses.ToolUnionParam{OfShell: &responses.FunctionShellToolParam{
Environment: responses.FunctionShellToolEnvironmentUnionParam{OfContainerAuto: &responses.ContainerAutoParam{
NetworkPolicy: responses.ContainerAutoNetworkPolicyUnionParam{OfAllowlist: &responses.ContainerNetworkPolicyAllowlistParam{
AllowedDomains: []string{"pypi.org", "files.pythonhosted.org", "github.com"},
}},
}},
}}
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
ToolChoice: responses.ResponseNewParamsToolChoiceUnion{OfToolChoiceMode: openai.Opt(responses.ToolChoiceOptionsRequired)},
Tools: []responses.ToolUnionParam{tool},
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("In the container, pip install httpx beautifulsoup4, fetch release pages, and write /mnt/data/release_digest.md.")},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.responses.ContainerAuto;
import com.openai.models.responses.ContainerNetworkPolicyAllowlist;
import com.openai.models.responses.FunctionShellTool;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.ToolChoiceOptions;
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("Fetch release pages and write /mnt/data/release_digest.md.")
.toolChoice(ToolChoiceOptions.REQUIRED)
.addTool(
FunctionShellTool.builder()
.environment(
ContainerAuto.builder()
.networkPolicy(
ContainerNetworkPolicyAllowlist.builder()
.addAllowedDomain("pypi.org")
.addAllowedDomain("files.pythonhosted.org")
.addAllowedDomain("github.com")
.build())
.build())
.build())
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
input: "Fetch release pages and write /mnt/data/release_digest.md.",
tool_choice: :required,
tools: [
{
type: :shell,
environment: {
type: :container_auto,
network_policy: {
type: :allowlist,
allowed_domains: ["pypi.org", "files.pythonhosted.org", "github.com"]
}
}
}
]
)
puts(response.output_text)
Incluir domínios na lista de permissão traz riscos de segurança, como a exfiltração de dados por injeção de prompt.
Inclua apenas domínios em que você confia e que invasores não possam usar para receber dados roubados.
Antes de usar a ferramenta, leia com atenção a seção Riscos e segurança abaixo.
Quando vários controles se aplicam:
- A lista de permissão da organização define o conjunto completo de domínios que podem constar em
allowed_domains.
- A política
network_policy de cada solicitação restringe ainda mais o acesso.
- A solicitação falha se
allowed_domains incluir domínios fora da lista de permissão da organização.
Enquanto estiverem ativos, os contêineres hospedados usados pelo Shell hospedado e pelo Code Interpreter podem gravar o estado temporário do aplicativo no sistema de arquivos do contêiner, que usa armazenamento efêmero em blocos. Os dados do contêiner são excluídos quando ele expira ou quando você o exclui explicitamente.
Para saber mais sobre os controles de dados, consulte ZDR e residência de dados.
O Shell hospedado pode gerar arquivos para download. Use as mesmas APIs de contêineres e arquivos do Code Interpreter para recuperar os artefatos gravados em /mnt/data.
Para manter o conteúdo e os arquivos disponíveis apenas durante o ciclo de vida do contêiner hospedado, você pode incluir os arquivos diretamente na solicitação e montar no contêiner as habilidades fornecidas também dessa forma.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55INLINE_ZIP=$(base64 -i ./csv_insights.zip)
REPORT_CSV=$(base64 -i ./report.csv)
CONTAINER_ID=$(
curl -sL 'https://api.openai.com/v1/containers' \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"name": "inline-skill-container",
"skills": [
{
"type": "inline",
"name": "csv-insights",
"description": "Summarize CSV files and produce a markdown report.",
"source": {
"type": "base64",
"media_type": "application/zip",
"data": "'"$INLINE_ZIP"'"
}
}
]
}' | jq -r '.id'
)
curl -L 'https://api.openai.com/v1/responses' \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "gpt-6-astra",
"tools": [
{
"type": "shell",
"environment": {
"type": "container_reference",
"container_id": "'"$CONTAINER_ID"'"
}
}
],
"input": [
{
"role": "user",
"content": [
{
"type": "input_file",
"filename": "report.csv",
"file_data": "data:text/csv;base64,'"${REPORT_CSV}"'"
},
{
"type": "input_text",
"text": "Use the csv-insights skill to summarize report.csv."
}
]
}
]
}'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56import fs from "fs";
import OpenAI from "openai";
const client = new OpenAI();
const inlineZip = fs
.readFileSync("fixtures/csv_insights.zip")
.toString("base64");
const reportCsv = fs.readFileSync("fixtures/report.csv").toString("base64");
const container = await client.containers.create({
name: "inline-skill-container",
skills: [
{
type: "inline",
name: "csv-insights",
description: "Summarize CSV files and produce a markdown report.",
source: {
type: "base64",
media_type: "application/zip",
data: inlineZip,
},
},
],
});
const response = await client.responses.create({
model: "gpt-6-astra",
tools: [
{
type: "shell",
environment: {
type: "container_reference",
container_id: container.id,
},
},
],
input: [
{
role: "user",
content: [
{
type: "input_file",
filename: "report.csv",
file_data: `data:text/csv;base64,${reportCsv}`,
},
{
type: "input_text",
text: "Use the csv-insights skill to summarize report.csv.",
},
],
},
],
});
console.log(response.output_text);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57import base64
from openai import OpenAI
client = OpenAI()
with open("csv_insights.zip", "rb") as f:
inline_zip = base64.b64encode(f.read()).decode("utf-8")
with open("report.csv", "rb") as f:
base64_string = base64.b64encode(f.read()).decode("utf-8")
container = client.containers.create(
name="inline-skill-container",
skills=[
{
"type": "inline",
"name": "csv-insights",
"description": "Summarize CSV files and produce a markdown report.",
"source": {
"type": "base64",
"media_type": "application/zip",
"data": inline_zip,
},
}
],
)
response = client.responses.create(
model="gpt-6-astra",
tools=[
{
"type": "shell",
"environment": {
"type": "container_reference",
"container_id": container.id,
},
}
],
input=[
{
"role": "user",
"content": [
{
"type": "input_file",
"filename": "report.csv",
"file_data": f"data:text/csv;base64,{base64_string}",
},
{
"type": "input_text",
"text": "Use the csv-insights skill to summarize report.csv.",
},
],
}
],
)
print(response.output_text)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77var bundle bytes.Buffer
archive := zip.NewWriter(&bundle)
file, err := archive.Create("csv-insights/SKILL.md")
if err != nil {
log.Fatal(err)
}
if _, err := file.Write([]byte("---\nname: csv-insights\ndescription: Summarize CSV files.\n---\nRead the CSV and produce a Markdown report of totals.\n")); err != nil {
log.Fatal(err)
}
if err := archive.Close(); err != nil {
log.Fatal(err)
}
client := openai.NewClient()
inlineZip := base64.StdEncoding.EncodeToString(bundle.Bytes())
reportCSV := base64.StdEncoding.EncodeToString([]byte("product,revenue\nA,120\nB,80\n"))
container, err := client.Containers.New(context.Background(), openai.ContainerNewParams{
Name: "inline-skill-container",
Skills: []openai.ContainerNewParamsSkillUnion{
{
OfInline: &responses.InlineSkillParam{
Name: "csv-insights",
Description: "Summarize CSV files.",
Source: responses.InlineSkillSourceParam{
Data: inlineZip,
},
},
},
},
})
if err != nil {
log.Fatal(err)
}
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Tools: []responses.ToolUnionParam{
{
OfShell: &responses.FunctionShellToolParam{
Environment: responses.FunctionShellToolEnvironmentUnionParam{
OfContainerReference: &responses.ContainerReferenceParam{
ContainerID: container.ID,
},
},
},
},
},
Input: responses.ResponseNewParamsInputUnion{
OfInputItemList: []responses.ResponseInputItemUnionParam{
{
OfMessage: &responses.EasyInputMessageParam{
Role: responses.EasyInputMessageRoleUser,
Content: responses.EasyInputMessageContentUnionParam{
OfInputItemContentList: []responses.ResponseInputContentUnionParam{
{
OfInputFile: &responses.ResponseInputFileParam{
Filename: openai.String("report.csv"),
FileData: openai.String("data:text/csv;base64," + reportCSV),
},
},
{
OfInputText: &responses.ResponseInputTextParam{
Text: "Use the csv-insights skill to summarize report.csv.",
},
},
},
},
},
},
},
},
})
if err != nil {
log.Fatal(err)
}
if response.Status != responses.ResponseStatusCompleted {
log.Fatalf("Response ended with status %s", response.Status)
}
fmt.Println(response.OutputText())
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.containers.ContainerCreateParams;
import com.openai.models.responses.*;
import java.util.*;
var bundle = new java.io.ByteArrayOutputStream();
try (var zip = new java.util.zip.ZipOutputStream(bundle)) {
zip.putNextEntry(new java.util.zip.ZipEntry("csv-insights/SKILL.md"));
zip.write(
"---\nname: csv-insights\ndescription: Summarize CSV files.\n---\nRead the CSV and produce a Markdown report of totals.\n"
.getBytes(java.nio.charset.StandardCharsets.UTF_8));
zip.closeEntry();
}
var client = OpenAIOkHttpClient.fromEnv();
var inlineZip = Base64.getEncoder().encodeToString(bundle.toByteArray());
var reportCsv =
Base64.getEncoder()
.encodeToString(
"product,revenue\nA,120\nB,80\n".getBytes(java.nio.charset.StandardCharsets.UTF_8));
var container =
client
.containers()
.create(
ContainerCreateParams.builder()
.name("inline-skill-container")
.addSkill(
InlineSkill.builder()
.name("csv-insights")
.description("Summarize CSV files.")
.source(InlineSkillSource.builder().data(inlineZip).build())
.build())
.build());
var response =
client
.responses()
.create(
ResponseCreateParams.builder()
.model("gpt-6-astra")
.addTool(
FunctionShellTool.builder()
.containerReferenceEnvironment(container.id())
.build())
.inputOfResponse(
List.of(
ResponseInputItem.ofMessage(
ResponseInputItem.Message.builder()
.role(ResponseInputItem.Message.Role.USER)
.addContent(
ResponseInputFile.builder()
.filename("report.csv")
.fileData("data:text/csv;base64," + reportCsv)
.build())
.addInputTextContent(
"Use the csv-insights skill to summarize report.csv.")
.build())))
.build());
if (response.status().filter(ResponseStatus.COMPLETED::equals).isEmpty())
throw new IllegalStateException(
"Response ended with status " + response.status().orElse(null));
response.output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50require "base64"
require "openai"
client = OpenAI::Client.new
inline_zip = Base64.strict_encode64(File.binread("csv_insights.zip"))
base64_string = Base64.strict_encode64(File.binread("report.csv"))
container = client.containers.create(
name: "inline-skill-container",
skills: [
{
type: :inline,
name: "csv-insights",
description: "Summarize CSV files and produce a markdown report.",
source: {
type: :base64,
media_type: "application/zip",
data: inline_zip
}
}
]
)
response = client.responses.create(
model: "gpt-6-astra",
tools: [
{
type: :shell,
environment: {
type: :container_reference,
container_id: container.id
}
}
],
input: [
{
role: :user,
content: [
{
type: :input_file,
filename: "report.csv",
file_data: "data:text/csv;base64,#{base64_string}"
},
{
type: :input_text,
text: "Use the csv-insights skill to summarize report.csv."
}
]
}
]
)
puts(response.output_text)
Nas solicitações seguintes, passe o mesmo container_id com container_reference. As habilidades montadas e os arquivos existentes no contêiner continuam disponíveis enquanto ele estiver ativo.
Ao concluir o trabalho, você pode excluir o contêiner explicitamente, sem esperar que ele expire por inatividade.
curl -L -X DELETE 'https://api.openai.com/v1/containers/container_id' \
-H "Authorization: Bearer $OPENAI_API_KEY"
import OpenAI from "openai";
const client = new OpenAI();
await client.containers.delete("container_id");
console.log("Container deleted");
# Replace the illustrative IDs and URLs below with your own resource values.
from openai import OpenAI
client = OpenAI()
container_id = "cntr_123"
client.containers.delete(container_id)
print("Container deleted")
package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
)
func main() {
client := openai.NewClient()
if err := client.Containers.Delete(context.Background(), "container_id"); err != nil {
panic(err)
}
fmt.Println("Container deleted")
}
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
String containerId = "container_id";
client.containers().delete(containerId);
System.out.println("Container deleted.");
require "openai"
client = OpenAI::Client.new
client.containers.delete("container_id")
puts("Container deleted")
Use domain_secrets quando um domínio da lista allowed_domains exigir cabeçalhos de autorização privados, como Authorization: Bearer <token>.
Cada entrada inclui:
- Domínio de destino
- Nome descritivo do segredo
- Valor do segredo
Durante a execução:
- O modelo e o ambiente de execução veem nomes que representam as credenciais (por exemplo,
$API_KEY) em vez das credenciais reais.
- O componente auxiliar de autenticação substitui esses nomes pelos valores reais dos segredos apenas para destinos aprovados.
- Os valores reais dos segredos não ficam armazenados nos servidores da API nem aparecem no contexto visível para o modelo.
Assim, o assistente pode chamar serviços protegidos com menor risco de vazamento de credenciais.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32curl -L 'https://api.openai.com/v1/responses' \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-6-astra",
"input": [
{
"role": "user",
"content": "Use curl to call https://httpbin.org/headers with header Authorization: Bearer $API_KEY. Tell me what you see in the final text response."
}
],
"tool_choice": "required",
"tools": [
{
"type": "shell",
"environment": {
"type": "container_auto",
"network_policy": {
"type": "allowlist",
"allowed_domains": ["httpbin.org"],
"domain_secrets": [
{
"domain": "httpbin.org",
"name": "API_KEY",
"value": "debug-secret-123"
}
]
}
}
}
]
}'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36import OpenAI from "openai";
const client = new OpenAI();
const response = await client.responses.create({
model: "gpt-6-astra",
input: [
{
role: "user",
content:
"Use curl to call https://httpbin.org/headers with header Authorization: Bearer $API_KEY. Tell me what you see in the final text response.",
},
],
tool_choice: "required",
tools: [
{
type: "shell",
environment: {
type: "container_auto",
network_policy: {
type: "allowlist",
allowed_domains: ["httpbin.org"],
domain_secrets: [
{
domain: "httpbin.org",
name: "API_KEY",
value: "debug-secret-123",
},
],
},
},
},
],
});
console.log(response.output_text);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
input=[
{
"role": "user",
"content": "Use curl to call https://httpbin.org/headers with header Authorization: Bearer $API_KEY. Tell me what you see in the final text response.",
}
],
tool_choice="required",
tools=[
{
"type": "shell",
"environment": {
"type": "container_auto",
"network_policy": {
"type": "allowlist",
"allowed_domains": ["httpbin.org"],
"domain_secrets": [
{
"domain": "httpbin.org",
"name": "API_KEY",
"value": "debug-secret-123",
}
],
},
},
}
],
)
print(response.output_text)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
tool := responses.ToolUnionParam{OfShell: &responses.FunctionShellToolParam{
Environment: responses.FunctionShellToolEnvironmentUnionParam{OfContainerAuto: &responses.ContainerAutoParam{
NetworkPolicy: responses.ContainerAutoNetworkPolicyUnionParam{OfAllowlist: &responses.ContainerNetworkPolicyAllowlistParam{
AllowedDomains: []string{"httpbin.org"},
DomainSecrets: []responses.ContainerNetworkPolicyDomainSecretParam{{
Domain: "httpbin.org",
Name: "API_KEY",
Value: "debug-secret-123",
}},
}},
}},
}}
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
ToolChoice: responses.ResponseNewParamsToolChoiceUnion{OfToolChoiceMode: openai.Opt(responses.ToolChoiceOptionsRequired)},
Tools: []responses.ToolUnionParam{tool},
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Use curl to call https://httpbin.org/headers with header Authorization: Bearer $API_KEY. Tell me what you see in the final text response.")},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.responses.ContainerAuto;
import com.openai.models.responses.ContainerNetworkPolicyAllowlist;
import com.openai.models.responses.ContainerNetworkPolicyDomainSecret;
import com.openai.models.responses.FunctionShellTool;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.ToolChoiceOptions;
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input(
"Run exactly this one shell command, without additional commands:\n"
+ "curl -q -sS -o /dev/null -w '%{http_code}' "
+ "-H 'Authorization: Bearer $API_KEY' https://httpbin.org/status/204\n"
+ "Keep the header single-quoted so $API_KEY remains literal. "
+ "Return only the HTTP status code. Never print request headers or secret values.")
.toolChoice(ToolChoiceOptions.REQUIRED)
.addTool(
FunctionShellTool.builder()
.environment(
ContainerAuto.builder()
.networkPolicy(
ContainerNetworkPolicyAllowlist.builder()
.addAllowedDomain("httpbin.org")
.addDomainSecret(
ContainerNetworkPolicyDomainSecret.builder()
.domain("httpbin.org")
.name("API_KEY")
.value(System.getenv("OPENAI_EXAMPLE_DOMAIN_SECRET"))
.build())
.build())
.build())
.build())
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
input: "Use curl to call https://httpbin.org/headers with an " \
'"Authorization: Bearer $API_KEY" header.',
tool_choice: :required,
tools: [
{
type: :shell,
environment: {
type: :container_auto,
network_policy: {
type: :allowlist,
allowed_domains: ["httpbin.org"],
domain_secrets: [
{
domain: "httpbin.org",
name: "API_KEY",
value: "debug-secret-123"
}
]
}
}
}
]
)
puts(response.output_text)
Para continuar o trabalho no mesmo ambiente hospedado, reutilize o contêiner e passe previous_response_id.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17curl -L 'https://api.openai.com/v1/responses' \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "gpt-6-astra",
"previous_response_id": "resp_2a8e5c9174d63b0f18a4c572de9f64a1b3c76d508e12f9ab47",
"tools": [
{
"type": "shell",
"environment": {
"type": "container_reference",
"container_id": "cntr_f19c2b51e4a06793d82d54a7be0fc9154d3361ab28ce7f6041"
}
}
],
"input": "Read /mnt/data/top5.csv and report the top candidate."
}'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21import OpenAI from "openai";
const client = new OpenAI();
const response = await client.responses.create({
model: "gpt-6-astra",
previous_response_id:
"resp_2a8e5c9174d63b0f18a4c572de9f64a1b3c76d508e12f9ab47",
tools: [
{
type: "shell",
environment: {
type: "container_reference",
container_id: "cntr_f19c2b51e4a06793d82d54a7be0fc9154d3361ab28ce7f6041",
},
},
],
input: "Read /mnt/data/top5.csv and report the top candidate.",
});
console.log(response.output_text);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
previous_response_id="resp_2a8e5c9174d63b0f18a4c572de9f64a1b3c76d508e12f9ab47",
tools=[
{
"type": "shell",
"environment": {
"type": "container_reference",
"container_id": "cntr_f19c2b51e4a06793d82d54a7be0fc9154d3361ab28ce7f6041",
},
}
],
input="Read /mnt/data/top5.csv and report the top candidate.",
)
print(response.output_text)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
tool := responses.ToolUnionParam{OfShell: &responses.FunctionShellToolParam{
Environment: responses.FunctionShellToolEnvironmentUnionParam{OfContainerReference: &responses.ContainerReferenceParam{ContainerID: "cntr_f19c2b51e4a06793d82d54a7be0fc9154d3361ab28ce7f6041"}},
}}
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
PreviousResponseID: openai.String("resp_2a8e5c9174d63b0f18a4c572de9f64a1b3c76d508e12f9ab47"),
Tools: []responses.ToolUnionParam{tool},
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Read /mnt/data/top5.csv and report the top candidate.")},
})
if err != nil {
panic(err)
}
fmt.Println(response.OutputText())
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.responses.FunctionShellTool;
import com.openai.models.responses.ResponseCreateParams;
String responseId = "resp_2a8e5c9174d63b0f18a4c572de9f64a1b3c76d508e12f9ab47";
String containerId = "cntr_f19c2b51e4a06793d82d54a7be0fc9154d3361ab28ce7f6041";
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("Read /mnt/data/top5.csv and report the top candidate.")
.previousResponseId(responseId)
.addTool(FunctionShellTool.builder().containerReferenceEnvironment(containerId).build())
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.message().stream())
.flatMap(message -> message.content().stream())
.flatMap(content -> content.outputText().stream())
.forEach(text -> System.out.println(text.text()));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
input: "Read /mnt/data/top5.csv and report the top candidate.",
previous_response_id: "resp_2a8e5c9174d63b0f18a4c572de9f64a1b3c76d508e12f9ab47",
tools: [
{
type: :shell,
environment: {
type: :container_reference,
container_id: "cntr_f19c2b51e4a06793d82d54a7be0fc9154d3361ab28ce7f6041"
}
}
]
)
puts(response.output_text)
O Shell hospedado e o shell local usam os mesmos tipos de itens de saída. Cada execução do shell é representada por um par de itens de saída:
shell_call: comandos solicitados pelo modelo.
shell_call_output: saída dos comandos e informações sobre como a execução terminou.
1
2
3
4
5
6
7
8
9
10{
"type": "shell_call",
"call_id": "call_9d14ac6f2b73485e91c0f4da6e1b27c8",
"action": {
"commands": ["ls -l"],
"timeout_ms": 120000,
"max_output_length": 4096
},
"status": "in_progress"
}
Você também pode executar comandos de shell no seu próprio ambiente de execução local. Para isso, execute as ações shell_call e envie shell_call_output de volta ao modelo.
Use esse modo quando precisar controlar o sistema de arquivos ou acessar ferramentas internas. Defina o tipo da ferramenta como shell e o tipo de ambiente da ferramenta como local. Se estiver migrando da ferramenta descontinuada local_shell, atualize a solicitação e trate os itens shell_call e shell_call_output no loop de execução.
1
2
3
4
5
6
7
8
9curl -L 'https://api.openai.com/v1/responses' \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"model": "gpt-6-astra",
"instructions": "The local bash shell environment is on Mac.",
"input": "find me the largest pdf file in ~/Documents",
"tools": [{ "type": "shell", "environment": { "type": "local" } }]
}'
1
2
3
4
5
6
7
8
9
10
11
12import OpenAI from "openai";
const client = new OpenAI();
const response = await client.responses.create({
model: "gpt-6-astra",
instructions: "The local bash shell environment is on Mac.",
input: "find me the largest pdf file in ~/Documents",
tools: [{ type: "shell", environment: { type: "local" } }],
});
console.log(response);
1
2
3
4
5
6
7
8
9
10
11
12from openai import OpenAI
client = OpenAI()
response = client.responses.create(
model="gpt-6-astra",
instructions="The local bash shell environment is on Mac.",
input="find me the largest pdf file in ~/Documents",
tools=[{"type": "shell", "environment": {"type": "local"}}],
)
print(response)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
tool := responses.ToolUnionParam{OfShell: &responses.FunctionShellToolParam{
Environment: responses.FunctionShellToolEnvironmentUnionParam{OfLocal: &responses.LocalEnvironmentParam{}},
}}
response, err := client.Responses.New(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Instructions: openai.String("The local bash shell environment is on Mac."),
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("find me the largest pdf file in ~/Documents")},
Tools: []responses.ToolUnionParam{tool},
})
if err != nil {
panic(err)
}
fmt.Println(response.Output)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.JsonValue;
import com.openai.models.responses.ResponseCreateParams;
import java.util.List;
import java.util.Map;
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("Find the largest PDF in ~/Documents.")
.instructions("The local shell environment is macOS.")
.putAdditionalBodyProperty(
"tools",
JsonValue.from(
List.of(Map.of("type", "shell", "environment", Map.of("type", "local")))))
.build();
client.responses().create(params).output().stream()
.flatMap(item -> item.shellCall().stream())
.flatMap(call -> call.action().commands().stream())
.forEach(System.out::println);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16require "openai"
client = OpenAI::Client.new
response = client.responses.create(
model: "gpt-6-astra",
instructions: "The local shell environment is macOS.",
input: "Find the largest PDF in ~/Documents.",
tools: [
{
type: :shell,
environment: { type: :local }
}
]
)
puts(response.output)
Ao receber itens de saída do tipo shell_call:
- Examine cada comando antes de executá-lo. Use uma lista de comandos permitidos e exija aprovação para comandos destrutivos ou que acessem dados sensíveis.
- Execute os comandos aprovados em um ambiente isolado, com limites explícitos de acesso ao sistema de arquivos e à rede, de uso de memória e de tempo de execução. Aplique seu próprio limite de tempo mesmo que o modelo sugira um.
- Registre os comandos e seus resultados para fins de auditoria.
- Capture
stdout, stderr e o resultado da execução.
- Retorne os resultados como
shell_call_output na solicitação seguinte.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64import { spawn } from "node:child_process";
class ShellExecutor {
constructor(defaultTimeoutMs = 60_000) {
this.defaultTimeoutMs = defaultTimeoutMs;
}
async run(cmd, timeoutMs) {
return new Promise((resolve, reject) => {
const child = spawn("sh", ["-c", cmd], {
detached: true,
stdio: ["ignore", "pipe", "pipe"],
});
let stdout = "";
let stderr = "";
let timedOut = false;
let outputLimited = false;
const stop = () => {
try {
process.kill(-child.pid, "SIGKILL");
} catch (error) {
if (error.code !== "ESRCH") reject(error);
}
};
const timer = setTimeout(() => {
timedOut = true;
stop();
}, timeoutMs ?? this.defaultTimeoutMs);
const collect = (stream, append) => {
let bytes = 0;
stream.setEncoding("utf8");
stream.on("data", (chunk) => {
bytes += Buffer.byteLength(chunk);
if (bytes > 1024 * 1024) {
outputLimited = true;
clearTimeout(timer);
stop();
} else {
append(chunk);
}
});
};
collect(child.stdout, (chunk) => {
stdout += chunk;
});
collect(child.stderr, (chunk) => {
stderr += chunk;
});
child.on("error", (error) => {
clearTimeout(timer);
reject(error);
});
child.on("close", (code) => {
clearTimeout(timer);
resolve({
stdout,
stderr,
exitCode: outputLimited ? 1 : code,
timedOut,
});
});
});
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38from contextlib import suppress
from dataclasses import dataclass
import os
import signal
import subprocess
@dataclass
class CmdResult:
stdout: str
stderr: str
exit_code: int | None
timed_out: bool
class ShellExecutor:
def __init__(self, default_timeout: float = 60):
self.default_timeout = default_timeout
def run(self, cmd: str, timeout: float | None = None) -> CmdResult:
t = self.default_timeout if timeout is None else timeout
p = subprocess.Popen(
cmd,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
start_new_session=True,
)
try:
out, err = p.communicate(timeout=t)
return CmdResult(out, err, p.returncode, False)
except subprocess.TimeoutExpired:
# Stop the shell and its children before draining their output pipes.
with suppress(ProcessLookupError):
os.killpg(p.pid, signal.SIGKILL)
out, err = p.communicate()
return CmdResult(out, err, p.returncode, True)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58import (
"bytes"
"os/exec"
"syscall"
"time"
)
type shellResult struct {
Stdout string `json:"stdout"`
Stderr string `json:"stderr"`
ExitCode int `json:"exit_code"`
TimedOut bool `json:"timed_out"`
}
type shellExecutor struct {
DefaultTimeout time.Duration
}
func (e shellExecutor) run(command string, timeout time.Duration) shellResult {
if timeout == 0 {
timeout = e.DefaultTimeout
}
cmd := exec.Command("sh", "-c", command)
// Own a process group so cancellation also stops the shell's children.
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Start(); err != nil {
return shellResult{Stderr: err.Error(), ExitCode: -1}
}
// Keep the deadline active until output pipes close, even if the shell
// exits before a background child does.
expired := make(chan struct{})
timer := time.AfterFunc(timeout, func() {
_ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
close(expired)
})
err := cmd.Wait()
result := shellResult{Stdout: stdout.String(), Stderr: stderr.String()}
if !timer.Stop() {
<-expired
result.TimedOut = true
result.ExitCode = -1
return result
}
if err != nil {
if exitError, ok := err.(*exec.ExitError); ok {
result.ExitCode = exitError.ExitCode()
return result
}
if result.Stderr == "" {
result.Stderr = err.Error()
}
result.ExitCode = -1
}
return result
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42require "open3"
class ShellExecutor
Result = Data.define(:stdout, :stderr, :exit_code, :timed_out)
def initialize(default_timeout: 60)
@default_timeout = default_timeout
end
def run(command, timeout: @default_timeout)
Open3.popen3("sh", "-c", command, pgroup: true) do |stdin, stdout, stderr, wait_thread|
stdin.close
stdout_reader = Thread.new { stdout.read }
stderr_reader = Thread.new { stderr.read }
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout
finished = [wait_thread, stdout_reader, stderr_reader].all? do |thread|
remaining = [deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC), 0].max
thread.join(remaining)
end
terminate_process_group(wait_thread) unless finished
Result.new(
stdout: stdout_reader.value,
stderr: stderr_reader.value,
exit_code: wait_thread.value.exitstatus || -1,
timed_out: !finished
)
end
end
private
def terminate_process_group(wait_thread)
Process.kill("TERM", -wait_thread.pid)
wait_thread.join(1)
Process.kill("KILL", -wait_thread.pid)
rescue Errno::ESRCH
nil
ensure
wait_thread.join
end
end
Estes exemplos exigem Linux ou macOS. Quando o tempo limite de execução de um comando se esgota, o código encerra todo o grupo de processos desse comando.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22{
"type": "shell_call_output",
"call_id": "call_3ef1b8c79a4d6520f9e3ab7d41c68f25",
"max_output_length": 4096,
"output": [
{
"stdout": "...",
"stderr": "...",
"outcome": {
"type": "exit",
"exit_code": 0
}
},
{
"stdout": "...",
"stderr": "...",
"outcome": {
"type": "timeout"
}
}
]
}
Se um comando falhar ou exceder o tempo limite, retorne o resultado da execução para que o modelo possa decidir se deve tentar novamente. Consulte o aviso de desativação da ferramenta anterior.
Se você usa o SDK de Agentes, pode passar sua própria implementação do executor de shell para a função auxiliar da ferramenta de shell.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42import { Agent, run, withTrace, shellTool } from "@openai/agents";
class LocalShell {
async run(action) {
return {
output: [
{
stdout: "Shell is not available. Needs to be implemented first.",
stderr: "",
outcome: {
type: "exit",
exitCode: 1,
},
},
],
maxOutputLength: action.maxOutputLength,
};
}
}
const shell = new LocalShell();
const agent = new Agent({
name: "Shell Assistant",
model: "gpt-6-astra",
instructions:
"You can execute shell commands to inspect the repository. Keep responses concise and include command output when helpful.",
tools: [
shellTool({
shell,
needsApproval: true,
onApproval: async (_ctx, _approvalItem) => {
return { approve: true };
},
}),
],
});
await withTrace("shell-tool-example", async () => {
const result = await run(agent, "Show the Node.js version.");
console.log(`\nFinal response:\n${result.finalOutput}`);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50from agents import (
Agent,
Runner,
ShellCallOutcome,
ShellCommandOutput,
ShellCommandRequest,
ShellResult,
ShellTool,
)
class LocalShell:
async def __call__(self, request: ShellCommandRequest) -> ShellResult:
action = request.data.action
return ShellResult(
output=[
ShellCommandOutput(
command="(not executed)",
stdout="Shell is not available. Needs to be implemented first.",
stderr="",
outcome=ShellCallOutcome(type="exit", exit_code=1),
)
],
max_output_length=action.max_output_length,
)
shell_tool = ShellTool(
executor=LocalShell(),
needs_approval=True,
on_approval=lambda _ctx, _approval_item: {"approve": True},
)
agent = Agent(
name="Shell Assistant",
model="gpt-6-astra",
instructions="You can execute shell commands to inspect the repository. Keep responses concise and include command output when helpful.",
tools=[shell_tool],
)
async def main():
result = await Runner.run(agent, input="Show the Node.js version.")
print(f"\nFinal response:\n{result.final_output}")
if __name__ == "__main__":
import asyncio
asyncio.run(main())
Os repositórios do SDK contêm exemplos que você pode executar.
Exemplo da ferramenta de shell em TypeScript
Exemplo em TypeScript de como usar a ferramenta de shell no SDK de Agentes.
Exemplo da ferramenta de shell em Python
Exemplo em Python de como usar a ferramenta de shell no SDK de Agentes.
- Se um comando exceder o tempo limite de execução, retorne um resultado que indique que o tempo se esgotou e inclua a saída parcial capturada.
- Se
max_output_length estiver presente em shell_call, inclua esse campo em shell_call_output.
- Não dependa de comandos interativos. A ferramenta de shell deve executar os comandos sem interação.
- Preserve a saída dos comandos que terminarem com um código diferente de zero para que o modelo possa avaliar como se recuperar do erro.
Habilitar o acesso à rede na API de Contêineres amplia o que os contêineres podem fazer e traz riscos significativos para a segurança e a governança de dados. Por padrão, o acesso à rede fica desabilitado. Se você o habilitar, restrinja o acesso de saída aos domínios confiáveis necessários para a tarefa.
Contêineres com acesso à rede podem interagir com serviços de terceiros e registros de pacotes. Isso traz riscos como vazamento de dados, uso indevido de ferramentas por injeção de prompt e acesso acidental além dos limites previstos. Esses riscos aumentam quando as políticas são amplas, estáticas ou aplicadas de maneira inconsistente.
Qualquer conteúdo externo obtido pela rede pode conter instruções ocultas que tentam manipular o comportamento do modelo. Trate o conteúdo de rede de fontes não confiáveis como potencialmente malicioso e exija cuidado redobrado em ações que possam modificar dados ou sistemas.
Permita apenas domínios em que você confia e cuja manutenção você faz ativamente. Tenha cuidado com intermediários e agregadores que encaminham solicitações a outros serviços. Antes de incluí-los na lista de domínios permitidos, verifique como eles tratam e retêm os dados.
Revise o comando da ferramenta de shell e a saída da execução. Ambos aparecem na resposta da API Responses. Registre, em cada sessão, os hosts solicitados e os destinos reais das conexões de saída. Revise os logs periodicamente para verificar se os padrões de acesso correspondem ao esperado, detectar desvios e identificar comportamentos suspeitos.
Os controles de dados da OpenAI se aplicam dentro dos limites da OpenAI. Já os dados transmitidos pela rede a serviços de terceiros estão sujeitos às políticas de retenção desses serviços. Verifique se os endpoints externos atendem aos seus requisitos de residência, retenção e conformidade.