Membaca di Kondisi Redup dan Terhalang
Gambar ini adalah menu bar Brasil yang difoto dalam pencahayaan redup. Tiga panel kaca mencantumkan minuman dengan teks emas, tetapi lampu gantung menghalangi sebagian panel kiri, panel kanan nyaris tak terlihat di tepi bingkai, dan pantulan kaca tersebar di permukaan.

Prompt tidak menyebut kecerahan, crop, atau teknik spesifik apa pun. Hanya meminta isinya:
image_path = Path("images/bar_menu.jpg")
image_part = types.Part.from_bytes(
data=image_path.read_bytes(), mime_type="image/jpeg"
)
prompt = (
"Read every drink listed on this menu board, including the ones "
"partially hidden behind the lamps and on the side panels. For "
"each drink, give me the name and beer style."
)
Visi standar membaca panel tengah yang terang tanpa masalah, mengambil delapan bir dari bagian "FRUTADAS" dan empat dari "CHOPP." Namun panel kiri kembali seperti ini:
[ANSWER]
### Left Panel (Partially visible)
* **[Unreadable] LAGER** | [Style unreadable]
* **RED LAGER** | [Style unreadable]
* **[Unreadable] LAGER** | [Style unreadable]
* **[Unreadable]GER** | [Style unreadable]
* **HONKERS ALE** | [Style unreadable]
* **[Unreadable]SELLE** | [Style unreadable]
Enam entri, sebagian besar dengan nama yang tidak terbaca dan semuanya tanpa gaya. Model melihat ada teks di sana tetapi tidak dapat membacanya.
Panggilan agen menjalankan dua putaran kode. Pertama, ia melakukan crop ketiga panel:
[CODE]
middle_panel = [200, 320, 950, 780]
left_panel = [200, 0, 950, 260]
right_panel = [200, 840, 700, 1000]
# ... crops and saves each region

Setelah mengamati hasil crop tersebut, model memutuskan panel kiri dan kanan membutuhkan pembesaran lebih lanjut. Ia menjalankan putaran kedua:
[CODE]
left_panel_top = [100, 0, 500, 250]
left_panel_bottom = [500, 0, 980, 250]
right_panel_top = [100, 800, 600, 1000]

Dengan pembesaran ekstra itu, model memulihkan nama yang diberi label tak terbaca oleh visi standar: Amber Lager, Honkers Ale (Goose Island), dan Demoiselle (Colorado) dari panel kiri, plus Wals Dubbel, Tripel, Quadrupel, dan Petroleum dari panel kanan. Model juga mengidentifikasi judul panel kiri sebagai "MISTURAS" dan judul panel kanan sebagai "ALCOÓLICAS."
Prompt tidak pernah mengatakan "crop panel" atau "perbesar area gelap." Ia meminta daftar minuman dan gayanya, dan model menelusuri balik dari tujuan itu.
Yang membuat contoh ini berbeda dari rak bahan makanan adalah pass kedua. Setelah putaran pertama crop, model melihat apa yang dimilikinya, memutuskan panel kiri dan kanan masih terlalu sulit dibaca, dan menjalankan putaran lain dengan pembesaran lebih ketat tanpa diminta.
Loop tersebut mengoreksi diri.
Itulah hasil dari membiarkan metodenya terbuka: model tidak hanya melakukan prapemrosesan sekali, ia beriterasi hingga hasilnya memenuhi standar yang ditetapkan oleh prompt.
Penghitungan dan Anotasi dengan Visi Agen di Gemini 3
Menu bar berhasil karena prompt membiarkan metodenya terbuka, dan model mengetahui apa yang harus dilakukan. Itu tidak selalu berlaku. Terkadang model membutuhkan instruksi eksplisit tentang alat mana yang digunakan, dan contoh berikut menunjukkan batasnya.
Gambar di bawah menampilkan koin yang berserakan di atas meja dan map hitam. Beberapa tumpang tindih, sebagian tertutup, dan campuran tembaga serta perak membuatnya mudah terlewat.

