All notes

Structured output là hợp đồng, không phải lời đề nghị

Language model trả về văn xuôi. Hệ thống production thì ăn JSON. Ranh giới giữa hai bên là một validator chưa từng nghe đến AI.

3 phút đọcViết từ Seoul và Nha Trang

Thứ đầu tiên chúng tôi chuẩn hóa khi các lời gọi model bắt đầu len vào pipeline chính là cái ranh giới đó. Mọi lời gọi mà output của nó chảy vào máy móc đều phải trả JSON theo một schema, và schema được ép bằng một validator bình thường. Không phải bằng cách nhỏ nhẹ nhờ vả trong prompt. Mà bằng cách gạt phăng output rồi bắt model làm lại, dí thẳng lỗi của validator vào mặt nó.

Vòng lặp

async function extract(input, schema, tries = 3) {
  let feedback = '';
  for (let i = 0; i < tries; i++) {
    const raw = await llm({
      system: RULES + feedback,
      user: input,
      response_format: schema,   // constrained decoding where the API supports it
      temperature: 0,
    });
    const errors = validate(schema, raw);   // plain JSON Schema, same lib as our forms
    if (errors.length === 0) return raw;
    feedback = ' Previous output failed validation: ' + errors.join('; ');
  }
  return quarantine(input);   // a review queue. never a crash, never a guess.
}

Ba chi tiết gánh gần hết sức nặng:

  • Enum ăn đứt free text. Cú nhảy vọt lớn nhất về độ tin cậy đến từ việc thay các field kiểu chuỗi bằng enum đóng. Một pipeline trích xuất tụt tỷ lệ bị loại từ 4.1% xuống 0.2% ngay trong tuần chúng tôi enum hóa các field phân loại của nó. Một model buộc phải chọn trong bảy giá trị thì không thể bịa ra giá trị thứ tám.
  • Ném nguyên văn lỗi của validator trở lại. Một câu "thử lại đi" trơ trọi chỉ khiến nó lặp lại đúng cái sai cũ. Còn thông báo chính xác, "items[2].price must be integer", thì gần như lần nào cũng sửa được ngay ở lượt sau.
  • Temperature 0 là cần, nhưng chưa đủ. Greedy decoding gỡ đi tính ngẫu nhiên, chứ không gỡ được cái sai. Người nói thật vẫn cứ là validator.

Cách ly là một tính năng

Cái item trượt cả ba lượt mới là cái đáng chú ý. Nó rơi vào hàng đợi review, một người gán nhãn cho nó, rồi case đã gán nhãn đó gia nhập bộ regression. Schema của chúng tôi được tôi luyện cứng cáp qua hai năm bởi chính những ca mà nó loại ra. Điều này chỉ thẳng vào bài học thật sự: schema là một nửa của prompt. Phần lớn thời gian làm prompt engineering của chúng tôi là để xóa bớt field, siết chặt kiểu dữ liệu và đóng kín enum, bởi mỗi bậc tự do mà ta lấy đi khỏi output là một hallucination không còn cơ hội xảy ra nữa.