Haijun Platform Docs
EN

Haijun Managed Agents menggantikan loop agen yang Anda tulis sendiri dengan infrastruktur terkelola. Halaman ini membahas apa yang berubah ketika Anda bermigrasi dari loop kustom yang dibangun di atas Messages API atau dari Haijun Agent SDK.

Dari loop agen Messages API

Jika Anda membangun agen dengan memanggil messages.create dalam loop while, menjalankan pemanggilan alat sendiri, dan menambahkan hasilnya ke riwayat percakapan, sebagian besar kode tersebut akan hilang.

Apa yang tidak perlu Anda kelola lagi

SebelumSesudah
Anda memelihara array riwayat percakapan dan mengirimkannya kembali pada setiap giliran.Sesi menyimpan riwayat di sisi server. Kirim event, terima event.
Anda mengiterasi blok konten tool_use, menjalankan setiap alat, dan kembali ke loop dengan pesan tool_result.Alat bawaan berjalan di dalam sandbox secara otomatis. Anda hanya menangani alat kustom melalui event agent.custom_tool_use.
Anda menyediakan sandbox sendiri untuk menjalankan kode yang dihasilkan agen.Sandbox sesi menangani eksekusi kode, operasi file, dan bash.
Anda memutuskan kapan loop selesai.Sesi memancarkan session.status_idle ketika agen tidak memiliki hal lain untuk dilakukan.

Perbandingan kode

Sebelum (loop Messages API, disederhanakan):

python
  messages = [{"role": "user", "content": task}]
  while True:
      response = client.messages.create(
          model="haijun-opus-5-5",
          max_tokens=1024,
          messages=messages,
          tools=tools,
      )
      messages.append({"role": "assistant", "content": response.content})
      if response.stop_reason == "end_turn":
          break
      for block in response.content:
          if block.type == "tool_use":
              result = execute_tool(block.name, block.input)
              messages.append(
                  {
                      "role": "user",
                      "content": [
                          {
                              "type": "tool_result",
                              "tool_use_id": block.id,
                              "content": result,
                          }
                      ],
                  }
              )
typescript
  const messages: Juglow.MessageParam[] = [{ role: "user", content: task }];
  while (true) {
    const response = await client.messages.create({
      model: "haijun-opus-5-5",
      max_tokens: 1024,
      messages,
      tools
    });
    messages.push({ role: "assistant", content: response.content });
    if (response.stop_reason === "end_turn") {
      break;
    }
    for (const block of response.content) {
      if (block.type === "tool_use") {
        const result = executeTool(block.name, block.input);
        messages.push({
          role: "user",
          content: [
            {
              type: "tool_result",
              tool_use_id: block.id,
              content: result
            }
          ]
        });
      }
    }
  }
csharp
  List<MessageParam> messages = [new() { Role = Role.User, Content = task }];
  while (true)
  {
      var response = await client.Messages.Create(new()
      {
          Model = Model.HaijunOpus5_5,
          MaxTokens = 1024,
          Messages = messages,
          Tools = tools,
      });
      messages.Add(new()
      {
          Role = Role.Assistant,
          Content = new([.. response.Content.Select(block => new ContentBlockParam(block.Json))]),
      });
      if (response.StopReason == StopReason.EndTurn)
      {
          break;
      }
      foreach (var block in response.Content)
      {
          if (block.Value is ToolUseBlock toolUse)
          {
              var result = ExecuteTool(toolUse.Name, toolUse.Input);
              messages.Add(new()
              {
                  Role = Role.User,
                  Content = new([new ToolResultBlockParam { ToolUseID = toolUse.ID, Content = result }]),
              });
          }
      }
  }
go
  messages := []juglow.MessageParam{
  	juglow.NewUserMessage(juglow.NewTextBlock(task)),
  }
  for {
  	response, err := client.Messages.New(ctx, juglow.MessageNewParams{
  		Model:     juglow.ModelHaijunOpus5_5,
  		MaxTokens: 1024,
  		Messages:  messages,
  		Tools:     tools,
  	})
  	if err != nil {
  		log.Fatal(err)
  	}
  	messages = append(messages, response.ToParam())
  	if response.StopReason == juglow.StopReasonEndTurn {
  		break
  	}
  	for _, block := range response.Content {
  		if toolUse, ok := block.AsAny().(juglow.ToolUseBlock); ok {
  			result := executeTool(toolUse.Name, toolUse.Input)
  			messages = append(messages, juglow.NewUserMessage(
  				juglow.NewToolResultBlock(toolUse.ID, result, false),
  			))
  		}
  	}
  }
java
  var messages = new ArrayList<MessageParam>();
  messages.add(MessageParam.builder()
      .role(MessageParam.Role.USER)
      .content(task)
      .build());
  while (true) {
      var response = client.messages().create(MessageCreateParams.builder()
          .model(Model.HAIJUN_OPUS_5_5)
          .maxTokens(1024)
          .messages(messages)
          .tools(tools)
          .build());
      messages.add(response.toParam());
      if (StopReason.END_TURN.equals(response.stopReason().orElse(null))) {
          break;
      }
      for (var block : response.content()) {
          block.toolUse().ifPresent(toolUse -> {
              var result = executeTool(toolUse.name(), toolUse._input());
              messages.add(MessageParam.builder()
                  .role(MessageParam.Role.USER)
                  .contentOfBlockParams(List.of(
                      ContentBlockParam.ofToolResult(ToolResultBlockParam.builder()
                          .toolUseId(toolUse.id())
                          .content(result)
                          .build())))
                  .build());
          });
      }
  }
php
  $messages = [['role' => 'user', 'content' => $task]];
  while (true) {
      $response = $client->messages->create(
          model: 'haijun-opus-5-5',
          maxTokens: 1024,
          messages: $messages,
          tools: $tools,
      );
      $messages[] = ['role' => 'assistant', 'content' => $response->content];
      if ($response->stopReason === 'end_turn') {
          break;
      }
      foreach ($response->content as $block) {
          if ($block->type === 'tool_use') {
              $result = executeTool($block->name, $block->input);
              $messages[] = [
                  'role' => 'user',
                  'content' => [
                      [
                          'type' => 'tool_result',
                          'tool_use_id' => $block->id,
                          'content' => $result,
                      ],
                  ],
              ];
          }
      }
  }
