シェルツールを使うと、モデルは一通りの機能を備えたターミナル環境で作業できます。Responses API では、ローカル環境とホスト型環境のどちらでもシェルツールを利用できます。
モデルは、シェルツールを使って次のいずれかの環境でコマンドを実行できます。
シェルは Responses API で利用できます。Chat Completions API では利用できません。
任意のシェルコマンドの実行には危険が伴います。コマンドは必ずサンドボックス内で実行し、可能な限り、実行を許可するコマンドやブロックするコマンドを個別に指定してください。監査のため、ツールの実行履歴をログに記録してください。
ホスト型シェルは、計算からマルチメディアの処理まで、高度で決定論的な処理が必要なタスクに手軽に使える組み込み機能です。
リクエストに使うコンテナの作成と管理を OpenAI に任せるには、container_auto を使用します。
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)
- 現在のランタイムは
Debian 12 をベースとしており、今後変更される可能性があります。
- デフォルトの作業ディレクトリは
/mnt/data です。
/mnt/data は常に存在し、ユーザーがダウンロードできる成果物の保存先としてサポートされています。
- ホスト型シェルは対話型の TTY セッションに対応していません。
- ホスト型シェルのコマンドは
sudo では実行されません。
- ワークフローに必要なサービスをコンテナ内で実行できます。
現在、以下の言語がプリインストールされています。
- Python
3.11
- Node.js
22.16
- Java
17.0
- PHP
8.2
- Ruby
3.1
- Go
1.23
作業を繰り返すために長時間稼働する環境が必要な場合は、コンテナを作成し、以降の Responses API 呼び出しでそのコンテナを参照します。
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)
スキルは、ホスト型シェル環境にマウントできる、バージョン管理された再利用可能なバンドルです。マウントしたスキルが利用可能になり、シェルの実行時にモデルがそれらを呼び出すかどうかを判断します。
アップロードとバージョン管理の詳細は、スキルガイドを参照してください。
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)
ホスト型コンテナでは、デフォルトで外部へのネットワークアクセスが無効になっています。
有効にするには、以下の両方の設定が必要です。
- 管理者がダッシュボードで組織の許可リストを設定します。
- リクエストの送信者が、リクエスト内でコンテナ環境の
network_policy を明示的に設定します。
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)
ドメインを許可リストに追加すると、プロンプトインジェクションによってデータを外部に持ち出されるなどのセキュリティリスクが生じます。許可するのは、信頼でき、かつ攻撃者が盗んだデータの受信先として利用できないドメインに限ってください。このツールを使用する前に、以下のリスクと安全対策をよく確認してください。
複数のアクセス制御を適用する場合は、以下のルールに従います。
- 組織の許可リストによって、
allowed_domains に指定できるドメインの範囲が決まります。
- リクエスト単位の
network_policy でアクセスをさらに制限します。
allowed_domains に組織の許可リストにないドメインが含まれている場合、リクエストは失敗します。
ホスト型シェルと Code Interpreter で使用するホスト型コンテナは、稼働中にアプリケーションの状態を一時的にコンテナのファイルシステムに書き込むことがあります。このファイルシステムには一時ブロックストレージを使用します。コンテナの有効期限が切れるか、コンテナを明示的に削除すると、コンテナのデータは削除されます。
データ管理の詳細は、ZDR とデータレジデンシーを参照してください。
ホスト型シェルでは、ダウンロード可能なファイルを生成できます。/mnt/data 配下に書き込まれた成果物は、Code Interpreter と同じコンテナ API とファイル API を使って取得します。
コンテンツやファイルをホスト型コンテナの存続期間中だけ保持するには、リクエストにファイルをインラインで含め、コンテナにインラインスキルをマウントできます。
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)
以降のリクエストでは、同じ container_id を container_reference とともに渡します。コンテナの稼働中は、マウントしたスキルとコンテナ内の既存ファイルを引き続き利用できます。
作業が完了したら、非アクティブ状態が続いて有効期限が切れるのを待たずに、コンテナを明示的に削除できます。
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")
domain_secrets は、allowed_domains リスト内のドメインが Authorization: Bearer <token> のように機密情報を含む認証ヘッダーを必要とする場合に使用します。
各シークレットのエントリには、以下を含めます。
- 対象ドメイン
- 識別しやすいシークレット名
- シークレットの値
実行時の動作は以下のとおりです。
- モデルとランタイムには、実際の認証情報の代わりにプレースホルダー名(例:
$API_KEY)が表示されます。
- 認証情報の置換を担うサイドカーは、承認済みの宛先に対してのみシークレットの実際の値を適用します。
- シークレットの実際の値は API サーバーに永続化されず、モデルが参照できるコンテキストにも含まれません。
これにより、アシスタントは認証情報の漏えいリスクを抑えながら、保護されたサービスを呼び出せます。
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)
同じホスト型環境で作業を続けるには、コンテナを再利用し、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)
ホスト型シェルとローカルシェルでは、同じ種類の出力項目を使用します。シェルの実行は、次の 2 つの出力項目のペアで表されます。
shell_call:モデルが実行を要求したコマンド
shell_call_output:コマンドの出力と終了結果
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"
}
shell_call アクションを実行し、shell_call_output をモデルに返すことで、独自のローカルランタイムでもシェルコマンドを実行できます。
ファイルシステムを自分で制御する必要がある場合や、内部ツールにアクセスする必要がある場合は、このモードを使用します。ツールの種類を shell に、環境の種類を local に設定します。廃止された local_shell ツールから移行する場合は、リクエストを更新し、実行ループで shell_call と shell_call_output を処理してください。
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)
shell_call の出力項目を受け取ったら、以下の手順に従います。
- 実行前に各コマンドを確認します。許可リストを使用し、破壊的な操作を行うコマンドや機密データにアクセスするコマンドには、実行前の承認を必須とします。
- ファイルシステムとネットワークへのアクセス範囲、メモリ使用量、実行時間に明示的な制限を設けたサンドボックスで、承認済みのコマンドを実行します。モデルがタイムアウト値を提案した場合も、自分で設定したタイムアウトを適用します。
- 監査のため、コマンドとその実行結果をログに記録します。
stdout、stderr、終了結果を取得します。
- 次のリクエストで、結果を
shell_call_output として返します。
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
28import { 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
55package 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
40require "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"
}
}
]
}
コマンドが失敗した場合やタイムアウトした場合は、モデルが再試行するかどうかを判断できるよう、その結果を返してください。旧ツールの提供終了に関するお知らせを参照してください。
Agents SDK を使用している場合は、独自に実装したシェルエグゼキューターをシェルツールのヘルパーに渡せます。
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())
SDK のリポジトリには、実行可能なサンプルがあります。
Agents SDK でシェルツールを使う TypeScript のサンプルです。
Agents SDK でシェルツールを使う Python のサンプルです。
- コマンドの実行がタイムアウトした場合は、タイムアウトを示す結果と、それまでに取得した出力を返してください。
max_output_length が shell_call に指定されている場合は、その値を shell_call_output にも含めてください。
- 対話型コマンドに依存せず、シェルツールのコマンドは非対話形式で実行してください。
- モデルが復旧手順を検討できるよう、終了コードがゼロ以外の場合も出力を保持してください。
Containers API でネットワークアクセスを有効にすると、コンテナでできることが広がる一方、セキュリティとデータガバナンスに関わる重大なリスクが生じます。デフォルトでは、ネットワークアクセスは無効です。有効にする場合は、外部へのアクセスを、タスクに必要な信頼できるドメインに厳しく限定してください。
ネットワークアクセスが有効なコンテナは、サードパーティのサービスやパッケージレジストリと通信できます。そのため、データ漏えい、プロンプトインジェクションによるツールの悪用、意図した範囲を超えた偶発的なアクセスなどのリスクが生じます。ポリシーの適用範囲が広すぎる場合、ポリシーが固定されている場合、適用に一貫性がない場合には、こうしたリスクが高まります。
ネットワーク経由で取得する外部コンテンツには、モデルの動作を操るための指示が隠されている可能性があります。信頼できないネットワークコンテンツは、攻撃に使われる可能性があるものとして扱い、データやシステムを変更し得る操作には特に注意してください。
信頼でき、自分で継続的に管理しているドメインだけを許可してください。他のサービスへのアクセスを中継する仲介サービスやアグリゲーターには注意が必要です。許可リストにドメインを追加する前に、そのサービスでのデータの取り扱いと保持方法を確認してください。
Responses API のレスポンスに含まれるシェルツールのコマンドと実行出力を確認してください。セッションごとに、要求されたホストと実際の外部接続先を記録してください。定期的にログを確認し、アクセスパターンが想定どおりかを検証して、想定からのずれや不審な動作を特定してください。
OpenAI のデータ管理は、OpenAI の管理範囲内で適用されます。一方、ネットワーク接続を通じてサードパーティのサービスに送信したデータには、そのサービスのデータ保持ポリシーが適用されます。外部エンドポイントが、データレジデンシー、データ保持、コンプライアンスに関する要件を満たしていることを確認してください。