Thực hành tạo câu lệnh

Sách: Kỹ thuật thiết lập câu lệnh - Tác giả: Lee Boonstra


Thực hành tạo câu lệnh

Để tìm ra lời câu lệnh hợp đòi hỏi sự thử nghiệm. Language Studio trong Vertex AI là một nơi lý tưởng để bạn thử nghiệm các lời nhắc của mình, với khả năng kiểm tra trên nhiều mô hình khác nhau.

Chương này là một vài cách giúp bạn trở thành chuyên gia trong kỹ thuật thiết kế lời nhắc (prompt engineering) mà bạn có thể áp dụng như dưới đây:

Cung cấp ví dụ

Để câu lệnh cho ra kết quả tốt, quan trọng nhất là cung cấp các ví dụ (một lần - one-shot / vài lần - few-shot) trong lời nhắc. Điều này cực kỳ hiệu quả vì nó đóng vai trò như một công cụ dạy học mạnh mẽ. Các ví dụ này trình bày các đầu ra mong muốn hoặc các phản hồi tương tự, cho phép mô hình học hỏi từ chúng và điều chỉnh thế hệ của chính nó cho phù hợp. Nó giống như việc cung cấp cho mô hình một điểm tham chiếu hoặc mục tiêu để hướng tới, cải thiện độ chính xác, phong cách và giọng điệu phản hồi của nó để phù hợp hơn với kỳ vọng của bạn.

Thiết kế đơn giản

Các câu lệnh phải ngắn gọn, rõ ràng và dễ hiểu cho cả bạn và mô hình. Theo nguyên tắc chung, nếu nó đã gây bối rối cho bạn thì khả năng cao nó cũng sẽ gây bối rối cho mô hình. Cố gắng không sử dụng ngôn ngữ phức tạp và không cung cấp thông tin không cần thiết.

Ví dụ:

TRƯỚC:

Tôi đang ở New York ngay bây giờ và muốn nghe thêm về những địa điểm tuyệt vời. Tôi đi cùng hai đứa trẻ 3 tuổi. Chúng tôi nên đi đâu trong kỳ nghỉ của mình?

SAU KHI VIẾT LẠI:

Đóng vai trò là hướng dẫn viên du lịch cho khách du lịch. Mô tả những địa điểm tuyệt vời để tham quan ở New York Manhattan cùng trẻ 3 tuổi.

Hãy thử sử dụng các động từ mô tả hành động. Dưới đây là một tập hợp các ví dụ: Hành động (Act), Phân tích (Analyze), Phân loại (Categorize), Phân loại (Classify), Đối chiếu (Contrast), So sánh (Compare), Sáng tạo (Create), Mô tả (Describe), Định nghĩa (Define), Đánh giá (Evaluate), Trích xuất (Extract), Tìm (Find), Tạo ra (Generate), Nhận diện (Identify), Liệt kê (List), Đo lường (Measure), Tổ chức (Organize), Phân tích cú pháp (Parse), Chọn (Pick), Dự đoán (Predict), Cung cấp (Provide), Xếp hạng (Rank), Đề xuất (Recommend), Trả về (Return), Truy xuất (Retrieve), Viết lại (Rewrite), Chọn (Select), Hiển thị (Show), Sắp xếp (Sort), Tóm tắt (Summarize), Dịch (Translate), Viết (Write).

Hãy cụ thể về đầu ra mong muốn. Một hướng dẫn ngắn gọn có thể không đủ để hướng dẫn LLM hoặc có thể quá chung chung. Cung cấp các chi tiết cụ thể trong lời nhắc (thông qua lời nhắc hệ thống hoặc ngữ cảnh) có thể giúp mô hình tập trung vào những gì liên quan, cải thiện độ chính xác tổng thể.

Ví dụ:

NÊN:

Hãy tạo một bài viết blog gồm 3 đoạn về top 5 máy chơi game console hàng đầu. Bài viết blog này nên mang tính cung cấp thông tin, hấp dẫn người đọc và nên được viết theo một phong cách trò chuyện, gần gũi.