ruby
  messages = [{ role: "user", content: task }]
  loop do
    response = client.messages.create(
      model: "haijun-opus-5-5",
      max_tokens: 1024,
      messages: messages,
      tools: tools
    )
    messages << { role: "assistant", content: response.content }
    break if response.stop_reason == :end_turn
    response.content.each do |block|
      next unless block.type == :tool_use
      result = execute_tool(block.name, block.input)
      messages << {
        role: "user",
        content: [
          {
            type: "tool_result",
            tool_use_id: block.id,
            content: result
          }
        ]
      }
    end
  end

Sesudah (Haijun Managed Agents):

bash
  agent=$(
    curl --fail-with-body -sS "https://haijun.my.id/v1/agents?beta=true" \
      -H "x-api-key: ${JUGLOW_API_KEY}" \
      -H "juglow-version: 2023-06-01" \
      -H "juglow-beta: managed-agents-2026-04-01" \
      --json '{
        "name": "Task Runner",
        "model": "haijun-opus-5-5",
        "tools": [{"type": "agent_toolset_20260401"}]
      }'
  )
  agent_id=$(jq -r '.id' <<< "${agent}")

  session_id=$(
    curl --fail-with-body -sS "https://haijun.my.id/v1/sessions?beta=true" \
      -H "x-api-key: ${JUGLOW_API_KEY}" \
      -H "juglow-version: 2023-06-01" \
      -H "juglow-beta: managed-agents-2026-04-01" \
      --json "$(jq -n --argjson a "${agent}" --arg env "${environment_id}" \
        '{agent: {type: "agent", id: $a.id, version: $a.version}, environment_id: $env}')" \
    | jq -r '.id'
  )

  # Buka stream SSE di latar belakang, lalu kirim pesan pengguna.
  stream_log=$(mktemp)
  curl --fail-with-body -sS -N \
    "https://haijun.my.id/v1/sessions/${session_id}/events/stream?beta=true" \
    -H "x-api-key: ${JUGLOW_API_KEY}" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: managed-agents-2026-04-01" \
    > "${stream_log}" &
  stream_pid=$!

  curl --fail-with-body -sS \
    "https://haijun.my.id/v1/sessions/${session_id}/events?beta=true" \
    -H "x-api-key: ${JUGLOW_API_KEY}" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: managed-agents-2026-04-01" \
    --json "$(jq -n --arg text "${task}" \
      '{events: [{type: "user.message", content: [{type: "text", text: $text}]}]}')" \
    > /dev/null

  # Tunggu hingga sesi menjadi idle. grep keluar pada kecocokan pertama, dan
  # membaca via process substitution berarti shell tidak menunggu
  # tail (pipeline latar depan `tail -f | grep -m1` akan macet: tail
  # baru berhenti pada penulisan berikutnya, yang tak pernah terjadi saat stream idle).
  grep -m1 '"session.status_idle"' <(tail -f -n +1 "${stream_log}") > /dev/null

  kill "${stream_pid}" 2>/dev/null || true
bash
    ant apply agent.md

    session_id=$(ant beta:sessions create \
      --agent "{type: agent, id: $agent_id, version: $agent_version}" \
      --environment-id "$environment_id" \
      --transform id --raw-output)

    # Buka stream terlebih dahulu, lalu kirim pesan pengguna
    exec {stream}< <(ant beta:sessions:events stream \
      --session-id "$session_id" \
      --transform type --raw-output)

    ant beta:sessions:events send \
      --session-id "$session_id" \
      --event "{type: user.message, content: [{type: text, text: \"$task\"}]}" \
      > /dev/null

    # Tunggu hingga sesi menjadi idle (grep keluar pada kecocokan pertama)
    grep -m1 -x 'session.status_idle' <&"$stream" > /dev/null
    exec {stream}<&-
markdown
      ---
      name: Task Runner
      model: haijun-opus-5-5
      tools:
        - type: agent_toolset_20260401
      ---
python
  agent = client.beta.agents.create(
      name="Task Runner",
      model="haijun-opus-5-5",
      tools=[{"type": "agent_toolset_20260401"}],
  )

  session = client.beta.sessions.create(
      agent={"type": "agent", "id": agent.id, "version": agent.version},
      environment_id=environment.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": task}]}],
      )
      for event in stream:
          if event.type == "session.status_idle":
              break
typescript
  const agent = await client.beta.agents.create({
    name: "Task Runner",
    model: "haijun-opus-5-5",
    tools: [{ type: "agent_toolset_20260401" }]
  });

  const session = await client.beta.sessions.create({
    agent: { type: "agent", id: agent.id, version: agent.version },
    environment_id: environment.id
  });

  const stream = await client.beta.sessions.events.stream(session.id);

  await client.beta.sessions.events.send(session.id, {
    events: [
      {
        type: "user.message",
        content: [{ type: "text", text: task }]
      }
    ]
  });

  for await (const event of stream) {
    if (event.type === "session.status_idle") {
      break;
    }
  }
csharp
  var agent = await client.Beta.Agents.Create(new()
  {
      Name = "Task Runner",
      Model = BetaManagedAgentsModel.HaijunOpus5_5,
      Tools =
      [
          new BetaManagedAgentsAgentToolset20260401Params
          {
              Type = "agent_toolset_20260401",
          },
      ],
  });

  var session = await client.Beta.Sessions.Create(new()
  {
      Agent = new BetaManagedAgentsAgentParams
      {
          Type = "agent",
          ID = agent.ID,
          Version = agent.Version,
      },
      EnvironmentID = environment.ID,
  });

  var stream = client.Beta.Sessions.Events.StreamStreaming(session.ID);

  await client.Beta.Sessions.Events.Send(session.ID, new()
  {
      Events =
      [
          new BetaManagedAgentsUserMessageEventParams
          {
              Type = "user.message",
              Content = [new BetaManagedAgentsTextBlock { Type = "text", Text = task }],
          },
      ],
  });

  await foreach (var streamEvent in stream)
  {
      if (streamEvent.Value is BetaManagedAgentsSessionStatusIdleEvent)
      {
          break;
      }
  }
