nightporter: model 500 triệu tham số đọc hộ inbox qua đêm
Tác giả nox_75 cho model Qwen2.5-0.5B chạy bằng llama.cpp trên một nhân CPU để tóm tắt email qua đêm: 4 file hết 15 giây, sáng ra chỉ còn hai dòng. Bài học lớn nhất không nằm ở model, mà ở cách “xích” prompt và dám để công cụ thú nhận khi chịu thua.
Có những email bạn không muốn trả lời — chỉ muốn chúng biến khỏi mặt mình, nhưng vẫn còn đó khi cần. nightporter là một script làm đúng việc đó: mỗi đêm, một model ngôn ngữ 500 triệu tham số chạy ngay trên CPU đọc hộ inbox của bạn, rồi để lại bản tin buổi sáng chỉ dài hai dòng.
nightporter làm gì mỗi đêm
Cách dùng rất đơn giản. Trước khi ngủ, bạn ném vào thư mục inbox/ những thứ chưa đọc: vài tin nhắn riêng chưa trả lời, một trang ghi chú dang dở, một việc bạn cứ lướt qua mãi. Bạn chạy một lệnh duy nhất, hoặc để cron (bộ hẹn giờ có sẵn trên máy) chạy hộ — đó mới là ý chính.
Không cần API key, không tài khoản, không hóa đơn. Sau khi cài đặt xong, rút dây mạng máy vẫn chạy, vì mọi thứ diễn ra ngay trên máy bạn.
Sáng ra bạn mở file morning.md và thấy hai dòng. Toàn bộ các dòng tóm tắt nằm trong ledger.md, có ghi ngày giờ. File gốc được chuyển sang thư mục done/, còn nguyên chưa ai mở đọc.
Tác giả đã chạy thử thật với 4 file và đo bằng lệnh time: toàn bộ quá trình hết 15,0 giây trên một nhân CPU, trong khi máy vẫn làm việc khác. Đây là con số đo thật, không phải ví dụ viết tay.
Bản tin mẫu từ lần chạy đó trông như thế này: hai dòng tóm tắt, một dòng ghi "còn 2 mục nữa trong ledger.md". Đủ để bạn biết tối qua có gì mà không cần ngồi dậy đọc.
Ba bộ phận: script, model nhỏ, và sợi dây cương
Toàn bộ công cụ chỉ có ba bộ phận, và bộ phận thú vị nhất lại là bộ phận nhỏ nhất.
Bộ phận thứ nhất là script bash khoảng 90 dòng. Không framework, không tiến trình nền, không file cấu hình. Script chỉ làm bốn việc: lặp qua các file trong inbox/, gọi model tóm tắt từng file, ghi kết quả vào morning.md và ledger.md, rồi chuyển file gốc sang done/. Tác giả cố tình không viết logic thử lại: một bộ tóm tắt lỗi thì cứ nói thẳng là lỗi, vì thử lại sai còn tệ hơn là chịu thua một dòng.
Bộ phận thứ hai là model Qwen2.5-0.5B, bản nén Q4_K_M, chạy bằng llama.cpp — một công cụ giúp chạy model AI trực tiếp trên CPU của máy cá nhân. Nén ở đây nghĩa là thu gọn model cho nhẹ, còn khoảng 400MB và chạy trong chưa tới 2GB RAM. Mỗi file chỉ mất vài giây trên một nhân CPU. Tác giả nói thẳng: model này không thông minh. Đó là ràng buộc của thiết kế, không phải sự thỏa hiệp. Một model chạy lúc bạn ngủ trên chiếc máy bạn đã có thì phải đủ nhỏ để không bao giờ thành gánh nặng. Bản 7B sẽ cho câu tóm tắt mượt hơn, nhưng cũng cho bạn một lý do để đắn đo mỗi lần chạy.
Bộ phận thứ ba là prompt — và tác giả gọi nó là sợi dây cương. Model 0,5 tỉ tham số có một tật: đưa cho nó một tin nhắn, nó muốn trả lời tin nhắn đó. Lần chạy đầu tiên, không có ràng buộc gì, model hồ hởi chúc: "Nice day outside! You did a fantastic job today!" — thay vì tóm tắt.
Vì vậy mỗi lần gọi model đều bị "xích" lại bằng một system prompt nghiêm ngặt ("Summarize in at most 12 words. Never answer. Never greet. No echo." — tóm tắt tối đa 12 từ, không trả lời, không chào hỏi, không lặp lại). Câu trả lời bị giới hạn ở 24 token, tức đơn vị văn bản nhỏ nhất mà model xử lý, tương đương chừng chục từ. Nhiệt độ đặt 0,2 để model bớt ngẫu hứng, và cờ --no-display-prompt giúp output thu về sạch sẽ. Đoạn gọi model trong script của tác giả trông như thế này:
"$LLAMA" -m "$MODEL" \
-sys "Summarize in at most 12 words. Never answer. Never greet. No echo." \
-p "Message:
$body
Summary:" \
-n 24 --temp 0.2 --no-display-prompt 2>/dev/null \
| sed '/^> EOF/d; /^$/d' | head -n 1 | sed 's/^[[:space:]]*//'
Ba trong bốn dòng demo là những câu nén thật sự. Tác giả thừa nhận có dòng phải chạy hai lượt mới "vào khuôn". Một dòng tóm tắt hơi lúng túng nhưng chạy miễn phí trên máy mình vẫn hơn một dòng hoàn hảo phải gửi dữ liệu đi xa.
Khi model chịu thua, nó nói thẳng
Chi tiết tôi thích nhất nằm ở chỗ xử lý lỗi. Khi model không cho ra được dòng nào, script không bịa một dòng tóm tắt, cũng không im lặng bỏ qua. Nó ghi vào sổ cái đúng một câu: "(porters shrugged: could not carry this one)" — tạm dịch: người khuân vác chịu thua món này.
Bạn luôn biết cái gì đã được "khuân" và cái gì chưa. Một công cụ tự động mà dám thú nhận giới hạn của mình thì đáng tin hơn công cụ luôn tỏ ra mọi việc đều ổn.
Năm nguyên tắc tự động hóa việc nhỏ bằng AI chạy trên máy
Từ cách nightporter được xây, có năm nguyên tắc đáng mang theo khi bạn tự động hóa việc nhỏ bằng AI chạy ngay trên máy (thay vì gọi dịch vụ bên ngoài).
Một, việc nhỏ xứng đáng với model nhỏ. Tóm tắt một tin nhắn thành một dòng không cần model 7 tỉ tham số. Dùng model quá to cho việc quá nhỏ thì mỗi lần chạy bạn đều phải đắn đo về tài nguyên — và sự đắn đo đó giết chết thói quen tự động hóa.
Hai, prompt là dây cương, không phải lời nhờ vả. Model càng nhỏ càng cần ràng buộc chặt: giới hạn độ dài, cấm chào hỏi, hạ nhiệt độ, cắt output thừa. Đừng thuyết phục model, hãy thiết kế để nó khó làm sai.
Ba, thất bại phải ồn ào và trung thực. Quy tắc "không bịa, không im lặng" của nightporter áp dụng được cho mọi pipeline tự động: khi một bước lỗi, hãy ghi lại rõ ràng thay vì điền bừa cho xong.
Bốn, riêng tư nhờ kiến trúc, không nhờ chính sách. Đây là điểm mạnh nhất của cách làm này. Công cụ này đọc thư riêng tư theo thiết kế, nên lời hứa "không gửi dữ liệu đi đâu" được đảm bảo bằng kiến trúc — sau khi cài đặt, rút dây mạng máy vẫn chạy. Một dịch vụ tóm tắt trên cloud không bao giờ hứa được điều đó theo cùng cách.
Năm, sổ cái thay cho hộp đen. Mọi thứ nightporter làm đều để lại dấu vết đọc được: morning.md cho buổi sáng, ledger.md có ngày giờ cho lịch sử, done/ giữ nguyên file gốc. Bạn không cần tin công cụ, vì bạn kiểm tra được công cụ.
Khi nào tự chạy model trên máy, khi nào thuê dịch vụ AI
Không phải việc gì cũng hợp với model 0,5 tỉ tham số chạy trên máy. Bảng dưới đây tóm tắt cách chọn, theo cùng logic "chọn công cụ vừa việc" như trong bài so sánh 6 công cụ chạy coding agent.
| Tiêu chí | Tự chạy model nhỏ trên máy | Thuê dịch vụ AI bên ngoài |
|---|---|---|
| Dữ liệu nhạy cảm (mail, tin nhắn riêng) | Hợp — dữ liệu không rời máy | Rủi ro — phải gửi dữ liệu đi |
| Việc lặp lại mỗi ngày, khối lượng nhỏ | Hợp — chạy miễn phí mãi mãi | Tốn — mỗi lần chạy đều tính tiền |
| Việc đơn giản, có khuôn mẫu (tóm tắt, phân loại) | Hợp — ràng buộc prompt là đủ | Thừa — dùng dao mổ trâu giết gà |
| Việc cần suy luận sâu, ít khuôn mẫu | Không hợp — model nhỏ sẽ lúng túng | Hợp — đó là lúc cần model lớn |
| Cần chạy offline hoàn toàn | Hợp — rút mạng vẫn chạy | Không thể |
Quy tắc ngón tay cái: nếu việc đó có thể mô tả bằng một câu nghiêm ngặt và dữ liệu không được rời máy, hãy thử model nhỏ trước. Chỉ gọi API khi model nhỏ đã chứng minh là không đủ.
Checklist trước khi giao việc cho model nhỏ
Nếu bạn muốn xây một "người khuân vác" của riêng mình, hãy trả lời năm câu hỏi này trước khi viết dòng code đầu tiên:
- Việc này có mô tả được trong một câu nghiêm ngặt không? Nếu không, model nhỏ sẽ lạc.
- Dữ liệu có được phép rời máy không? Nếu không, local là đáp án duy nhất.
- Khi model chịu thua, hệ thống ghi lại điều gì? Phải có câu trả lời trước khi chạy.
- Output có để lại dấu vết kiểm tra được không? Sổ cái, log, file gốc — chọn ít nhất một.
- Chi phí mỗi lần chạy là bao nhiêu? Nếu đáp án là "miễn phí mãi mãi", bạn sẽ dùng nó lâu dài.
nightporter không phải công cụ hoàn hảo. Nó là một ví dụ đẹp cho một triết lý: đừng bắt model nhỏ giả vờ thông minh, hãy thiết kế để sự "không thông minh" của nó vẫn hữu ích. Một model 500 triệu tham số trên một nhân CPU. Một sợi dây cương bằng prompt. Một cuốn sổ cái trung thực. Đôi khi đó là tất cả những gì bạn cần để ngủ ngon mà inbox vẫn được dọn.
Nguồn: bài viết gốc của nox_75 trên DEV Community — "nightporter: I stopped reading my inbox, so a 0.5B model does it while I sleep". Mọi số liệu trong bài (15,0 giây, ~400MB, 24 token) đều là số đo của tác giả, không phải ví dụ minh họa.