KHÔNG ĐƯỢC:

Viết bài blog về máy chơi game console.

Sử dụng Hướng dẫn thay vì Ràng buộc

Hướng dẫn (Instructions) và ràng buộc (constraints) được sử dụng trong câu lệnh để định hướng đầu ra của Mô hình ngôn ngữ lớn (LLM).

  • Hướng dẫn cung cấp các chỉ dẫn rõ ràng về định dạng, phong cách hoặc nội dung mong muốn của phản hồi. Nó hướng dẫn mô hình về những gì mô hình nên làm hoặc tạo ra.

  • Ràng buộc là tập hợp các giới hạn hoặc ranh giới đối với phản hồi. Nó giới hạn những gì mô hình không nên làm hoặc nên tránh.

Nghiên cứu ngày càng chứng minh rằng việc tập trung vào các hướng dẫn tích cực trong prompt có thể hiệu quả hơn là phụ thuộc nhiều vào các ràng buộc. Cách tiếp cận này phù hợp với cách con người thích các chỉ dẫn tích cực hơn là danh sách những điều không nên làm. Hướng dẫn trực tiếp truyền đạt kết quả mong muốn, trong khi ràng buộc có thể khiến mô hình phải đoán xem điều gì được phép. Hướng dẫn mang lại sự linh hoạt và khuyến khích sự sáng tạo trong các ranh giới được xác định, trong khi ràng buộc có thể giới hạn tiềm năng của mô hình. Ngoài ra, danh sách các ràng buộc có thể mâu thuẫn với nhau.

Ràng buộc vẫn có giá trị nhưng trong một số tình huống nhất định. Ví dụ, để ngăn mô hình tạo ra nội dung độc hại hoặc thiên vị, hoặc khi cần định dạng hoặc phong cách đầu ra nghiêm ngặt.

Nếu có thể, hãy sử dụng các hướng dẫn tích cực: thay vì nói với mô hình những gì không nên làm, hãy nói với nó những gì nên làm. Điều này có thể tránh nhầm lẫn và cải thiện độ chính xác của đầu ra.

NÊN:

Tạo một bài đăng blog dài 1 đoạn về top 5 máy chơi game console. Chỉ thảo luận về tên máy, công ty sản xuất, năm phát hành và tổng doanh số bán hàng.

KHÔNG NÊN:

Tạo 1 bài blog về top 5 máy chơi game consloe. Không liệt kê tên game.

Một phương pháp tốt nhất là, hãy bắt đầu bằng cách ưu tiên các hướng dẫn, nêu rõ ràng điều bạn muốn mô hình thực hiện và chỉ sử dụng các ràng buộc khi cần thiết cho mục đích an toàn, rõ ràng hoặc các yêu cầu cụ thể. Thử nghiệm và lặp lại để kiểm tra các sự kết hợp khác nhau giữa hướng dẫn và ràng buộc nhằm tìm ra phương pháp hoạt động tốt nhất cho các nhiệm vụ cụ thể của bạn, và ghi chép lại chúng.

Kiểm soát độ dài token tối đa

Để kiểm soát độ dài của phản hồi do LLM tạo ra, bạn có thể đặt giới hạn token tối đa trong cấu hình hoặc yêu cầu rõ ràng độ dài cụ thể trong lời nhắc của mình. Ví dụ:

Giải thích vật lý lượng tử bằng một tweet.

Sử dụng biến trong câu lệnh

Để tái sử dụng câu lệnh và làm cho chúng trở nên linh hoạt hơn, hãy sử dụng các biến trong câu lệnh, chúng có thể thay đổi tùy theo các đầu vào khác nhau. Ví dụ, như được minh họa trong Bảng 20, một câu lệnh cung cấp thông tin thực tế về một thành phố. Thay vì mã hóa cứng tên thành phố trong câu lệnh, hãy sử dụng một biến. Các biến có thể giúp bạn tiết kiệm thời gian và công sức bằng cách cho phép bạn tránh lặp lại chính mình. Nếu bạn cần sử dụng cùng một thông tin trong nhiều câu lệnh khác nhau, bạn có thể lưu trữ thông tin đó trong một biến và sau đó tham chiếu biến đó trong từng câu lệnh. Điều này rất hợp lý khi tích hợp câu lệnh vào các ứng dụng của riêng bạn.