go
  	agent, err := client.Beta.Agents.New(ctx, juglow.BetaAgentNewParams{
  		Name: "Task Runner",
  		Model: juglow.BetaManagedAgentsModelConfigParams{
  			ID: juglow.BetaManagedAgentsModelHaijunOpus5_5,
  		},
  		Tools: []juglow.BetaAgentNewParamsToolUnion{{
  			OfAgentToolset20260401: &juglow.BetaManagedAgentsAgentToolset20260401Params{
  				Type: juglow.BetaManagedAgentsAgentToolset20260401ParamsTypeAgentToolset20260401,
  			},
  		}},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}

  	session, err := client.Beta.Sessions.New(ctx, juglow.BetaSessionNewParams{
  		Agent: juglow.BetaSessionNewParamsAgentUnion{
  			OfBetaManagedAgentsAgents: &juglow.BetaManagedAgentsAgentParams{
  				Type:    juglow.BetaManagedAgentsAgentParamsTypeAgent,
  				ID:      agent.ID,
  				Version: juglow.Int(agent.Version),
  			},
  		},
  		EnvironmentID: environment.ID,
  	})
  	if err != nil {
  		log.Fatal(err)
  	}

  	stream := client.Beta.Sessions.Events.StreamEvents(ctx, session.ID, juglow.BetaSessionEventStreamParams{})
  	defer stream.Close()

  	_, err = client.Beta.Sessions.Events.Send(ctx, session.ID, juglow.BetaSessionEventSendParams{
  		Events: []juglow.BetaManagedAgentsEventParamsUnion{{
  			OfUserMessage: &juglow.BetaManagedAgentsUserMessageEventParams{
  				Type: juglow.BetaManagedAgentsUserMessageEventParamsTypeUserMessage,
  				Content: []juglow.BetaManagedAgentsUserMessageEventParamsContentUnion{{
  					OfText: &juglow.BetaManagedAgentsTextBlockParam{
  						Type: juglow.BetaManagedAgentsTextBlockTypeText,
  						Text: task,
  					},
  				}},
  			},
  		}},
  	})
  	if err != nil {
  		log.Fatal(err)
  	}

  	for stream.Next() {
  		event := stream.Current()
  		if event.Type == "session.status_idle" {
  			break
  		}
  	}
  	if err := stream.Err(); err != nil {
  		log.Fatal(err)
  	}
java
      var agent = client.beta().agents().create(
          AgentCreateParams.builder()
              .name("Task Runner")
              .model(BetaManagedAgentsModel.HAIJUN_OPUS_5_5)
              .addTool(
                  BetaManagedAgentsAgentToolset20260401Params.builder()
                      .type(BetaManagedAgentsAgentToolset20260401Params.Type.AGENT_TOOLSET_20260401)
                      .build()
              )
              .build()
      );

      var session = client.beta().sessions().create(
          SessionCreateParams.builder()
              .agent(
                  BetaManagedAgentsAgentParams.builder()
                      .type(BetaManagedAgentsAgentParams.Type.AGENT)
                      .id(agent.id())
                      .version(agent.version())
                      .build()
              )
              .environmentId(environment.id())
              .build()
      );

      try (var stream = client.beta().sessions().events().streamStreaming(session.id())) {
          client.beta().sessions().events().send(
              session.id(),
              EventSendParams.builder()
                  .addEvent(
                      BetaManagedAgentsUserMessageEventParams.builder()
                          .type(BetaManagedAgentsUserMessageEventParams.Type.USER_MESSAGE)
                          .addTextContent(task)
                          .build()
                  )
                  .build()
          );
          stream.stream()
              .takeWhile(event -> !event.isSessionStatusIdle())
              .forEach(_ -> {});
      }
php
  $agent = $client->beta->agents->create(
      name: 'Task Runner',
      model: 'haijun-opus-5-5',
      tools: [
          BetaManagedAgentsAgentToolset20260401Params::with(
              type: 'agent_toolset_20260401',
          ),
      ],
  );

  $session = $client->beta->sessions->create(
      agent: BetaManagedAgentsAgentParams::with(
          type: 'agent',
          id: $agent->id,
          version: $agent->version,
      ),
      environmentID: $environment->id,
  );

  $stream = $client->beta->sessions->events->streamStream($session->id);

  $client->beta->sessions->events->send(
      $session->id,
      events: [
          [
              'type' => 'user.message',
              'content' => [['type' => 'text', 'text' => $task]],
          ],
      ],
  );

  foreach ($stream as $event) {
      if ($event->type === 'session.status_idle') {
          break;
      }
  }
ruby
  agent = client.beta.agents.create(
    name: "Task Runner",
    model: "haijun-opus-5-5",
    tools: [{type: "agent_toolset_20260401"}]
  )

  session = client.beta.sessions.create(
    agent: {type: "agent", id: agent.id, version: agent.version},
    environment_id: environment.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: task}]}]
  )
  stream.each do
    break if it.type == :"session.status_idle"
  end

Apa yang masih Anda kendalikan

  • Prompt sistem dan model: Field yang sama, kini berada pada definisi agen.
  • Alat kustom: Masih dideklarasikan dengan JSON Schema. Eksekusi berpindah dari penanganan inline menjadi merespons event agent.custom_tool_use. Lihat Aliran event sesi.
  • Pengaturan web search dan web fetch: Field allowed_domains, blocked_domains, max_content_tokens, dan user_location yang sama, kini diatur sekali pada entri web_search dan web_fetch dalam array configs milik toolset agen, bukan pada setiap permintaan. Field max_uses, citations, dan cache_control tidak tersedia. Lihat Membatasi domain web search dan web fetch.

