Menjeda pembuatan menggunakan interupsi

Interupsi adalah jenis alat khusus yang dapat menjeda loop pembuatan dan pemanggilan alat LLM untuk mengembalikan kontrol kepada Anda. Jika sudah siap, Anda dapat melanjutkan pembuatan dengan mengirim balasan yang diproses LLM untuk pembuatan lebih lanjut.

Penggunaan yang paling umum untuk interupsi terbagi menjadi beberapa kategori:

  • Human-in-the-Loop: Memungkinkan pengguna AI interaktif untuk mengklarifikasi informasi yang diperlukan atau mengonfirmasi tindakan LLM sebelum selesai, sehingga memberikan ukuran keamanan dan kepercayaan.
  • Pemrosesan Asinkron: Memulai tugas asinkron yang hanya dapat selesai di luar band, seperti mengirim notifikasi persetujuan ke peninjau manusia atau memulai proses latar belakang yang berjalan lama.
  • Keluar dari Tugas Otonom: Memberikan cara kepada model untuk menandai tugas sebagai selesai, dalam alur kerja yang mungkin melakukan iterasi melalui serangkaian panggilan alat yang panjang.

Sebelum memulai

Semua contoh yang didokumentasikan di sini mengasumsikan bahwa Anda telah menyiapkan project dengan dependensi Genkit yang diinstal. Jika Anda ingin menjalankan contoh kode di halaman ini, selesaikan langkah-langkah dalam panduan Memulai terlebih dahulu.

Sebelum mempelajarinya lebih lanjut, Anda juga harus memahami konsep berikut:

Ringkasan interupsi

Pada tingkat tinggi, tampilan interupsi saat berinteraksi dengan LLM adalah sebagai berikut:

  1. Aplikasi panggilan meminta LLM dengan permintaan. Perintah ini menyertakan daftar alat, termasuk setidaknya satu untuk gangguan yang dapat digunakan LLM untuk menghasilkan respons.
  2. LLM menghasilkan respons lengkap atau permintaan panggilan alat dalam format tertentu. Untuk LLM, panggilan interupsi terlihat seperti panggilan alat lainnya.
  3. Jika LLM memanggil alat interupsi, library Genkit akan otomatis menjeda pembuatan, bukan langsung meneruskan respons kembali ke model untuk pemrosesan tambahan.
  4. Developer memeriksa apakah panggilan interupsi dilakukan, dan melakukan tugas apa pun yang diperlukan untuk mengumpulkan informasi yang diperlukan untuk respons interupsi.
  5. Developer melanjutkan pembuatan dengan meneruskan respons gangguan ke model. Tindakan ini akan memicu kembali ke Langkah 2.

Menentukan interupsi respons manual

Jenis gangguan yang paling umum memungkinkan LLM meminta klarifikasi dari pengguna, misalnya dengan mengajukan pertanyaan pilihan ganda.

Untuk kasus penggunaan ini, gunakan metode defineInterrupt() instance Genkit:

import { genkit, z } from 'genkit';
import { googleAI, gemini15Flash } from '@genkitai/google-ai';

const ai = genkit({
  plugins: [googleAI()],
  model: gemini15Flash,
});

const askQuestion = ai.defineInterrupt({
  name: 'askQuestion',
  description: 'use this to ask the user a clarifying question',
  inputSchema: z.object({
    choices: z.array(z.string()).describe('the choices to display to the user'),
    allowOther: z.boolean().optional().describe('when true, allow write-ins')
  }),
  outputSchema: z.string()
});

Perhatikan bahwa outputSchema interupsi sesuai dengan data respons yang akan Anda berikan, bukan sesuatu yang akan otomatis diisi oleh fungsi alat.

Menggunakan interupsi

Interupsi diteruskan ke array tools saat membuat konten, seperti jenis alat lainnya. Anda dapat meneruskan alat normal dan gangguan ke panggilan generate yang sama:

membuat

const response = await ai.generate({
  prompt: 'Ask me a movie trivia question.',
  tools: [askQuestion],
});

definePrompt

const triviaPrompt = ai.definePrompt(
  {
    name: 'triviaPrompt',
    tools: [askQuestion],
    input: {
      schema: z.object({subject: z.string()})
    },
    prompt: 'Ask me a trivia question about {{subject}}
    .',
  }
);

const response = await triviaPrompt({ subject: 'computer history' });

File perintah

---
tools: [askQuestion]
input:
  schema:
    partyType: string
---
{{role "system"}}
Use the askQuestion tool if you need to clarify something.

{{role "user"}}
Help me plan a {{partyType}} party next week.

Kemudian, Anda dapat menjalankan perintah dalam kode Anda sebagai berikut:

```ts
// assuming prompt file is named partyPlanner.prompt
const partyPlanner = ai.prompt('partyPlanner');

const response = await partyPlanner({ partyType: 'birthday' });
```

Chat

const chat = ai.chat({
  system: 'Use the askQuestion tool if you need to clarify something.',
  tools: [askQuestion],
});

const response = await chat.send('make a plan for my birthday party');

Genkit segera menampilkan respons saat menerima panggilan alat interupsi.

Merespons gangguan

Jika telah meneruskan satu atau beberapa gangguan ke panggilan generate, Anda harus memeriksa respons untuk gangguan agar dapat menanganinya:

