หยุดการสร้างชั่วคราวโดยใช้การขัดจังหวะ

การขัดจังหวะคือเครื่องมือประเภทพิเศษที่สามารถหยุดลูปการสร้างและเรียกใช้ LLM ชั่วคราวเพื่อส่งคืนการควบคุมให้คุณ เมื่อพร้อมแล้ว คุณสามารถกลับมาสร้างต่อได้โดยส่งคำตอบที่ LLM จะประมวลผลเพื่อสร้างต่อ

การใช้งานที่พบบ่อยที่สุดของอินเตอร์รัปต์จะแบ่งออกเป็น 2-3 หมวดหมู่ ดังนี้

  • การมีส่วนร่วมของมนุษย์: ช่วยให้ผู้ใช้ AI แบบอินเทอร์แอกทีฟสามารถชี้แจงข้อมูลที่จําเป็นหรือยืนยันการดําเนินการของ LLM ได้ก่อนที่จะดําเนินการเสร็จสมบูรณ์ ซึ่งช่วยเพิ่มความมั่นใจและความปลอดภัย
  • การประมวลผลแบบไม่พร้อมกัน: การเริ่มงานแบบไม่พร้อมกันที่ดำเนินการได้นอกแบนด์เท่านั้น เช่น การส่งการแจ้งเตือนการอนุมัติไปยังผู้ตรวจสอบที่เป็นมนุษย์ หรือเริ่มกระบวนการที่ทำงานอยู่เบื้องหลังเป็นเวลานาน
  • ออกจากงานแบบสํารวจ: ระบุวิธีทําให้โมเดลทําเครื่องหมายงานว่าเสร็จแล้วในเวิร์กโฟลว์ที่อาจวนผ่านชุดการเรียกใช้เครื่องมือที่ยาว

ก่อนเริ่มต้น

ตัวอย่างทั้งหมดที่บันทึกไว้ที่นี่จะถือว่าคุณได้ตั้งค่าโปรเจ็กต์ที่มีการติดตั้งข้อกําหนดของ Genkit แล้ว หากต้องการเรียกใช้ตัวอย่างโค้ดในหน้านี้ ให้ทำตามขั้นตอนในคู่มือเริ่มต้นใช้งานก่อน

ก่อนเจาะลึก คุณควรทำความคุ้นเคยกับแนวคิดต่อไปนี้ด้วย

ภาพรวมของการขัดจังหวะ

ในระดับสูง ลักษณะของการขัดจังหวะเมื่อโต้ตอบกับ LLM มีดังนี้

  1. แอปพลิเคชันที่เรียกใช้จะส่งคําขอไปยัง LLM พรอมต์ประกอบด้วยรายการเครื่องมือ ซึ่งรวมถึงเครื่องมืออย่างน้อย 1 รายการสำหรับการขัดจังหวะที่ LLM สามารถใช้เพื่อสร้างคำตอบ
  2. LLM จะสร้างคำตอบที่สมบูรณ์หรือคำขอเรียกใช้เครื่องมือในรูปแบบที่เฉพาะเจาะจง LLM จะเห็นว่าการเรียกใช้การขัดจังหวะเหมือนกับการเรียกใช้เครื่องมืออื่นๆ
  3. หาก LLM เรียกใช้เครื่องมือขัดจังหวะ ไลบรารี Genkit จะหยุดการสร้างชั่วคราวโดยอัตโนมัติแทนที่จะส่งการตอบกลับกลับไปยังโมเดลเพื่อประมวลผลเพิ่มเติมทันที
  4. นักพัฒนาซอฟต์แวร์จะตรวจสอบว่ามีการทำคอลที่ขัดจังหวะหรือไม่ และดำเนินการที่จำเป็นเพื่อรวบรวมข้อมูลที่จำเป็นสำหรับการตอบกลับการขัดจังหวะ
  5. นักพัฒนาแอปจะกลับมาสร้างต่อโดยส่งการตอบกลับการขัดจังหวะไปยังโมเดล การดำเนินการนี้จะทริกเกอร์ให้กลับไปที่ขั้นตอนที่ 2

กำหนดการขัดจังหวะการตอบกลับด้วยตนเอง

การขัดจังหวะประเภทที่พบบ่อยที่สุดคือ LLM จะขอคำชี้แจงจากผู้ใช้ เช่น โดยการถามคำถามแบบเลือกตอบ

สําหรับกรณีการใช้งานนี้ ให้ใช้เมธอด defineInterrupt() ของอินสแตนซ์ 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()
});

โปรดทราบว่า outputSchema ของการขัดจังหวะจะสอดคล้องกับข้อมูลการตอบกลับที่คุณจะให้ ไม่ใช่ข้อมูลที่ระบบจะป้อนโดยอัตโนมัติจากฟังก์ชันของเครื่องมือ

ใช้การขัดจังหวะ

ระบบจะส่งการขัดจังหวะไปยังอาร์เรย์ tools เมื่อสร้างเนื้อหา เช่นเดียวกับเครื่องมือประเภทอื่นๆ คุณสามารถส่งทั้งเครื่องมือปกติและการขัดจังหวะไปยังgenerateการเรียกเดียวกันได้ ดังนี้