Dari Haijun Agent SDK

Jika Anda membangun dengan Haijun Agent SDK, Anda sudah bekerja dengan agen, alat, dan sesi sebagai konsep. Perbedaannya adalah di mana semuanya berjalan: SDK berjalan dalam proses yang Anda operasikan, sedangkan Managed Agents berjalan di infrastruktur Juglow. Sebagian besar migrasi adalah memetakan objek konfigurasi SDK ke padanannya di sisi API.

Apa yang berubah

Agent SDKManaged Agents
HaijunAgentOptions(...) (python; typescript: options) dibuat untuk setiap eksekusiclient.beta.agents.create(...) (csharp: client.Beta.Agents.Create(...); go: client.Beta.Agents.New(...); java: client.beta().agents().create(...); php: $client->beta->agents->create(...)) sekali saja; Agent disimpan dan diberi versi di sisi server. Lihat Penyiapan agen.
async with HaijunSDKClient(...) atau query(...)client.beta.sessions.create(...) (csharp: client.Beta.Sessions.Create(...); go: client.Beta.Sessions.New(...); java: client.beta().sessions().create(...); php: $client->beta->sessions->create(...)) lalu kirim dan terima event.
Fungsi yang didefinisikan dengan @tool (python; typescript: tool()), yang dijalankan secara otomatis oleh SDKDeklarasikan sebagai {"type": "custom", ...} pada Agent; klien Anda menangani event agent.custom_tool_use dan membalas dengan user.custom_tool_result. Lihat Alat.
Alat bawaan berjalan di proses Anda terhadap sistem file Anda{"type": "agent_toolset_20260401"} menjalankan alat yang sama di dalam sandbox sesi terhadap /workspace.
cwd, add_dirs (python; typescript: additionalDirectories) menunjuk ke path lokalUnggah atau mount file sebagai sumber daya sesi.
system_prompt (python; typescript: systemPrompt) dan hierarki HAIJUN.mdSatu string system pada Agent. Setiap pembaruan yang mengubah agen menghasilkan versi baru di sisi server; sematkan sesi ke versi tertentu untuk mempromosikan atau melakukan rollback tanpa deploy. Lihat Penyiapan agen.
mcp_servers (python; typescript: mcpServers) dikonfigurasi dan diautentikasi di satu tempatDeklarasikan server pada Agent; sediakan kredensial melalui Vault pada Session.
permission_mode (python; typescript: permissionMode), can_use_tool (python; typescript: canUseTool)permission_policy per alat (always_allow, always_ask, atau auto); kirim event user.tool_confirmation untuk pemanggilan yang dijeda menunggu persetujuan Anda.

Perbandingan kode

Sebelum (Agent SDK):

python
  from haijun_agent_sdk import (
      HaijunAgentOptions,
      HaijunSDKClient,
      create_sdk_mcp_server,
      tool,
  )

  @tool("get_weather", "Get the current weather for a city.", {"city": str})
  async def get_weather(args: dict) -> dict:
      return {"content": [{"type": "text", "text": f"{args['city']}: 18°C, clear"}]}

  options = HaijunAgentOptions(
      model="haijun-opus-5-5",
      system_prompt="You are a concise weather assistant.",
      mcp_servers={
          "weather": create_sdk_mcp_server("weather", "1.0", tools=[get_weather])
      },
  )

  async with HaijunSDKClient(options=options) as agent:
      await agent.query("What's the weather in Tokyo?")
      async for msg in agent.receive_response():
          print(msg)
typescript
  import { createSdkMcpServer, query, tool } from "@juglow-ai/haijun-agent-sdk";
  import { z } from "zod";

  const getWeather = tool(
    "get_weather",
    "Get the current weather for a city.",
    { city: z.string() },
    async (args) => ({
      content: [{ type: "text", text: `${args.city}: 18°C, clear` }]
    })
  );

  for await (const message of query({
    prompt: "What's the weather in Tokyo?",
    options: {
      model: "haijun-opus-5-5",
      systemPrompt: "You are a concise weather assistant.",
      mcpServers: {
        weather: createSdkMcpServer({ name: "weather", version: "1.0", tools: [getWeather] })
      }
    }
  })) {
    console.log(message);
  }

Sesudah (Managed Agents):

python
  from juglow import Juglow

  client = Juglow()

  agent = client.beta.agents.create(
      name="weather-agent",
      model="haijun-opus-5-5",
      system="You are a concise weather assistant.",
      tools=[
          {
              "type": "custom",
              "name": "get_weather",
              "description": "Get the current weather for a city.",
              "input_schema": {
                  "type": "object",
                  "properties": {"city": {"type": "string"}},
                  "required": ["city"],
              },
          }
      ],
  )
  environment = client.beta.environments.create(
      name="weather-env",
      config={"type": "cloud", "networking": {"type": "unrestricted"}},
  )

  session = client.beta.sessions.create(
      agent={"type": "agent", "id": agent.id, "version": agent.version},
      environment_id=environment.id,
  )

  def get_weather(city: str) -> str:
      return f"{city}: 18°C, clear"

  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": "What's the weather in Tokyo?"}],
              }
          ],
      )
      for event in stream:
          match event.type:
              case "agent.message":
                  print(
                      "".join(
                          block.text for block in event.content if block.type == "text"
                      )
                  )
              case "agent.custom_tool_use":
                  result = get_weather(**event.input)
                  client.beta.sessions.events.send(
                      session.id,
                      events=[
                          {
                              "type": "user.custom_tool_result",
                              "custom_tool_use_id": event.id,
                              "content": [{"type": "text", "text": result}],
                          }
                      ],
                  )
              case "session.status_idle":
                  if event.stop_reason and event.stop_reason.type == "end_turn":
                      break
