---
title: 在自行託管的伺服器上呼叫 API
url: https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/call-the-api
description: 將官方 SDK、cURL 或 ant CLI 指向您自行託管的 Claude Managed Agents 伺服器，啟動工作階段，並讀取代理的回覆。然後視需要連接您自己的 worker。
---

<Note>
  若要申請自行託管的 Claude Managed Agents 存取權，請聯絡您的 Anthropic 客戶團隊。
</Note>

自行託管的 Claude Managed Agents 伺服器具有與 Anthropic 託管的 [Managed Agents](https://platform.claude.com/docs/zh-TW/managed-agents/overview) 服務相同的 API。您的程式碼可以使用官方 SDK、cURL 或 `ant` CLI 來呼叫它。有兩件事會改變：「base URL」（基礎 URL）是您伺服器的位址，而 API 金鑰是您的[伺服器 API 金鑰](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted#the-three-credentials)，而不是 Claude API 金鑰。

若要在瀏覽器中建立代理並執行工作階段，請參閱[使用您伺服器上的主控台](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/console)。

## 先決條件

* **執行中的伺服器：** 請依照[開始使用自行託管的 Claude Managed Agents](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/quickstart)或[在 Kubernetes 上部署](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/kubernetes)啟動一個伺服器。
* **伺服器的存取權：** 如果伺服器由其他人執行，請向他們詢問其位址及驗證方式。如果他們使用單一登入，請參閱[讓開發人員從終端機登入](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/configure#let-developers-log-in-from-a-terminal)。
* **`ant` CLI：** 請依照 [CLI 快速入門](https://platform.claude.com/docs/zh-TW/cli-sdks-libraries/cli/quickstart#installation)所示，安裝最新版本。

## 將您的用戶端指向您的伺服器

SDK 與 `ant` CLI 會從 `ANTHROPIC_BASE_URL` 讀取伺服器位址，並從 `ANTHROPIC_API_KEY` 讀取 API 金鑰。請在您為此伺服器保留的終端機中設定這兩個變數。

對於[快速入門堆疊](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/quickstart)，請從存放您 `.env` 檔案的目錄執行以下指令：

```bash
export ANTHROPIC_BASE_URL=http://127.0.0.1:8400
export ANTHROPIC_API_KEY=$(grep '^CMA_SERVER__API_KEY=' .env | cut -d= -f2)
```

在 Kubernetes 上，請依照[執行工作階段](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/kubernetes#run-a-session)所示設定這兩個變數，並讓其 port-forward 保持執行。

在該終端機中，每個 `ant` 指令（例如 `ant beta:agents list`）都會連線到您的伺服器。請勿在該終端機中執行 Claude Code 或 `docker compose`，因為它們會讀取相同的變數。

## 啟動工作階段

工作階段需要一個代理的 ID 和一個環境的 ID：

* **代理：** 依照[建立代理](https://platform.claude.com/docs/zh-TW/managed-agents/agent-setup#create-an-agent)所示建立一個代理，並將您的用戶端指向您的伺服器。如果您傳送原始 HTTP 請求，請將 URL 中的 `https://api.anthropic.com` 替換為 `$ANTHROPIC_BASE_URL`。
* **環境：** 快速入門堆疊會建立一個名為 `compose-sandbox` 的環境，而 Helm chart 會建立一個名為 `default` 的環境。`ant beta:environments list` 會印出每個環境的名稱和 ID。對於您自己的 worker，則是管理 CLI 在[執行您自己的 worker](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/call-the-api#point-a-worker-at-your-server)中印出的 ID。

將您的兩個 ID 放入您的 shell，取代這些範例 ID：

```bash
export AGENT_ID=agent_011CYm1BLqPXpQRk5khsSXrs
export ENVIRONMENT_ID=env_01595EKxaaTTGwwY3kyXdtbs
```

以下範例會建立一個工作階段，並在同一個請求中傳送其第一則訊息：

<CodeGroup>
  ```bash cURL
  curl -sS --fail-with-body "$ANTHROPIC_BASE_URL/v1/sessions" \
    -H "x-api-key: $ANTHROPIC_API_KEY" \
    -H "anthropic-version: 2026-10-21" \
    -H "anthropic-beta: managed-agents-2026-04-01" \
    -H "content-type: application/json" \
    -d '{
      "agent": "'"$AGENT_ID"'",
      "environment_id": "'"$ENVIRONMENT_ID"'",
      "initial_events": [
        {
          "type": "user.message",
          "content": [{"type": "text", "text": "List the files in the working directory."}]
        }
      ]
    }'
  ```

  ```bash CLI
  ant beta:sessions create --agent "$AGENT_ID" --environment-id "$ENVIRONMENT_ID" \
    --initial-event '{type: user.message, content: [{type: text, text: "List the files in the working directory."}]}'
  ```

  ```python Python
  import os

  from anthropic import Anthropic

  client = Anthropic()

  session = client.beta.sessions.create(
      agent=os.environ["AGENT_ID"],
      environment_id=os.environ["ENVIRONMENT_ID"],
      initial_events=[
          {
              "type": "user.message",
              "content": [
                  {"type": "text", "text": "List the files in the working directory."}
              ],
          },
      ],
  )
  print(session.id)
  ```

  ```typescript TypeScript
  import Anthropic from "@anthropic-ai/sdk";

  const client = new Anthropic();

  const session = await client.beta.sessions.create({
    agent: process.env["AGENT_ID"]!,
    environment_id: process.env["ENVIRONMENT_ID"]!,
    initial_events: [
      {
        type: "user.message",
        content: [{ type: "text", text: "List the files in the working directory." }]
      }
    ]
  });
  console.log(session.id);
  ```

  ```csharp C#
  using System;
  using Anthropic;
  using Anthropic.Models.Beta.Agents;
  using Anthropic.Models.Beta.Sessions;
  using Anthropic.Models.Beta.Sessions.Events;

  AnthropicClient client = new();

  var session = await client.Beta.Sessions.Create(new()
  {
      Agent = Environment.GetEnvironmentVariable("AGENT_ID")!,
      EnvironmentID = Environment.GetEnvironmentVariable("ENVIRONMENT_ID")!,
      InitialEvents =
      [
          new BetaManagedAgentsUserMessageEventParams
          {
              Content =
              [
                  new BetaManagedAgentsTextBlock
                  {
                      Text = "List the files in the working directory.",
                  },
              ],
          },
      ],
  });
  Console.WriteLine(session.ID);
  ```

  ```go Go
  package main

  import (
  	"context"
  	"fmt"
  	"os"

  	"github.com/anthropics/anthropic-sdk-go/v2"
  )

  func main() {
  	client := anthropic.NewClient()

  	session, err := client.Beta.Sessions.New(context.Background(), anthropic.BetaSessionNewParams{
  		Agent: anthropic.BetaSessionNewParamsAgentUnion{
  			OfString: anthropic.String(os.Getenv("AGENT_ID")),
  		},
  		EnvironmentID: os.Getenv("ENVIRONMENT_ID"),
  		InitialEvents: []anthropic.BetaSessionNewParamsInitialEventUnion{{
  			OfUserMessage: &anthropic.BetaManagedAgentsUserMessageEventParams{
  				Content: []anthropic.BetaManagedAgentsUserMessageEventParamsContentUnion{{
  					OfText: &anthropic.BetaManagedAgentsTextBlockParam{
  						Text: "List the files in the working directory.",
  					},
  				}},
  			},
  		}},
  	})
  	if err != nil {
  		panic(err)
  	}
  	fmt.Println(session.ID)
  }
  ```

  ```java Java
  import com.anthropic.client.AnthropicClient;
  import com.anthropic.client.okhttp.AnthropicOkHttpClient;
  import com.anthropic.models.beta.sessions.SessionCreateParams;
  import com.anthropic.models.beta.sessions.events.BetaManagedAgentsUserMessageEventParams;

  void main() {
      AnthropicClient client = AnthropicOkHttpClient.fromEnv();

      var session = client.beta().sessions().create(SessionCreateParams.builder()
          .agent(System.getenv("AGENT_ID"))
          .environmentId(System.getenv("ENVIRONMENT_ID"))
          .addInitialEvent(BetaManagedAgentsUserMessageEventParams.builder()
              .addTextContent("List the files in the working directory.")
              .build())
          .build());
      IO.println(session.id());
  }
  ```

  ```php PHP
  <?php

  use Anthropic\Client;

  $client = new Client();

  $session = $client->beta->sessions->create(
      agent: (string) getenv('AGENT_ID'),
      environmentID: (string) getenv('ENVIRONMENT_ID'),
      initialEvents: [
          [
              'type' => 'user.message',
              'content' => [['type' => 'text', 'text' => 'List the files in the working directory.']],
          ],
      ],
  );
  echo $session->id, "\n";
  ```

  ```ruby Ruby
  require "anthropic"

  client = Anthropic::Client.new

  session = client.beta.sessions.create(
    agent: ENV.fetch("AGENT_ID"),
    environment_id: ENV.fetch("ENVIRONMENT_ID"),
    initial_events: [
      {
        type: :"user.message",
        content: [{type: :text, text: "List the files in the working directory."}]
      }
    ]
  )
  puts session.id
  ```
</CodeGroup>

回應中包含工作階段的 ID，其開頭為 `sesn_`。代理會立即開始工作。其內建工具（例如 `bash`）會在服務該環境的 worker 上執行。

若要觀看工作階段，請在瀏覽器中開啟[您伺服器上的主控台](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/console)，選取 **Sessions**，然後選取該工作階段。如本頁面所示使用伺服器 API 金鑰啟動的工作階段，只有管理員才能看到。

若要改為在終端機中追蹤工作階段，請將其 ID 傳遞給 [`ant beta:sessions connect`](https://platform.claude.com/docs/zh-TW/cli-sdks-libraries/cli/sessions-connect)：

```bash
ant beta:sessions connect sesn_011CZkZAtmR3yMPDzynEDxu7
```

關於工作階段還能做的其他事情（例如固定代理版本），請參閱[啟動工作階段](https://platform.claude.com/docs/zh-TW/managed-agents/sessions)。

## 傳送訊息並讀取回覆

將您工作階段的 ID 放入您的 shell，取代此範例 ID：

```bash
export SESSION_ID=sesn_011CZkZAtmR3yMPDzynEDxu7
```

請等到工作階段閒置後再執行下一個範例。此範例會在回合結束時停止讀取。如果較早的回合仍在執行中，其結束可能會使範例在您訊息的回覆抵達之前就停止。主控台和 `ant beta:sessions connect` 都會顯示工作階段的狀態。

以下範例會傳送訊息並印出代理的回覆。範例會先開啟工作階段的事件串流，因為串流只會傳遞其開啟之後發生的事件：

<CodeGroup exclude="shell">
  <CodeGroupItem language="cURL">
    此工作流程需要的邏輯超出單一 shell 指令所能處理的範圍。請使用 [Anthropic SDK](https://platform.claude.com/docs/zh-TW/cli-sdks-libraries/overview) 來執行。
  </CodeGroupItem>

  <CodeGroupItem language="CLI">
    此工作流程需要的邏輯超出單一 shell 指令所能處理的範圍。若要在終端機中傳送訊息並讀取回覆，請使用 [`ant beta:sessions connect`](https://platform.claude.com/docs/zh-TW/cli-sdks-libraries/cli/sessions-connect)。
  </CodeGroupItem>

  ```python Python
  import os

  from anthropic import Anthropic

  client = Anthropic()
  session_id = os.environ["SESSION_ID"]

  with client.beta.sessions.events.stream(session_id) as stream:
      client.beta.sessions.events.send(
          session_id,
          events=[
              {
                  "type": "user.message",
                  "content": [
                      {"type": "text", "text": "Summarize what you did in one sentence."}
                  ],
              },
          ],
      )

      for event in stream:
          match event.type:
              case "agent.message":
                  for block in event.content:
                      if block.type == "text":
                          print(block.text, end="")
              case "session.error":
                  error_message = event.error.message if event.error else "unknown"
                  print(f"\n[Error: {error_message}]")
              case "session.status_idle":
                  if event.stop_reason.type != "requires_action":
                      break
              case "session.status_terminated":
                  break
  ```

  ```typescript TypeScript
  import Anthropic from "@anthropic-ai/sdk";

  const client = new Anthropic();
  const sessionId = process.env["SESSION_ID"]!;

  const stream = await client.beta.sessions.events.stream(sessionId);
  await client.beta.sessions.events.send(sessionId, {
    events: [
      {
        type: "user.message",
        content: [{ type: "text", text: "Summarize what you did in one sentence." }]
      }
    ]
  });

  events: for await (const event of stream) {
    switch (event.type) {
      case "agent.message":
        for (const block of event.content) {
          if (block.type === "text") {
            process.stdout.write(block.text);
          }
        }
        break;
      case "session.error":
        console.log(`\n[Error: ${event.error?.message ?? "unknown"}]`);
        break;
      case "session.status_idle":
        if (event.stop_reason.type !== "requires_action") {
          break events;
        }
        break;
      case "session.status_terminated":
        break events;
    }
  }
  ```

  ```csharp C#
  using System;
  using Anthropic;
  using Anthropic.Models.Beta.Sessions.Events;

  AnthropicClient client = new();
  var sessionId = Environment.GetEnvironmentVariable("SESSION_ID")!;

  using var stream = await client.Beta.Sessions.Events.WithRawResponse.StreamStreaming(sessionId);
  await client.Beta.Sessions.Events.Send(sessionId, new()
  {
      Events =
      [
          new BetaManagedAgentsUserMessageEventParams
          {
              Content = [new BetaManagedAgentsTextBlock { Text = "Summarize what you did in one sentence." }],
          },
      ],
  });

  await foreach (var streamEvent in stream.Enumerate())
  {
      if (streamEvent.Value is BetaManagedAgentsAgentMessageEvent message)
      {
          foreach (var block in message.Content)
          {
              if (block.Value is BetaManagedAgentsTextBlock textBlock)
              {
                  Console.Write(textBlock.Text);
              }
          }
      }
      else if (streamEvent.Value is BetaManagedAgentsSessionErrorEvent error)
      {
          Console.WriteLine($"\n[Error: {error.Error?.Message ?? "unknown"}]");
      }
      else if (streamEvent.Value is BetaManagedAgentsSessionStatusIdleEvent idle
          && idle.StopReason?.Value is not BetaManagedAgentsSessionRequiresAction)
      {
          break;
      }
      else if (streamEvent.Value is BetaManagedAgentsSessionStatusTerminatedEvent)
      {
          break;
      }
  }
  ```

  ```go Go
  package main

  import (
  	"cmp"
  	"context"
  	"fmt"
  	"os"

  	"github.com/anthropics/anthropic-sdk-go/v2"
  )

  func main() {
  	client := anthropic.NewClient()
  	ctx := context.Background()
  	sessionID := os.Getenv("SESSION_ID")

  	stream := client.Beta.Sessions.Events.StreamEvents(ctx, sessionID, anthropic.BetaSessionEventStreamParams{})
  	defer stream.Close()

  	if _, err := client.Beta.Sessions.Events.Send(ctx, sessionID, anthropic.BetaSessionEventSendParams{
  		Events: []anthropic.BetaManagedAgentsEventParamsUnion{{
  			OfUserMessage: &anthropic.BetaManagedAgentsUserMessageEventParams{
  				Content: []anthropic.BetaManagedAgentsUserMessageEventParamsContentUnion{{
  					OfText: &anthropic.BetaManagedAgentsTextBlockParam{
  						Text: "Summarize what you did in one sentence.",
  					},
  				}},
  			},
  		}},
  	}); err != nil {
  		panic(err)
  	}

  events:
  	for stream.Next() {
  		switch event := stream.Current().AsAny().(type) {
  		case anthropic.BetaManagedAgentsAgentMessageEvent:
  			for _, block := range event.Content {
  				if block.Type == "text" {
  					fmt.Print(block.Text)
  				}
  			}
  		case anthropic.BetaManagedAgentsSessionErrorEvent:
  			fmt.Printf("\n[Error: %s]\n", cmp.Or(event.Error.Message, "unknown"))
  		case anthropic.BetaManagedAgentsSessionStatusIdleEvent:
  			if _, paused := event.StopReason.AsAny().(anthropic.BetaManagedAgentsSessionRequiresAction); !paused {
  				break events
  			}
  		case anthropic.BetaManagedAgentsSessionStatusTerminatedEvent:
  			break events
  		}
  	}
  	if err := stream.Err(); err != nil {
  		panic(err)
  	}
  }
  ```

  ```java Java
  import com.anthropic.client.AnthropicClient;
  import com.anthropic.client.okhttp.AnthropicOkHttpClient;
  import com.anthropic.core.JsonObject;
  import com.anthropic.models.beta.sessions.events.BetaManagedAgentsUserMessageEventParams;
  import com.anthropic.models.beta.sessions.events.EventSendParams;

  void main() {
      AnthropicClient client = AnthropicOkHttpClient.fromEnv();
      var sessionId = System.getenv("SESSION_ID");

      try (var stream = client.beta().sessions().events().streamStreaming(sessionId)) {
          client.beta().sessions().events().send(
              sessionId,
              EventSendParams.builder()
                  .addEvent(BetaManagedAgentsUserMessageEventParams.builder()
                      .addTextContent("Summarize what you did in one sentence.")
                      .build())
                  .build()
          );

          events:
          for (var event : stream) {
              switch (event.type().value()) {
                  case AGENT_MESSAGE -> {
                      for (var block : event.asAgentMessage().content()) {
                          block.text().ifPresent(textBlock -> IO.print(textBlock.text()));
                      }
                  }
                  case SESSION_ERROR -> {
                      var errorMessage =
                          event.asSessionError().error()._json().orElse(null) instanceof JsonObject json
                              ? json.values().get("message").asStringOrThrow()
                              : "unknown";
                      IO.println("\n[Error: " + errorMessage + "]");
                  }
                  case SESSION_STATUS_IDLE -> {
                      if (!event.asSessionStatusIdle().stopReason().isRequiresAction()) {
                          break events;
                      }
                  }
                  case SESSION_STATUS_TERMINATED -> {
                      break events;
                  }
                  default -> {}
              }
          }
      }
  }
  ```

  ```php PHP
  <?php

  use Anthropic\Beta\Sessions\Events\ManagedAgentsAgentMessageEvent;
  use Anthropic\Beta\Sessions\Events\ManagedAgentsSessionErrorEvent;
  use Anthropic\Beta\Sessions\Events\ManagedAgentsSessionRequiresAction;
  use Anthropic\Beta\Sessions\Events\ManagedAgentsSessionStatusIdleEvent;
  use Anthropic\Beta\Sessions\Events\ManagedAgentsSessionStatusTerminatedEvent;
  use Anthropic\Beta\Sessions\Events\ManagedAgentsTextBlock;
  use Anthropic\Client;

  $client = new Client();
  $sessionId = (string) getenv('SESSION_ID');

  $stream = $client->beta->sessions->events->streamStream($sessionId);
  $client->beta->sessions->events->send(
      $sessionId,
      events: [
          [
              'type' => 'user.message',
              'content' => [['type' => 'text', 'text' => 'Summarize what you did in one sentence.']],
          ],
      ],
  );

  foreach ($stream as $event) {
      switch (true) {
          case $event instanceof ManagedAgentsAgentMessageEvent:
              foreach ($event->content as $block) {
                  if ($block instanceof ManagedAgentsTextBlock) {
                      echo $block->text;
                  }
              }
              break;
          case $event instanceof ManagedAgentsSessionErrorEvent:
              printf("\n[Error: %s]\n", $event->error->message);
              break;
          case $event instanceof ManagedAgentsSessionStatusIdleEvent:
              if (!($event->stopReason instanceof ManagedAgentsSessionRequiresAction)) {
                  break 2; // Break foreach loop
              }
              break;
          case $event instanceof ManagedAgentsSessionStatusTerminatedEvent:
              break 2; // Break foreach loop
      }
  }
  $stream->close();
  ```

  ```ruby Ruby
  require "anthropic"

  client = Anthropic::Client.new
  session_id = ENV.fetch("SESSION_ID")

  stream = client.beta.sessions.events.stream_events(session_id)

  client.beta.sessions.events.send_(
    session_id,
    events: [{
      type: "user.message",
      content: [{type: "text", text: "Summarize what you did in one sentence."}]
    }]
  )

  stream.each do |event|
    case event
    when Anthropic::Beta::Sessions::BetaManagedAgentsAgentMessageEvent
      event.content.each do |block|
        print block.text if block.is_a?(Anthropic::Beta::Sessions::BetaManagedAgentsTextBlock)
      end
    when Anthropic::Beta::Sessions::BetaManagedAgentsSessionErrorEvent
      puts "\n[Error: #{event.error&.message || "unknown"}]"
    when Anthropic::Beta::Sessions::BetaManagedAgentsSessionStatusIdleEvent
      break unless event.stop_reason.is_a?(Anthropic::Beta::Sessions::BetaManagedAgentsSessionRequiresAction)
    when Anthropic::Beta::Sessions::BetaManagedAgentsSessionStatusTerminatedEvent
      break
    else
      # 忽略其他事件類型
    end
  end
  ```
</CodeGroup>

除了印出 `agent.message` 文字之外，迴圈還會處理三種事件：

* **`session.status_idle`：** 迴圈結束，除非該事件的 `stop_reason.type` 是 `requires_action`。
* **`session.status_terminated`：** 迴圈結束。
* **`session.error`：** 迴圈會印出該錯誤的訊息並繼續讀取，因為伺服器可以重試失敗的模型呼叫並完成該回合。

`requires_action` 表示工作階段已為了工具呼叫而暫停。結束暫停的方式取決於該呼叫：

* **自訂工具呼叫，或確認工具呼叫的請求：** 只有您的程式碼能回應它。如果您的代理使用其中任一種，請參閱[回應使工作階段暫停的工具呼叫](https://platform.claude.com/docs/zh-TW/managed-agents/events-and-streaming#answer-tool-calls-that-pause-the-session)。
* **對由 worker 執行的工具（例如 `bash`）的呼叫：** 此呼叫通常不會暫停工作階段。如果約 3 分鐘內沒有任何 worker 接手該呼叫，工作階段就會暫停，直到 worker 傳回結果為止。此時您的程式碼無需回應任何內容。請保持串流開啟，當 worker 執行該呼叫時，回合就會繼續。

關於工作階段傳送的其他事件，以及如何在不遺漏任何事件的情況下重新連線，請參閱[工作階段事件串流](https://platform.claude.com/docs/zh-TW/managed-agents/events-and-streaming)。

## 在工作階段中移入與移出資料

資料透過 worker 的工作目錄進入和離開工作階段。

在快速入門堆疊上，每個工作階段的容器都會以空的 `/workspace` 目錄啟動，但工作階段儲存庫的複製除外。預設情況下，在一個回合結束約一分鐘後，容器就會結束，而 Docker 會將它連同該目錄一起刪除。工作階段必須保留的任何內容，都必須在此之前透過工具呼叫（例如上傳或 `git push`）移出。

容器也會在啟動約一小時後結束，即使正處於回合中途也是如此。沒有任何設定可以改變這一點。回合會在新的容器中繼續，通常在一到兩分鐘之後，而您的用戶端不會收到任何關於此切換的事件。此切換有兩項代價：

* **指令：** 在舊容器中執行的所有內容都會停止。新容器會從頭再次執行被中斷的工具呼叫，但代理留在背景執行的指令不會被任何機制重新啟動。
* **檔案：** 新容器的 `/workspace` 會再次變為空的，但工作階段儲存庫的全新複製除外。

在較長的回合中，請讓代理隨著工作進行上傳或推送其成果，並避免使用執行兩次會造成損害的指令。

在 Kubernetes 上，一個工作階段最多保留同一個 pod 約一小時，而回合會以相同的方式繼續。有兩點不同：新的 pod 會在 `/workspace` 中找到工作階段的檔案，而且它會在舊 pod 停止約 6 分鐘後啟動。

對於您自行啟動的 worker，請在傳送需要輸入檔案的訊息之前將這些檔案放在工作目錄中。之後再從該目錄讀取輸出檔案。

當您自行為每個工作階段啟動容器時，請將存放該工作階段輸入檔案的目錄掛載為容器的工作目錄。在容器結束後，從該目錄收集輸出檔案。請參閱[為工作階段準備檔案](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted-sandboxes-workers#stage-files-for-a-session)。

## 在託管服務指南中尋找更多範例

託管服務的指南說明了 API 的[核心概念](https://platform.claude.com/docs/zh-TW/managed-agents/overview#core-concepts)，並提供每項功能的範例。當您在已設定 `ANTHROPIC_BASE_URL` 和 `ANTHROPIC_API_KEY` 的終端機中執行這些指南中的 SDK 和 CLI 範例時，這些範例會呼叫您的伺服器。這些指南中的 cURL 範例指定的是 `https://api.anthropic.com`，因此在執行之前，請將該位址替換為 `$ANTHROPIC_BASE_URL`。

對於某些功能，您的伺服器需要先進行設定，或範例需要修改：

| 任務               | 託管服務指南                                                                                                                                                   | 在您的伺服器上                                                                                                                                                                                                                    |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 設定代理的模型、系統提示和版本  | [定義您的代理](https://platform.claude.com/docs/zh-TW/managed-agents/agent-setup)                                                                              | 某些欄位[取決於您的推論供應商](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/differences#differences-that-depend-on-your-inference-provider)。                                                                        |
| 選擇內建工具，或新增自訂工具   | [工具](https://platform.claude.com/docs/zh-TW/managed-agents/tools)                                                                                        | 若要使用網路工具，請先依照[網路工具需要伺服器設定](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/differences#web-tools-need-a-server-setting)中的說明啟用它們。可用的工具取決於您的推論供應商。                                                         |
| 連接 MCP 伺服器並儲存其權杖 | [MCP 連接器](https://platform.claude.com/docs/zh-TW/managed-agents/mcp-connector)和[使用保管庫進行驗證](https://platform.claude.com/docs/zh-TW/managed-agents/vaults) | 將 MCP 伺服器的主機新增至 [MCP 伺服器的允許清單](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/differences#mcp-servers-need-an-allowlist-entry)。將權杖儲存為 `static_bearer` 憑證。                                               |
| 為代理提供技能          | [技能](https://platform.claude.com/docs/zh-TW/managed-agents/skills)                                                                                       | 請將每個技能以個別檔案上傳，因為您的伺服器會拒絕 zip 封存檔。該指南範例中的[預建技能](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/differences#features-that-arent-available)無法使用。                                                           |
| 跨工作階段保留記憶        | [使用代理記憶](https://platform.claude.com/docs/zh-TW/managed-agents/memory)                                                                                   | 如果您執行自己的 worker，請為它們提供可寫入的 `/mnt/memory` 目錄，如[自行託管沙箱中的記憶儲存區](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted-sandboxes-memory)所示。                                                                        |
| 將工作交給其他代理        | [多代理協調](https://platform.claude.com/docs/zh-TW/managed-agents/multiagent-orchestration)                                                                  | 將該指南範例中的 `multiagent_20261001` 區塊替換為 [`coordinator` 區塊](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/differences#features-that-behave-differently)。                                                   |
| 在 Git 儲存庫中工作     | [存取 GitHub](https://platform.claude.com/docs/zh-TW/managed-agents/github)                                                                                | 請依照 [Git 儲存庫需要允許清單項目](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/differences#git-repositories-need-an-allowlist-entry)中的說明，在您伺服器的設定中列出 Git 主機。該指南的範例代理也需要將 `api.githubcopilot.com` 加入 MCP 伺服器的允許清單。 |
| 讓評分者依據評分標準檢查工作   | [定義成果](https://platform.claude.com/docs/zh-TW/managed-agents/define-outcomes)                                                                            | 以文字傳送評分標準。Files API 無法使用，因此請從工作目錄[收集輸出檔案](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/call-the-api#move-data-in-and-out-of-a-session)。                                                               |
| 追蹤工作階段並回應其工具呼叫   | [工作階段事件串流](https://platform.claude.com/docs/zh-TW/managed-agents/events-and-streaming)                                                                   | 該指南的第一個範例會在任何暫停時關閉串流，因此請改從[傳送訊息並讀取回覆](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/call-the-api#send-a-message-and-read-the-reply)中的迴圈開始。                                                             |

[與託管服務的差異](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/differences)列出了完整清單。在移轉現有應用程式之前，請先查看該清單。

## 執行您自己的 worker

[快速入門堆疊](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/quickstart)會建立一個環境並執行其 worker。[在 Kubernetes 上部署](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/kubernetes)中的 Helm chart 也是如此。如果您只需要這些 worker，請略過本節。

您執行 worker 的方式與[自行託管的沙箱](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted-sandboxes)相同：使用相同的 `ant beta:worker` 指令和 SDK worker，並採用[部署自行託管的 worker](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted-sandboxes-workers) 所示的方式。例外是由 webhook 觸發的 worker，因為您的伺服器不會傳送任何 webhook。

### 建立環境及其金鑰

請使用管理 CLI 建立一個環境和一個環境金鑰，而不是在 Claude Console 中建立。此 CLI `cma-server-admin` 位於版本封存檔的頂層，因此請從該處執行指令。對於 `-base-url`，請傳遞您的 worker 連線到伺服器的位址：

* **位於執行快速入門堆疊之機器上的 worker：** `http://127.0.0.1:8400`
* **位於其他機器上的 worker：** [您放在伺服器前方的 TLS 代理伺服器](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/kubernetes#put-tls-in-front-of-the-server)的 `https` 位址

管理 CLI 會在相同的位址呼叫伺服器，因此請在可連線到該位址的機器上執行它。

```bash
echo "$ANTHROPIC_API_KEY" | ./cma-server-admin bootstrap \
  -base-url http://127.0.0.1:8400 -name my-env -key-file -
```

此指令會印出 worker 的設定、啟動 worker 的指令，以及金鑰的 ID 和到期時間：

```text wrap
export ANTHROPIC_BASE_URL='http://127.0.0.1:8400'
export ANTHROPIC_ENVIRONMENT_ID='env_...'
export ANTHROPIC_ENVIRONMENT_KEY='sk-ant-oat01-...'
# then: histchars= HISTFILE=/dev/null ant beta:worker poll --workdir /workspace
# key id: 'etok_...'  expires: '...'
```

此指令只會顯示金鑰一次，因此請將金鑰、其 ID 和到期時間儲存在您的密鑰管理器中。金鑰會在 365 天後失效。在此之前，請依照[為您自己的 worker 更換環境金鑰](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/kubernetes#replace-an-environment-key-for-your-own-workers)進行操作。

請勿將伺服器 API 金鑰放在執行 worker 的機器上。請在不含任何需要保護之內容的容器或 VM 中執行 worker。關於您的其他安全職責，請參閱[您的安全責任](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted#your-security-responsibilities)。

### 執行本機實驗

請在您啟動 worker 的 shell 中執行這三行 `export`。然後使用以下指令啟動 worker，而不是使用印出的那個指令。第一個指令會建立 worker 的工作目錄，因為 worker 不會建立它：

```bash
mkdir -p "$HOME/cma-workspace" "$HOME/.config/cma"
cat > "$HOME/.config/cma/worker.inputrc" <<'EOF'
$include /etc/inputrc
set disable-completion on
EOF
histchars= HISTFILE=/dev/null INPUTRC="$HOME/.config/cma/worker.inputrc" \
  ant beta:worker poll --workdir "$HOME/cma-workspace"
```

`histchars`、`INPUTRC` 和 `HISTFILE` 會設定 worker 的 `bash` 工具背後的 shell，使其依照代理撰寫的方式執行每個指令：

* **`histchars=`：** shell 不會將 `!` 視為對其指令歷史記錄的參照。
* **`INPUTRC`：** 該檔案會關閉補全功能，否則補全會吃掉指令中的每個 tab 字元。請將該檔案放在工作目錄之外，因為代理的檔案工具可能會在工作目錄中改寫它。
* **`HISTFILE=/dev/null`：** shell 既不會讀取也不會寫入歷史記錄檔。

請勿將此 worker 用於其他任何用途。它持有環境金鑰，並在單一程序中執行每個工作階段的工具。它也不會複製工作階段的儲存庫。工作階段會在沒有儲存庫的情況下執行，而您的用戶端不會收到任何錯誤。

### 在正式環境中執行 worker

若是本機實驗以外的任何情況，請依照[為每個工作階段執行一個沙箱](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted-sandboxes-workers#run-one-sandbox-per-session)所示，為每個工作階段啟動一個容器，並在其中執行 `ant beta:worker run`。請為其映像檔提供[建置您自己的沙箱映像檔](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/call-the-api#build-your-own-sandbox-image)中列出的內容，並在該設定中變更三項內容：

* **環境金鑰：** 請勿將 `ANTHROPIC_ENVIRONMENT_KEY` 傳入工作階段的容器。伺服器會針對該環境中的每個工作階段接受該金鑰，而容器只需要存取單一工作階段。
* **工作項目的密鑰：** 請改為傳遞此密鑰以取代金鑰，因為伺服器只會針對該單一工作階段接受它。[轉送工作項目的密鑰](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted-sandboxes-workers#forward-the-work-items-secret)說明了您的程式碼接收它的位置。在該節設定 `ANTHROPIC_WORK_SECRET` 之處，請將密鑰放在只有容器使用者能讀取的檔案中。例如，將密鑰透過管線傳送至容器的標準輸入，並讓容器在啟動 worker 之前寫入該檔案。然後將 `ANTHROPIC_WORK_SECRET_FILE` 設定為該檔案的路徑。
* **伺服器位址：** 將 `ANTHROPIC_BASE_URL` 設定為容器可連線的位址。`http://127.0.0.1:8400` 不是這樣的位址。

對於 SDK worker，還有兩點適用：

* **版本：** 請依照 [SDK、CLI 與函式庫](https://platform.claude.com/docs/zh-TW/cli-sdks-libraries/overview)中的說明，使用最新版本的 SDK 建置 worker。該版本的 worker 會在執行需要核准的工具呼叫之前等待核准，且絕不會執行遭政策拒絕的呼叫。以較舊版本建置的 worker 會在每個工具呼叫抵達時立即執行它。
* **範例：** [在沙箱內執行 SDK worker](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted-sandboxes-workers#run-the-sdk-worker-inside-the-sandbox) 中的範例會讀取環境金鑰，因此無法直接在此使用。

### 建置您自己的沙箱映像檔

您自己的映像檔需要以下套件和設定：

* **套件：** Git 2.38 或更新版本、`curl`、`jq` 和 ripgrep（worker 的 `grep` 工具會使用它進行搜尋）。

* **Shell 設定：** 如同本機實驗中的 `histchars`、`HISTFILE` 和 `INPUTRC`，以及 `TERMINFO`。請在 Dockerfile 中任何 `USER` 行之前加入以下幾行：

  ```dockerfile
  ENV histchars=
  RUN printf '\nset disable-completion on\n' >> /etc/inputrc
  ENV INPUTRC=/etc/inputrc TERMINFO=/etc/terminfo HISTFILE=/dev/null
  RUN test -r /etc/terminfo/d/dumb
  ```

  `TERMINFO` 必須指定存放 `d/dumb`（worker 的 `bash` 所讀取的終端機描述）的目錄。在已安裝 `bash` 的 Alpine 上，該目錄是 `/etc/terminfo`，如上所示。在其他基礎映像檔（例如 Debian 或 Ubuntu）上，則是 `/lib/terminfo` 或 `/usr/share/terminfo`，以存放 `d/dumb` 者為準。請將該目錄寫入 `TERMINFO` 設定和 `RUN test` 行中；如果缺少該檔案，該行會使建置停止。

映像檔還需要封存檔中的兩個指令碼，它們會在 worker 啟動之前執行：

1. 將 `image-build/with-repositories.sh` 複製到 `/usr/local/bin/cma-with-repositories`。它會複製單一工作階段的儲存庫。
2. 將 `image-build/with-egress.sh` 複製到 `/usr/local/bin/cma-with-egress`。如果您的伺服器執行[「egress gateway」（輸出閘道）](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/agent-identity#environment-variable-credentials)，它會將工作階段的工具指向該閘道。
3. 將這兩個檔案設為可執行。
4. 將兩者放在 worker 指令的前面，例如 `cma-with-egress cma-with-repositories ant beta:worker run ...`。如果您的映像檔執行封存檔中的 `kubernetes/sandbox/worker.sh`，請略過此步驟，因為該指令碼會以這種方式啟動 worker。

未透過 `cma-with-repositories` 啟動的 worker 會在沒有儲存庫的情況下執行工作階段。您的用戶端不會收到任何錯誤。

### 從託管服務移轉 worker

如果您已經為 Anthropic 的託管服務執行 worker，相同的 worker 也可以改為服務您的伺服器。請執行以下三件事：

* 將管理 CLI 印出的三個 `export` 值提供給輪詢工作的程序。
* 依照本機實驗中的方式設定 `histchars`、`HISTFILE` 和 `INPUTRC` 來啟動該程序。
* 進行[在正式環境中執行 worker](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/call-the-api#run-workers-in-production)中所述的變更，並依照[建置您自己的沙箱映像檔](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/call-the-api#build-your-own-sandbox-image)檢查每個工作階段的映像檔。

## 後續步驟

* [讓開發人員從終端機登入](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/configure#let-developers-log-in-from-a-terminal)：設定單一登入，讓開發人員能在沒有 `ANTHROPIC_API_KEY` 的終端機中以自己的身分使用 `ant` CLI。
* [代理身分與憑證](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/agent-identity)：讓外部系統信任您的代理，並將 vault 憑證保存在您的密鑰管理器中。
* [推論供應商](https://platform.claude.com/docs/zh-TW/managed-agents/self-hosted/inference-provider)：將模型呼叫傳送至雲端供應商或您自己的模型閘道。
