La herramienta shell permite a los modelos trabajar en un entorno de terminal completo. Admitimos la ejecución de shell tanto localmente como en entornos alojados a través de la API Responses.
La herramienta shell permite a los modelos ejecutar comandos mediante cualquiera de estas opciones:
Shell está disponible a través de la API Responses . No está disponible a través de la API para completar chats.
Ejecutar comandos de shell arbitrarios puede ser peligroso. Ejecuta siempre los comandos en un sandbox,
aplica listas de permitidos o bloqueados cuando sea posible y registra la actividad de la herramienta para
realizar auditorías.
La terminal alojada en la nube es una opción nativa y sencilla para tareas que requieren un procesamiento determinista con más capacidades, desde realizar cálculos hasta trabajar con contenido multimedia.
Usa container_auto cuando quieras que OpenAI aprovisione y administre un contenedor para la solicitud.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 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_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
23 import 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
23 from 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
25 package 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
19 import 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
15 require "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)
El entorno de ejecución se basa actualmente en Debian 12 y puede cambiar con el tiempo.
El directorio de trabajo predeterminado es /mnt/data.
/mnt/data siempre está presente y es la ruta admitida para los artefactos que los usuarios pueden descargar.
La terminal alojada en la nube no admite sesiones TTY interactivas.
Los comandos de la terminal alojada en la nube no se ejecutan con sudo.
Puedes ejecutar servicios dentro del contenedor cuando tu flujo de trabajo los necesite.
Los lenguajes preinstalados actualmente incluyen:
Python 3.11
Node.js 22.16
Java 17.0
PHP 8.2
Ruby 3.1
Go 1.23
Si necesitas un entorno de larga duración para flujos de trabajo iterativos, crea un contenedor y luego haz referencia a él en las llamadas posteriores a la API Responses.
1
2
3
4
5
6
7
8 curl -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
11 import 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
11 from 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
24 package 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
18 import 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
10 require "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
16 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": "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
19 import 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
15 response = 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
25 package 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
19 import 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
18 require "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)
Las habilidades son paquetes reutilizables con versiones que puedes montar en entornos de terminal alojada en la nube. Esto define las habilidades disponibles y, al ejecutar shell, el modelo decide si las invoca.
Consulta la guía de habilidades para obtener detalles sobre cómo cargarlas y gestionar sus versiones.
1
2
3
4
5
6
7
8
9
10 curl -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
20 import 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
24 package 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
22 import 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
19 require "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)
Los contenedores alojados no tienen acceso de salida a la red de forma predeterminada.
Para habilitarlo:
Un administrador debe configurar la lista de permitidos de tu organización en el panel.
Debes configurar explícitamente network_policy en el entorno del contenedor en tu solicitud.
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 curl -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
29 import 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
32 from 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
30 package 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
32 import 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
22 require "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 dominios en una lista de permitidos introduce riesgos de seguridad, como la exfiltración de datos mediante
inyección de prompts. Incluye únicamente dominios en los que confíes y que
los atacantes no puedan usar para recibir datos exfiltrados. Revisa detenidamente la sección Riesgos
y seguridad más adelante antes de usar esta herramienta.
Cuando hay varios controles:
La lista de permitidos de tu organización define el conjunto completo de allowed_domains.
La configuración de network_policy de cada solicitud restringe aún más el acceso.
Las solicitudes fallan si allowed_domains incluye dominios que no están en la lista de permitidos de tu organización.
Los contenedores alojados que utilizan la terminal alojada en la nube y el intérprete de código pueden escribir el estado temporal de la aplicación en el sistema de archivos del contenedor (basado en almacenamiento efímero en bloques) mientras el contenedor está activo. Los datos del contenedor se eliminan cuando este caduca o se elimina explícitamente.
Para obtener más detalles sobre los controles de datos, consulta ZDR y residencia de datos .
La terminal alojada en la nube puede generar archivos descargables. Usa las mismas API de contenedores y archivos que el intérprete de código para recuperar los artefactos escritos en /mnt/data.
Si quieres que el contenido y los archivos sean efímeros durante el ciclo de vida del entorno alojado, puedes incluir los archivos directamente en la solicitud y montar habilidades incluidas directamente en el contenedor.
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 INLINE_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
56 import 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
57 import 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 require "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)
Para las solicitudes posteriores, pasa el mismo container_id con container_reference. Las habilidades montadas y los archivos existentes en el contenedor permanecen disponibles mientras el contenedor esté activo.
Puedes eliminar explícitamente el contenedor cuando termines el trabajo, en lugar de esperar a que expire por inactividad.
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();
const deleted = await client.containers.delete("container_id");
console.log(deleted); # Replace the illustrative IDs and URLs below with your own resource values.
from openai import OpenAI
client = OpenAI()
container_id = "cntr_123"
deleted = client.containers.delete(container_id)
print(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("Deleted container_id")
Secretos de dominio
Usa domain_secrets cuando un dominio de tu lista allowed_domains requiera encabezados de autorización privados, como Authorization: Bearer <token>.
Cada entrada de secreto incluye:
Dominio de destino
Nombre descriptivo del secreto
Valor del secreto
Durante la ejecución:
El modelo y el entorno de ejecución ven nombres de marcadores de posición (por ejemplo, $API_KEY) en lugar de las credenciales reales.
El sidecar de traducción de autenticación aplica los valores reales de los secretos solo para los destinos aprobados.
Los valores reales de los secretos no se conservan en los servidores de la API ni aparecen en el contexto visible para el modelo.
Esto permite que el asistente llame a servicios protegidos y reduce el riesgo de filtraciones.
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 curl -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
36 import 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
35 from 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
35 package 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 import 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(
"Use curl to call https://httpbin.org/status/204 with an "
+ "Authorization: Bearer $API_KEY header. Print 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
30 require "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 el trabajo en el mismo entorno alojado, reutiliza el contenedor y pasa previous_response_id.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 curl -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
21 import 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
20 from 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
26 package 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
22 import 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
19 require "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)
La terminal alojada en la nube y el shell local usan los mismos tipos de elementos de salida. Las ejecuciones de Shell se representan mediante pares de elementos de salida:
shell_call: comandos solicitados por el modelo.
shell_call_output: salida de los comandos y resultados de su finalización.
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"
}
También puedes ejecutar comandos de shell en tu propio entorno de ejecución local: ejecuta las acciones shell_call y envía shell_call_output de vuelta al modelo.
Usa este modo cuando necesites control total sobre el entorno de ejecución, el acceso al sistema de archivos o las herramientas internas existentes.
1
2
3
4
5
6
7
8
9 curl -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
12 import 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
12 from 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
26 package 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
22 import 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
16 require "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)
Cuando recibas elementos de salida shell_call:
Ejecuta los comandos solicitados en tu entorno de ejecución.
Captura stdout, stderr y el resultado.
Devuelve los resultados como shell_call_output en la siguiente solicitud.
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 import { exec as execCallback } from "node:child_process";
import { promisify } from "node:util";
const exec = promisify(execCallback);
class ShellExecutor {
constructor(defaultTimeoutMs = 60_000) {
this.defaultTimeoutMs = defaultTimeoutMs;
}
async run(cmd, timeoutMs) {
const timeout = timeoutMs ?? this.defaultTimeoutMs;
try {
const { stdout, stderr } = await exec(cmd, { timeout });
return { stdout, stderr, exitCode: 0, timedOut: false };
} catch (error) {
const timedOut = Boolean(error?.killed) && error?.signal === "SIGTERM";
const exitCode = timedOut ? null : (error?.code ?? null);
return {
stdout: error?.stdout ?? "",
stderr: error?.stderr ?? String(error),
exitCode,
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 @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 = timeout or self .default_timeout
p = subprocess.Popen(
cmd,
shell = True ,
stdout = subprocess. PIPE ,
stderr = subprocess. PIPE ,
text = True ,
)
try :
out, err = p.communicate( timeout = t)
return CmdResult(out, err, p.returncode, False )
except subprocess.TimeoutExpired:
p.kill()
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 package main
import (
"bytes"
"context"
"fmt"
"os/exec"
"time"
)
type shellResult struct {
Stdout string
Stderr string
ExitCode int
TimedOut bool
}
type shellExecutor struct {
DefaultTimeout time.Duration
}
func (e shellExecutor) run(command string, timeout time.Duration) shellResult {
if timeout == 0 {
timeout = e.DefaultTimeout
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
cmd := exec.CommandContext(ctx, "sh", "-c", command)
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err := cmd.Run()
result := shellResult{Stdout: stdout.String(), Stderr: stderr.String()}
if ctx.Err() == context.DeadlineExceeded {
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
}
func main() {
executor := shellExecutor{DefaultTimeout: time.Minute}
fmt.Println(executor.run("printf shell-executor-ready", 0))
} 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 require "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 }
finished = wait_thread.join(timeout)
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.nil?
)
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
puts(ShellExecutor.new.run("printf shell-executor-ready"))
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"
}
}
]
}
Para conocer los detalles de la migración desde la versión anterior, consulta la guía de shell local anterior.
Si usas Agents SDK , puedes pasar tu propia implementación del ejecutor de shell a la función auxiliar de la herramienta 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
42 import { 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 ( ` \n Final 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
50 from 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())
Puedes encontrar ejemplos funcionales en los repositorios del SDK.
Ejemplo de la herramienta Shell - TypeScript
Ejemplo en TypeScript de la herramienta Shell en Agents SDK.
Ejemplo de la herramienta Shell - Python
Ejemplo en Python de la herramienta Shell en Agents SDK.
Si un comando supera el tiempo de espera de ejecución, devuelve un resultado que indique que se agotó el tiempo e incluye la salida parcial capturada.
Si max_output_length está presente en shell_call, inclúyelo en shell_call_output.
No dependas de comandos interactivos; la ejecución de la herramienta shell debe ser no interactiva.
Conserva las salidas de los comandos que terminan con un código de salida distinto de cero para que el modelo pueda razonar sobre los pasos de recuperación.
Habilitar el acceso a la red en la API Containers ofrece una capacidad potente e introduce riesgos significativos para la seguridad y la gobernanza de datos. De forma predeterminada, el acceso a la red no está habilitado. Cuando se habilita, el acceso saliente debe limitarse estrictamente a los dominios de confianza necesarios para la tarea.
Los contenedores con acceso a la red pueden interactuar con servicios de terceros y registros de paquetes. Esto genera riesgos como la filtración de datos, el uso indebido de herramientas provocado por la inyección de prompts y el acceso accidental más allá de los límites previstos. Estos riesgos aumentan cuando las políticas son amplias, estáticas o se aplican de manera inconsistente.
Comprende los riesgos de inyección de prompts en el contenido obtenido de la red
Cualquier contenido externo obtenido a través de la red puede contener instrucciones ocultas destinadas a manipular el comportamiento del modelo. Trata el contenido de red que no sea de confianza como potencialmente malicioso y exige mayor precaución en las acciones que puedan modificar datos o sistemas.
Permite solo dominios en los que confíes y cuyo mantenimiento realices activamente. Ten precaución con los intermediarios y agregadores que actúan como proxy de otros servicios, y revisa sus prácticas de manejo y retención de datos antes de agregarlos a tu lista de dominios permitidos.
Revisa el comando de la herramienta shell y la salida de su ejecución, que se incluyen en la respuesta de la API Responses. Registra los hosts solicitados y los destinos reales de las conexiones salientes de cada sesión. Revisa periódicamente los registros para verificar que los patrones de acceso coincidan con lo esperado, detectar desviaciones e identificar comportamientos sospechosos.
Los controles de datos de OpenAI se aplican dentro de los límites de OpenAI. Sin embargo, los datos transmitidos a servicios de terceros a través de conexiones de red están sujetos a las políticas de retención de datos de esos servicios. Asegúrate de que los puntos de acceso externos cumplan tus requisitos de residencia, retención y cumplimiento.