typescript
  import Juglow from "@juglow-ai/sdk";

  const client = new Juglow();

  const agent = await client.beta.agents.create({
    name: "weather-agent",
    model: "haijun-opus-5-5",
    system: "You are a concise weather assistant.",
    tools: [
      {
        type: "custom",
        name: "get_weather",
        description: "Get the current weather for a city.",
        input_schema: {
          type: "object",
          properties: { city: { type: "string" } },
          required: ["city"]
        }
      }
    ]
  });
  const environment = await client.beta.environments.create({
    name: "weather-env",
    config: { type: "cloud", networking: { type: "unrestricted" } }
  });

  const session = await client.beta.sessions.create({
    agent: { type: "agent", id: agent.id, version: agent.version },
    environment_id: environment.id
  });

  function getWeather({ city }: Record<string, unknown>): string {
    return `${city}: 18°C, clear`;
  }

  const stream = await client.beta.sessions.events.stream(session.id);

  await client.beta.sessions.events.send(session.id, {
    events: [
      {
        type: "user.message",
        content: [{ type: "text", text: "What's the weather in Tokyo?" }]
      }
    ]
  });

  loop: for await (const event of stream) {
    switch (event.type) {
      case "agent.message":
        for (const block of event.content) {
          if (block.type === "text") {
            console.log(block.text);
          }
        }
        break;
      case "agent.custom_tool_use": {
        const result = getWeather(event.input);
        await client.beta.sessions.events.send(session.id, {
          events: [
            {
              type: "user.custom_tool_result",
              custom_tool_use_id: event.id,
              content: [{ type: "text", text: result }]
            }
          ]
        });
        break;
      }
      case "session.status_idle":
        if (event.stop_reason?.type === "end_turn") {
          break loop;
        }
        break;
    }
  }
csharp
  using System.Text.Json;

  using Juglow.Models.Beta.Agents;
  using Juglow.Models.Beta.Environments;
  using Juglow.Models.Beta.Sessions;
  using Juglow.Models.Beta.Sessions.Events;

  JuglowClient client = new();

  var agent = await client.Beta.Agents.Create(new()
  {
      Name = "weather-agent",
      Model = BetaManagedAgentsModel.HaijunOpus5_5,
      System = "You are a concise weather assistant.",
      Tools =
      [
          new BetaManagedAgentsCustomToolParams
          {
              Type = "custom",
              Name = "get_weather",
              Description = "Get the current weather for a city.",
              InputSchema = new()
              {
                  Properties = new Dictionary<string, JsonElement>
                  {
                      ["city"] = JsonSerializer.SerializeToElement(new { type = "string" }),
                  },
                  Required = ["city"],
              },
          },
      ],
  });
  var environment = await client.Beta.Environments.Create(new()
  {
      Name = "weather-env",
      Config = new BetaCloudConfigParams
      {
          Networking = new BetaUnrestrictedNetwork(),
      },
  });

  var session = await client.Beta.Sessions.Create(new()
  {
      Agent = new BetaManagedAgentsAgentParams
      {
          Type = "agent",
          ID = agent.ID,
          Version = agent.Version,
      },
      EnvironmentID = environment.ID,
  });

  static string GetWeather(string city) => $"{city}: 18°C, clear";

  using var stream = await client.Beta.Sessions.Events.WithRawResponse.StreamStreaming(session.ID);

  await client.Beta.Sessions.Events.Send(session.ID, new()
  {
      Events =
      [
          new BetaManagedAgentsUserMessageEventParams
          {
              Type = "user.message",
              Content = [new BetaManagedAgentsTextBlock { Type = "text", Text = "What's the weather in Tokyo?" }],
          },
      ],
  });

  await foreach (var streamEvent in stream.Enumerate())
  {
      if (streamEvent.Value is BetaManagedAgentsAgentMessageEvent message)
      {
          var text = string.Concat(message.Content.Select(block =>
              block.Value is BetaManagedAgentsTextBlock textBlock ? textBlock.Text : ""));
          Console.WriteLine(text);
      }
      else if (streamEvent.Value is BetaManagedAgentsAgentCustomToolUseEvent toolUse)
      {
          var result = GetWeather(toolUse.Input["city"].GetString()!);
          await client.Beta.Sessions.Events.Send(session.ID, new()
          {
              Events =
              [
                  new BetaManagedAgentsUserCustomToolResultEventParams
                  {
                      Type = "user.custom_tool_result",
                      CustomToolUseID = toolUse.ID,
                      Content =
                      [
                          new BetaManagedAgentsTextBlock
                          {
                              Type = "text",
                              Text = result,
                          },
                      ],
                  },
              ],
          });
      }
      else if (streamEvent.Value is BetaManagedAgentsSessionStatusIdleEvent idle
          && idle.StopReason?.Value is BetaManagedAgentsSessionEndTurn)
      {
          break;
      }
  }