Pendekatan awal yang masuk akal adalah meminta model menganotasi setiap koin sehingga ia dapat menghitung dari penandaannya sendiri. Dokumentasi Google menegaskan bahwa visi agen dapat menggambar bounding box dan label langsung pada gambar menggunakan PIL dan OpenCV di sandbox. Maka:
image_path = Path("images/coins.jpg")
image_part = types.Part.from_bytes(
data=image_path.read_bytes(), mime_type="image/jpeg"
)
prompt_v1 = (
"Count every coin in this image. Draw a bounding box or circle "
"around each one and number them. Check for overlapping or "
"partially hidden coins. Give me the final count."
)
response_v1 = client.models.generate_content(
model=MODEL,
contents=[image_part, prompt_v1],
config=agentic_config,
)
print_response(response_v1, show_thoughts=True)
Model melakukan crop lima area, menganalisis masing-masing, dan mengembalikan hitungan 34. Namun perhatikan format jawabannya:
[ANSWER] Final Count: 34 coins in total.
[
{"box_2d": [387, 175, 451, 236], "label": "1"},
{"box_2d": [356, 196, 407, 248], "label": "2"},
{"box_2d": [319, 231, 375, 274], "label": "3"},
...
]
Ia mengembalikan koordinat bounding box sebagai JSON alih-alih menggambar apa pun di gambar. Prompt mengatakan "draw," tetapi model memperlakukannya sebagai tugas deskripsi dan memberikan keluaran teks terstruktur.
Dokumentasi eksekusi kode memberi petunjuk alasannya: "Meskipun model secara otomatis menangani pembesaran untuk detail kecil, Anda harus memintanya secara eksplisit untuk menggunakan kode untuk tugas lain." Kata "draw" cukup ambigu sehingga model memilih jalur hanya-teks.
Menggambarkan tugas dalam istilah kode yang Anda ingin dijalankan menghilangkan ambiguitas itu:
prompt_v2 = (
"Count every coin in this image. Use Python to draw a numbered "
"bounding box on the image around each coin you find. Check for "
"overlapping or partially hidden coins. After annotating, give "
"me the final count."
)
response_v2 = client.models.generate_content(
model=MODEL,
contents=[image_part, prompt_v2],
config=agentic_config,
)
print_response(response_v2, show_thoughts=True)
Kali ini, model menulis kode gambar yang sebenarnya:
[CODE]
import PIL.Image
import PIL.ImageDraw
img = PIL.Image.open('input_file_0.jpeg')
width, height = img.size
coins = [
[383, 175, 452, 227], [352, 196, 411, 255], ... # 34 total
]
draw = PIL.ImageDraw.Draw(img)
for i, coin in enumerate(coins):
ymin, xmin, ymax, xmax = coin
left, top, right, bottom = (xmin*width/1000, ymin*height/1000,
xmax*width/1000, ymax*height/1000)
draw.rectangle([left, top, right, bottom], outline='red', width=3)
draw.text((left, top), str(i + 1), fill='red')
[RESULT] (Outcome.OUTCOME_OK)
Total coins detected: 34
[IMAGE] image/png, 3,685,043 bytes
[ANSWER] I have identified and counted a total of 34 coins.

Setiap koin mendapat kotak merah dan nomor. Hitungan sebenarnya adalah 35 atau 36, tergantung bagaimana Anda menilai dua koin yang tumpang tindih di dekat bagian bawah bingkai.
Model menghasilkan 34, melewatkan satu atau dua di kluster tersebut. Namun anotasi berfungsi sebagai papan coret visual: alih-alih angka yang harus Anda terima begitu saja, Anda mendapatkan artefak yang dapat diverifikasi, yang bisa diperiksa kotak demi kotak.
Keluaran lengkap tanpa dipangkas untuk kedua panggilan tersedia di gist ini.
Ekstraksi Multi-Langkah dan Pembuatan Plot
Contoh sebelumnya melibatkan satu tugas utama masing-masing (membaca teks, menghitung objek). Yang satu ini merangkai beberapa tugas: crop tabel dari foto, mengekstrak angka ke DataFrame, dan menghasilkan bagan. Gambar tersebut adalah tabel pajak gaya IRS yang difoto di atas meja dengan kalkulator.

