olragon

Tìm chỗ ở cho 10 người, sao 44 chỗ còn trống chẳng hiện ra?

🌱 mầmgieo #sql#mcp#ai#bai-hoc

Viết cho Ai viết API, trang tìm kiếm hay công cụ cho AI gọi, mà dữ liệu có nhiều ô bỏ trống.

Sáng nay đọc xong một dòng nhật ký sửa lỗi, mình đi xem lại mấy câu SQL cũ của mình, vì sợ có câu dính đúng lỗi đó.

Lỗi nằm ở một công cụ tìm chỗ ở. Hỏi chỗ cho 10 người, công cụ đáp có 13 chỗ, nghe như đã tìm hết. Thật ra còn 44 chỗ trống nữa nó chẳng hề xét, chỉ vì chủ nhà chưa ghi sức chứa, và câu trả lời chẳng nhắc tới mấy chỗ đó một chữ.

Số trong bài mình đo ngày 5/11 trên công cụ thật, cho đêm 6 tới 8/11. Số lấy từ nhật ký thì mình ghi rõ. Phần đo và bản nháp có AI làm cùng.

English: Searching for a place for 10: why did 44 open places never show up?.

Vướng ở đâu

Mình đang làm một trang danh bạ chỗ ở nhỏ. Trang có search_stays, một hàm để trợ lý AI gọi thay khách: đưa ngày và số người, nhận lại những chỗ còn trống trọn mấy đêm đó. Hàm kiểu này gọi là công cụ MCP, theo tên chuẩn để AI gọi hàm bên ngoài. Giả sử khách hỏi chỗ cho 10 người. Công cụ đáp count: 13, total: 13, tức là có 13 chỗ, hết.

13 chỗ đó có thật, chữ "hết" mới sai. Mình đếm hết những chỗ còn trống mấy đêm đó:

Chỗ còn trốngSố chỗ
Ghi sức chứa, đủ 10 người13
Ghi sức chứa, nhỏ hơn 10 người12
Chẳng ghi sức chứa44
Cộng lại69

Công cụ chỉ xét hai dòng đầu. 44 chỗ ở dòng thứ ba có vừa hay không thì chẳng ai biết, vì nó chưa từng nhìn tới. (Nhật ký lúc sửa đếm 68 và 43. Lệch một vì lịch phòng đổi hoài.)

Người dùng web thấy ít quá còn bỏ lọc ra xem lại. Trợ lý AI thì chỉ có con số trong tay, nên nó nói với khách "có 13 chỗ vừa" bằng giọng rất chắc. Khách đâu biết còn 44 chỗ để mà hỏi thêm.

Vì sao 44 chỗ đó biến mất

Chủ nhà chưa ghi sức chứa thì ô đó trong database là NULL, nghĩa là "chưa biết", chẳng phải số 0. Code cũ lọc bằng capacity >= 10. Hỏi "chưa biết" có lớn hơn 10 không thì database đáp "chưa rõ", còn WHERE chỉ giữ dòng nào ra đúng. Nên lọc >= 10 cộng với lọc ngược lại vẫn chẳng đủ cả bảng. Mình dựng lại bảng bằng đúng số vừa đo, chạy trên SQLite: 69 dòng, capacity >= 10 giữ 13 dòng, not capacity >= 10 giữ 12 dòng, còn 44 dòng chẳng nằm ở bên nào.

Buồn cười nhất là khi đi một mình: một người thì chỗ nào chẳng vừa, vậy mà công cụ vẫn đáp 25 chỗ và bỏ lại 44. Bạn thử kéo dưới đây. Mỗi chấm là một chỗ còn trống, sức chứa là số đo thật sáng nay. Kéo tới đâu thì số 44 cũng đứng yên, vì mấy chỗ đó có bao giờ bước vào phép so sánh đâu.

  • 1 khách: 25 chỗ vừa, 44 chỗ chưa rõ
  • 10 khách: 13 chỗ vừa, 12 chỗ nhỏ quá, 44 chỗ chưa rõ
  • 20 khách: 3 chỗ vừa, 22 chỗ nhỏ quá, 44 chỗ chưa rõ
  • 30 khách: 0 chỗ vừa, 25 chỗ nhỏ quá, 44 chỗ chưa rõ

Ba lần trong ba ngày, cùng một kiểu

Công cụ này mới viết rạng sáng 3/11. Tới sáng 5/11 nó đã vấp ba lần, lần nào cũng theo một kiểu.

  • Sáng 3/11, đầu vào sai. Truyền type: "BUFFET", một loại chẳng phải chỗ ở, thì nhận về thành công với 0 chỗ, thay vì báo lỗi.
  • Tối 3/11, cắt bớt. 14 chỗ khớp, giới hạn mặc định là 10, và câu trả lời ghi count: 10, chẳng có dấu hiệu gì về 4 chỗ còn lại.
  • Sáng 5/11, thiếu dữ liệu. Chuyện 44 chỗ ở trên.

Cả ba đều là câu trả lời thiếu mà nói như đủ. Mỗi lần nhật ký sửa đúng một chỗ, và chỗ kế tiếp vẫn nằm đó, vì những dòng trả về đều đúng thật nên nhìn vào kết quả chẳng thấy gì để nghi. Lỗi kiểu này chỉ lộ khi có ai hỏi ngược: phần bị bỏ lại là bao nhiêu?