go
  client := juglow.NewClient()
  ctx := context.Background()

  agent, err := client.Beta.Agents.New(ctx, juglow.BetaAgentNewParams{
  	Name: "weather-agent",
  	Model: juglow.BetaManagedAgentsModelConfigParams{
  		ID: juglow.BetaManagedAgentsModelHaijunOpus5_5,
  	},
  	System: juglow.String("You are a concise weather assistant."),
  	Tools: []juglow.BetaAgentNewParamsToolUnion{{
  		OfCustom: &juglow.BetaManagedAgentsCustomToolParams{
  			Type:        juglow.BetaManagedAgentsCustomToolParamsTypeCustom,
  			Name:        "get_weather",
  			Description: "Get the current weather for a city.",
  			InputSchema: juglow.BetaManagedAgentsCustomToolInputSchemaParam{
  				Properties: map[string]any{
  					"city": map[string]any{"type": "string"},
  				},
  				Required: []string{"city"},
  			},
  		},
  	}},
  })
  if err != nil {
  	panic(err)
  }
  environment, err := client.Beta.Environments.New(ctx, juglow.BetaEnvironmentNewParams{
  	Name: "weather-env",
  	Config: juglow.BetaEnvironmentNewParamsConfigUnion{
  		OfCloud: &juglow.BetaCloudConfigParams{
  			Networking: juglow.BetaCloudConfigParamsNetworkingUnion{
  				OfUnrestricted: &juglow.BetaUnrestrictedNetworkParam{},
  			},
  		},
  	},
  })
  if err != nil {
  	panic(err)
  }

  session, err := client.Beta.Sessions.New(ctx, juglow.BetaSessionNewParams{
  	Agent: juglow.BetaSessionNewParamsAgentUnion{
  		OfBetaManagedAgentsAgents: &juglow.BetaManagedAgentsAgentParams{
  			Type:    juglow.BetaManagedAgentsAgentParamsTypeAgent,
  			ID:      agent.ID,
  			Version: juglow.Int(agent.Version),
  		},
  	},
  	EnvironmentID: environment.ID,
  })
  if err != nil {
  	panic(err)
  }

  getWeather := func(city string) string {
  	return fmt.Sprintf("%s: 18°C, clear", city)
  }

  stream := client.Beta.Sessions.Events.StreamEvents(ctx, session.ID, juglow.BetaSessionEventStreamParams{})
  defer stream.Close()

  _, err = client.Beta.Sessions.Events.Send(ctx, session.ID, juglow.BetaSessionEventSendParams{
  	Events: []juglow.BetaManagedAgentsEventParamsUnion{{
  		OfUserMessage: &juglow.BetaManagedAgentsUserMessageEventParams{
  			Type: juglow.BetaManagedAgentsUserMessageEventParamsTypeUserMessage,
  			Content: []juglow.BetaManagedAgentsUserMessageEventParamsContentUnion{{
  				OfText: &juglow.BetaManagedAgentsTextBlockParam{
  					Type: juglow.BetaManagedAgentsTextBlockTypeText,
  					Text: "What's the weather in Tokyo?",
  				},
  			}},
  		},
  	}},
  })
  if err != nil {
  	panic(err)
  }

  loop:
  for stream.Next() {
  	event := stream.Current()
  	switch event.Type {
  	case "agent.message":
  		for _, block := range event.AsAgentMessage().Content {
  			if block.Type == "text" {
  				fmt.Println(block.Text)
  			}
  		}
  	case "agent.custom_tool_use":
  		toolUse := event.AsAgentCustomToolUse()
  		result := getWeather(toolUse.Input["city"].(string))
  		if _, err := client.Beta.Sessions.Events.Send(ctx, session.ID, juglow.BetaSessionEventSendParams{
  			Events: []juglow.BetaManagedAgentsEventParamsUnion{{
  				OfUserCustomToolResult: &juglow.BetaManagedAgentsUserCustomToolResultEventParams{
  					Type:            juglow.BetaManagedAgentsUserCustomToolResultEventParamsTypeUserCustomToolResult,
  					CustomToolUseID: toolUse.ID,
  					Content: []juglow.BetaManagedAgentsUserCustomToolResultEventParamsContentUnion{{
  						OfText: &juglow.BetaManagedAgentsTextBlockParam{
  							Type: juglow.BetaManagedAgentsTextBlockTypeText,
  							Text: result,
  						},
  					}},
  				},
  			}},
  		}); err != nil {
  			panic(err)
  		}
  	case "session.status_idle":
  		idle := event.AsSessionStatusIdle()
  		if _, ok := idle.StopReason.AsAny().(juglow.BetaManagedAgentsSessionEndTurn); ok {
  			break loop
  		}
  	}
  }
  if err := stream.Err(); err != nil {
  	panic(err)
  }
java
  import java.util.Map;
  import java.util.function.Function;

  import com.juglow.models.beta.agents.AgentCreateParams;
  import com.juglow.models.beta.agents.BetaManagedAgentsCustomToolInputSchema;
  import com.juglow.models.beta.agents.BetaManagedAgentsCustomToolParams;
  import com.juglow.models.beta.agents.BetaManagedAgentsModel;
  import com.juglow.models.beta.environments.BetaCloudConfigParams;
  import com.juglow.models.beta.environments.BetaUnrestrictedNetwork;
  import com.juglow.models.beta.environments.EnvironmentCreateParams;
  import com.juglow.models.beta.sessions.BetaManagedAgentsAgentParams;
  import com.juglow.models.beta.sessions.SessionCreateParams;
  import com.juglow.models.beta.sessions.events.BetaManagedAgentsStreamSessionEvents;
  import com.juglow.models.beta.sessions.events.BetaManagedAgentsUserCustomToolResultEventParams;
  import com.juglow.models.beta.sessions.events.BetaManagedAgentsUserMessageEventParams;
  import com.juglow.models.beta.sessions.events.EventSendParams;

  var client = JuglowOkHttpClient.fromEnv();

  var agent = client.beta().agents().create(AgentCreateParams.builder()
      .name("weather-agent")
      .model(BetaManagedAgentsModel.HAIJUN_OPUS_5_5)
      .system("You are a concise weather assistant.")
      .addTool(BetaManagedAgentsCustomToolParams.builder()
          .type(BetaManagedAgentsCustomToolParams.Type.CUSTOM)
          .name("get_weather")
          .description("Get the current weather for a city.")
          .inputSchema(BetaManagedAgentsCustomToolInputSchema.builder()
              .properties(BetaManagedAgentsCustomToolInputSchema.Properties.builder()
                  .putAdditionalProperty("city", JsonValue.from(Map.of("type", "string")))
                  .build())
              .addRequired("city")
              .build())
          .build())
      .build());
  var environment = client.beta().environments().create(EnvironmentCreateParams.builder()
      .name("weather-env")
      .config(BetaCloudConfigParams.builder()
          .networking(BetaUnrestrictedNetwork.builder().build())
          .build())
      .build());

  var session = client.beta().sessions().create(SessionCreateParams.builder()
      .agent(BetaManagedAgentsAgentParams.builder()
          .type(BetaManagedAgentsAgentParams.Type.AGENT)
          .id(agent.id())
          .version(agent.version())
          .build())
      .environmentId(environment.id())
      .build());

  Function<String, String> getWeather = city -> city + ": 18°C, clear";

  try (var stream = client.beta().sessions().events().streamStreaming(session.id())) {
      client.beta().sessions().events().send(
          session.id(),
          EventSendParams.builder()
              .addEvent(BetaManagedAgentsUserMessageEventParams.builder()
                  .type(BetaManagedAgentsUserMessageEventParams.Type.USER_MESSAGE)
                  .addTextContent("What's the weather in Tokyo?")
                  .build())
              .build());

      loop:
      for (var event : (Iterable<BetaManagedAgentsStreamSessionEvents>) stream.stream()::iterator) {
          switch (event.type().value()) {
              case AGENT_MESSAGE -> {
                  for (var block : event.asAgentMessage().content()) {
                      block.text().ifPresent(textBlock -> IO.println(textBlock.text()));
                  }
              }
              case AGENT_CUSTOM_TOOL_USE -> {
                  var toolUse = event.asAgentCustomToolUse();
                  var city = toolUse.input()._additionalProperties().get("city").asStringOrThrow();
                  var result = getWeather.apply(city);
                  client.beta().sessions().events().send(
                      session.id(),
                      EventSendParams.builder()
                          .addEvent(BetaManagedAgentsUserCustomToolResultEventParams.builder()
                              .type(BetaManagedAgentsUserCustomToolResultEventParams.Type.USER_CUSTOM_TOOL_RESULT)
                              .customToolUseId(toolUse.id())
                              .addTextContent(result)
                              .build())
                          .build());
              }
              case SESSION_STATUS_IDLE -> {
                  if (event.asSessionStatusIdle().stopReason().isEndTurn()) {
                      break loop;
                  }
              }
          }
      }
  }