| (Prompt) | (VARIABLES)<br>{thành phố} = "Amsterdam"<br><br>(PROMPT)<br>Bạn là một hướng dẫn viên du lịch. Hãy cho tôi biết một sự thật về thành phố: {thành phố} |
|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| (Output)  | Amsterdam là một thành phố xinh đẹp với nhiều kênh đào, cầu và những con phố hẹp. Đây là một nơi tuyệt vời để ghé thăm nhờ lịch sử phong phú, văn hóa và cuộc sống về đêm sôi động. |

Bảng 20: Sử dụng biến trong câu lệnh

Thử nghiệm với các định dạng đầu vào và các phong cách viết khác nhau

Các mô hình khác nhau, cấu hình mô hình, định dạng lời nhắc, lựa chọn từ ngữ và cách gửi (submit) có thể mang lại kết quả khác nhau. Do đó, điều quan trọng là phải thử nghiệm với các thuộc tính của nhắc như kiểu, lựa chọn từ ngữ và loại nhắc (zero shot, few shot, system prompt).

Ví dụ, một nhắc với mục tiêu tạo văn bản về máy chơi game console mang tính cách mạng Sega Dreamcast, có thể được định dạng dưới dạng câu hỏi, câu tuyên bố hoặc hướng dẫn, dẫn đến các đầu ra khác nhau:

  • Câu hỏi: Sega Dreamcast là gì và tại sao nó lại là một console mang tính cách mạng như vậy?

  • Câu tuyên bố: Sega Dreamcast là một máy chơi game console thế hệ thứ sáu được Sega phát hành vào năm 1999. Nó...

  • Hướng dẫn: Viết một đoạn văn duy nhất mô tả máy chơi game console Sega Dreamcast và giải thích tại sao nó lại mang tính cách mạng đến vậy.

Đối với nhắc few-shot với các nhiệm vụ phân loại, hãy trộn lẫn các lớp

Nói chung, thứ tự của các ví dụ few-shot của bạn không quan trọng lắm. Tuy nhiên, khi thực hiện các nhiệm vụ phân loại, hãy đảm bảo bạn trộn lẫn các lớp phản hồi có thể có trong các ví dụ few-shot. Điều này là do nếu không làm vậy, bạn có thể bị overfitting vào thứ tự cụ thể của các ví dụ. Bằng cách trộn lẫn các lớp phản hồi có thể có, bạn có thể đảm bảo rằng mô hình đang học cách xác định các đặc điểm chính của từng lớp, thay vì chỉ đơn giản là ghi nhớ thứ tự của các ví dụ. Điều này sẽ dẫn đến hiệu suất mạnh mẽ và khái quát hóa tốt hơn trên dữ liệu chưa từng thấy.

Một nguyên tắc chung hữu ích là bắt đầu với 6 ví dụ few-shot và bắt đầu kiểm tra độ chính xác từ đó.

Thích ứng với các bản cập nhật mô hình

Điều quan trọng là bạn phải nắm bắt được các thay đổi về kiến trúc mô hình, dữ liệu bổ sung và khả năng mới. Hãy thử nghiệm các phiên bản mô hình mới hơn và điều chỉnh các nhắc của bạn để tận dụng tốt hơn các tính năng mới của mô hình. Các công cụ như Vertex AI Studio rất hữu ích để lưu trữ, kiểm tra và ghi lại các phiên bản khác nhau của nhắc của bạn.

Thử nghiệm với các định dạng đầu ra

Bên cạnh định dạng đầu vào cho câu lệnh, hãy cân nhắc thử nghiệm với định dạng đầu ra. Đối với các tác vụ không sáng tạo như trích xuất, lựa chọn, phân tích cú pháp (parsing), sắp xếp thứ tự, xếp hạng hoặc phân loại dữ liệu, hãy thử yêu cầu đầu ra được trả về dưới định dạng có cấu trúc như JSON hoặc XML.