// you can check the 'finishReason' of the response
response.finishReason === 'interrupted'
// or you can check to see if any interrupt requests are on the response
response.interrupts.length > 0

Merespons gangguan dilakukan menggunakan opsi resume pada panggilan generate berikutnya, dengan memastikan untuk meneruskan histori yang ada. Setiap alat memiliki metode .respond() untuk membantu membuat respons.

Setelah dilanjutkan, model akan kembali memasuki loop pembuatan, termasuk eksekusi alat, hingga selesai atau gangguan lain dipicu:

let response = await ai.generate({
  tools: [askQuestion],
  system: 'ask clarifying questions until you have a complete solution',
  prompt: 'help me plan a backyard BBQ',
});

while (response.interrupts.length) {
  const answers = [];
  // multiple interrupts can be called at once, so we handle them all
  for (const question in response.interrupts) {
    answers.push(
      // use the `respond` method on our tool to populate answers
      askQuestion.respond(
        question,
        // send the tool request input to the user to respond
        await askUser(question.toolRequest.input)
      )
    );
  }

  response = await ai.generate({
    tools: [askQuestion],
    messages: response.messages,
    resume: {
      respond: answers
    }
  })
}

// no more interrupts, we can see the final response
console.log(response.text);

Alat dengan gangguan yang dapat dimulai ulang

Pola umum lainnya untuk gangguan adalah kebutuhan untuk mengonfirmasi tindakan yang LLM sarankan sebelum benar-benar melakukannya. Misalnya, aplikasi pembayaran mungkin ingin pengguna mengonfirmasi jenis transfer tertentu.

Untuk kasus penggunaan ini, Anda dapat menggunakan metode defineTool standar untuk menambahkan logika kustom tentang kapan harus memicu gangguan, dan apa yang harus dilakukan saat gangguan dimulai ulang dengan metadata tambahan.

Menentukan alat yang dapat dimulai ulang

Setiap alat memiliki akses ke dua helper khusus dalam argumen kedua dari definisi penerapannya:

  • interrupt: saat dipanggil, metode ini akan menampilkan jenis pengecualian khusus yang tertangkap untuk menjeda loop pembuatan. Anda dapat memberikan metadata tambahan sebagai objek.
  • resumed: saat permintaan dari pembuatan yang terganggu dimulai ulang menggunakan opsi {resume: {restart: ...}} (lihat di bawah), helper ini berisi metadata yang diberikan saat memulai ulang.

Misalnya, jika Anda membuat aplikasi pembayaran, sebaiknya konfirmasi dengan pengguna sebelum melakukan transfer yang melebihi jumlah tertentu:

const transferMoney = ai.defineTool({
  name: 'transferMoney',
  description: 'Transfers money between accounts.',
  inputSchema: z.object({
    toAccountId: z.string().describe('the account id of the transfer destination'),
    amount: z.number().describe('the amount in integer cents (100 = $1.00)'),
  }),
  outputSchema: z.object({
    status: z.string().describe('the outcome of the transfer'),
    message: z.string().optional(),
  })
}, async (input, {context, interrupt, resumed})) {
  // if the user rejected the transaction
  if (resumed?.status === "REJECTED") {
    return {status: 'REJECTED', message: 'The user rejected the transaction.'};
  }
  // trigger an interrupt to confirm if amount > $100
  if (resumed?.status !== "APPROVED" && input.amount > 10000) {
    interrupt({
      message: "Please confirm sending an amount > $100.",
    });
  }
  // complete the transaction if not interrupted
  return doTransfer(input);
}

Dalam contoh ini, pada eksekusi pertama (saat resumed tidak ditentukan), alat ini akan memeriksa apakah jumlah tersebut melebihi $100, dan memicu gangguan jika ya. Pada eksekusi kedua, alat ini akan mencari status dalam metadata baru yang diberikan dan melakukan transfer atau menampilkan respons penolakan, bergantung pada apakah disetujui atau ditolak.

Memulai ulang alat setelah gangguan

Alat interupsi memberi Anda kontrol penuh atas:

  1. Kapan permintaan alat awal harus memicu gangguan.
  2. Kapan dan apakah akan melanjutkan loop pembuatan.
  3. Informasi tambahan yang harus diberikan ke alat saat melanjutkan.

Pada contoh yang ditampilkan di bagian sebelumnya, aplikasi mungkin meminta pengguna untuk mengonfirmasi permintaan yang terputus untuk memastikan jumlah transfernya sudah benar:

let response = await ai.generate({
  tools: [transferMoney],
  prompt: "Transfer $1000 to account ABC123",
});

while (response.interrupts.length) {
  const confirmations = [];
  // multiple interrupts can be called at once, so we handle them all
  for (const interrupt in response.interrupts) {
    confirmations.push(
      // use the 'restart' method on our tool to provide `resumed` metadata
      transferMoney.restart(
        interrupt,
        // send the tool request input to the user to respond. assume that this
        // returns `{status: "APPROVED"}` or `{status: "REJECTED"}`
        await requestConfirmation(interrupt.toolRequest.input);
      )
    );
  }

  response = await ai.generate({
    tools: [transferMoney],
    messages: response.messages,
    resume: {
      restart: confirmations,
    }
  })
}

// no more interrupts, we can see the final response
console.log(response.text);