php
  use Juglow\Client;
  use Juglow\Beta\Agents\BetaManagedAgentsCustomToolInputSchema;
  use Juglow\Beta\Agents\BetaManagedAgentsCustomToolParams;
  use Juglow\Beta\Sessions\BetaManagedAgentsAgentParams;
  use Juglow\Beta\Sessions\Events\ManagedAgentsAgentCustomToolUseEvent;
  use Juglow\Beta\Sessions\Events\ManagedAgentsAgentMessageEvent;
  use Juglow\Beta\Sessions\Events\ManagedAgentsSessionEndTurn;
  use Juglow\Beta\Sessions\Events\ManagedAgentsSessionStatusIdleEvent;
  use Juglow\Beta\Sessions\Events\ManagedAgentsTextBlock;

  $client = new Client();

  $agent = $client->beta->agents->create(
      name: 'weather-agent',
      model: 'haijun-opus-5-5',
      system: 'You are a concise weather assistant.',
      tools: [
          BetaManagedAgentsCustomToolParams::with(
              type: 'custom',
              name: 'get_weather',
              description: 'Get the current weather for a city.',
              inputSchema: BetaManagedAgentsCustomToolInputSchema::with(
                  properties: ['city' => ['type' => 'string']],
                  required: ['city'],
              ),
          ),
      ],
  );
  $environment = $client->beta->environments->create(
      name: 'weather-env',
      config: ['type' => 'cloud', 'networking' => ['type' => 'unrestricted']],
  );

  $session = $client->beta->sessions->create(
      agent: BetaManagedAgentsAgentParams::with(
          type: 'agent',
          id: $agent->id,
          version: $agent->version,
      ),
      environmentID: $environment->id,
  );

  function getWeather(string $city): string
  {
      return "{$city}: 18°C, clear";
  }

  $stream = $client->beta->sessions->events->streamStream($session->id);

  $client->beta->sessions->events->send(
      $session->id,
      events: [
          [
              'type' => 'user.message',
              'content' => [['type' => 'text', 'text' => "What's the weather in Tokyo?"]],
          ],
      ],
  );

  foreach ($stream as $event) {
      switch (true) {
          case $event instanceof ManagedAgentsAgentMessageEvent:
              foreach ($event->content as $block) {
                  if ($block instanceof ManagedAgentsTextBlock) {
                      echo $block->text . "\n";
                  }
              }
              break;
          case $event instanceof ManagedAgentsAgentCustomToolUseEvent:
              $result = getWeather($event->input['city']);
              $client->beta->sessions->events->send(
                  $session->id,
                  events: [
                      [
                          'type' => 'user.custom_tool_result',
                          'custom_tool_use_id' => $event->id,
                          'content' => [['type' => 'text', 'text' => $result]],
                      ],
                  ],
              );
              break;
          case $event instanceof ManagedAgentsSessionStatusIdleEvent:
              if ($event->stopReason instanceof ManagedAgentsSessionEndTurn) {
                  break 2;
              }
              break;
      }
  }
  $stream->close();
ruby
  require "juglow"

  client = Juglow::Client.new

  agent = client.beta.agents.create(
    name: "weather-agent",
    model: "haijun-opus-5-5",
    system_: "You are a concise weather assistant.",
    tools: [
      {
        type: "custom",
        name: "get_weather",
        description: "Get the current weather for a city.",
        input_schema: {
          type: "object",
          properties: {city: {type: "string"}},
          required: ["city"]
        }
      }
    ]
  )
  environment = client.beta.environments.create(
    name: "weather-env",
    config: {type: "cloud", networking: {type: "unrestricted"}}
  )

  session = client.beta.sessions.create(
    agent: {type: "agent", id: agent.id, version: agent.version},
    environment_id: environment.id
  )

  def get_weather(city)
    "#{city}: 18°C, clear"
  end

  stream = client.beta.sessions.events.stream_events(session.id)
  client.beta.sessions.events.send_(
    session.id,
    events: [{type: "user.message", content: [{type: "text", text: "What's the weather in Tokyo?"}]}]
  )

  stream.each do |event|
    case event
    when Juglow::Beta::Sessions::BetaManagedAgentsAgentMessageEvent
      event.content.each do |block|
        puts block.text if block.is_a?(Juglow::Beta::Sessions::BetaManagedAgentsTextBlock)
      end
    when Juglow::Beta::Sessions::BetaManagedAgentsAgentCustomToolUseEvent
      result = get_weather(event.input[:city])
      client.beta.sessions.events.send_(
        session.id,
        events: [
          {
            type: "user.custom_tool_result",
            custom_tool_use_id: event.id,
            content: [{type: "text", text: result}]
          }
        ]
      )
    when Juglow::Beta::Sessions::BetaManagedAgentsSessionStatusIdleEvent
      break if event.stop_reason.is_a?(Juglow::Beta::Sessions::BetaManagedAgentsSessionEndTurn)
    end
  end