Việc trả về các đối tượng JSON từ một lời nhắc trích xuất dữ liệu mang lại một số lợi ích. Trong một ứng dụng thực tế, tôi không cần phải tự tay tạo định dạng JSON này, tôi có thể nhận dữ liệu đã được sắp xếp (rất tiện lợi khi làm việc với các đối tượng ngày giờ), nhưng quan trọng nhất là, bằng cách yêu cầu định dạng JSON, nó buộc mô hình phải tạo ra một cấu trúc và hạn chế các hiện tượng "ảo giác" (hallucinations).

Tóm lại, lợi ích của việc sử dụng JSON cho đầu ra của bạn:

  • Luôn trả về theo cùng một kiểu

  • Tập trung vào dữ liệu bạn muốn nhận

  • Ít khả năng xảy ra "ảo giác"

  • Làm cho nó nhận biết được mối quan hệ

  • Bạn nhận được các kiểu dữ liệu

  • Bạn có thể sắp xếp nó

Bảng 4 trong phần "few-shot prompting" (lời nhắc với vài ví dụ) cho thấy một ví dụ về cách trả về đầu ra có cấu trúc.

Sửa JSON (JSON Repair)

Mặc dù việc trả về dữ liệu dưới định dạng JSON mang lại nhiều lợi ích, nhưng nó cũng không phải không có nhược điểm. Bản chất có cấu trúc của JSON, mặc dù hữu ích cho việc phân tích cú pháp và sử dụng trong các ứng dụng, đòi hỏi lượng mã thông báo (tokens) nhiều hơn đáng kể so với văn bản thuần túy, dẫn đến thời gian xử lý tăng và chi phí cao hơn. Hơn nữa, sự dài dòng của JSON có thể dễ dàng chiếm hết cửa sổ đầu ra, trở nên đặc biệt có vấn đề khi quá trình tạo bị cắt đột ngột do giới hạn mã thông báo. Việc cắt bớt này thường dẫn đến JSON không hợp lệ, thiếu các dấu ngoặc nhọn hoặc ngoặc vuông đóng cần thiết, khiến đầu ra không thể sử dụng được. May mắn thay, các công cụ như thư viện json-repair (có sẵn trên PyPI) có thể cực kỳ hữu ích trong những tình huống này. Thư viện này thông minh cố gắng tự động sửa các đối tượng JSON không đầy đủ hoặc bị định dạng sai, biến nó thành một đồng minh quan trọng khi làm việc với JSON được tạo ra bởi LLM (Mô hình ngôn ngữ lớn), đặc biệt khi đối phó với các vấn đề cắt bớt tiềm ẩn.

Làm việc với Schemas

Sử dụng JSON có cấu trúc làm đầu ra là một giải pháp tuyệt vời, như chúng ta đã thấy nhiều lần trong bài viết này. Nhưng còn đầu vào thì sao? Mặc dù JSON rất xuất sắc để cấu trúc đầu ra mà LLM tạo ra, nó cũng có thể cực kỳ hữu ích để cấu trúc đầu vào bạn cung cấp. Đây là lúc JSON Schemas phát huy tác dụng. Một JSON Schema định nghĩa cấu trúc và các kiểu dữ liệu dự kiến của đầu vào JSON của bạn. Bằng cách cung cấp một schema, bạn cung cấp cho LLM một bản thiết kế rõ ràng về dữ liệu mà nó nên mong đợi, giúp nó tập trung sự chú ý vào thông tin liên quan và giảm nguy cơ hiểu sai đầu vào. Hơn nữa, schemas có thể giúp thiết lập mối quan hệ giữa các mẩu dữ liệu khác nhau và thậm chí làm cho LLM "nhận biết thời gian" bằng cách bao gồm các trường ngày hoặc dấu thời gian với các định dạng cụ thể.

Dưới đây là một ví dụ đơn giản:

