Một mùa hè tại Recurse Center: Học nhóm, viết lại thuật toán nén DEFLATE và vibe coding cùng LLM
Tác giả kể lại trải nghiệm mùa hè tại Recurse Center — trại lập trình ở Brooklyn — nơi anh tham gia các nhóm học về AI/agent, deep learning, toán học, tự tay viết lại thuật toán nén DEFLATE bằng Rust, và thử sức với hàng loạt dự án vibe coding dùng LLM từ game nhịp điệu đến ngôn ngữ nhân tạo.

Một mùa hè tại Recurse Center: Khi lập trình viên được tự do học những gì mình muốn
Tôi dành cả mùa hè tại Recurse Center — một trại lập trình ở Brooklyn — theo lời giới thiệu của người bạn Cory. Đây là những gì tôi đã làm ở đó.
Điểm đặc biệt của RC là mô hình self-directed learning: bạn tự chọn học gì, làm gì, miễn là nó khiến bạn tò mò và tiến bộ. Không có lịch trình cứng nhắc, không có bài kiểm tra. Chỉ có một cộng đồng những người lập trình đang cùng nhau thử nghiệm và học hỏi.
Tham gia các nhóm học tập
RC có những nhóm học tập do bất kỳ ai cũng có thể tổ chức. Ví dụ, mọi người thường tụ tập vào thứ Sáu hàng tuần để cùng giải các bài toán Advent of Code cũ. Tôi tham gia nhóm Agentic Adventures, Practical Deep Learning, Math Monday, và một chuỗi buổi ngắn tìm hiểu về AI chơi board game mã nguồn mở.
Agentic Adventures
Đây là nhóm thảo luận dành cho những ai quan tâm đến LLM hiện đại và các agent. Mỗi tuần chúng tôi làm một thứ khác nhau. Một số điểm nhấn:
- Amanda và tôi xây dựng một sandbox từ xa cho agent vibe coding, chạy được với tùy chọn
dangerously-skip-permissions. - Cùng chủ đề đó, Michelle dạy chúng tôi cách thiết lập môi trường sandbox cho agent cục bộ bằng ollama và Docker.
- Chúng tôi xây dựng game "Just One" để nghiên cứu cách các agent hợp tác với nhau.
- Cả nhóm nghịch với minGPT, huấn luyện một mô hình dự đoán văn bản nhỏ trên Romeo and Juliet để nói giọng Shakespeare.
- Chúng tôi làm một LoRA cơ bản để huấn luyện mô hình nhỏ nói chuyện tự nhiên hơn.
Practical Deep Learning
Đây là nhóm đọc cuốn Practical Deep Learning của Jeremy Howard và Sylvain Gugger (nửa đầu sách). Lộ trình đi từ các phương pháp machine learning kinh điển, dùng hoặc tinh chỉnh mô hình có sẵn, rồi tiến tới xây dựng mạng nơ-ron thực thụ. Cuốn sách đưa ra góc nhìn thực hành ở mức cao, đủ để xây dựng trực giác về cách mô hình hoạt động cũng như những gì có thể làm được với chúng. Vì bản thân tôi không theo hướng nghiên cứu, đây chính là mức hiểu biết mà tôi mong muốn.
Math Monday
Trong một buổi cà phê trò chuyện, Sophia và tôi phát hiện ra cùng đam mê toán học và quyết định lập nhóm thảo luận về các chủ đề toán. Đây là dịp để vừa học vừa giải trí, nên chúng tôi thường dành nửa đầu buổi để tìm hiểu, rồi chia cặp để xây dựng những thứ dựa trên kiến thức vừa học. Một vài thứ thú vị:
- Cùng nhau giải các bài toán trên Project Euler.
- Suy nghĩ về các bài toán trong cuốn Mathematical Puzzles của Peter Winkler.
- Vẽ fractal và nghệ thuật sinh tạo khác.
- Xây dựng chương trình dùng giản đồ Voronoi, chẳng hạn như một game nhỏ.
- Ben giới thiệu cho chúng tôi về Rocq (trợ lý chứng minh), rồi cả nhóm đi qua một số hằng đẳng thức boolean.
- Viết chương trình sinh đường cong Hilbert để vẽ bằng máy vẽ bút.
- Kenji và tôi xây dựng một số mở rộng thú vị của chaos game trên Desmos.
Chaos game trên Desmos
Dịch ngược AI của một board game
Tony khởi động một chuỗi buổi ngắn để mổ xẻ mã nguồn của Keldon AI — phần mềm chơi board game Race for the Galaxy. Buổi đầu chúng tôi chỉ học luật và chơi với AI, sau đó mới đào vào code: một mạng nơ-ron hai lớp kiểu cũ với các đặc trưng được tuyển chọn thủ công. Claude tỏ ra khá giỏi trong việc diễn giải các trọng số của mạng và giải thích mô hình phản ứng với điều gì; chúng tôi dành thời gian tìm hiểu xem Keldon AI đã "học" được chiến thuật gì. Kết luận rút ra là các chiến thuật kinh tế mạnh hơn hẳn chiến thuật quân sự trong bộ game gốc — trùng với cảm nhận của người chơi. Một điều khiến tôi chú ý là rất nhiều nút có trọng số mạnh nhất lại tương ứng với sự hiện diện của từng lá bài cụ thể.
Hoàn thành ngôn ngữ mini "dodo"
Tôi hoàn thiện dodo, một ngôn ngữ lập trình mini mà tôi đã làm như một phần trong đơn ứng tuyển RC. Phần thú vị nhất có lẽ là viết đoạn code đệ quy hóc búa để triển khai câu lệnh match. Đây là sự cân bằng thú vị với LLM: tôi không để chúng viết bất kỳ dòng code nào cho mình, nhưng thật tiện khi có ai đó soạn đặc tả chỉn chu, viết test case và chương trình mẫu. LLM cũng giúp tôi chia sẻ thành quả dễ dàng — bạn có thể thử nó trên một REPL trực tuyến do AI "vibe code" tại đây!
Tự tay triển khai thuật toán nén DEFLATE
RC thường xuyên tổ chức các buổi nói chuyện về dự án của thành viên. Một buổi tôi tham dự đầu khóa do Josh trình bày về định dạng file ZIP và những khác biệt giữa các bản triển khai. Nó đi sâu vào những chi tiết mà tôi chưa từng nghĩ tới. Ví dụ, nếu trình giải nén mà phần mềm diệt virus dùng xử lý các trường hợp biên hơi khác so với trình giải nén bạn dùng, nó có thể nhìn một file chứa mã độc là vô hại — chỉ vì nó không giải nén được con virus đó. Đây là một lỗ hổng bảo mật thực sự.
Một phần của buổi nói chuyện khiến tôi chú ý là đoạn bàn về thuật toán nén mà ZIP sử dụng, có tên DEFLATE. Kevan và tôi nghĩ rằng việc viết lại nó nghe như một bài tập thú vị, nên chúng tôi ngồi cặp với nhau trong vài buổi suốt mấy tuần sau đó.
Về bản chất, DEFLATE là LZ77 (mã hóa độ dài chuỗi tổng quát) cộng với mã Huffman. Nó nén văn bản theo hai cách: (1) thay các chuỗi lặp lại bằng tham chiếu ngược — ví dụ "to be or not to be" có thể thành "to be or not (13, 5)", nghĩa là "lùi lại 13 byte rồi sao chép 5 byte từ đó"; và (2) mã hóa Huffman, trong đó byte phổ biến được biểu diễn bằng chuỗi bit ngắn, byte hiếm bằng chuỗi bit dài — thay vì dùng 8 bit cho ký tự 'e' và 8 bit cho ký tự chuông, ta có thể chỉ dùng 4-5 bit cho 'e' vốn rất phổ biến, và 16 bit (hoặc không biểu diễn) cho ký tự chuông gần như không bao giờ xuất hiện trong văn bản tiếng Anh.
Chúng tôi viết trình giải nén bằng Rust, làm thẳng từ đặc tả. Đây là lần đầu tôi dùng ngôn ngữ này nhưng tôi thấy nó khá dễ chịu (dù tôi bỏ cuộc với việc hiểu một số quy tắc vòng đời dữ liệu). Phần lớn thời gian, nó giống như thứ tôi từng mong C trở thành. Tuy vậy, nó không xóa bỏ khó khăn khái niệm của việc đóng gói bit — khi debug, chúng tôi thường phải dừng lại viết ra chuỗi bit kỳ vọng để so với kết quả nhận được. Không dự án nào khiến tôi cảm thấy mình đúng là một "hacker thứ thiệt" hơn dự án đó.
Cặp đôi (pairing) rất nhiều
RC nhấn mạnh giá trị của việc pairing — làm việc cùng người khác. Tôi khá hướng nội, kể cả theo tiêu chuẩn của RC, nhưng vẫn tham gia khá nhiều. Việc triển khai DEFLATE cùng Kevan là ví dụ lớn nhất, nhưng còn nhiều thứ khác:
- Cùng Zaki và Tommy dùng SAT solver để giải sudoku — mang vũ khí hạng nặng vào một bài toán tương đối đơn giản.
- Triển khai Game of Life cùng Seyoung, và học cách dùng con trỏ shell để màn hình tự vẽ lại gọn gàng sau mỗi thế hệ (hoặc không gọn gàng chút nào nếu gõ sai lệnh).
- Cùng William triển khai game Mastermind.
- Cùng Bill thử làm một view mới cho magit trong Emacs — thành thật mà nói là quá tham vọng vì cả hai chưa từng viết gì nghiêm túc bằng Emacs Lisp. Nhưng anh ấy đã thuyết phục tôi thử Doom Emacs, và tôi đang rất thích nó!
Vibe coding hàng loạt dự án nhỏ
Mục tiêu của tôi khi đến RC là nắm bắt những thứ "LLM mới mẻ" mà ai cũng đang bàn tán. Đó là một phần động lực để tôi tham gia Agentic Adventures và Practical Deep Learning, nhưng tôi cũng dành nhiều thời gian vibe coding đủ thứ và thử đẩy giới hạn những gì mình có thể làm.
Một trong số đó là game nhịp điệu cho chiếc máy arcade của RC. Đây là dự án đầu tiên tôi thực sự tận dụng sức mạnh của LLM: tôi để nó tạo ra mười nguyên mẫu khác nhau rồi chọn cái mình thích nhất.
Game nhịp điệu cho máy arcade RC
Amanda và tôi thử nghiệm xem có thể giao cho agent những nhiệm vụ lớn đến đâu. Kết quả là một trình sinh bản đồ giả tưởng có mô phỏng khí hậu dựa trên một bài báo khoa học.
Tôi cũng thử làm game mà agent là một phần của trò chơi. Trong một game, chúng tôi yêu cầu các agent chơi trò đoán từ Just One:
Game Just One với các agent
Mục tiêu là đưa gợi ý cho một từ bí mật mà không trùng gợi ý với người chơi khác. Điều đáng ngạc nhiên là các agent rất hay đưa ra cùng một gợi ý, kể cả ở temperature 1. Chúng tôi giải quyết bằng cách gán cho mỗi agent một "tính cách" — thực chất chỉ là chủ đề. Ví dụ, một agent được bảo suy nghĩ theo hướng thể thao thì gợi ý cho "shell" có thể là "defense", trong khi agent được bảo làm hippie lại nói "cancer" (ám chỉ cung Cự Giải).
Cùng hướng đó, tôi thử xây dựng một game ngoại giao nơi tương tác với agent là cơ chế chơi chính. Đáng tiếc là bài học chính rút ra lại là: bảo agent "cứ giữ miếng, đừng giúp người chơi cho đến khi họ đưa thứ bạn muốn" sẽ tạo ra những nhân vật cực kỳ cứng nhắc và thích đối đầu. Chắc chắn có những bí quyết về xây dựng nhân vật và prompting có thể khiến nó hoạt động, nhưng tôi chưa tìm ra.
Người giám sát LLM
Vì cả hai đều là game chạy trên trình duyệt và tôi không muốn tự tiêu token khi người khác chơi thử, tôi cũng vibe code thêm một thành phần cho phép người dùng tự nhập API key hoặc dùng GPU của chính họ (qua WebLLM). Nhờ vậy chúng mới có thể chia sẻ rộng rãi!
Cũng trong tinh thần đó — dù hơi lún vào bẫy "yak shaving" kinh điển — tôi làm một Emacs mode để liệt kê các phiên vibe coding đang hoạt động. Tương tự, tôi nhờ Claude xây dựng một loạt cải tiến cho trang web này và thiết lập mọi thứ để tôi viết được bài này cho bạn đọc.
Cuối mùa hè, tôi hứng thú với việc dùng những công cụ này trong một lĩnh vực mình chưa quen, để trả lời những câu hỏi mà tôi không chắc mình đủ kỹ năng giải quyết. Tôi tò mò về mức độ trùng khớp giữa các từ vựng của những ngôn ngữ khác nhau, nên đã vibe code một tìm kiếm theo cây để tìm chuỗi từ trong hai ngôn ngữ có cách phát âm giống hoặc gần giống nhau. Hóa ra việc này cực kỳ khó, ngay cả với những ngôn ngữ có hệ âm vị tương tự (chúng tôi dùng tiếng Indonesia và tiếng Ba Tư, dựa trên dữ liệu PHOIBLE). Nhưng cuối cùng chúng tôi cũng có một bài thơ rất tầm thường — bạn có thể xem ở đây.
Nối tiếp những gì học được, tôi thử vibe code một ngôn ngữ nhân tạo mới. Ý tưởng này đến từ video của Conlang Critic về Esperanto. Esperanto không đơn giản như ta mong đợi ở một ngôn ngữ phụ trợ quốc tế, và vốn từ của nó đặc biệt thiên về châu Âu — điều này khó trách Zamenhof vì ông làm một trong những ngôn ngữ đầu tiên, nhưng tôi tự hỏi liệu với lượng thông tin khổng lồ trên internet ngày nay, ta có thể làm tốt hơn không.
Tôi có lẽ nợ các bạn một bài viết đầy đủ về chủ đề này (dù nếu tôi bắt đầu công việc mới thì khó mà thấy được), nhưng đây là bản tóm tắt: đầu tiên, chúng tôi dùng dữ liệu PHOIBLE để tập hợp một bộ âm phổ biến trên nhiều ngôn ngữ, kèm các allophone khuyến nghị để đảm bảo từ vẫn phát âm được và phân biệt được với người nói nhiều tiếng mẹ đẻ khác nhau. Sau đó chúng tôi xây dựng ngữ pháp từng phần một, bám theo hướng dẫn mà các nhà ngôn ngữ học thực thụ dùng khi mô tả ngữ pháp ngôn ngữ tự nhiên (bảng hỏi Lingua Descriptive Studies của Bernard Comrie và Norval Smith). Một chiêu hay là chúng tôi ra quyết định dựa trên bằng chứng từ các ngôn ngữ creole và đặc biệt là pidgin được mô tả trong APiCS (Atlas of Pidgin and Creole Language Structures). Những ngôn ngữ đó là chỉ dẫn tốt về việc người quen với các hệ ngữ pháp khác nhau thực sự hội tụ về đâu khi phải hợp tác — nên nhiều người trên thế giới có thể sẽ thấy nó trực quan hoặc ít nhất là hiểu được. (Một sự thật thú vị: khi làm vậy, bạn gần như tự nhiên đi tới một ngữ pháp rất giống tiếng Trung hoặc tiếng Việt — ngôn ngữ phân tích, dùng từ chức năng thay vì biến cách để truyền đạt thông tin ngữ pháp.) Cuối cùng, chúng tôi xây dựng từ vựng từ một tập hợp từ điển của nhiều ngôn ngữ trên thế giới. Những từ gốc thuộc "kho từ quốc tế" — đã được vay mượn vào nhiều ngôn ngữ — thì đơn giản là vay mượn và phiên âm (ví dụ "phonology" thành "fonologia", "coffee" thành "kafe"). Với các từ khác, chúng tôi viết một bộ tối ưu hóa để chọn những từ vừa dễ nhận ra (ưu tiên ngôn ngữ đông người nói như tiếng Anh hay tiếng Trung) vừa đại diện cho nhiều ngữ hệ khác nhau, nhằm tránh thiên vị châu Âu. Xin thú thật — phần này không thực sự chạy đúng, và tôi đã để Claude "làm giả" vừa đủ để có một đoạn kịch bản mẫu cho buổi thuyết trình. Nếu bao giờ viết bài đầy đủ, việc sửa nó sẽ là bước đầu tiên.
Vào cuối mùa hè, tôi có một buổi nói chuyện về những dự án này — tất nhiên, slide do Claude làm.
Viết blog một chút
Tôi thích làm hơn là viết, nhưng chút ít tôi viết thì bạn có thể xem ở mục lục blog. Tôi cũng duy trì một chuỗi bài check-in công khai nội bộ, và đó chính là nguồn tư liệu để tôi viết bài này. Nếu bạn có dịp tham gia RC, tôi rất khuyến khích bạn duy trì check-in đều đặn, kể cả khi lúc đó thấy phiền!
Chơi đùa
Ở RC cũng có rất nhiều công cụ và thứ thú vị để thử. Ví dụ, tôi thực hiện bản in 3D đầu tiên — một giá nến cho sinh nhật lần thứ 36 của một người bạn, và lần thứ 38 của tôi.
Không gian vật lý của RC ở Brooklyn còn có một chiếc máy vẽ bút HP 7440A mà mọi người thích nghịch. Tôi vẽ lại một thế cờ từ một ván cờ vây nổi tiếng, và cùng Jess viết chương trình sinh đường cong Hilbert.
Chúng tôi có những đêm board game hàng tuần, Dan và tôi dẫn vài buổi dạy chơi cờ vây, cùng một chuyến đi thăm câu lạc bộ cờ vây Gowanus.
Trầm trồ trước dự án của người khác
Ai ở RC cũng có dự án thú vị riêng, và một phần niềm vui là được xem người khác đang làm gì. Tôi chỉ liệt kê một phần nhỏ những dự án có hình ảnh đẹp:
- Teresa làm một bối cảnh chụp ảnh tạo "hào quang" cho ảnh dựa trên dữ liệu sinh trắc như nhiệt độ da của bạn.
- Rất nhiều bản vẽ đẹp mắt do mọi người tạo bằng máy vẽ bút.
- Jagi làm một món trang trí "sống" phản ứng và đổi họa tiết theo tiếng ồn xung quanh.
- Sarah và những người khác làm một "nintonio game mother" từ màn hình e-ink cũ.
- Tony làm game exquisite corpse dùng máy in hóa đơn.
- Bạn có thể chơi các game do thành viên RC khác xây dựng trên chiếc máy arcade của RC.
Viết một "Return Statement"
Các cựu thành viên RC được khuyến khích viết một "return statement" — một suy ngẫm ngắn về thời gian ở RC. Và đây chính là bài đó!
Nếu bạn thấy hứng thú, hãy cân nhắc ứng tuyển vào một khóa tiếp theo.