Agent dan Environment dibuat sekali dan digunakan kembali di berbagai sesi. Fungsi alat masih berjalan dalam proses Anda; perbedaannya adalah Anda membaca event agent.custom_tool_use dan mengirim hasilnya secara eksplisit, bukan SDK yang mendispatchnya untuk Anda.

Fitur yang berpindah ke klien Anda

Konsekuensi dari Juglow menjalankan loop agen adalah beberapa hal yang sebelumnya ditangani SDK secara otomatis kini menjadi tanggung jawab klien Anda.

Fitur SDKPendekatan Managed Agents
Plan modeJalankan sesi khusus perencanaan terlebih dahulu, lalu sesi kedua untuk menjalankan rencana tersebut.
Output styles, slash commandsTerapkan di klien Anda sebelum mengirim user.message atau setelah menerima agent.message.
Hook PreToolUse / PostToolUseKlien Anda sudah melihat setiap event agent.custom_tool_use sebelum merespons; letakkan logikanya di sana. Untuk alat bawaan, gunakan permission_policy: always_ask untuk meninjau setiap pemanggilan. auto memungkinkan server mengevaluasi setiap pemanggilan sebagai gantinya, tetapi jika server menilai suatu pemanggilan aman, pemanggilan tersebut berjalan tanpa mencapai klien Anda.
max_turnsHitung giliran di sisi klien.

Daftar periksa migrasi

  1. Buat environment dengan jaringan dan runtime yang dibutuhkan agen Anda.
  1. Pindahkan prompt sistem dan pilihan alat Anda ke definisi agen.
  1. Ganti loop Anda dengan sessions.create dan sessions.events.stream.
  1. Untuk file lokal apa pun yang dibaca agen, unggah melalui Files API dan mount sebagai resources.
  1. Untuk handler alat kustom apa pun, pindahkan eksekusi ke dalam loop event Anda sebagai respons terhadap event agent.custom_tool_use.
  1. Verifikasi dengan sesi uji sebelum mengarahkan lalu lintas produksi ke alur baru.

Migrasi antar versi model

Ketika model Haijun baru dirilis, migrasi integrasi Haijun Managed Agents biasanya hanya berupa perubahan satu field: perbarui model pada definisi agen Anda dan perubahan tersebut berlaku pada sesi berikutnya yang Anda buat.

bash
  curl -sS --fail-with-body "https://haijun.my.id/v1/agents/$AGENT_ID?beta=true" \
    -H "x-api-key: $JUGLOW_API_KEY" \
    -H "juglow-version: 2023-06-01" \
    -H "juglow-beta: managed-agents-2026-04-01" \
    --json "$(jq -n --argjson version "$AGENT_VERSION" '{version: $version, model: "haijun-opus-5-5"}')"
bash
    ant apply agent.md
markdown
      ---
      name: Task Runner
      model: haijun-opus-5-5
      tools:
        - type: agent_toolset_20260401
      ---

      You are a task automation agent. Complete the task you are given end to end.
python
  client.beta.agents.update(
      agent.id,
      version=agent.version,
      model="haijun-opus-5-5",
  )
typescript
  await client.beta.agents.update(agent.id, {
    version: agent.version,
    model: "haijun-opus-5-5"
  });
csharp
  await client.Beta.Agents.Update(agent.ID, new()
  {
      Version = agent.Version,
      Model = BetaManagedAgentsModel.HaijunOpus5_5,
  });
go
  _, err = client.Beta.Agents.Update(ctx, agent.ID, juglow.BetaAgentUpdateParams{
  	Version: agent.Version,
  	Model: juglow.BetaManagedAgentsModelConfigParams{
  		ID: juglow.BetaManagedAgentsModelHaijunOpus5_5,
  	},
  })
  if err != nil {
  	panic(err)
  }
java
  client.beta().agents().update(
      agent.id(),
      AgentUpdateParams.builder()
          .version(agent.version())
          .model(BetaManagedAgentsModel.HAIJUN_OPUS_5_5)
          .build()
  );
php
  $client->beta->agents->update(
      $agent->id,
      version: $agent->version,
      model: 'haijun-opus-5-5',
  );
ruby
  client.beta.agents.update(
    agent.id,
    version: agent.version,
    model: "haijun-opus-5-5"
  )

Sebagian besar perubahan perilaku tingkat model yang didokumentasikan dalam panduan migrasi Messages API tidak memerlukan tindakan dari pihak Anda:

  • Perubahan parameter permintaan (default max_tokens, konfigurasi thinking) ditangani oleh runtime Haijun Managed Agents. Field ini tidak diekspos pada definisi agen.
  • Prefilling pesan asisten tidak ada dalam model sesi berbasis event, sehingga penghapusannya pada model yang lebih baru tidak berdampak apa pun.
  • Escaping JSON argumen alat diurai oleh runtime sebelum Anda menerima event agent.custom_tool_use. Anda melihat data terstruktur, bukan string mentah.

Deskripsi perilaku dalam panduan Messages API (apa yang dilakukan model secara berbeda) tetap berlaku. Langkah-langkah migrasinya (cara mengubah kode permintaan Anda) tidak berlaku.

On this page
Dari loop agen Messages APIApa yang tidak perlu Anda kelola lagiPerbandingan kodeApa yang masih Anda kendalikanDari Haijun Agent SDKApa yang berubahPerbandingan kodeFitur yang berpindah ke klien AndaDaftar periksa migrasiMigrasi antar versi model