สร้าง

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' });

ไฟล์พรอมต์

---
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.

จากนั้นเรียกใช้พรอมต์ในโค้ดของคุณดังนี้

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

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

แชท

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 จะแสดงการตอบกลับทันทีที่ได้รับคําเรียกเครื่องมือขัดจังหวะ

ตอบสนองต่อการขัดจังหวะ

หากส่งการขัดจังหวะอย่างน้อย 1 รายการไปยังการเรียกใช้ Generate คุณจะต้องตรวจสอบการตอบสนองเพื่อหาการขัดจังหวะเพื่อให้จัดการการขัดจังหวะได้ ดังนี้

// 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

การตอบสนองต่อการขัดจังหวะทำได้โดยใช้ตัวเลือก resume ในgenerateการเรียกใช้ครั้งถัดไป โดยอย่าลืมส่งประวัติที่มีอยู่ เครื่องมือแต่ละรายการมีเมธอด .respond() เพื่อช่วยสร้างคำตอบ

เมื่อกลับมาทำงานอีกครั้ง โมเดลจะเข้าสู่ลูปการสร้างอีกครั้ง ซึ่งรวมถึงการเรียกใช้เครื่องมือ จนกว่าการสร้างจะเสร็จสมบูรณ์หรือมีการขัดจังหวะอื่นเกิดขึ้น

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);

เครื่องมือที่มีการขัดจังหวะที่รีสตาร์ทได้

รูปแบบที่พบบ่อยอีกอย่างหนึ่งของการขัดจังหวะคือต้องยืนยันการดำเนินการที่ LLM แนะนำก่อนที่จะดำเนินการจริง เช่น แอปการชำระเงินอาจต้องการให้ผู้ใช้ยืนยันการโอนบางประเภท

สําหรับกรณีการใช้งานนี้ คุณสามารถใช้เมธอด defineTool มาตรฐานเพื่อเพิ่มตรรกะที่กำหนดเองเกี่ยวกับเวลาที่จะทริกเกอร์การขัดจังหวะ และสิ่งที่จะทําเมื่อมีการเริ่มการขัดจังหวะอีกครั้งพร้อมข้อมูลเมตาเพิ่มเติม

กำหนดเครื่องมือที่รีสตาร์ทได้

เครื่องมือทุกรายการมีสิทธิ์เข้าถึงตัวช่วยพิเศษ 2 ตัวในอาร์กิวเมนต์ที่ 2 ของคําจํากัดความการติดตั้งใช้งาน ดังนี้

  • interrupt: เมื่อเรียกใช้ วิธีการนี้จะแสดงข้อยกเว้นประเภทพิเศษที่รับได้เพื่อหยุดลูปการสร้างชั่วคราว คุณสามารถระบุข้อมูลเมตาเพิ่มเติมเป็นออบเจ็กต์ได้
  • resumed: เมื่อเริ่มคําขอจากการสร้างที่หยุดชะงักอีกครั้งโดยใช้ตัวเลือก {resume: {restart: ...}} (ดูด้านล่าง) ตัวช่วยนี้จะประกอบด้วยข้อมูลเมตาที่ระบุไว้เมื่อเริ่มอีกครั้ง

เช่น หากกำลังสร้างแอปการชำระเงิน คุณอาจต้องยืนยันกับผู้ใช้ก่อนทำการโอนเกินจำนวนเงินที่กำหนด

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);
}

ในตัวอย่างนี้ ในการเรียกใช้ครั้งแรก (เมื่อไม่มีการกำหนดค่า resumed) เครื่องมือจะตรวจสอบว่าจํานวนเงินเกิน $100 หรือไม่ และทริกเกอร์การขัดจังหวะหากเป็นเช่นนั้น ในการเรียกใช้ครั้งที่ 2 เครื่องมือจะค้นหาสถานะในข้อมูลเมตาใหม่ที่ระบุและดําเนินการโอนหรือแสดงผลตอบกลับการปฏิเสธ ทั้งนี้ขึ้นอยู่กับว่าได้รับอนุมัติหรือไม่

รีสตาร์ทเครื่องมือหลังจากหยุดชะงัก

เครื่องมือขัดจังหวะช่วยให้คุณควบคุมสิ่งต่อไปนี้ได้อย่างเต็มที่

  1. กรณีที่คำขอเครื่องมือครั้งแรกควรทริกเกอร์การขัดจังหวะ
  2. เวลาที่ควรและไม่ควรกลับมาสร้างรายงานต่อ
  3. ข้อมูลเพิ่มเติมที่ควรให้เครื่องมือเมื่อกลับมาดำเนินการต่อ

ในตัวอย่างที่แสดงในส่วนก่อนหน้า แอปพลิเคชันอาจขอให้ผู้ใช้ยืนยันคำขอที่ขัดจังหวะเพื่อให้แน่ใจว่าจำนวนเงินที่โอนนั้นถูกต้อง

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);