Prompt menjabarkan seluruh pipeline sekaligus:
image_path = Path("images/data_table.jpg")
image_part = types.Part.from_bytes(
data=image_path.read_bytes(), mime_type="image/jpeg"
)
prompt = (
"Crop and zoom into the table in this image. Extract the first "
"10 rows of data into a structured format. Identify the column "
"headers. Then create a bar chart comparing the values in the "
"numeric columns for those 10 rows. Summarize which row has the "
"highest and lowest values."
)
response = client.models.generate_content(
model=MODEL,
contents=[image_part, prompt],
config=agentic_config,
)
print_response(response, show_thoughts=True)
Model membutuhkan empat putaran kode untuk berpindah dari foto penuh ke bagan jadi. Tiga pertama adalah pembesaran progresif: ia melakukan crop tabel "Single or Married Filing Separately", lalu judul kolom, lalu 10 baris pertama data, menghasilkan lima gambar perantara sepanjang proses. Berikut crop pertama saat ia mengisolasi tabel:

Pada putaran keempat, ia telah membaca detail yang cukup untuk mengekstrak angka. Ia membuat pandas DataFrame, mencetak tabel, dan menghasilkan bagan batang berkelompok dalam satu blok kode:
[CODE]
import matplotlib.pyplot as plt
import pandas as pd
data = [
{"Higher Paying Job Wage": "$0 - 9,999",
"$0 - 9,999": 0, "$10,000 - 19,999": 940,
"$20,000 - 29,999": 1020, "$30,000 - 39,999": 1020},
... # 10 rows total
]
df = pd.DataFrame(data)
df.set_index("Higher Paying Job Wage").plot(kind='bar', figsize=(12, 6))
plt.title('Tax Table: Single or Married Filing Separately')
plt.ylabel('Tax Amount')
plt.savefig('tax_bar_chart.png')
Tabel yang diekstrak:
|
Higher Paying Job Wage |
$0 - 9,999 |
$10,000 - 19,999 |
$20,000 - 29,999 |
$30,000 - 39,999 |
|
$0 - 9,999 |
$0 |
$940 |
$1,020 |
$1,020 |
|
$10,000 - 19,999 |
$940 |
$1,540 |
$1,620 |
$2,020 |
|
$20,000 - 29,999 |
$1,020 |
$1,620 |
$2,100 |
$3,100 |
|
$30,000 - 39,999 |
$1,020 |
$2,020 |
$3,100 |
$4,100 |
|
$40,000 - 59,999 |
$1,870 |
$3,470 |
$4,550 |
$5,550 |
|
$60,000 - 79,999 |
$1,870 |
$3,470 |
$4,690 |
$5,890 |
|
$80,000 - 99,999 |
$2,000 |
$3,810 |
$5,090 |
$6,290 |
|
$100,000 - 124,999 |
$2,040 |
$3,840 |
$5,120 |
$6,320 |
|
$125,000 - 149,999 |
$2,040 |
$3,840 |
$5,120 |
$6,910 |
|
$150,000 - 174,999 |
$2,220 |
$4,830 |
$6,910 |
$8,910 |
Dan bagan batang yang dihasilkan:

