Haijun dapat berinteraksi dengan lingkungan komputer melalui alat "computer use" (penggunaan komputer), yang menyediakan kemampuan tangkapan layar serta kendali mouse/keyboard untuk interaksi desktop secara otonom.
Alat penggunaan komputer adalah sebuah client toolset (toolset klien) yang didefinisikan oleh Juglow: satu entri {"type": "computer_toolset_20260801"} dalam tools memberi Haijun 17 alat anggota seperti screenshot, left_click, type, dan zoom, dan aplikasi Anda menjalankan setiap panggilan di lingkungan yang Anda kendalikan. Alat ini saat ini belum tersedia di Haijun Managed Agents. Panggilan Haijun berupa blok tool_use yang name-nya adalah nama anggota dan yang membawa "toolset_name": "computer", sering kali beberapa per giliran (sebuah aksi batch).
Untuk tugas yang tetap berada di dalam halaman web, alat penggunaan browser lebih cocok: alat anggotanya membaca dan bertindak pada halaman itu sendiri, dan tidak memerlukan lingkungan desktop penuh.
Note: Penggunaan komputer tersedia di Haijun API dan Google Cloud sebagai toolset
computer_toolset_20260801; lihat Kompatibilitas untuk model yang didukung. Integrasicomputer_20251124yang sudah ada tetap berfungsi pada model yang tercantum untuknya di bagian Versi alat sebelumnya, dan versi alat sebelumnya tetap tersedia dalam beta untuk model dan platform yang tidak mendukung toolset ini. Lihat Migrasi daricomputer_20251124untuk meningkatkan versi, atau Versi alat sebelumnya untuk header beta.
Pertimbangan keamanan
Penggunaan komputer memiliki risiko unik yang berbeda dari fitur API standar. Risiko ini meningkat saat berinteraksi dengan internet.
Warning: Untuk meminimalkan risiko, pertimbangkan untuk mengambil tindakan pencegahan seperti: 1. Menggunakan mesin virtual atau container khusus dengan hak akses minimal untuk mencegah serangan sistem langsung atau kecelakaan. 2. Menghindari pemberian akses kepada model ke data sensitif, seperti informasi login akun, untuk mencegah pencurian informasi. 3. Membatasi akses internet ke daftar domain yang diizinkan (allowlist) untuk mengurangi paparan terhadap konten berbahaya. 4. Meminta manusia untuk mengonfirmasi keputusan yang dapat mengakibatkan konsekuensi nyata yang berarti dan tugas apa pun yang memerlukan persetujuan afirmatif, seperti menerima cookie, menyelesaikan transaksi keuangan, atau menyetujui ketentuan layanan.
Dalam beberapa keadaan, Haijun akan mengikuti perintah yang ditemukan dalam konten meskipun bertentangan dengan instruksi Anda. Misalnya, instruksi pada halaman web atau yang terkandung dalam gambar dapat mengesampingkan instruksi Anda atau menyebabkan Haijun membuat kesalahan. Ambil tindakan pencegahan untuk mengisolasi Haijun dari data dan tindakan sensitif guna menghindari risiko terkait prompt injection.
Juglow telah melatih model untuk menahan prompt injection ini dan telah menambahkan lapisan pertahanan ekstra. Jika Anda menggunakan alat computer use, classifier akan secara otomatis memindai apa yang dikembalikan oleh alat, seperti tangkapan layar, untuk menandai potensi prompt injection. Ketika classifier ini mengidentifikasi potensi prompt injection, classifier akan secara otomatis mengarahkan model untuk memeriksa apakah instruksi tersebut benar-benar berasal dari Anda sebelum menindaklanjutinya.
Perlindungan ekstra ini tidak akan ideal untuk setiap kasus penggunaan (misalnya, kasus penggunaan tanpa keterlibatan manusia), jadi jika Anda ingin memilih keluar dan menonaktifkannya, hubungi dukungan. Tindakan pencegahan di atas tetap penting meskipun classifier ini sudah diterapkan.
Informasikan risiko yang relevan kepada pengguna akhir dan dapatkan persetujuan mereka sebelum mengaktifkan penggunaan komputer dalam produk Anda sendiri.
Mulai cepat
Tambahkan toolset penggunaan komputer ke array tools dari permintaan Messages API sebagai {"type": "computer_toolset_20260801"}. Permintaan ini tidak memerlukan header beta. Contoh ini juga mendeklarasikan alat editor teks dan alat bash, yang biasanya digunakan Haijun bersama penggunaan komputer:
curl https://haijun.my.id/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $JUGLOW_API_KEY" \
-H "juglow-version: 2023-06-01" \
-d '{
"model": "haijun-opus-5-5",
"max_tokens": 1024,
"tools": [
{
"type": "computer_toolset_20260801"
},
{
"type": "text_editor_20250728",
"name": "str_replace_based_edit_tool"
},
{
"type": "bash_20250124",
"name": "bash"
}
],
"messages": [
{
"role": "user",
"content": "Save a picture of a cat to my desktop."
}
]
}' ant messages create <<'YAML'
model: haijun-opus-5-5
max_tokens: 1024
tools:
- type: computer_toolset_20260801
- type: text_editor_20250728
name: str_replace_based_edit_tool
- type: bash_20250124
name: bash
messages:
- role: user
content: Save a picture of a cat to my desktop.
YAML client = juglow.Juglow()
response = client.messages.create(
model="haijun-opus-5-5",
max_tokens=1024,
tools=[
{"type": "computer_toolset_20260801"},
{"type": "text_editor_20250728", "name": "str_replace_based_edit_tool"},
{"type": "bash_20250124", "name": "bash"},
],
messages=[{"role": "user", "content": "Save a picture of a cat to my desktop."}],
)
print(response) const client = new Juglow();
const response = await client.messages.create({
model: "haijun-opus-5-5",
max_tokens: 1024,
tools: [
{
type: "computer_toolset_20260801"
},
{
type: "text_editor_20250728",
name: "str_replace_based_edit_tool"
},
{
type: "bash_20250124",
name: "bash"
}
],
messages: [{ role: "user", content: "Save a picture of a cat to my desktop." }]
});
console.log(response); var client = new JuglowClient();
var parameters = new MessageCreateParams
{
Model = Model.HaijunOpus5_5,
MaxTokens = 1024,
Tools =
[
new ComputerToolset20260801(),
new ToolTextEditor20250728(),
new ToolBash20250124(),
],
Messages =
[
new MessageParam
{
Role = Role.User,
Content = "Save a picture of a cat to my desktop.",
},
],
};
var response = await client.Messages.Create(parameters);
Console.WriteLine(response); client := juglow.NewClient()
response, err := client.Messages.New(context.TODO(), juglow.MessageNewParams{
Model: juglow.ModelHaijunOpus5_5,
MaxTokens: 1024,
Tools: []juglow.ToolUnionParam{
{OfComputerToolset20260801: &juglow.ComputerToolset20260801Param{}},
{OfTextEditor20250728: &juglow.ToolTextEditor20250728Param{}},
{OfBashTool20250124: &juglow.ToolBash20250124Param{}},
},
Messages: []juglow.MessageParam{
juglow.NewUserMessage(juglow.NewTextBlock("Save a picture of a cat to my desktop.")),
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response.RawJSON()) import com.juglow.models.messages.ComputerToolset20260801;
// ...
import com.juglow.models.messages.ToolBash20250124;
import com.juglow.models.messages.ToolTextEditor20250728;
void main() {
JuglowClient client = JuglowOkHttpClient.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.HAIJUN_OPUS_5_5)
.maxTokens(1024L)
.addTool(ComputerToolset20260801.builder().build())
.addTool(ToolTextEditor20250728.builder().build())
.addTool(ToolBash20250124.builder().build())
.addUserMessage("Save a picture of a cat to my desktop.")
.build();
Message response = client.messages().create(params);
IO.println(response);
} $client = new Client();
$response = $client->messages->create(
maxTokens: 1024,
messages: [
['role' => 'user', 'content' => 'Save a picture of a cat to my desktop.'],
],
model: 'haijun-opus-5-5',
tools: [
['type' => 'computer_toolset_20260801'],
[
'type' => 'text_editor_20250728',
'name' => 'str_replace_based_edit_tool',
],
[
'type' => 'bash_20250124',
'name' => 'bash',
],
],
);
echo $response; client = Juglow::Client.new
response = client.messages.create(
model: "haijun-opus-5-5",
max_tokens: 1024,
tools: [
{ type: "computer_toolset_20260801" },
{
type: "text_editor_20250728",
name: "str_replace_based_edit_tool"
},
{
type: "bash_20250124",
name: "bash"
}
],
messages: [
{ role: "user", content: "Save a picture of a cat to my desktop." }
]
)
puts responseKetika Haijun bertindak pada desktop, respons memiliki stop_reason berupa tool_use dan berisi satu atau lebih blok tool_use anggota, masing-masing menyebutkan nama alat anggota dan membawa "toolset_name": "computer". Di tengah tugas ini, setelah Haijun melihat tangkapan layar desktop, respons mungkin terlihat seperti ini:
{
"id": "msg_01UZ3bXcQH8mTqNhVfL9eK2p",
"type": "message",
"role": "assistant",
"model": "haijun-opus-5-5",
"content": [
{
"type": "text",
"text": "I'll open the web browser to find a picture of a cat."
},
{
"type": "tool_use",
"id": "toolu_01WkoTUvSHDzTBu2xnGk8Ep8",
"name": "left_click",
"toolset_name": "computer",
"input": { "coordinate": [512, 742] }
},
{
"type": "tool_use",
"id": "toolu_017nJn3RgSCkTMwuZDb4uUov",
"name": "screenshot",
"toolset_name": "computer",
"input": {}
}
],
"stop_reason": "tool_use",
"stop_sequence": null
}Aplikasi Anda menjalankan setiap panggilan secara berurutan di lingkungan Anda sendiri, mengembalikan satu blok tool_result per blok tool_use, dan memanggil API lagi; Cara kerja penggunaan komputer menjelaskan loop tersebut, dan bagian selanjutnya dari halaman ini menunjukkan cara mengimplementasikannya.
Cara kerja penggunaan komputer
- Berikan Haijun alat penggunaan komputer dan prompt pengguna
- Tambahkan toolset penggunaan komputer (dan secara opsional alat lain) ke array
toolsdari permintaan API Anda.
- Sertakan prompt pengguna yang memerlukan interaksi desktop, misalnya, "Simpan gambar kucing ke desktop saya."
- Haijun merespons dengan panggilan alat anggota
- Haijun menilai apakah bertindak pada desktop dapat membantu menjawab pertanyaan pengguna.
- Jika ya, Haijun merespons dengan satu atau lebih blok
tool_useanggota, sepertiscreenshot,left_click, atautype, masing-masing membawa"toolset_name": "computer". Respons dengan beberapa blok ini adalah sebuah aksi batch.
- Respons API memiliki
stop_reasonberupatool_use, yang menandakan permintaan penggunaan alat.
- Jalankan panggilan secara berurutan dan kembalikan hasilnya
- Iterasi setiap blok
tool_usedalam respons, secara berurutan. Untuk masing-masing, lakukan dispatch berdasarkannameanggota bersamatoolset_name, dan lakukan tindakan tersebut denganinputblok pada container atau mesin virtual Anda.
- Lanjutkan percakapan dengan pesan
userbaru yang berisi satu bloktool_resultper bloktool_use, dicocokkan berdasarkantool_use_iddan masing-masing menyertakan"toolset_name": "computer". Kembalikan gambar untukscreenshotdanzoom; teks singkat sepertiOKsudah cukup untuk tindakan lainnya.
- Jika suatu tindakan gagal, kembalikan
is_error: trueuntuk blok tersebut dan jawab sisa batch seperti yang dijelaskan dalam Aksi batch.
- Haijun melanjutkan hingga tugas selesai
- Haijun menganalisis hasil alat untuk menentukan apakah diperlukan tindakan lebih lanjut atau tugas telah selesai.
- Jika Haijun menentukan bahwa diperlukan tindakan lebih lanjut, Haijun merespons dengan
stop_reasontool_uselainnya dan Anda harus kembali ke langkah 3.
- Jika tidak, Haijun mengembalikan respons teks kepada pengguna.
Pengulangan langkah 3 dan 4 tanpa input pengguna disebut sebagai "agent loop" (loop agen), yaitu Haijun merespons dengan permintaan penggunaan alat dan aplikasi Anda merespons Haijun dengan hasil evaluasi permintaan tersebut.
Aksi batch
Haijun dapat merencanakan urutan tindakan singkat, seperti klik, ketik, lalu mengambil tangkapan layar, dan mengembalikannya bersama-sama dalam satu respons. Ini disebut "batch action" (aksi batch); aksi ini menggunakan bentuk respons yang sama dengan penggunaan alat paralel dengan satu perbedaan: Anda menjalankan blok secara berurutan, bukan secara bersamaan.
Respons dengan batch tiga tindakan terlihat seperti ini:
{
"role": "assistant",
"content": [
{
"type": "tool_use",
"id": "toolu_01HqCF3nJ4Vzr8sTkPZ2wxYA",
"name": "left_click",
"toolset_name": "computer",
"input": { "coordinate": [640, 60] }
},
{
"type": "tool_use",
"id": "toolu_01Ppr3sZ3TnE9m6VUu4RyH2K",
"name": "type",
"toolset_name": "computer",
"input": { "text": "pictures of cats" }
},
{
"type": "tool_use",
"id": "toolu_01Xf5W1sD8Q9aBcJ7kLmN2pQ",
"name": "screenshot",
"toolset_name": "computer",
"input": {}
}
]
}Kembalikan satu blok tool_result untuk setiap blok tool_use, dicocokkan berdasarkan tool_use_id, semuanya dalam pesan user berikutnya. Setiap hasil untuk alat anggota harus membawa "toolset_name": "computer"; hasil yang menghilangkannya, atau yang menyebutkan toolset berbeda dari blok tool_use-nya, akan ditolak. Hanya hasil screenshot dan zoom yang memerlukan gambar; untuk anggota lainnya, pengakuan teks singkat seperti OK sudah cukup (cursor_position mengembalikan koordinat sebagai teks):
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01HqCF3nJ4Vzr8sTkPZ2wxYA",
"toolset_name": "computer",
"content": [{ "type": "text", "text": "OK" }]
},
{
"type": "tool_result",
"tool_use_id": "toolu_01Ppr3sZ3TnE9m6VUu4RyH2K",
"toolset_name": "computer",
"content": [{ "type": "text", "text": "OK" }]
},
{
"type": "tool_result",
"tool_use_id": "toolu_01Xf5W1sD8Q9aBcJ7kLmN2pQ",
"toolset_name": "computer",
"content": [
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/png",
"data": "iVBORw0KGgo..."
}
}
]
}
]
}Jalankan blok secara berurutan dan berhenti pada kegagalan pertama. Tindakan selanjutnya dalam batch biasanya bergantung pada tindakan sebelumnya: type dalam contoh ini memasukkan teks ke apa pun yang difokuskan oleh klik sebelumnya. Jalankan blok secara berurutan sesuai urutan kemunculannya dalam content, dan jika salah satu gagal, jangan jalankan sisanya. Setiap blok tool_use tetap memerlukan tool_result, jadi jawab batch sebagai berikut:
- Untuk setiap tindakan yang berhasil, kembalikan hasil normalnya.
- Untuk tindakan yang gagal, kembalikan
is_error: truedengan deskripsi teks tentang apa yang salah.
- Untuk setiap tindakan selanjutnya dalam batch, kembalikan
is_error: truedengan teks persis berikut (alat penggunaan browser menggunakan teks penghentian miliknya sendiri):
{
"type": "tool_result",
"tool_use_id": "toolu_01Xf5W1sD8Q9aBcJ7kLmN2pQ",
"toolset_name": "computer",
"is_error": true,
"content": "Not executed: an earlier computer action in this turn failed."
}Haijun kemudian melihat tindakan mana yang berhasil, mana yang gagal, dan mana yang dilewati, lalu merencanakan ulang pada giliran berikutnya. Permintaan yang membiarkan blok tool_use mana pun dalam batch tidak terjawab akan ditolak dengan invalid_request_error, sehingga loop agen yang hanya membaca blok pertama akan gagal pada panggilan berikutnya. Jika aplikasi Anda meminta manusia untuk mengonfirmasi tindakan yang berdampak, lakukan pemeriksaan itu sebelum setiap blok dijalankan, karena sebuah batch dapat menyelesaikan tindakan multilangkah dalam satu giliran.
Haijun biasanya mengakhiri batch dengan screenshot agar dapat mengamati hasilnya sebelum memutuskan apa yang harus dilakukan selanjutnya. Ketika batch tidak diakhiri dengan tangkapan layar, aplikasi Anda dapat melampirkan tangkapan layar sebagai blok image tambahan pada hasil terakhir dalam batch sehingga Haijun selalu melihat keadaan layar saat ini, yang menghemat satu perjalanan bolak-balik dibandingkan menunggu Haijun memintanya. Anda juga dapat meminta Haijun melalui prompt untuk mengakhiri setiap batch dengan tangkapan layar (lihat Optimalkan kinerja model dengan prompting).
Lingkungan komputasi
Penggunaan komputer memerlukan lingkungan komputasi ter-sandbox tempat Haijun dapat berinteraksi dengan aman dengan aplikasi dan web. Lingkungan ini mencakup:
- Tampilan virtual: Server tampilan X11 virtual (menggunakan Xvfb) yang merender antarmuka desktop yang akan dilihat Haijun melalui tangkapan layar dan dikendalikan dengan tindakan mouse/keyboard.
- Lingkungan desktop: UI ringan dengan window manager (Mutter) dan panel (Tint2) yang berjalan di Linux, yang menyediakan antarmuka grafis yang konsisten untuk berinteraksi dengan Haijun.
- Aplikasi: Aplikasi Linux yang sudah terpasang seperti Firefox, LibreOffice, editor teks, dan pengelola file yang dapat digunakan Haijun untuk menyelesaikan tugas.
- Implementasi alat: Kode integrasi yang menerjemahkan permintaan alat abstrak Haijun (seperti "gerakkan mouse" atau "ambil tangkapan layar") menjadi operasi aktual di lingkungan virtual.
- Loop agen: Program yang menangani komunikasi antara Haijun dan lingkungan, mengirimkan tindakan Haijun ke lingkungan dan mengembalikan hasilnya (tangkapan layar, output perintah) kembali ke Haijun.
Saat Anda menggunakan penggunaan komputer, Haijun tidak terhubung langsung ke lingkungan ini. Sebaliknya, aplikasi Anda:
- Menerima permintaan penggunaan alat dari Haijun
- Menerjemahkannya menjadi tindakan di lingkungan komputasi Anda
- Menangkap hasilnya (seperti tangkapan layar dan output perintah)
- Mengembalikan hasil ini ke Haijun
Untuk keamanan dan isolasi, implementasi referensi menjalankan semua ini di dalam container Docker dengan pemetaan port yang sesuai untuk melihat dan berinteraksi dengan lingkungan.
Cara mengimplementasikan penggunaan komputer
Meningkatkan versi integrasi computer_20251124 yang sudah ada? Mulailah dengan Migrasi dari computer_20251124; sisa bagian ini berlaku untuk integrasi baru maupun yang dimigrasikan.
Tip: Implementasi referensi penggunaan komputer adalah contoh kerja yang lengkap: sebuah lingkungan dalam container yang cocok untuk penggunaan komputer, implementasi alat penggunaan komputer, sebuah loop agen yang memanggil Haijun API dan menjalankan alat, serta antarmuka web untuk container, loop, dan alat tersebut.
Pahami loop agen
Inti dari penggunaan komputer adalah "loop agen": sebuah siklus di mana Haijun meminta tindakan alat, aplikasi Anda menjalankannya, dan mengembalikan hasilnya ke Haijun. Loop ini menggunakan client yang Anda buat di Mulai cepat, array tools yang hanya mendeklarasikan toolset penggunaan komputer, dan helper pemrosesan panggilan alat di bawah Implementasikan alat penggunaan komputer. Jika Anda juga mendeklarasikan alat lain, seperti alat bash dan editor teks dari Mulai cepat, lakukan dispatch blok tool_use mereka dalam proses yang sama; helper hanya menjawab panggilan anggota penggunaan komputer, dan loop memperlakukan giliran tanpa panggilan yang terjawab sebagai selesai. Berikut contoh yang disederhanakan:
def sampling_loop(model: str, messages: list[MessageParam], max_iterations: int = 10):
"""
Run the computer-use agent loop until Haijun stops requesting tools
or the iteration limit is reached.
"""
for _ in range(max_iterations):
response = client.messages.create(
model=model,
max_tokens=4096,
messages=messages,
tools=TOOLS,
)
# Tambahkan respons Haijun ke riwayat percakapan
messages.append({"role": "assistant", "content": response.content})
# Jalankan aksi yang diminta Haijun, secara berurutan, dan kumpulkan hasilnya
tool_results = process_tool_calls(response)
if not tool_results:
return messages # No more tool use; task complete
# Kirim semua hasil kembali ke Haijun dalam satu pesan user
messages.append({"role": "user", "content": tool_results})
return messages async function samplingLoop(
model: string,
messages: Juglow.MessageParam[],
maxIterations = 10,
): Promise<Juglow.MessageParam[]> {
// Jalankan loop agen computer-use hingga Haijun berhenti meminta alat
// atau batas iterasi tercapai.
for (let i = 0; i < maxIterations; i++) {
const response = await client.messages.create({
model,
max_tokens: 4096,
messages,
tools,
});
// Tambahkan respons Haijun ke riwayat percakapan
messages.push({ role: "assistant", content: response.content });
// Jalankan alat apa pun yang diminta Haijun dan kumpulkan hasilnya
const toolResults = processToolCalls(response);
if (toolResults.length === 0) {
return messages; // No more tool use; task complete
}
// Kirim hasil alat kembali ke Haijun untuk iterasi berikutnya
messages.push({ role: "user", content: toolResults });
}
return messages;
} async Task<List<MessageParam>> SamplingLoop(
Model model,
List<MessageParam> messages,
int maxIterations = 10
)
{
// Jalankan loop agen computer-use hingga Haijun berhenti meminta alat
// atau batas iterasi tercapai.
for (var i = 0; i < maxIterations; i++)
{
var response = await client.Messages.Create(
new MessageCreateParams
{
Model = model,
MaxTokens = 4096,
Messages = messages,
Tools = tools,
}
);
// Tambahkan respons Haijun ke riwayat percakapan
messages.Add(
new()
{
Role = Role.Assistant,
Content = response
.Content.Select(block => new ContentBlockParam(block.Json))
.ToList(),
}
);
// Jalankan alat apa pun yang diminta Haijun dan kumpulkan hasilnya
var toolResults = ProcessToolCalls(response);
if (toolResults.Count == 0)
{
return messages; // No more tool use; task complete
}
// Kirim hasil alat kembali ke Haijun untuk iterasi berikutnya
messages.Add(new() { Role = Role.User, Content = toolResults });
}
return messages;
} // samplingLoop menjalankan loop agen computer-use hingga Haijun berhenti
// meminta alat atau batas iterasi tercapai.
func samplingLoop(ctx context.Context, model juglow.Model, messages []juglow.MessageParam, maxIterations int) ([]juglow.MessageParam, error) {
for range maxIterations {
response, err := client.Messages.New(ctx, juglow.MessageNewParams{
Model: model,
MaxTokens: 4096,
Messages: messages,
Tools: tools,
})
if err != nil {
return nil, err
}
// Tambahkan respons Haijun ke riwayat percakapan
messages = append(messages, response.ToParam())
// Jalankan aksi yang diminta Haijun secara berurutan, dan kumpulkan hasilnya
toolResults := processToolCalls(response)
if len(toolResults) == 0 {
return messages, nil // No more tool use; task complete
}
// Kirim semua hasil kembali ke Haijun dalam satu pesan pengguna
messages = append(messages, juglow.NewUserMessage(toolResults...))
}
return messages, nil
}
/**
* Run the computer-use agent loop until Haijun stops requesting tools
* or the iteration limit is reached.
*/
List<MessageParam> samplingLoop(Model model, List<MessageParam> messages, int maxIterations) {
for (int i = 0; i < maxIterations; i++) {
Message response = client.messages().create(MessageCreateParams.builder()
.model(model)
.maxTokens(4096)
.messages(messages)
.addTool(COMPUTER_TOOLSET)
.build());
// Tambahkan respons Haijun ke riwayat percakapan
messages.add(MessageParam.builder()
.role(MessageParam.Role.ASSISTANT)
.contentOfBlockParams(response.content().stream().map(ContentBlock::toParam).toList())
.build());
// Jalankan alat apa pun yang diminta Haijun dan kumpulkan hasilnya
List<ContentBlockParam> toolResults = processToolCalls(response);
if (toolResults.isEmpty()) {
return messages; // No more tool use; task complete
}
// Kirim hasil alat kembali ke Haijun untuk iterasi berikutnya
messages.add(MessageParam.builder()
.role(MessageParam.Role.USER)
.contentOfBlockParams(toolResults)
.build());
}
return messages;
} /**
* Run the computer-use agent loop until Haijun stops requesting tools
* or the iteration limit is reached.
*/
function samplingLoop(string $model, array $messages, int $maxIterations = 10): array
{
global $client, $tools;
for ($i = 0; $i < $maxIterations; $i++) {
$response = $client->messages->create(
model: $model,
maxTokens: 4096,
messages: $messages,
tools: $tools,
);
// Tambahkan respons Haijun ke riwayat percakapan
$messages[] = MessageParam::with(role: Role::ASSISTANT, content: $response->content);
// Jalankan alat yang diminta Haijun dan kumpulkan hasilnya
$toolResults = processToolCalls($response);
if ($toolResults === []) {
return $messages; // No more tool use; task complete
}
// Kirim hasil alat kembali ke Haijun untuk iterasi berikutnya
$messages[] = MessageParam::with(role: Role::USER, content: $toolResults);
}
return $messages;
} # Jalankan loop agen computer-use hingga Haijun berhenti meminta alat
# atau batas iterasi tercapai.
def sampling_loop(model, messages, max_iterations: 10)
max_iterations.times do
response = CLIENT.messages.create(
model: model,
max_tokens: 4096,
messages: messages,
tools: TOOLS
)
# Tambahkan respons Haijun ke riwayat percakapan
messages << { role: "assistant", content: response.content }
# Jalankan aksi yang diminta Haijun, secara berurutan, dan kumpulkan hasilnya
tool_results = process_tool_calls(response)
return messages if tool_results.empty? # No more tool use; task complete
# Kirim semua hasil kembali ke Haijun dalam satu pesan pengguna
messages << { role: "user", content: tool_results }
end
messages
endLoop berlanjut hingga Haijun merespons tanpa meminta alat apa pun (tugas selesai) atau batas iterasi maksimum tercapai. Pengaman ini mencegah potensi loop tak terbatas yang dapat mengakibatkan biaya API yang tidak terduga.
Optimalkan kinerja model dengan prompting
- Tentukan tugas yang sederhana dan terdefinisi dengan baik serta berikan instruksi eksplisit untuk setiap langkah.
- Haijun terkadang mengasumsikan hasil dari tindakannya tanpa secara eksplisit memeriksa hasilnya. Untuk mencegah hal ini, Anda dapat memberi prompt kepada Haijun dengan
After each step, take a screenshot and carefully evaluate if you have achieved the right outcome. Explicitly show your thinking: "I have evaluated step X..." If not correct, try again. Only when you confirm a step was executed correctly should you move on to the next one.
- Beberapa elemen UI (seperti dropdown dan scrollbar) mungkin sulit dimanipulasi oleh Haijun menggunakan gerakan mouse. Jika Anda mengalami hal ini, coba beri prompt kepada model untuk menggunakan pintasan keyboard.
- Untuk tugas atau interaksi UI yang berulang, sertakan contoh tangkapan layar dan panggilan alat dari hasil yang berhasil dalam prompt Anda.
- Jika Anda memerlukan model untuk login, berikan nama pengguna dan kata sandi dalam prompt Anda di dalam tag XML seperti
. Menggunakan penggunaan komputer dalam aplikasi yang memerlukan login meningkatkan risiko hasil buruk akibat prompt injection. Tinjau Mitigasi jailbreak dan prompt injection sebelum memberikan kredensial login kepada model.
- Saat menyusun array
contentdari giliran pengguna, tempatkan teks instruksi sebelum gambar tangkapan layar. Memberikan deskripsi target sebelum gambar diproses meningkatkan akurasi klik.
- Haijun menggunakan tindakan
zoomuntuk memeriksa suatu wilayah pada resolusi penuh ketika ditanya tentang teks kecil atau elemen UI tertentu yang tidak terbaca pada resolusi default tangkapan layar, seperti nama file di sidebar, judul tab, teks status-bar, nomor baris, atau label tombol. Jika Haijun tidak melakukan zoom saat Anda mengharapkannya, tanyakan tentang wilayah atau elemen tertentu, bukan layar secara keseluruhan.
- Jika Anda ingin setiap aksi batch diakhiri dengan tangkapan layar, nyatakan hal itu dalam prompt sistem, misalnya,
End each group of actions with a screenshot so you can verify the result before continuing.
Tip: Jika Anda berulang kali menemui serangkaian masalah yang jelas atau mengetahui sebelumnya tugas yang perlu diselesaikan Haijun, gunakan prompt sistem untuk memberi Haijun tips atau instruksi eksplisit tentang cara menyelesaikan tugas dengan sukses.
Tip: Untuk agen yang mencakup beberapa sesi, jalankan verifikasi end-to-end di awal setiap sesi, bukan hanya setelah implementasi. Pemeriksaan berbasis browser menangkap regresi dari sesi sebelumnya yang terlewat oleh tinjauan tingkat kode saja. Lihat Harness efektif untuk agen yang berjalan lama untuk detailnya.
Prompt sistem
Saat Anda menyertakan alat penggunaan komputer dalam permintaan, API menghasilkan "system prompt" (prompt sistem) khusus penggunaan komputer. Prompt ini mirip dengan prompt sistem penggunaan alat tetapi dimulai dengan:
You have access to a set of functions you can use to answer the user's question. This includes access to a sandboxed computing environment. You do NOT currently have the ability to inspect files or interact with external resources, except by invoking the below functions.
Seperti pada penggunaan alat biasa, parameter system yang diberikan pengguna tetap dihormati dan digunakan dalam penyusunan prompt sistem gabungan.
Tindakan yang tersedia
Setiap tindakan adalah alat anggota dari toolset penggunaan komputer: Haijun menyebutkan nama anggota dalam blok tool_use yang membawa "toolset_name": "computer", dan input blok hanya berisi parameter anggota tersebut, tanpa field action. Toolset ini memiliki 17 alat anggota:
| Anggota | Input | Deskripsi |
|---|---|---|
screenshot | Tidak ada ({}) | Menangkap seluruh tampilan dan mengembalikannya sebagai gambar. |
zoom | region: [x0, y0, x1, y1], sudut kiri atas dan kanan bawah dari area yang akan diperiksa | Menangkap hanya wilayah tampilan tersebut pada resolusi penuh dan mengembalikannya sebagai gambar, diskalakan agar muat dalam dimensi tangkapan layar biasa Anda dengan rasio aspek dipertahankan. Ini memungkinkan Haijun membaca teks kecil atau UI padat yang tidak terbaca dalam tangkapan layar penuh yang diperkecil. |
left_click | coordinate (opsional): [x, y]; text (opsional): tombol modifier yang ditahan selama klik: shift, ctrl, alt, super (tombol Command atau Windows), atau kombinasi yang digabung dengan + seperti ctrl+shift | Mengklik tombol kiri mouse pada coordinate, atau pada posisi kursor saat ini jika coordinate dihilangkan. |
right_click, middle_click, double_click, triple_click | Sama seperti left_click | Tombol mouse lain dan klik berganda. |
left_click_drag | start_coordinate: [x, y]; coordinate: [x, y]; text (opsional): tombol modifier | Menekan pada start_coordinate, menyeret ke coordinate, dan melepaskan. |
mouse_move | coordinate: [x, y] | Memindahkan kursor tanpa mengklik, misalnya, untuk hover. |
left_mouse_down, left_mouse_up | Tidak ada ({}) | Menekan atau melepaskan tombol kiri mouse pada posisi kursor saat ini, untuk seretan yang tidak dapat diekspresikan oleh left_click_drag. Pindahkan kursor dengan mouse_move terlebih dahulu. |
cursor_position | Tidak ada ({}) | Melaporkan posisi [x, y] kursor saat ini sebagai teks. |
scroll | scroll_direction: "up", "down", "left", atau "right"; scroll_amount: jumlah klik roda gulir; coordinate (opsional): [x, y]; text (opsional): tombol modifier | Menggulir pada coordinate, atau pada posisi kursor saat ini. |
type | text: string yang akan diketik | Mengetik teks literal pada fokus keyboard saat ini. |
key | text: sebuah tombol atau kombinasi yang digabung dengan + seperti "Return", "ctrl+s", atau "alt+Tab"; repeat (opsional): 1 hingga 100, default 1 | Menekan tombol atau kombinasi tombol, sebanyak repeat kali. |
hold_key | text: sebuah tombol atau kombinasi; duration: detik, hingga 300 | Menahan tombol selama durasi yang diberikan. |
wait | duration: detik, hingga 300 | Berhenti sejenak sebelum tindakan berikutnya, misalnya, saat aplikasi sedang dimuat. |
Perhatikan hal-hal berikut saat mengimplementasikan anggota:
- Koordinat dalam piksel tangkapan layar. Setiap nilai
coordinate,start_coordinate, danregion, serta posisi yang dilaporkancursor_position, berada dalam ruang piksel tangkapan layar tampilan penuh yang Anda kembalikan, dengan titik asal di kiri atas. Gambar zoom tidak mengubah hal ini: setelahzoom, Haijun tetap menyatakan koordinat dalam ruang tangkapan layar penuh, tidak pernah relatif terhadap gambar yang di-zoom. Jika Anda memperkecil tangkapan layar sebelum mengembalikannya, skalakan kembali koordinat Haijun sebelum menerapkannya ke tampilan sebenarnya (lihat Sesuaikan ukuran tangkapan layar agar sesuai batas gambar).
- Semua anggota diaktifkan secara default, termasuk
zoom. Jika lingkungan Anda tidak dapat menghasilkan gambar zoom, tahan anggota tersebut denganconfigs(lihat Parameter alat) daripada membiarkannya aktif dan mengembalikan error. Jika Haijun memanggil anggota yang Anda tahan atau tidak Anda implementasikan, kembalikantool_resultdenganis_error: trueuntuk blok tersebut.
- Lakukan dispatch berdasarkan pasangan (
toolset_name,name).toolset_nameadalah yang menandai sebuah blok sebagai tindakan komputer: alat kustom dalam permintaan yang sama dapat memiliki nama yang sama dengan anggota, dan versi toolset yang lebih baru dapat menambahkan anggota (lihat Toolset klien).
Contoh tindakan
Setiap contoh adalah blok tool_use lengkap seperti yang muncul dalam respons Haijun.
Shift+klik pada suatu posisi, misalnya, untuk memperluas seleksi. Tidak seperti hold_key, text menahan modifier hanya selama durasi klik atau gulir tersebut:
{
"type": "tool_use",
"id": "toolu_01Qg8m3XqC5aRy7tD2eS4jUg",
"name": "left_click",
"toolset_name": "computer",
"input": { "coordinate": [500, 300], "text": "shift" }
}Seret dari satu titik ke titik lain:
{
"type": "tool_use",
"id": "toolu_01Ed6j9VnA3yPw5rB8cQ2gSe",
"name": "left_click_drag",
"toolset_name": "computer",
"input": {
"start_coordinate": [200, 300],
"coordinate": [600, 300]
}
}Gulir ke bawah tiga klik roda:
{
"type": "tool_use",
"id": "toolu_01Yc5h8UmZ2xNv4qA7bP9fRd",
"name": "scroll",
"toolset_name": "computer",
"input": {
"coordinate": [500, 400],
"scroll_direction": "down",
"scroll_amount": 3
}
}Tekan Tab empat kali:
{
"type": "tool_use",
"id": "toolu_01Sb4g7TkY9wLu3pX6zM8eQc",
"name": "key",
"toolset_name": "computer",
"input": { "text": "Tab", "repeat": 4 }
}Zoom untuk memeriksa suatu wilayah pada resolusi penuh:
{
"type": "tool_use",
"id": "toolu_01Kf7k2WpB4zQx6sC9dR3hTf",
"name": "zoom",
"toolset_name": "computer",
"input": { "region": [100, 200, 400, 350] }
}Laporkan posisi kursor. Jawab panggilan ini dengan hasil teks singkat yang memberikan posisi dalam piksel tangkapan layar, misalnya, X=512, Y=384:
{
"type": "tool_use",
"id": "toolu_01Ekh3vqB6yTs2mNc4Rw8pLd",
"name": "cursor_position",
"toolset_name": "computer",
"input": {}
}Parameter alat
Entri toolset dalam array tools menerima empat parameter; aturan yang dimilikinya bersama toolset penggunaan browser tercantum di bawah Toolset klien.
| Parameter | Wajib | Deskripsi |
|---|---|---|
type | Ya | computer_toolset_20260801 |
configs | Tidak | Pengaturan per anggota dengan kunci nama anggota; setiap anggota menerima enabled (default true untuk semua 17, termasuk zoom) dan defer_loading (default false, untuk pencarian alat), dan anggota yang Anda hilangkan mempertahankan default-nya. |
cache_control | Tidak | Breakpoint caching prompt pada definisi toolset; hanya pada entri. Breakpoint pada blok tool_use atau tool_result mana pun dalam batch berlaku di akhir batch tersebut; lihat Penggunaan alat dengan caching prompt. |
allowed_callers | Tidak | Hanya ["direct"]. |
Misalnya, entri ini menahan zoom untuk lingkungan yang tidak mengimplementasikannya dan menetapkan breakpoint cache pada definisi toolset:
{
"type": "computer_toolset_20260801",
"configs": {
"zoom": { "enabled": false }
},
"cache_control": { "type": "ephemeral" }
}Jika loop agen Anda hanya dapat menjalankan satu tindakan per perjalanan bolak-balik, setel disable_parallel_tool_use ke true dalam tool_choice; Haijun kemudian mengembalikan paling banyak satu blok tool_use anggota per giliran (lihat Nonaktifkan penggunaan alat paralel).
Entri ini menolak parameter berikut dari versi alat sebelumnya, dan permintaan yang menyertakan salah satunya mengembalikan invalid_request_error:
name: nama anggota ditetapkan oleh versi toolset.
display_width_px,display_height_px, dandisplay_number: koordinat selalu berada dalam ruang piksel tangkapan layar yang Anda kembalikan.
enable_zoom: zoom adalah alat anggota yang Anda kendalikan melaluiconfigs.
Entri ini juga tidak dapat dideklarasikan dalam permintaan yang sama dengan entri computer_20251124 atau alat lain bernama computer. Untuk strict, input_examples, penempatan defer_loading, tool_choice, streaming, dan pembatasan pemanggil, lihat Toolset klien.
Menggabungkan dengan thinking
Untuk menggabungkan penggunaan komputer dengan thinking, lihat Thinking.
Tip: Untuk alat
computer_20251124sebelumnya, benchmark internal pada model yang menggunakannya menyarankan pengaturaneffortberikut: * Haijun Opus 4.7: gunakanhighsebagai default; gunakanlowuntuk beban kerja throughput tinggi atau yang sensitif terhadap biaya. * Haijun Sonnet 4.6 dan Haijun Opus 4.6: gunakanmediumsebagai default (rasio akurasi terhadap biaya terbaik). Hindarimax, yang menambah biaya token tanpa meningkatkan akurasi pada tugas UI. Pada model ini,lowmenggunakan token output lebih sedikit daripada menonaktifkan thinking sepenuhnya (lebih sedikit kesalahan berarti lebih sedikit percobaan ulang), menjadikannya pilihan kuat untuk loop yang sensitif terhadap biaya.
Memperkaya penggunaan komputer dengan alat lain
Untuk menambahkan alat lain bersama penggunaan komputer, sertakan alat tersebut dalam array tools yang sama. Bagian Mulai cepat menunjukkan pola ini dengan alat bash dan alat editor teks. Anda dapat menambahkan definisi alat kustom Anda sendiri dengan cara yang sama.
Untuk tugas yang tetap berada di dalam halaman web, Anda juga dapat mendeklarasikan alat penggunaan browser dalam permintaan yang sama: kedua toolset bekerja secara independen, masing-masing dalam kerangka koordinatnya sendiri, dan panggilan ke anggota yang memiliki nama sama, seperti screenshot atau key, dibedakan berdasarkan toolset_name.
Bangun lingkungan penggunaan komputer kustom
Implementasi referensi dimaksudkan untuk membantu Anda memulai penggunaan komputer. Implementasi ini mencakup semua komponen yang diperlukan agar Haijun dapat menggunakan komputer. Namun, Anda dapat membangun lingkungan Anda sendiri untuk penggunaan komputer sesuai kebutuhan Anda. Anda akan memerlukan:
- Lingkungan tervirtualisasi atau dalam container yang cocok untuk penggunaan komputer dengan Haijun
- Implementasi tindakan-tindakan alat penggunaan komputer
- Loop agen yang berinteraksi dengan Haijun API dan menjalankan hasil
tool_usemenggunakan implementasi alat Anda
- API atau UI yang memungkinkan input pengguna untuk memulai loop agen
Implementasikan alat penggunaan komputer
Alat penggunaan komputer diimplementasikan sebagai alat tanpa skema. Saat menggunakan alat ini, Anda tidak perlu menyediakan skema input seperti pada alat lain; skema sudah tertanam dalam model Haijun dan tidak dapat dimodifikasi.
- Siapkan lingkungan komputasi Anda
Buat tampilan virtual atau hubungkan ke tampilan yang sudah ada yang akan berinteraksi dengan Haijun. Ini biasanya melibatkan penyiapan Xvfb (X Virtual Framebuffer) atau teknologi serupa.
- Implementasikan handler tindakan
Buat fungsi untuk menangani setiap jenis tindakan yang mungkin diminta Haijun:
# Data gambar placeholder; executor sungguhan menangkap layar dan mengembalikan byte PNG
PLACEHOLDER_PNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="
def capture_screenshot() -> list[ImageBlockParam]:
# screenshot menjawab dengan blok gambar, bukan teks: kembalikan daftar konten hasil
return [
{
"type": "image",
"source": {"type": "base64", "media_type": "image/png", "data": PLACEHOLDER_PNG},
}
]
def click(coordinate=None):
if coordinate is None:
return "clicked at current cursor"
x, y = coordinate
return f"clicked at ({x}, {y})"
def type_text(text):
return f"typed: {text}"
def handle_computer_action(name, tool_input):
match name:
case "screenshot":
return capture_screenshot()
case "left_click":
# coordinate bersifat opsional; tanpanya, klik di posisi kursor saat ini
return click(tool_input.get("coordinate"))
case "type":
return type_text(tool_input["text"])
# Tangani aksi lain sesuai kebutuhan
raise ValueError(f"Unknown or unimplemented member: {name}") // Data gambar placeholder; eksekutor nyata menangkap layar sebagai byte PNG
const PLACEHOLDER_PNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";
function captureScreenshot(): Juglow.ImageBlockParam[] {
// screenshot menjawab dengan blok gambar, bukan teks
return [
{
type: "image",
source: {
type: "base64",
media_type: "image/png",
data: PLACEHOLDER_PNG,
},
},
];
}
function clickAt(x: number, y: number): string {
return `clicked at (${x}, ${y})`;
}
function clickAtCursor(): string {
return "clicked at the current cursor position";
}
function typeText(text: string): string {
return `typed: ${text}`;
}
function handleComputerAction(
action: string,
input: unknown,
): string | Juglow.ImageBlockParam[] {
const params: object =
typeof input === "object" && input !== null ? input : {};
switch (action) {
case "screenshot":
return captureScreenshot();
case "left_click":
// coordinate bersifat opsional pada toolset; tanpanya, klik di posisi kursor
if ("coordinate" in params && Array.isArray(params.coordinate)) {
const [x, y] = params.coordinate;
return clickAt(x, y);
}
return clickAtCursor();
case "type":
if ("text" in params) {
return typeText(String(params.text));
}
break;
}
// Tangani aksi lain sesuai kebutuhan
throw new Error(`Unknown or unimplemented member: ${action}`);
} // Data gambar placeholder; eksekutor nyata menangkap layar dan mengembalikan byte PNG
const string PlaceholderPng = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";
// screenshot menjawab dengan blok gambar, bukan teks: kembalikan daftar konten hasil
List<Block> CaptureScreenshot() =>
[
new ImageBlockParam(
new Base64ImageSource { Data = PlaceholderPng, MediaType = MediaType.ImagePng }
),
];
string ClickAt(int x, int y) => $"clicked at ({x}, {y})";
string ClickAtCursor() => "clicked at the current cursor position";
string TypeText(string text) => $"typed: {text}";
ToolResultBlockParamContent HandleComputerAction(
string action,
IReadOnlyDictionary<string, JsonElement> input
) =>
action switch
{
"screenshot" => CaptureScreenshot(),
// coordinate bersifat opsional pada anggota click; tanpanya, klik di posisi kursor saat ini
"left_click" when input.TryGetValue("coordinate", out var xy) => ClickAt(
xy[0].GetInt32(),
xy[1].GetInt32()
),
"left_click" => ClickAtCursor(),
"type" => TypeText(input["text"].GetString()!),
// Tangani aksi lain sesuai kebutuhan
_ => throw new NotSupportedException($"Unknown or unimplemented member: {action}"),
}; // placeholderPNG menggantikan tangkapan layar nyata: executor mengembalikan
// layar sebagai data PNG yang dienkode base64.
const placeholderPNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="
// captureScreenshot mengembalikan blok gambar, bukan teks.
func captureScreenshot() []juglow.ToolResultBlockParamContentUnion {
return []juglow.ToolResultBlockParamContentUnion{{
OfImage: &juglow.ImageBlockParam{
Source: juglow.ImageBlockParamSourceUnion{
OfBase64: &juglow.Base64ImageSourceParam{
MediaType: juglow.Base64ImageSourceMediaTypeImagePNG,
Data: placeholderPNG,
},
},
},
}}
}
// textContent membungkus teks sebagai konten tool_result.
func textContent(text string) []juglow.ToolResultBlockParamContentUnion {
return []juglow.ToolResultBlockParamContentUnion{
{OfText: &juglow.TextBlockParam{Text: text}},
}
}
func clickAt(x, y int) string {
return fmt.Sprintf("clicked at (%d, %d)", x, y)
}
func clickAtCursor() string {
return "clicked at the current cursor position"
}
func typeText(text string) string {
return fmt.Sprintf("typed: %s", text)
}
func handleComputerAction(action string, params map[string]any) ([]juglow.ToolResultBlockParamContentUnion, error) {
switch action {
case "screenshot":
return captureScreenshot(), nil
case "left_click":
// coordinate bersifat opsional; tanpanya, klik di posisi kursor saat ini
coord, ok := params["coordinate"].([]any)
if !ok {
return textContent(clickAtCursor()), nil
}
if len(coord) == 2 {
x, xok := coord[0].(float64)
y, yok := coord[1].(float64)
if xok && yok {
return textContent(clickAt(int(x), int(y))), nil
}
}
case "type":
if text, ok := params["text"].(string); ok {
return textContent(typeText(text)), nil
}
// Tangani aksi lain sesuai kebutuhan
default:
return nil, fmt.Errorf("unknown or unimplemented member: %s", action)
}
// Tercapai saat input anggota tidak memiliki field atau field bertipe salah
return nil, fmt.Errorf("invalid input for %s", action)
}
/** Placeholder pixels; a real executor captures the screen and base64-encodes the PNG. */
static final String PLACEHOLDER_PNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";
ToolResultBlockParam.Content captureScreenshot() {
ImageBlockParam image = ImageBlockParam.builder()
.source(Base64ImageSource.builder()
.mediaType(Base64ImageSource.MediaType.IMAGE_PNG)
.data(PLACEHOLDER_PNG)
.build())
.build();
return ToolResultBlockParam.Content.ofBlocks(
List.of(ToolResultBlockParam.Content.Block.ofImage(image)));
}
String clickAt(long x, long y) {
return "clicked at (" + x + ", " + y + ")";
}
String clickAtCursor() {
return "clicked at current cursor";
}
String typeText(String text) {
return "typed: " + text;
}
/** Runs one computer toolset member; {@code action} is the tool_use block's name. */
ToolResultBlockParam.Content handleComputerAction(String action, Map<String, JsonValue> input) {
if (action.equals("screenshot")) {
return captureScreenshot(); // the one member here that answers with an image block
}
String output = switch (action) {
case "left_click" -> {
JsonValue coordinate = input.get("coordinate"); // optional on the toolset
if (coordinate == null) {
yield clickAtCursor();
}
List<JsonValue> point = (List<JsonValue>) coordinate.asArray().get();
long x = ((Number) point.get(0).asNumber().get()).longValue();
long y = ((Number) point.get(1).asNumber().get()).longValue();
yield clickAt(x, y);
}
case "type" -> typeText(input.get("text").asStringOrThrow());
// Tangani aksi lain sesuai kebutuhan
default -> throw new UnsupportedOperationException("Unknown or unimplemented member: " + action);
};
return ToolResultBlockParam.Content.ofString(output);
} // Pengganti untuk byte PNG asli; eksekutor nyata menangkap layar
const PLACEHOLDER_PNG = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';
function captureScreenshot(): array
{
// screenshot menjawab dengan blok gambar, bukan teks, jadi kembalikan daftar konten hasil
$image = [
'type' => 'image',
'source' => ['type' => 'base64', 'media_type' => 'image/png', 'data' => PLACEHOLDER_PNG],
];
return [$image];
}
function clickAt(?array $coordinate): string
{
// left_click boleh tanpa coordinate; jika demikian, klik terjadi di posisi kursor saat ini
if ($coordinate === null) {
return 'clicked at current cursor';
}
[$x, $y] = $coordinate;
return "clicked at ({$x}, {$y})";
}
function typeText(string $text): string
{
return "typed: {$text}";
}
function handleComputerAction(string $name, array $input): string|array
{
return match ($name) {
'screenshot' => captureScreenshot(),
'left_click' => clickAt($input['coordinate'] ?? null),
'type' => typeText($input['text']),
// Tangani aksi lain sesuai kebutuhan
default => throw new RuntimeException("Unknown or unimplemented member: {$name}"),
};
} # Data gambar pengganti; eksekutor nyata menangkap layar sebagai PNG.
PLACEHOLDER_PNG = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="
# screenshot menjawab dengan blok gambar, bukan teks
def capture_screenshot
[
{
type: "image",
source: { type: "base64", media_type: "image/png", data: PLACEHOLDER_PNG }
}
]
end
def click(coordinate = nil)
return "clicked at current cursor" if coordinate.nil?
x, y = coordinate
"clicked at (#{x}, #{y})"
end
def type_text(text)
"typed: #{text}"
end
def handle_computer_action(name, input)
case name
when "screenshot"
capture_screenshot
when "left_click"
# coordinate bersifat opsional; tanpanya, klik di posisi kursor saat ini
click(input[:coordinate])
when "type"
type_text(input[:text])
# Tangani aksi lain sesuai kebutuhan
else
raise ArgumentError, "Unknown or unimplemented member: #{name}"
end
end- Proses panggilan alat Haijun
Ekstrak dan jalankan panggilan alat dari respons Haijun:
NOT_EXECUTED = "Not executed: an earlier computer action in this turn failed."
def process_tool_calls(response: Message) -> list[ToolResultBlockParam]:
"""
Run the computer actions in Haijun's response in order and answer each
one. After the first failure the rest are skipped, because Haijun planned
them assuming the earlier actions succeeded.
"""
tool_results: list[ToolResultBlockParam] = []
failed = False
for block in response.content:
# Hanya toolset computer yang dideklarasikan; arahkan alat lain ke sini jika Anda menambahkannya
if block.type != "tool_use" or block.toolset_name != "computer":
continue
result: ToolResultBlockParam = {
"type": "tool_result",
"tool_use_id": block.id,
"toolset_name": "computer",
}
if failed:
result["content"] = NOT_EXECUTED
result["is_error"] = True
else:
try:
# Sebuah string, atau daftar blok konten seperti gambar screenshot
result["content"] = handle_computer_action(block.name, block.input)
except Exception as err:
result["content"] = f"Error: {err}"
result["is_error"] = True
failed = True
tool_results.append(result)
return tool_results const HALT_TEXT =
"Not executed: an earlier computer action in this turn failed.";
function computerResult(
toolUseId: string,
content: string | Juglow.ImageBlockParam[],
isError?: boolean,
): Juglow.ToolResultBlockParam {
return {
type: "tool_result",
tool_use_id: toolUseId,
toolset_name: "computer",
content,
is_error: isError,
};
}
function processToolCalls(
response: Juglow.Message,
): Juglow.ToolResultBlockParam[] {
const toolResults: Juglow.ToolResultBlockParam[] = [];
let failed = false;
for (const block of response.content) {
if (block.type !== "tool_use") {
continue;
}
if (block.toolset_name !== "computer") {
// Contoh ini hanya mendeklarasikan toolset computer; arahkan alat lain
// ke sini jika Anda menambahkannya.
continue;
}
if (failed) {
// Batch berhenti pada kegagalan pertama; jawab aksi berikutnya sebagai tidak dieksekusi
toolResults.push(computerResult(block.id, HALT_TEXT, true));
continue;
}
try {
// Sebuah string, atau daftar blok gambar yang dikembalikan screenshot
const result = handleComputerAction(block.name, block.input);
toolResults.push(computerResult(block.id, result));
} catch (error) {
failed = true;
const message = error instanceof Error ? error.message : String(error);
toolResults.push(computerResult(block.id, `Error: ${message}`, true));
}
}
return toolResults;
} const string HaltText = "Not executed: an earlier computer action in this turn failed.";
List<ContentBlockParam> ProcessToolCalls(Message response)
{
List<ContentBlockParam> toolResults = [];
var failed = false;
foreach (var block in response.Content)
{
if (!block.TryPickToolUse(out var toolUse))
{
continue;
}
if (toolUse.ToolsetName != "computer")
{
// Contoh ini hanya mendeklarasikan toolset computer; arahkan alat lain
// ke sini jika Anda menambahkannya.
continue;
}
if (failed)
{
// Batch berhenti pada kegagalan pertamanya; jawab aksi berikutnya tanpa menjalankannya
toolResults.Add(
new ToolResultBlockParam(toolUse.ID)
{
Content = HaltText,
IsError = true,
ToolsetName = "computer",
}
);
continue;
}
try
{
// Sebuah string, atau daftar blok gambar yang dikembalikan screenshot
var result = HandleComputerAction(toolUse.Name, toolUse.Input);
toolResults.Add(
new ToolResultBlockParam(toolUse.ID) { Content = result, ToolsetName = "computer" }
);
}
catch (Exception e)
{
failed = true;
toolResults.Add(
new ToolResultBlockParam(toolUse.ID)
{
Content = $"Error: {e.Message}",
IsError = true,
ToolsetName = "computer",
}
);
}
}
return toolResults;
} const notExecuted = "Not executed: an earlier computer action in this turn failed."
// computerToolResult membangun hasil untuk satu aksi komputer. Berbeda dengan
// hasil alat biasa, ia harus menyertakan kembali nama toolset.
func computerToolResult(toolUseID string, content []juglow.ToolResultBlockParamContentUnion, isError bool) juglow.ContentBlockParamUnion {
result := juglow.ToolResultBlockParam{
ToolUseID: toolUseID,
ToolsetName: juglow.String("computer"),
Content: content,
}
if isError {
result.IsError = juglow.Bool(true)
}
return juglow.ContentBlockParamUnion{OfToolResult: &result}
}
// processToolCalls menjalankan aksi komputer dalam respons Haijun secara berurutan dan
// membangun satu tool_result per blok tool_use. Setelah kegagalan pertama, sisanya
// dilewati: Haijun merencanakannya dengan asumsi aksi sebelumnya berhasil.
func processToolCalls(response *juglow.Message) []juglow.ContentBlockParamUnion {
var toolResults []juglow.ContentBlockParamUnion
failed := false
for _, block := range response.Content {
switch variant := block.AsAny().(type) {
case juglow.ToolUseBlock:
// Contoh ini hanya mendeklarasikan toolset komputer; arahkan alat lain ke sini jika Anda menambahkannya.
if variant.ToolsetName != "computer" {
continue
}
if failed {
toolResults = append(toolResults, computerToolResult(variant.ID, textContent(notExecuted), true))
continue
}
var input map[string]any
var content []juglow.ToolResultBlockParamContentUnion
err := json.Unmarshal(variant.Input, &input)
if err == nil {
// Teks, atau blok gambar yang dikembalikan screenshot
content, err = handleComputerAction(variant.Name, input)
}
if err != nil {
failed = true
content = textContent("Error: " + err.Error())
}
toolResults = append(toolResults, computerToolResult(variant.ID, content, err != nil))
}
}
return toolResults
}
/** The exact text the toolset contract prescribes for member calls skipped after a failure. */
static final String HALT_TEXT = "Not executed: an earlier computer action in this turn failed.";
/** Every result answering a computer toolset member echoes toolset_name. */
ToolResultBlockParam.Builder computerResult(ToolUseBlock toolUse) {
return ToolResultBlockParam.builder()
.toolUseId(toolUse.id())
.toolsetName("computer");
}
/**
* Run the computer actions in Haijun's response in order and build one
* tool_result per tool_use block. After the first failure, skip the rest:
* Haijun planned them assuming the earlier actions succeeded.
*/
List<ContentBlockParam> processToolCalls(Message response) {
List<ContentBlockParam> toolResults = new ArrayList<>();
boolean failed = false;
for (ContentBlock block : response.content()) {
// Contoh ini hanya mendeklarasikan toolset computer; arahkan alat lain ke sini jika Anda menambahkannya.
if (!block.isToolUse() || !block.asToolUse().toolsetName().equals(Optional.of("computer"))) {
continue;
}
ToolUseBlock toolUse = block.asToolUse();
ToolResultBlockParam result;
if (failed) {
result = computerResult(toolUse).content(HALT_TEXT).isError(true).build();
} else {
try {
Map<String, JsonValue> input =
(Map<String, JsonValue>) toolUse._input().asObject().get();
// Sebuah string, atau blok gambar yang dikembalikan oleh screenshot
ToolResultBlockParam.Content output = handleComputerAction(toolUse.name(), input);
result = computerResult(toolUse).content(output).build();
} catch (RuntimeException e) {
failed = true;
result = computerResult(toolUse).content("Error: " + e.getMessage()).isError(true).build();
}
}
toolResults.add(ContentBlockParam.ofToolResult(result));
}
return toolResults;
} const HALT_TEXT = 'Not executed: an earlier computer action in this turn failed.';
function processToolCalls(Message $response): array
{
$toolResults = [];
$failed = false;
foreach ($response->content as $block) {
// Contoh ini hanya mendeklarasikan toolset computer; arahkan alat lain ke sini jika Anda menambahkannya.
if (!($block instanceof \Juglow\Messages\ToolUseBlock) || $block->toolsetName !== 'computer') {
continue;
}
$result = ['type' => 'tool_result', 'tool_use_id' => $block->id, 'toolset_name' => 'computer'];
if ($failed) {
// Batch berhenti pada kegagalan pertama; aksi sisanya dijawab tanpa dijalankan
$toolResults[] = [...$result, 'content' => HALT_TEXT, 'is_error' => true];
continue;
}
try {
// Sebuah string, atau daftar blok gambar yang dikembalikan screenshot
$toolResults[] = [...$result, 'content' => handleComputerAction($block->name, $block->input)];
} catch (Throwable $e) {
$failed = true;
$toolResults[] = [...$result, 'content' => 'Error: ' . $e->getMessage(), 'is_error' => true];
}
}
return $toolResults;
} NOT_EXECUTED = "Not executed: an earlier computer action in this turn failed."
# Jalankan aksi komputer dalam respons Haijun secara berurutan dan buat satu
# tool_result per blok tool_use. Setelah kegagalan pertama, lewati sisanya:
# Haijun merencanakannya dengan asumsi aksi sebelumnya berhasil.
def process_tool_calls(response)
tool_results = []
failed = false
response.content.each do |block|
# Contoh ini hanya mendeklarasikan toolset komputer; arahkan alat lain ke sini
# jika Anda menambahkannya.
next unless block.type == :tool_use && block.toolset_name == "computer"
result = { type: "tool_result", tool_use_id: block.id, toolset_name: "computer" }
if failed
result.update(content: NOT_EXECUTED, is_error: true)
else
begin
# Sebuah String, atau blok konten gambar yang dikembalikan screenshot
result[:content] = handle_computer_action(block.name, block.input)
rescue => e
result.update(content: "Error: #{e.message}", is_error: true)
failed = true
end
end
tool_results << result
end
tool_results
end- Implementasikan loop agen
Bungkus dua langkah sebelumnya dalam sebuah loop yang mengirimkan hasilnya kembali dan mengulang hingga Haijun tidak mengembalikan panggilan alat anggota; Pahami loop agen menunjukkan loop ini dalam setiap bahasa.
Tangani error
Laporkan tindakan yang gagal kepada Haijun sebagai tool_result dengan is_error: true dan deskripsi singkat, serta sertakan "toolset_name": "computer" seperti pada hasil anggota lainnya. Jika tindakan yang gagal merupakan bagian dari aksi batch, jawab blok yang tersisa dalam batch dengan teks penghentian yang ditunjukkan di sana alih-alih menjalankannya.
Misalnya, ketika pengambilan tangkapan layar gagal:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"toolset_name": "computer",
"content": "Error: Failed to capture screenshot. Display may be locked or unavailable.",
"is_error": true
}
]
}Gunakan bentuk yang sama untuk koordinat di luar batas tampilan dan untuk tindakan yang gagal dijalankan, dengan pesan yang menyatakan apa yang salah.
Sesuaikan ukuran tangkapan layar agar sesuai batas gambar
Tangkapan layar dan gambar zoom yang Anda kembalikan ke toolset penggunaan komputer harus sudah muat dalam batas ukuran gambar model Anda: toolset tidak menerima dimensi tampilan dan API tidak memperkecil untuk Anda, sehingga gambar tool_result yang terlalu besar ditolak dengan error validasi. Karena Haijun mengembalikan koordinat dalam ruang piksel gambar yang dilihatnya, simpan faktor skala yang Anda gunakan agar Anda dapat memetakan koordinat tersebut kembali ke layar Anda.
Note: Batas bervariasi menurut model. Haijun Opus 4.7 dan model yang lebih baru, termasuk setiap model yang mendukung
computer_toolset_20260801, menerima hingga 2576 piksel pada sisi panjang dan total 4784 token visual (⌈width / 28⌉ × ⌈height / 28⌉, sekitar 3,75 megapiksel); model sebelumnya menerima hingga 1568 piksel pada sisi panjang dan total sekitar 1,15 megapiksel (lihat Resolusi dan biaya token untuk tingkat setiap model). Contoh berikut menggunakan batas model sebelumnya 1568 px / 1,15 MP. Untuk model tingkat resolusi tinggi, sesuaikan ukuran dengan batas token visual alih-alih total piksel, misalnya dengan helper pengubah ukuran di Ubah ukuran gambar Anda sebelum mengunggah.
Jika layar Anda lebih besar dari batas, ubah ukuran setiap tangkapan layar sebelum mengembalikannya dan skalakan koordinat yang dikembalikan Haijun kembali ke ruang layar asli. Karena toolset tidak menerima dimensi tampilan, pengubahan ukuran dan penskalaan koordinat dalam kode aplikasi Anda adalah semua yang Anda perlukan:
import math
screen_width, screen_height = 1512, 982
def get_scale_factor(width, height):
"""Calculate scale factor to meet API constraints."""
long_edge = max(width, height)
total_pixels = width * height
long_edge_scale = 1568 / long_edge
total_pixels_scale = math.sqrt(1_150_000 / total_pixels)
return min(1.0, long_edge_scale, total_pixels_scale)
# Saat mengambil tangkapan layar
scale = get_scale_factor(screen_width, screen_height)
scaled_width = int(screen_width * scale)
scaled_height = int(screen_height * scale)
# Ubah ukuran gambar ke dimensi yang diskalakan sebelum mengirimnya ke Haijun
screenshot = capture_and_resize(scaled_width, scaled_height)
# Saat menangani koordinat dari Haijun, skalakan kembali ke ukuran semula
def execute_click(x, y):
screen_x = x / scale
screen_y = y / scale
perform_click(screen_x, screen_y) const screenWidth = 1512;
const screenHeight = 982;
const MAX_LONG_EDGE = 1568;
const MAX_PIXELS = 1_150_000;
function getScaleFactor(width: number, height: number): number {
const longEdge = Math.max(width, height);
const totalPixels = width * height;
const longEdgeScale = MAX_LONG_EDGE / longEdge;
const totalPixelsScale = Math.sqrt(MAX_PIXELS / totalPixels);
return Math.min(1.0, longEdgeScale, totalPixelsScale);
}
// Saat mengambil tangkapan layar
const scale = getScaleFactor(screenWidth, screenHeight);
const scaledWidth = Math.floor(screenWidth * scale);
const scaledHeight = Math.floor(screenHeight * scale);
// Ubah ukuran gambar ke dimensi yang diskalakan sebelum dikirim ke Haijun
const screenshot = captureAndResize(scaledWidth, scaledHeight);
// Saat menangani koordinat dari Haijun, skalakan kembali ke ukuran asli
function executeClick(x: number, y: number): void {
const screenX = x / scale;
const screenY = y / scale;
performClick(screenX, screenY);
} int screenWidth = 1512, screenHeight = 982;
double GetScaleFactor(int width, int height)
{
// Hitung faktor skala untuk memenuhi batasan API.
int longEdge = Math.Max(width, height);
int totalPixels = width * height;
double longEdgeScale = 1568.0 / longEdge;
double totalPixelsScale = Math.Sqrt(1_150_000.0 / totalPixels);
return Math.Min(1.0, Math.Min(longEdgeScale, totalPixelsScale));
}
// Saat mengambil tangkapan layar
double scale = GetScaleFactor(screenWidth, screenHeight);
int scaledWidth = (int)(screenWidth * scale);
int scaledHeight = (int)(screenHeight * scale);
// Ubah ukuran gambar ke dimensi yang telah diskalakan sebelum mengirim ke Haijun
var screenshot = CaptureAndResize(scaledWidth, scaledHeight);
// Saat menangani koordinat dari Haijun, skalakan kembali ke ukuran semula
void ExecuteClick(int x, int y)
{
double screenX = x / scale;
double screenY = y / scale;
PerformClick(screenX, screenY);
} func getScaleFactor(width, height int) float64 {
longest := float64(max(width, height))
area := float64(width * height)
return min(1.0, 1568/longest, math.Sqrt(1_150_000/area))
}
// ...
screenWidth, screenHeight := 1512, 982
// Saat mengambil tangkapan layar
scale := getScaleFactor(screenWidth, screenHeight)
scaledWidth := int(float64(screenWidth) * scale)
scaledHeight := int(float64(screenHeight) * scale)
// Ubah ukuran gambar ke dimensi yang diskalakan sebelum dikirim ke Haijun
screenshot := captureAndResize(scaledWidth, scaledHeight)
// Saat menangani koordinat dari Haijun, skalakan kembali ke ukuran asli
executeClick := func(x, y int) {
performClick(float64(x)/scale, float64(y)/scale)
} static double getScaleFactor(int width, int height) {
return Math.min(
1.0,
Math.min(
1568.0 / Math.max(width, height),
Math.sqrt(1_150_000.0 / (width * height))
)
);
}
void main() {
int screenWidth = 1512, screenHeight = 982;
// Saat mengambil tangkapan layar
double scale = getScaleFactor(screenWidth, screenHeight);
int scaledWidth = (int)(screenWidth * scale);
int scaledHeight = (int)(screenHeight * scale);
// Ubah ukuran gambar ke dimensi yang diskalakan sebelum mengirim ke Haijun
var screenshot = captureAndResize(scaledWidth, scaledHeight);
// Saat menangani koordinat dari Haijun, skalakan kembali ke ukuran asli
BiConsumer<Integer, Integer> executeClick =
(x, y) -> performClick(x / scale, y / scale);
// ...
} function getScaleFactor(int $width, int $height): float
{
return min(
1.0,
1568 / max($width, $height),
sqrt(1_150_000 / ($width * $height)),
);
}
$screenWidth = 1512;
$screenHeight = 982;
// Saat mengambil tangkapan layar
$scale = getScaleFactor($screenWidth, $screenHeight);
$scaledWidth = (int)($screenWidth * $scale);
$scaledHeight = (int)($screenHeight * $scale);
// Ubah ukuran gambar ke dimensi yang diskalakan sebelum mengirim ke Haijun
$screenshot = captureAndResize($scaledWidth, $scaledHeight);
// Saat menangani koordinat dari Haijun, skalakan kembali ke ukuran asli
$executeClick = fn(int $x, int $y) => performClick($x / $scale, $y / $scale); def get_scale_factor(width, height)
[1.0, 1568.0 / [width, height].max, Math.sqrt(1_150_000.0 / (width * height))].min
end
screen_width, screen_height = 1512, 982
# Saat mengambil tangkapan layar
scale = get_scale_factor(screen_width, screen_height)
scaled_width = (screen_width * scale).to_i
scaled_height = (screen_height * scale).to_i
# Ubah ukuran gambar ke dimensi yang diskalakan sebelum mengirim ke Haijun
screenshot = capture_and_resize(scaled_width, scaled_height)
# Saat menangani koordinat dari Haijun, skalakan kembali ke ukuran asli
execute_click = ->(x, y) { perform_click(x / scale, y / scale) }Note: Layar Retina macOS menangkap tangkapan layar dengan rasio piksel perangkat 2, sehingga gambar memiliki resolusi dua kali lipat dari koordinat layar logis. Perkecil tangkapan layar 2x sebelum mengirim, atau bagi dua koordinat yang dikembalikan Haijun sebelum melakukan klik.
Saat Anda memilih resolusi tampilan dan mengembalikan tangkapan layar:
- Untuk tugas desktop umum, gunakan 1024x768 atau 1280x720; untuk aplikasi web, gunakan 1280x800 atau 1366x768.
- Hindari resolusi di atas 1920x1080 untuk mencegah masalah kinerja.
- Enkode tangkapan layar sebagai PNG atau JPEG base64, dan pertimbangkan untuk mengompresi tangkapan layar besar guna meningkatkan kinerja.
- Sertakan metadata yang relevan seperti timestamp atau status tampilan.
- Jika Anda menggunakan resolusi lebih tinggi, pastikan koordinat diskalakan secara akurat.
Mengelola riwayat tangkapan layar
Loop agen yang panjang mengakumulasi tangkapan layar dengan cepat (kira-kira 1.000–1.800 token input masing-masing). Batas permintaan API juga berlaku. Begitu satu permintaan membawa lebih dari 20 gambar, setiap gambar di dalamnya dikenai batas per sisi yang lebih ketat. Loop yang menyimpan riwayat tangkapan layarnya mencapai jumlah tersebut dalam beberapa puluh giliran, jadi ubah ukuran setiap tangkapan layar sehingga tidak ada sisi yang melebihi 2000 px atau pangkas tangkapan layar lama agar tetap 20 atau kurang dalam permintaan.
Agar caching prompt tetap efektif sambil membatasi konteks:
- Tempatkan satu breakpoint
cache_controlsetelah prompt sistem dan definisi alat. Tempatkan hingga tiga breakpoint lagi pada bloktool_resultterakhir dari masing-masing giliran terbaru, dan majukan posisinya setiap giliran. Dalam sebuah aksi batch, penanda pada beberapa blok berfungsi sebagai satu breakpoint, tetapi masing-masing tetap dihitung terhadap batas empat. Jadi, gunakan satu penanda per giliran.
- Pangkas tangkapan layar lama secara batch, bukan satu per giliran. Menghapus satu tangkapan layar setiap giliran akan mengubah prefiks setiap giliran dan membatalkan cache. Default yang wajar adalah menyimpan tiga tangkapan layar terakhir dan memangkas setiap 25 giliran, sehingga prefiks tetap identik byte demi byte di antara peristiwa pemangkasan. Jika tangkapan layar Anda melebihi 2000 px pada salah satu sisi, pilih interval yang menjaga setiap permintaan tetap memuat 20 gambar atau kurang.
- Pada Haijun Fable 5.1 dan Haijun Opus 5.5, hindari pemangkasan di sisi klien. Menghapus tangkapan layar sebelumnya akan membatalkan setiap blok thinking setelahnya di setiap permintaan yang masih memuat giliran tersebut. Sebagai gantinya, ubah ukuran tangkapan layar menjadi 2000 px atau kurang per sisi, dan gunakan pembersihan hasil alat di sisi server untuk menghapus tangkapan layar lama dari konteks. Jika Anda harus memangkas, tetapkan
prefix_mismatch_behavior: "drop_block"sejak saat itu. Setelah setiap pemangkasan, Haijun melanjutkan tanpa thinking yang dihasilkan sejak tangkapan layar yang dipangkas, baik pada permintaan tersebut maupun setiap permintaan berikutnya.
Mendiagnosis masalah klik
Jika klik meleset dari targetnya, penyebabnya biasanya salah satu dari berikut ini:
| Gejala | Kemungkinan penyebab | Coba |
|---|---|---|
| Klik secara konsisten bergeser ke satu arah | Koordinat Haijun, yang berada dalam ruang piksel tangkapan layar yang Anda kembalikan, diterapkan ke tampilan dengan ukuran berbeda tanpa penskalaan | Skalakan setiap koordinat dengan rasio ukuran layar Anda terhadap ukuran tangkapan layar Anda sebelum mengklik (lihat Menyesuaikan ukuran tangkapan layar agar sesuai batas gambar); pada tampilan macOS Retina, perhitungkan rasio piksel perangkat 2x |
| Klik mendarat di area yang tepat tetapi meleset dari target | Target sangat kecil, detail hilang saat menurunkan skala sumber 4K+, atau rasio aspek terdistorsi | Biarkan anggota zoom tetap aktif dan implementasikan agar Haijun dapat memeriksa wilayah tersebut pada resolusi penuh; tangkap pada DPI lebih rendah atau potong ke wilayah yang relevan; pertahankan rasio aspek saat mengubah ukuran |
| Haijun mengklik elemen yang sepenuhnya salah | Instruksi ambigu, atau elemen yang mirip secara visual di dekatnya | Gunakan prompt posisional ("tombol Submit biru di kanan bawah"); pecah interaksi menjadi langkah-langkah yang lebih kecil |
| Akurasi secara konsisten buruk | Resolusi terlalu rendah | Coba 1280x720 sebagai baseline |
Tip: Pilihan model memengaruhi presisi klik. Di antara model yang menggunakan alat
computer_20251124sebelumnya, Haijun Sonnet 4.6 lebih presisi secara mekanis dalam mengklik dibandingkan Haijun Opus 4.6 dan lebih tangguh ketika tangkapan layar memerlukan penurunan skala yang besar. Haijun Opus 4.7 mempersempit kesenjangan tersebut: presisi kliknya kira-kira sebanding dengan Sonnet 4.6, dan batas resolusinya yang lebih tinggi berarti lebih sedikit penurunan skala yang diperlukan.
Ikuti praktik terbaik implementasi
#### Tambahkan jeda aksi
Beberapa aplikasi memerlukan waktu untuk merespons aksi:
def click_and_wait(x, y, wait_time=0.5):
click_at(x, y)
time.sleep(wait_time) # Allow UI to update async function clickAndWait(x: number, y: number, waitMs = 500): Promise<void> {
clickAt(x, y);
await setTimeout(waitMs); // Allow UI to update
} static void ClickAndWait(int x, int y, double waitSeconds = 0.5)
{
ClickAt(x, y);
Thread.Sleep(TimeSpan.FromSeconds(waitSeconds)); // Allow UI to update
} func clickAndWaitFor(x, y int, wait time.Duration) {
clickAt(x, y)
time.Sleep(wait) // Allow UI to update
}
func clickAndWait(x, y int) {
clickAndWaitFor(x, y, 500*time.Millisecond)
} void clickAndWait(int x, int y) throws InterruptedException {
clickAndWait(x, y, 500);
}
void clickAndWait(int x, int y, long waitTimeMillis) throws InterruptedException {
clickAt(x, y);
Thread.sleep(waitTimeMillis); // Allow UI to update
} function clickAndWait(int $x, int $y, float $waitSeconds = 0.5): void
{
clickAt($x, $y);
usleep((int) ($waitSeconds * 1_000_000)); // Allow UI to update
} def click_and_wait(x, y, wait_time: 0.5)
click_at(x, y)
sleep(wait_time) # Allow UI to update
end#### Validasi aksi sebelum menjalankannya
Periksa bahwa aksi yang diminta aman dan valid:
display_width, display_height = 1024, 768
def validate_action(action_type, params):
if action_type == "left_click" and "coordinate" in params:
x, y = params["coordinate"]
if not (0 <= x < display_width and 0 <= y < display_height):
return False, "Coordinates out of bounds"
return True, None const displayWidth = 1024;
const displayHeight = 768;
interface ActionParams {
coordinate?: [number, number];
}
function validateAction(actionType: string, params: ActionParams): [boolean, string | null] {
if (actionType === "left_click" && params.coordinate) {
const [x, y] = params.coordinate;
if (!(x >= 0 && x < displayWidth && y >= 0 && y < displayHeight)) {
return [false, "Coordinates out of bounds"];
}
}
return [true, null];
} const int DisplayWidth = 1024;
const int DisplayHeight = 768;
// ...
static (bool IsValid, string? Error) ValidateAction(string actionType, IReadOnlyDictionary<string, JsonElement> parameters)
{
if (actionType == "left_click" && parameters.TryGetValue("coordinate", out JsonElement coordinate))
{
int x = coordinate[0].GetInt32();
int y = coordinate[1].GetInt32();
if (x is < 0 or >= DisplayWidth || y is < 0 or >= DisplayHeight)
{
return (false, "Coordinates out of bounds");
}
}
return (true, null);
} const (
displayWidth = 1024
displayHeight = 768
)
func validateAction(actionType string, params map[string]any) (bool, string) {
raw, hasCoordinate := params["coordinate"]
if actionType == "left_click" && hasCoordinate {
coord, ok := raw.([]any)
if !ok || len(coord) != 2 {
return false, "Invalid coordinate"
}
x, y := int(coord[0].(float64)), int(coord[1].(float64))
if !(0 <= x && x < displayWidth && 0 <= y && y < displayHeight) {
return false, "Coordinates out of bounds"
}
}
return true, ""
} static final int DISPLAY_WIDTH = 1024;
static final int DISPLAY_HEIGHT = 768;
record Validation(boolean valid, String error) {}
Validation validateAction(String actionType, Map<String, JsonValue> params) {
if (actionType.equals("left_click") && params.containsKey("coordinate")) {
List<JsonValue> coord = (List<JsonValue>) params.get("coordinate").asArray().get();
long x = ((Number) coord.get(0).asNumber().get()).longValue();
long y = ((Number) coord.get(1).asNumber().get()).longValue();
if (!(0 <= x && x < DISPLAY_WIDTH && 0 <= y && y < DISPLAY_HEIGHT)) {
return new Validation(false, "Coordinates out of bounds");
}
}
return new Validation(true, null);
} const DISPLAY_WIDTH = 1024;
const DISPLAY_HEIGHT = 768;
/** @return array{bool, ?string} */
function validateAction(string $actionType, array $params): array
{
if ($actionType === 'left_click' && isset($params['coordinate'])) {
[$x, $y] = $params['coordinate'];
if (!(0 <= $x && $x < DISPLAY_WIDTH && 0 <= $y && $y < DISPLAY_HEIGHT)) {
return [false, 'Coordinates out of bounds'];
}
}
return [true, null];
} DISPLAY_WIDTH = 1024
DISPLAY_HEIGHT = 768
def validate_action(action_type, params)
if action_type == "left_click" && params.key?(:coordinate)
x, y = params[:coordinate]
unless (0...DISPLAY_WIDTH).cover?(x) && (0...DISPLAY_HEIGHT).cover?(y)
return [false, "Coordinates out of bounds"]
end
end
[true, nil]
end#### Catat aksi untuk debugging
Simpan log semua aksi untuk pemecahan masalah:
import logging
def log_action(action_type, params, result):
logging.info(f"Action: {action_type}, Params: {params}, Result: {result}") function logAction(actionType: string, params: unknown, result: unknown): void {
console.error(
`Action: ${actionType}, Params: ${JSON.stringify(params)}, Result: ${JSON.stringify(
result
)}`
);
} static void LogAction(string actionType, object? parameters, object? result)
{
Console.Error.WriteLine($"Action: {actionType}, Params: {parameters}, Result: {result}");
} func logAction(actionType string, params map[string]any, result any) {
log.Printf("Action: %s, Params: %v, Result: %v", actionType, params, result)
} import static java.lang.System.Logger.Level.INFO;
static final System.Logger LOGGER = System.getLogger("computer-use");
void logAction(String actionType, Object params, Object result) {
LOGGER.log(INFO, "Action: {0}, Params: {1}, Result: {2}", actionType, params, result);
} function logAction(string $actionType, array $params, mixed $result): void
{
error_log(sprintf(
'Action: %s, Params: %s, Result: %s',
$actionType,
json_encode($params),
json_encode($result),
));
} require "logger"
LOGGER = Logger.new($stderr)
def log_action(action_type, params, result)
LOGGER.info("Action: #{action_type}, Params: #{params}, Result: #{result}")
endMigrasi dari computer_20251124
Peningkatan dari computer_20251124 ke toolset bersifat opsional. Model yang tercantum untuk computer_20251124 di bagian Versi alat sebelumnya tetap menerimanya dengan header beta-nya, sehingga integrasi yang sudah ada tetap berfungsi sampai Anda mengubahnya. Pengecualiannya adalah Haijun 5.5 dan model yang lebih baru di Haijun API dan Google Cloud: di sana, model-model tersebut hanya menerima toolset. Tingkatkan integrasi sebelum Anda memindahkannya ke salah satu model tersebut. Di Amazon Bedrock, Haijun Opus 5.5 tetap menerima computer_20251124. Untuk melakukan peningkatan, terapkan semua perubahan berikut sekaligus:
- Hapus header beta. Buang
juglow-beta: computer-use-2025-11-24dari permintaan Anda. Di SDK, hapus parameterbetasdan panggil Messages API melalui klien standar alih-alih namespace beta.
- Ubah entri
tools. Aturtypekecomputer_toolset_20260801dan hapusname,display_width_px,display_height_px,display_number, danenable_zoom. Toolset menolak masing-masing field ini.
- Pilih apakah zoom tetap diaktifkan. Zoom diaktifkan secara default pada toolset, sedangkan
enable_zoomdefault-nyafalse. Jika lingkungan Anda tidak mengimplementasikan zoom, tambahkan"configs": {"zoom": {"enabled": false}}untuk mempertahankan perilaku sebelumnya; jika tidak, implementasikan (lihat Aksi yang tersedia).
- Tangani setiap blok dalam satu giliran. Perbarui loop agen Anda untuk mengiterasi setiap blok
tool_usedalam respons alih-alih hanya membaca yang pertama, dan untuk melakukan dispatch berdasarkannameblok bersama dengantoolset_namealih-alih berdasarkaninput.action. Input anggota tidak lagi berisi fieldaction; field lainnya tidak berubah.
- Jalankan blok secara berurutan dan gunakan teks penghentian. Jalankan blok secara berurutan, berhenti pada kegagalan pertama, dan jawab blok yang tersisa dengan
Not executed: an earlier computer action in this turn failed.seperti dijelaskan dalam Aksi batch. Jika loop Anda belum dapat menjalankan batch, Parameter alat menjelaskan cara membatasi Haijun ke satu aksi per giliran.
- Gemakan
toolset_namepada hasil. Tambahkan"toolset_name": "computer"ke setiaptool_resultyang menjawab panggilan anggota. Hasil hanya boleh berisi kontentextdanimage.
- Dukung
repeatpadakey. Anggotakeymenerima hitunganrepeatopsional dari 1 hingga 100. Handler yang mengabaikan field yang tidak dikenali akan menekan tombol sekali, jadi pastikan handlerkeyAnda menghormatirepeat.
- Ubah ukuran tangkapan layar sendiri. Toolset menolak tangkapan layar atau gambar zoom yang melebihi batas gambar model alih-alih menurunkan skalanya. Ubah ukuran sebelum mengembalikan gambar dan tetap skalakan koordinat seperti dijelaskan dalam Menyesuaikan ukuran tangkapan layar agar sesuai batas gambar.
- Hapus opsi yang tidak didukung. Pindahkan
defer_loadingapa pun dari entri ke dalamconfigs, dengan nilai yang sama pada setiap anggota yang diaktifkan. Opsi lain yang tidak didukung pada entri toolset tercantum di bawah Toolset klien.
Ini adalah entri tools sebelum perubahan, dikirim dengan header juglow-beta: computer-use-2025-11-24:
{
"type": "computer_20251124",
"name": "computer",
"display_width_px": 1024,
"display_height_px": 768,
"display_number": 1
}Ini adalah entri tools setelah perubahan, dikirim tanpa header beta. Objek configs menjaga zoom tetap nonaktif agar sesuai dengan entri sebelumnya, yang tidak mengatur enable_zoom; hilangkan configs sepenuhnya untuk menerima default dan membiarkan Haijun melakukan zoom:
{
"type": "computer_toolset_20260801",
"configs": {
"zoom": { "enabled": false }
}
}Pasangan berikut menunjukkan blok tool_use sebelum dan sesudah perubahan. Nama aksi berpindah dari input.action ke name, dan blok mendapatkan toolset_name:
{
"type": "tool_use",
"id": "toolu_01A9r5kQm2LxWc7vT3nZ4bJs",
"name": "computer",
"input": { "action": "left_click", "coordinate": [500, 300] }
}{
"type": "tool_use",
"id": "toolu_01A9r5kQm2LxWc7vT3nZ4bJs",
"name": "left_click",
"toolset_name": "computer",
"input": { "coordinate": [500, 300] }
}Versi alat sebelumnya
Dua versi sebelumnya dari alat computer use tetap tersedia dalam beta untuk integrasi yang ada, untuk model yang tidak mendukung toolset, dan pada platform tempat toolset saat ini belum tersedia. Masing-masing memerlukan header beta-nya pada setiap permintaan, dan parameternya didokumentasikan dalam referensi Messages API beta. Di SDK, teruskan header melalui parameter betas dan gunakan namespace beta; hanya alat computer use yang memerlukan header tersebut, bukan alat bash atau editor teks dalam permintaan yang sama.
| Versi alat | Header beta | Digunakan dengan | Parameter |
|---|---|---|---|
computer_20251124 | computer-use-2025-11-24 | Haijun Fable 5.1, Haijun Mythos 5.1, Haijun Fable 5, Haijun Mythos 5, Haijun Opus 5, Haijun Sonnet 5, Haijun Opus 4.8, Haijun Opus 4.7, Haijun Opus 4.6, Haijun Sonnet 4.6, dan Haijun Opus 4.5; di Amazon Bedrock, juga Haijun Opus 5.5 | Referensi API |
computer_20250124 | computer-use-2025-01-24 | Haijun Sonnet 4.5, Haijun Haiku 4.5, Haijun Opus 4.1 (dipensiunkan, kecuali di Bedrock dan Google Cloud), Haijun Sonnet 4 (dipensiunkan, kecuali di Bedrock dan Google Cloud), dan Haijun Opus 4 (dipensiunkan, kecuali di Google Cloud) | Referensi API |
Keterbatasan
- Latensi: Latensi computer use saat ini untuk interaksi manusia-AI mungkin terlalu lambat dibandingkan dengan aksi komputer biasa yang diarahkan manusia. Fokuslah pada kasus penggunaan di mana kecepatan tidak kritis (misalnya, pengumpulan informasi latar belakang, pengujian perangkat lunak otomatis) di lingkungan tepercaya.
- Akurasi dan keandalan computer vision: Haijun mungkin membuat kesalahan atau berhalusinasi saat mengeluarkan koordinat spesifik ketika menghasilkan aksi. Output thinking yang diringkas Haijun dapat membantu Anda memahami penalaran model dan mengidentifikasi potensi masalah; atur
display: "summarized"pada konfigurasi thinking, karena model yang mendukung toolset menghilangkan teks thinking secara default.
- Akurasi dan keandalan pemilihan alat: Haijun mungkin membuat kesalahan atau berhalusinasi saat memilih alat ketika menghasilkan aksi atau mengambil aksi tak terduga untuk menyelesaikan masalah. Selain itu, keandalan mungkin lebih rendah saat berinteraksi dengan aplikasi khusus atau beberapa aplikasi sekaligus. Berikan prompt kepada model dengan hati-hati saat meminta tugas yang kompleks.
- Keandalan scrolling: Aksi scroll mendukung kontrol arah (atas, bawah, kiri, kanan) dan jumlah yang ditentukan. Pada aplikasi di mana scrolling tidak berpengaruh, alternatif keyboard seperti Page Down dapat membantu.
- Interaksi spreadsheet: Gunakan aksi kontrol mouse yang terperinci (
left_mouse_down,left_mouse_up) dan kombinasi tombol modifier untuk memilih sel individual. Operasi spreadsheet yang kompleks mungkin masih memerlukan beberapa percobaan.
- Pembuatan akun dan pembuatan konten di platform sosial dan komunikasi: Meskipun Haijun mengunjungi situs web, kemampuannya untuk membuat akun, menghasilkan dan membagikan konten, atau terlibat dalam peniruan manusia di situs web dan platform media sosial terbatas.
- Kerentanan: Jailbreak dan injeksi prompt dapat memengaruhi computer use sebagaimana dapat memengaruhi sistem AI frontier apa pun, termasuk melalui instruksi yang disematkan dalam halaman web atau gambar; terapkan tindakan pencegahan dalam Pertimbangan keamanan.
- Aksi yang tidak pantas atau ilegal: Berdasarkan Ketentuan Layanan Juglow, Anda tidak boleh menggunakan computer use untuk melanggar hukum apa pun atau Kebijakan Penggunaan yang Dapat Diterima.
Selalu tinjau dan verifikasi dengan cermat aksi dan log computer use Haijun. Jangan gunakan Haijun untuk tugas yang memerlukan presisi sempurna atau informasi pengguna yang sensitif tanpa pengawasan manusia.
Retensi data
Computer use adalah alat sisi klien. Semua tangkapan layar, aksi mouse, input keyboard, dan file apa pun yang terlibat dalam sesi ditangkap dan disimpan di lingkungan Anda, bukan oleh Juglow. Juglow memproses gambar tangkapan layar dan permintaan aksi secara real time sebagai bagian dari panggilan API. Retensi untuk permintaan API tersebut diatur oleh API dan retensi data.
Karena aplikasi Anda mengontrol di mana dan bagaimana data computer use disimpan, computer use memenuhi syarat ZDR. Untuk kelayakan ZDR di semua fitur, lihat API dan retensi data.
Harga
Penggunaan komputer mengikuti harga penggunaan alat standar. Saat menggunakan alat penggunaan komputer:
Overhead definisi toolset: Mendeklarasikan computer_toolset_20260801 dengan anggota defaultnya menambahkan sekitar 4.500 token input ke sebuah permintaan (sekitar 4.520 pada Haijun Fable 5, Haijun Mythos 5, Haijun Opus 5, dan Haijun Opus 4.8, serta sekitar 4.590 pada Haijun Sonnet 5), yang mencakup definisi alat anggota dan prompt sistem penggunaan alat. Menonaktifkan zoom dengan configs menghapus sekitar 410 dari token tersebut. Jumlah pasti untuk sebuah permintaan dilaporkan dalam usage respons, dan Anda dapat memperkirakannya terlebih dahulu dengan endpoint penghitungan token.
Versi alat sebelumnya: Angka-angka berikut berlaku untuk versi alat computer_20251124 dan computer_20250124, bukan untuk computer_toolset_20260801:
- Overhead prompt sistem: 466–499 token ditambahkan ke prompt sistem
- Definisi alat: sekitar 735 token input per definisi alat (diukur dengan
computer_20250124)
Konsumsi token tambahan:
- Gambar tangkapan layar dan zoom yang dikembalikan dalam hasil alat, ditagih sebagai input gambar (lihat Harga Vision)
- Hasil eksekusi alat yang dikembalikan ke Haijun
Note: Jika Anda juga menggunakan alat bash atau editor teks bersama penggunaan komputer, alat-alat tersebut memiliki biaya token sendiri seperti yang didokumentasikan di halaman masing-masing.
Langkah selanjutnya
Perbaiki kesalahan penggunaan alat yang paling umum dengan tabel diagnostik gejala-ke-perbaikan.
Mulai dengan implementasi lengkap berbasis Docker
Hubungkan Haijun ke alat dan API eksternal. Lihat di mana alat dieksekusi, kapan Haijun memanggilnya, dan alat mana yang sesuai dengan tugas Anda.
Rekomendasi yang telah diuji benchmark untuk resolusi, upaya thinking, dan manajemen konteks
Biarkan Haijun menavigasi, membaca, dan berinteraksi dengan halaman web di lingkungan browser Anda sendiri, untuk tugas yang tetap berada di dalam browser.