Giả sử bạn muốn sử dụng một Mô hình Ngôn ngữ Lớn (LLM) để tạo mô tả cho các sản phẩm trong danh mục thương mại điện tử. Thay vì chỉ cung cấp mô tả sản phẩm dưới dạng văn bản tự do, bạn có thể sử dụng một schema JSON để định nghĩa các thuộc tính của sản phẩm:

```json
{
 "type": "object",
 "properties": {
 "name": { "type": "string", "description": "Product name" },
 "category": { "type": "string", "description": "Product category" },
 "price": { "type": "number", "format": "float", "description": "Product price" },
 "features": {
 "type": "array",
 "items": { "type": "string" },
 "description": "Key features of the product"
 },
 "release_date": { "type": "string", "format": "date", "description": "Date the product was released"}
 },

Đoạn trích 5. Định nghĩa lược đồ đầu ra có cấu trúc

Sau đó, bạn có thể cung cấp dữ liệu sản phẩm thực tế dưới dạng một đối tượng JSON tuân thủ lược đồ này:

{
 "name": "Wireless Headphones",
 "category": "Electronics",
 "price": 99.99,
 "features": ["Noise cancellation", "Bluetooth 5.0", "20-hour battery life"],
 "release_date": "2023-10-27"
}

Đoạn trích 6. Đầu ra có cấu trúc từ LLM

Bằng cách tiền xử lý dữ liệu của bạn và thay vì cung cấp toàn bộ tài liệu, chỉ cung cấp cả lược đồ (schema) và dữ liệu, bạn giúp LLM hiểu rõ ràng các thuộc tính của sản phẩm, bao gồm cả ngày phát hành của nó, làm cho nó có khả năng tạo ra một mô tả chính xác và liên quan cao hơn nhiều. Cách tiếp cận đầu vào có cấu trúc này, hướng sự chú ý của LLM đến các trường liên quan, đặc biệt có giá trị khi làm việc với khối lượng dữ liệu lớn hoặc khi tích hợp LLM vào các ứng dụng phức tạp.

Thử nghiệm cùng với các kỹ sư câu lệnh khác

Nếu bạn đang trong tình huống phải cố gắng nghĩ ra một câu lệnh tốt, bạn có thể muốn tìm nhiều người cùng thử nghiệm. Khi mọi người tuân thủ các thực hành tốt nhất (như được liệt kê trong chương này), bạn sẽ thấy sự khác biệt về hiệu suất giữa tất cả các câu lệnh khác nhau.

Các sử dụng CoT tốt nhất

Đối với câu lệnh CoT (Chain of Thought - Chuỗi Suy nghĩ), việc đặt câu trả lời sau phần suy luận là cần thiết vì việc tạo ra phần suy luận sẽ thay đổi các token mà mô hình nhận được khi dự đoán câu trả lời cuối cùng.

Với CoT và tính tự nhất quán (self-consistency), bạn cần có khả năng trích xuất câu trả lời cuối cùng từ câu lệnh của mình, tách biệt khỏi phần suy luận.

Đối với câu lệnh CoT, hãy đặt nhiệt độ (temperature) bằng 0.

Câu lệnh chuỗi suy nghĩ dựa trên giải mã greedy decoding, dự đoán từ tiếp theo trong một chuỗi dựa trên xác suất cao nhất được mô hình ngôn ngữ gán. Nói chung, khi sử dụng suy luận để đưa ra câu trả lời cuối cùng, nhiều khả năng chỉ có một câu trả lời duy nhất đúng. Do đó, nhiệt độ luôn nên được đặt bằng 0.

Ghi chép

Mẹo cuối cùng đã được đề cập trước đó trong chương này, nhưng chúng tôi không thể nhấn mạnh đủ tầm quan trọng của nó: ghi lại chi tiết đầy đủ các thử nghiệm câu lệnh của bạn để bạn có thể học hỏi theo thời gian cái gì hiệu quả và cái gì không.

Kết quả đầu ra của cùng một câu lệnh có thể khác nhau giữa các mô hình, giữa các cài đặt lấy mẫu (sampling settings), và thậm chí giữa các phiên bản khác nhau của cùng một mô hình. Hơn nữa, ngay cả với các câu lệnh giống hệt nhau đưa vào cùng một mô hình, những khác biệt nhỏ về định dạng câu và lựa chọn từ trong kết quả đầu ra vẫn có thể xảy ra. (Ví dụ, như đã đề cập trước đây, nếu hai token có cùng xác suất dự đoán, việc phân định kết quả hòa có thể được thực hiện ngẫu nhiên. Điều này sau đó có thể ảnh hưởng đến các token được dự đoán tiếp theo).

Chúng tôi khuyến nghị tạo một Google Sheet với Bảng 21 làm mẫu. Lợi thế của cách tiếp cận này là bạn có một bản ghi hoàn chỉnh khi không thể tránh khỏi việc phải xem lại công việc câu lệnh của mình – dù là để tiếp tục trong tương lai (bạn sẽ ngạc nhiên về mức độ bạn có thể quên chỉ sau một thời gian nghỉ ngắn), để kiểm tra hiệu suất câu lệnh trên các phiên bản khác nhau của mô hình, và để giúp gỡ lỗi các lỗi trong tương lai.

Ngoài các trường trong bảng này, việc theo dõi phiên bản của câu lệnh (lần lặp), một trường để ghi nhận kết quả là OK/KHÔNG OK/ĐÔI KHI OK, và một trường để ghi lại phản hồi cũng rất hữu ích. Nếu bạn may mắn đang sử dụng Vertex AI Studio, hãy lưu các câu lệnh của mình (sử dụng cùng tên và phiên bản như đã liệt kê trong tài liệu của bạn) và theo dõi liên kết siêu văn bản đến câu lệnh đã lưu trong bảng. Bằng cách này, bạn luôn chỉ cách một cú nhấp chuột để chạy lại các câu lệnh của mình.

Khi làm việc trên một hệ thống tạo nội dung tăng cường truy xuất (retrieval augmented generation - RAG), bạn cũng nên ghi lại các khía cạnh cụ thể của hệ thống RAG ảnh hưởng đến nội dung nào được chèn vào câu lệnh, bao gồm truy vấn (query), cài đặt phân đoạn (chunk settings), kết quả phân đoạn (chunk output) và các thông tin khác.

Khi bạn cảm thấy câu lệnh đã gần hoàn hảo, hãy đưa nó vào cơ sở mã (codebase) dự án của bạn. Và trong cơ sở mã, hãy lưu các câu lệnh trong một tệp riêng biệt với mã, để việc duy trì dễ dàng hơn. Cuối cùng, lý tưởng nhất là các câu lệnh của bạn là một phần của hệ thống được vận hành (operationalized system), và với tư cách là một kỹ sư câu lệnh, bạn nên dựa vào các bài kiểm tra tự động và quy trình đánh giá để hiểu rõ câu lệnh của bạn khái quát hóa tốt đến mức nào đối với một tác vụ.

Kỹ thuật thiết lập lệnh là một quá trình lặp đi lặp lại. Tạo và kiểm tra các câu lệnh khác nhau, phân tích và ghi lại kết quả. Tinh chỉnh câu lệnh của bạn dựa trên hiệu suất của mô hình. Tiếp tục thử nghiệm cho đến khi bạn đạt được kết quả mong muốn. Khi bạn thay đổi mô hình hoặc cấu hình mô hình, hãy quay lại và tiếp tục thử nghiệm với các câu lệnh đã sử dụng trước đó.

Name[tên và phiên bản câu lệnh của bạn]
Goal[Giải thích mục tiêu của lần thử này trong một câu]
Model[tên và phiên bản của mô hình đã sử dụng]
Temperature[giá trị từ 0 - 1]
Top-K[số]
Prompt[Viết toàn bộ câu lệnh]
Output[Viết ra kết quả đầu ra hoặc nhiều kết quả đầu ra]

Bảng 21. Mẫu để ghi lại các câu lệnh