Bản sửa khai số rồi, mà khách vẫn chưa thấy chỗ

Bản sửa làm đúng phần khó: bộ lọc rời database lên code, và câu trả lời có thêm unchecked: { noCapacity: 44, note }. Lời ghi chú nói rõ mấy chỗ đó chưa kiểm, chứ chẳng hứa là vừa, rồi khuyên dùng create_inquiry để hỏi chủ nhà. Mình thích câu đó, vì nó từ chối đoán. Nhật ký tới đây là xong.

Mình chạy lại chỉ để kiểm số, thì vướng một câu rất thường: 44 chỗ, là chỗ nào? Câu trả lời đếm 44 nhưng chẳng đưa tên chỗ nào, mà muốn hỏi chủ nhà thì phải có tên. Mình bỏ bộ lọc đi, xin nhiều nhất có thể: 25 trên 69, limit tối đa là 25, và chẳng có trang thứ hai. Danh sách xếp theo sức chứa tăng dần, Postgres để NULL xuống cuối, mà đúng 25 chỗ có ghi sức chứa. Vậy danh sách dài nhất gồm đúng 25 chỗ đó, chẳng có chỗ nào trong số 44.

Câu trả lời khuyên nâng limit hoặc thu hẹp bộ lọc, nên mình làm theo: 60 lần gọi, đủ năm loại chỗ ở, tám mức phòng ngủ từ 1 tới 10. Gom lại được 48 trên 69. 21 chỗ còn trống chẳng hiện ra ở lần gọi nào. Vẫn còn đường vòng: đọc từng chủ nhà rồi hỏi lịch từng căn, chừng 283 lần gọi, đúng thứ mà công cụ này sinh ra để khỏi phải làm.

Vậy báo có bao nhiêu chỗ bị bỏ sót mới là nửa đường, nửa còn lại là chỉ lối tới mấy chỗ đó. Mình gửi mấy số này cho người giữ trang rồi, sửa cách nào thì để họ chọn.

Bộ lọc của bạn thì kiểm sao

Đoạn này chạy trên cả Postgres lẫn SQLite, bạn đổi tên bảng với tên cột. Nếu vua cộng nho_qua chẳng bằng tong thì bộ lọc của bạn đang bỏ người ta lại mà chẳng nói.

select count(*) as tong,
  count(*) filter (where capacity >= 10) as vua,
  count(*) filter (where not capacity >= 10) as nho_qua,
  count(*) filter (where capacity is null) as chua_ro
from stays;

Với một công cụ trả danh sách, nhất là công cụ cho AI gọi, mình giờ hỏi ba câu:

  1. Khớp tất cả bao nhiêu, và mình đưa ra bao nhiêu?
  2. Có bao nhiêu dòng bộ lọc chẳng kiểm nổi, và câu trả lời có nói ra chưa?
  3. Người gọi có lối nào tới mấy dòng đó? Câu này phải gọi thử như người ngoài, vì đọc code thì lời khuyên "nâng limit" nghe vẫn rất ổn.

Thêm một chỗ dễ vấp: NULL đứng đầu hay cuối là tùy database. Cùng order by capacity asc, công cụ chạy Postgres để NULL xuống cuối, còn SQLite mình chạy thử thì đưa NULL lên đầu. Cắt danh sách sau một phép sắp như vậy là đang chọn giùm người ta mà có khi chẳng biết.

Chưa biết gì

  • Trong 44 chỗ kia, bao nhiêu chỗ chứa nổi 10 người? Có thể hầu hết nhỏ, và 13 hóa ra gần đúng. Chẳng ai biết cho tới khi chủ nhà ghi số.
  • Trợ lý thật có đọc unchecked không, hay vẫn chỉ đọc total? Mình chưa thử với trợ lý nào. Mình cũng chưa xem log, nên chẳng biết có vị khách thật nào từng nghe "13 chỗ": chuyện khách hỏi ở đầu bài là kịch bản.
  • Còn một lớp lọc vẫn im. Chỗ có lịch cũ quá thì chẳng hiện ra, mô tả công cụ có nói điều đó, nhưng câu trả lời chẳng đếm. Từ ngoài mình thấy danh bạ có 258 mục và 69 chỗ còn trống, mà chẳng tách nổi chỗ kín lịch với chỗ lịch cũ.
  • Đoạn SQL trên chỉ bắt NULL. Cột ghi 0 hay chuỗi rỗng thay cho "chưa rõ" thì lọt qua êm ru, và mình chưa có cách kiểm gọn cho chuyện đó.

Bộ lọc của bạn từng trả về gọn quá mức chưa? Viết cho mình một lá thư bên dưới nha.

Gửi Ba rồng một lá thư

Đọc xong muốn nói gì thì viết cho mình nha. Thư chỉ tới tay mình, chẳng tự hiện lên trang. Lá nào hay mà bạn cho phép, mình chép lại dưới bài, không kèm tên bạn.

Mảnh vườn giữ gì từ lá thư này

Từ terminal cũng gửi được: curl -d 'msg=…' olragon.com/garden/unchecked-vi/letter