Keluaran lengkap tanpa dipangkas tersedia di gist ini.
Satu panggilan API menghasilkan DataFrame terstruktur, ringkasan dengan baris maksimum/minimum teridentifikasi, dan bagan batang yang siap terbit. Prompt mencantumkan empat hal (crop, ekstrak, bagan, ringkas), dan model memperlakukan masing-masing sebagai tahap dalam pipeline di mana keluaran satu langkah mengalir ke langkah berikutnya.
Contoh sebelumnya menggunakan loop untuk menyempurnakan satu operasi (crop lebih baik, anotasi lebih baik). Yang ini menunjukkan loop yang merangkai operasi berbeda, masing-masing membangun di atas apa yang dihasilkan putaran sebelumnya.
Kesimpulan
Visi agen mengubah satu panggilan API menjadi loop umpan balik di mana model menulis kode, melihat apa yang dihasilkannya, lalu memutuskan langkah berikutnya.
Contoh rak bahan makanan menunjukkan pola dasar: melakukan crop pada area yang terlalu kecil untuk dibaca, lalu menjawab dari versi yang diperbesar. Menu bar menambahkan dimensi kedua, di mana model menjalankan satu putaran crop, memutuskan itu belum cukup, dan memperbesar lebih lanjut area bermasalah secara mandiri.
Untuk koin, anotasi mengubah tebakan menjadi artefak yang dapat diperiksa. Tabel pajak merangkai empat operasi berbeda (crop, ekstraksi, komputasi, plot) dalam satu permintaan.
Setiap contoh juga mengungkap sesuatu tentang pembuatan prompt. Menetapkan standar kelengkapan ("cantumkan semua produk") mendorong model ke arah ketelitian. Membiarkan metodenya terbuka memungkinkan model memilih jalur prapemrosesan sendiri.
Namun verba ambigu seperti "draw" kadang membuatnya salah jalur, dan beralih ke "Use Python to draw" menyelesaikannya.
Polanya: deskripsikan apa yang Anda ingin dihasilkan, bersikap spesifik soal alat saat tugasnya ambigu, dan biarkan loop menangani sisanya.
Semua kode dari tutorial ini berjalan dengan gemini-3-flash-preview dan SDK google-genai. Jika Anda ingin menggali lebih dalam, kursus DataCamp Introduction to AI Agents membahas pola loop di balik semuanya.
Visi Agen di Gemini 3: FAQ
Apa itu visi agen dalam Gemini 3 Flash?
Visi agen adalah fitur di mana Gemini 3 Flash dapat menulis dan menjalankan kode Python di tengah respons untuk memanipulasi gambar sebelum menjawab. Ia menggunakan loop Think, Act, Observe untuk melakukan crop, pembesaran, anotasi, dan peningkatan gambar di lingkungan sandbox, semuanya dalam satu panggilan API.
Bagaimana cara mengaktifkan visi agen di Gemini API?
Tambahkan alat eksekusi kode ke GenerateContentConfig Anda: tools=[types.Tool(code_execution=types.ToolCodeExecution())]. Ini memberi model akses ke lingkungan Python tersandbox dengan 43 pustaka prainstal termasuk PIL, OpenCV, dan matplotlib.
Mengapa model melewatkan eksekusi kode meskipun telah diaktifkan?
Model hanya menulis kode ketika ia memutuskan bahwa sekali lihat gambar tidak cukup. Jika dapat menjawab dengan yakin dari resolusi asli, ia melewati jalur eksekusi kode. Ini sesuai desain dan didokumentasikan oleh Google.
Apa perbedaan antara "draw" dan "Use Python to draw" dalam prompt?
Mengatakan "draw" bersifat ambigu dan model mungkin mengembalikan deskripsi teks atau koordinat JSON alih-alih gambar yang sebenarnya. Menambahkan "Use Python to draw" memberi tahu model untuk menulis kode yang dapat dijalankan dan menghasilkan keluaran visual, yang memicu jalur eksekusi kode secara andal.
Bisakah saya menggabungkan visi agen dengan pemanggilan fungsi kustom?
Tidak. Eksekusi kode dan pemanggilan fungsi kustom tidak dapat digunakan dalam permintaan API yang sama. Jika alur kerja Anda membutuhkan keduanya, Anda harus membuat panggilan terpisah untuk masing-masing.

Saya adalah kreator konten data science dengan pengalaman lebih dari 2 tahun dan salah satu dengan jumlah pengikut terbesar di Medium. Saya suka menulis artikel mendalam tentang AI dan ML dengan sedikit gaya sarkastik—karena harus ada sesuatu untuk membuatnya tidak terlalu membosankan. Saya telah menghasilkan lebih dari 130 artikel dan satu kursus di DataCamp, dengan satu lagi sedang dalam proses. Konten saya telah dilihat oleh lebih dari 5 juta pasang mata, dan 20 ribu di antaranya menjadi pengikut di Medium dan LinkedIn.

Anda mengirim gambar ke API visi dan memintanya membaca label harga kecil. Respons yang kembali terdengar yakin namun salah. Hal serupa terjadi saat menghitung objek di foto yang berantakan: model melihat sekilas seluruh gambar lalu menebak angka yang meleset jauh.
Gemini 3 Flash menanganinya dengan cara berbeda. Dengan eksekusi kode diaktifkan, ia dapat menulis dan menjalankan Python di tengah respons untuk melakukan crop, pembesaran, dan anotasi gambar sebelum memberikan jawaban. Google menyebutnya visi agen (agentic vision) dan melaporkan peningkatan kualitas 5–10% pada tolok ukur visi.
Tutorial ini membahas empat contoh yang makin menantang: membaca tulisan kecil di rak bahan makanan, mengambil nama minuman dari menu bar yang remang, menghitung koin yang berserakan, dan mengekstrak data tabel ke dalam bagan.
Apa Itu Visi Agen? Loop "Think, Act, Observe"
Idenya sederhana. Dengan eksekusi kode aktif, Gemini 3 dapat berhenti di tengah respons, menulis dan menjalankan Python di sandbox, melihat apa yang dihasilkan kode, lalu memutuskan langkah berikutnya.
Untuk tugas visi, ini biasanya berarti model memotong area gambar, memperbesarnya, menggambar bounding box, atau menyesuaikan kontras untuk menonjolkan detail yang tidak terlihat pada resolusi awal.
Ini berjalan sebagai loop tiga fase.
Selama Think, model melihat apa yang dimilikinya (gambar asli, atau versi yang sudah dipotong atau dianotasi) dan merencanakan langkah berikut. Ia mungkin memutuskan label harga terlalu kecil untuk dibaca dan perlu dipotong serta diperbesar.
Act menerjemahkan rencana itu menjadi Python. Sandbox memiliki 43 pustaka prainstal (PIL, OpenCV, matplotlib, dan lainnya), sehingga model dapat menulis kode pemrosesan gambar apa pun yang dibutuhkannya.
Setelah kode berjalan, Observe menutup loop. Output (gambar baru, teks yang dicetak, nilai yang dihitung) masuk kembali ke konteks. Dari sana, model bisa menjawab atau mengulang lagi.
Satu panggilan API dapat berputar melalui loop ini beberapa kali. Di contoh berikut, Anda akan melihatnya melakukan crop tujuh area rak dalam satu permintaan. Pada contoh lain, ia mengekstrak data tabel ke pandas DataFrame dan menghasilkan bagan batang, semuanya dalam panggilan yang sama.
Jika konsep loop agen baru bagi Anda, alur AI Agent Fundamentals dari DataCamp membahasnya secara mendalam.
Keterbatasan dan batasan
Menyiapkan Lingkungan Anda untuk Visi Agen
Ambil kunci API dari ai.google.dev dan simpan sebagai
GOOGLE_API_KEYdi lingkungan Anda atau berkas.env. Lalu pasang SDK dan pustaka gambar:Mulailah dengan membuat klien:
Parameter
timeout=600_000penting. Panggilan visi agen melibatkan beberapa putaran pembuatan dan eksekusi kode di dalam sandbox, dan satu permintaan bisa memakan waktu sekitar empat menit.Batas waktu default httpx akan memutus koneksi jauh sebelum itu, meninggalkan Anda dengan
ReadTimeoutyang sunyi tanpa petunjuk apa yang terjadi.Selanjutnya, siapkan konfigurasi agen:
toolsadalah tombol untuk menyalakan eksekusi kode. Ini memberi model lingkungan Python tersandbox dengan 43 pustaka (numpy, pandas, PIL, matplotlib, OpenCV, dan lainnya). Setiap blok kode mendapat 30 detik untuk berjalan, dan sandbox mencoba ulang hingga 5 kali saat terjadi kesalahan.thinking_level="HIGH"memberi model ruang lebih untuk merencanakan sebelum menulis kode. Untuk pekerjaan visi multi-langkah, ini memberi perbedaan yang terasa. Menetapkaninclude_thoughts=Truemenampilkan penalaran itu di bagian respons, berguna untuk debugging.media_resolutionmengatur berapa banyak token yang didapat gambar input sebelum loop dimulai.MEDIA_RESOLUTION_HIGHadalah default yang baik. Anda dapat menaikkan keMEDIA_RESOLUTION_ULTRA_HIGHuntuk gambar yang sangat padat atau menurunkan keMEDIA_RESOLUTION_LOWsaat kecepatan lebih penting daripada detail.Pengaturan yang sama ini berfungsi di Vertex AI dengan perubahan minimal. Untuk panduan yang lebih luas tentang Gemini 3 API di luar visi, lihat tutorial pendampingnya.
Visi Agen di Gemini 3: Membaca Detail Halus dengan Crop dan Zoom
Gambar di bawah adalah rak toko bahan makanan Jerman dengan tiga baris produk. Label harga berjajar di tepi setiap rak, dan pemberitahuan "Liebe Kunden" (Pelanggan yang Terhormat) berada di dekat bagian bawah. Label dapat dibaca jika Anda memperbesar sendiri, tetapi cukup kecil sehingga model yang melihat seluruh gambar harus memutuskan apa yang bisa dan tidak bisa dibacanya.
Mengurai respons multi-bagian
Respons agen bukan satu blok teks. Respons datang sebagai daftar bagian: pemikiran, kode yang ditulis model, hasil eksekusi, gambar yang dihasilkan, dan jawaban akhir. Pembantu ini menelusurinya:
Setiap contoh dalam tutorial ini menggunakan fungsi yang sama, jadi definisikan sekali dan gunakan ulang.
Sekarang muat gambar rak dan tuliskan prompt yang meminta model membaca pengumuman dan mencantumkan setiap produk beserta harganya.
Jalankan terlebih dahulu tanpa eksekusi kode:
Berikut versi keluaran yang dipangkas:
Terjemahan pengumuman benar, dan model membaca label Uncle Ben yang bisa dikenalinya. Namun sekitar sepertiga entri bertuliskan "(Label obscured)," dan merek seperti Oryza, Reis-fit, dan Reiskugeln sama sekali tidak muncul.
Sekarang prompt yang sama dengan konfigurasi agen:
Struktur keluarannya berbeda. Alih-alih satu
[ANSWER], ia menelusuri penuh loop Think/Act/Observe:Tujuh hasil crop yang dihasilkan model:
Setelah mengamati hasil crop tersebut, model menghasilkan jawaban lengkap:
Keluaran lengkap tanpa dipangkas untuk kedua panggilan tersedia di gist ini.
Respons agen membaca 30+ produk di ketiga rak. Item Oryza, Reis-fit, Madras Curry, Reiskugeln, dan Steam Basmati yang diberi label "(obscured)" oleh visi standar semuanya muncul lengkap dengan nama dan harga.
Perbedaannya bukan soal halusinasi vs. akurasi; visi standar jujur tentang apa yang tidak bisa dibacanya. Visi agen menolak menerima batas itu dan memperbesar hingga bisa membaca.
Prompt berperan di sini. Meminta model untuk "mencantumkan semua nama produk yang terlihat beserta harganya" menetapkan standar kelengkapan yang mendorongnya untuk melakukan crop.
Prompt yang lebih samar seperti "deskripsikan gambar ini" kemungkinan akan menghasilkan jawaban sekali lewat meskipun eksekusi kode aktif, karena model tidak melihat alasan untuk melangkah lebih jauh.