3 điểm bởi GN⁺ 2024-08-30 | 1 bình luận | Chia sẻ qua WhatsApp
  • Một lập trình viên đã đưa JavaScript đã rút gọn vào ChatGPT để hiểu cách triển khai component ASCII art nhìn thấy trên web, và ChatGPT đã tháo gỡ luồng bị làm rối thành một cấu trúc có thể đọc được
  • Bản gốc là một component React tính toán số cột và hàng theo kích thước cửa sổ, rồi cập nhật lưới chuỗi bằng setInterval60fps
  • ChatGPT giải thích luồng chọn tập ký tự, tính toán ký tự dựa trên tọa độ và thời gian, cập nhật div.textContent, rồi viết lại thành mã TypeScript/React tương đương
  • Ban đầu tưởng rằng các ký tự khối như ░▒▓█ bị thiếu nên cách triển khai khác đi, nhưng khi kiểm tra lại script gốc thì hóa ra đó là vấn đề mã hóa khi sao chép-dán
  • Sau khi cập nhật với tập ký tự đúng, đầu ra trở nên giống component gốc, cho thấy có thể dùng ChatGPT một cách thực tiễn để hiểu và tái triển khai mã đã rút gọn

Tìm cách triển khai ASCII art trong JavaScript đã rút gọn

  • Đã xem mã nguồn để kiểm tra khối ASCII art trên trang reactive.network hackathon được triển khai như thế nào
  • Đoạn mã liên quan đã được rút gọn nên con người khó đọc ngay; thông thường sẽ phải tự giải mã hoặc tìm tệp .map để khôi phục bản gốc
  • Lần này, toàn bộ mã đã rút gọn được dán vào ChatGPT và yêu cầu giải thích cách mã hoạt động

Cấu trúc lộ ra từ mã đã rút gọn

  • Mã có cấu trúc tạo nội dung văn bản động bằng JavaScript và React
  • Các yếu tố chính gồm:
    • Dùng các bí danh ngắn cho những hàm toán học như Math.floor, Math.abs, Math.min
    • Có một tập ký tự gồm "reactive.network REACTIVE.NETWORK" cùng các ký tự đặc biệt và ký tự khối
    • Chọn tập ký tự sẽ dùng tùy theo kết quả của Date.now() % 3
    • Tính chỉ số ký tự sẽ hiển thị tại mỗi tọa độ dựa trên độ dài của tập ký tự đã chọn
  • Hàm cốt lõi eT nhận tọa độ vị trí và giá trị thời gian, rồi trả về một ký tự để hiển thị
    • Giá trị thời gian được chuyển đổi bằng t.time * 8e-5
    • Tọa độ được hiệu chỉnh bằng giá trị nhỏ hơn giữa số cột và số hàng, cùng tỷ lệ màn hình
    • Chỉ số ký tự được xác định bằng cách kết hợp kết quả tính Math.cos(position.x * position.x - position.y * position.y) với giá trị thời gian

Cách render của component React

  • Component tham chiếu tới div sẽ chứa văn bản bằng useRef, và lưu height cũng như width của cửa sổ bằng useState
  • useEffect đầu tiên thiết lập kích thước ban đầu bằng window.innerHeight, window.innerWidth, rồi cập nhật kích thước khi sự kiện resize xảy ra
  • useEffect thứ hai tính kích thước lưới ASCII dựa trên kích thước màn hình
    • colsMath.floor(width / 12) * 1.6
    • rowsMath.floor(height / 12)
    • aspectcols / rows
  • setInterval chạy với khoảng cách 1_000 / 60, tạo lại chuỗi theo 60fps
  • Ở mỗi frame, mã duyệt qua mọi hàng và cột để nối các ký tự đã tính, rồi đưa chuỗi cuối cùng vào element.textContent

Viết lại thành mã dễ đọc bằng TypeScript

  • Sau đó, lập trình viên yêu cầu triển khai mã tương đương bằng TypeScript nhưng dễ đọc hơn cho con người
  • Mã do ChatGPT tạo ra diễn giải vai trò của mã đã rút gọn bằng các tên sau:
    • selectedCharacterSet
    • characterSetLength
    • calculateCharacter
    • Kiểu Size
    • Component React AsciiArtGenerator
  • Các tên biến khó hiểu được đổi thành tên thể hiện ý định triển khai
    • calculateCharacter nhận tọa độ, số cột/hàng, tỷ lệ màn hình và giá trị thời gian để tính ký tự cần hiển thị
    • textRef trỏ tới div thực sự chứa văn bản
    • sizesetSize biểu thị trạng thái kích thước cửa sổ
  • Mã được viết lại dễ đọc hơn nhiều so với mã đã rút gọn ban đầu, và đủ tốt để học cách component này được triển khai

Hiểu nhầm về ký tự bị thiếu và vấn đề mã hóa

  • Trong lần so sánh ban đầu, có vẻ như LLM đã bỏ sót một vài chi tiết triển khai
  • Đặc biệt, người ta cho rằng các ký tự như ░▒▓█ xuất hiện trong component gốc đã bị thiếu khỏi kết quả của ChatGPT
  • Sau đó, một người dùng trên diễn đàn HN chỉ ra khả năng đây là vấn đề sao chép-dán, và sau khi kiểm tra thêm thì phát hiện mã gốc chứa những ký tự khác với các ký tự đã dán vào ChatGPT
  • Khi tải trực tiếp script xuống, các ký tự đúng đã được lấy về, và nguyên nhân được xác nhận là khác biệt về mã hóa
  • Sau khi sửa lại tập ký tự, đầu ra trở nên giống hệt component gốc

1 bình luận

 
GN⁺ 2024-08-30
Các ý kiến trên Hacker News
  • Tôi là người tạo ra HumanifyJS, và đã làm riêng một công cụ dựa trên LLM cho mục đích này
    Công cụ dùng LLM ở cấp AST để đảm bảo mã vẫn tiếp tục hoạt động sau khi gỡ rối mã: https://github.com/jehna/humanify

    • Tôi tự hỏi liệu có khó thêm chức năng đặt lại tên từ đầu hay không
      Ý tôi là một chức năng xóa toàn bộ các tên có ý nghĩa của người dùng trong mã thông thường, rồi chọn tên mới dựa trên thuật toán và các tên còn lại, chẳng hạn như tên built-in
      Khi refactor, thỉnh thoảng tôi làm thủ công bằng LLM; việc này hữu ích để tìm những thuật ngữ chuẩn hơn như antiparallel_line thay cho parallel_line_opposite_direction, hoặc phát hiện các tên có thể khái quát hóa tốt hơn như find_instance_in_list thay cho find_animal_instance_in_animals
    • Nên có nhiều công cụ hơn được xây dựng dựa trên AST; làm rất tốt
      Nói thật là tôi vẫn đang chờ quản lý phiên bản ở cấp AST
    • Tôi tò mò bạn đặt câu hỏi gì cho LLM
      Không biết bạn đưa cả hàm vào rồi lặp lại câu hỏi “cần đổi tên cái gì?” cho đến khi mọi thứ được đổi, hay yêu cầu xử lý toàn bộ một lần rồi parse đầu ra và kiểm tra xem AST có còn khớp hay không
    • Tôi tò mò liệu nó có chạy được với tệp rất lớn không
      Ý tôi là khoảng 50 nghìn dòng
      Sửa: hiện tôi đang thử chạy chế độ openai trên một tệp JS chỉ khoảng 1,2 nghìn dòng, mà sau 20 phút mới xong 70%. Về lý thuyết dù nó chạy được với tệp 50 nghìn dòng thì có lẽ cũng không nên thử
  • Nén JS khá máy móc và đơn giản, nên việc đảo ngược cũng tương đối dễ
    Tất nhiên nhìn chung thì vẫn đủ nhàm chán để con người không muốn tự làm, nhưng bản thân phép biến đổi khá hạn chế, nên chỉ cần có ghi chú theo dõi các định danh bị làm rối là có thể đọc được
    Việc giải nén hoặc gỡ rối mã tổng quát hơn dường như vẫn là một bài toán mở. Trước đây tôi từng viết vài chương trình cố tình làm rối, và theo trải nghiệm của tôi ChatGPT không hiểu được ngay cả ở mức bề mặt
    Ví dụ có một bình luận dùng GPT-4 để cố giải thích mã trong một gist về trình thông dịch Brainfuck viết bằng C chỉ 160 byte [1], nhưng “phiên bản đã làm rõ” đó hoàn toàn không giống mã gốc
    [1] https://gist.github.com/lifthrasiir/596667#gistcomment-47512...

    • Việc một thao tác nào đó đơn giản không có nghĩa là thao tác ngược của nó cũng phải đơn giản
      Ví dụ như nhân/phân tích thừa số nguyên tố, đạo hàm/tích phân, nhớ quá khứ/dự đoán tương lai
      Gỡ rối mã rõ ràng là một trong những bài toán ngược khó như vậy, và có thể dễ dàng trở nên khó hơn vì bug, các routine không dùng hoặc không liên quan, hay các triển khai sai nhưng tình cờ cho kết quả đúng
      Theo nghĩa đó, nếu ChatGPT cho kết quả ổn khi gỡ rối mã thì đó là điều đáng mừng, chứ không có kỳ vọng tiên nghiệm rằng nó phải làm được. Việc kiểm tra giới hạn của ChatGPT bằng một bài toán khó hơn là tốt, nhưng tôi cho rằng điều bạn tìm thấy chỉ là cận trên. Tôi không xem ví dụ trong bài gốc là tầm thường
    • Đổi let userSignedInTimestamp = new Date() thành let x = new Date() là chuyện tầm thường, nhưng để đi ngược lại thì nhiều khả năng phải đọc và hiểu mã xung quanh để xem x được dùng trong ngữ cảnh nào
      Hơn nữa phần mã còn lại cũng đã bị nén nên càng khó hơn, và dù làm đúng hết thì rốt cuộc đây vẫn là một phép biến đổi mất mát, vì tên biến có thể đã chứa những đặc tính không được thể hiện rõ trong mã
    • Chuyển ảnh màu sang đen trắng khá đơn giản, nhưng khôi phục về màu gốc thì không dễ
      Đó là vì dữ liệu đã biến mất trong quá trình này
      Nén cũng hoạt động theo cách tương tự. Rất nhiều thông tin cần thiết để hiểu mã đã biến mất, và việc khôi phục thông tin đó có thể cực kỳ khó
    • Việc đổi tên biến mang tính mô tả timeFactor thành i ngắn hơn nhiều có thể là máy móc và đơn giản, nhưng quá trình đó mang tính phá hủy, và việc đảo ngược thì tương đối không dễ
      Điều đó là bất khả thi nếu không hiểu khá tinh tế mã thực sự đang làm gì. Đó chính là phần LLM đã làm ở đây, và tuy không gây sốc nhưng rất ấn tượng. Thái độ vội vàng hạ thấp quá mức thì không hay lắm
    • Tôi đã thử ngẫu nhiên lần đầu với Claude 3.5 Sonnet: https://claude.site/artifacts/246c1b1a-3088-447a-a526-b1e716...
      Tôi không dùng PC nên chưa thử nghiệm được
  • Chẳng phải tiêu đề nên dùng ChatGPT hoặc tên mô hình GPT-4 thay vì OpenAI, vốn là tên công ty sao?

    • Xét việc OpenAI đã biến GPT, vốn từng là một thuật ngữ kỹ thuật chung, thành thương hiệu, thì việc dùng OpenAI như tên sản phẩm cũng có phần công bằng một cách mỉa mai
  • LLM rất giỏi biến đổi văn bản
    Đây là thế mạnh cốt lõi, nhưng có vẻ chưa được tận dụng đủ

    • Không chỉ là thế mạnh cốt lõi; đó chính là việc mà Transformer được thiết kế để làm, và có thể tranh luận rằng đó cũng là thứ duy nhất chúng làm được
      Những năng lực khác như suy luận hay lưu giữ tri thức chỉ là tác dụng phụ của khả năng siêu phàm trong việc tái tạo văn bản mà không “hiểu” ý nghĩa được chủ ý
    • Đặc biệt mạnh ở những phép biến đổi gần như tuyến tính, nơi thứ tự không thay đổi nhiều và không cần xét sâu quan hệ giữa các mục
      Ví dụ, nó không sắp xếp được danh sách, nhưng có thể dịch ngôn ngữ. Vì một danh sách được sắp xếp gần đúng vẫn là sai, còn một bản dịch gần đúng thì chúng ta thường chấp nhận
    • Nếu gắn với công cụ phù hợp, lẽ ra có thể chuyển cả codebase sang ngôn ngữ và framework khác gần như chỉ bằng một nút bấm
      Nếu một nhóm đang cân nhắc liệu có đáng chuyển một mảng lớn code Python sang Elixir hay không, họ không còn phải chỉ đoán mò nữa
      Gần đây tôi đã thử dịch một script Python sang JavaScript, kết quả không có gì để chê, và có vẻ chỉ cần thêm một vài guardrail là có thể mở rộng quy mô
    • Chẳng phải đây đã là mục đích sử dụng chính trong doanh nghiệp rồi sao?
      Chúng tôi chủ yếu dùng nó để trích xuất dữ liệu có cấu trúc từ các định dạng khác
    • Vấn đề là phải rơi vào những trường hợp không cần quá lo về nguy cơ ảo giác, hoặc có thể kiểm chứng đầu ra dù chưa có dữ liệu ở định dạng hữu ích
      Thêm nữa, bạn cũng phải thiếu kiến thức hoặc kỹ năng để xử lý nhanh hơn bằng awk, Python, Perl, v.v.
  • Một số lập trình viên từng dùng nén làm phương tiện làm rối mã chắc hẳn sẽ nổi giận
    Tôi nhớ trong các phiên bản ColdFusion cũ từng có công cụ “mã hóa” code. Thuật toán rất yếu nên chẳng mất lâu để ai đó làm ra công cụ giải mã
    Dù vậy, một số người không thích điều đó vì họ tin công cụ kia an toàn và đang dùng nó để bán code mà không công khai mã nguồn. Đó là chuyện cuối thập niên 90/đầu những năm 2000, trước khi mã nguồn mở trở thành mặc định áp đảo

    • Có lẽ chẳng bao lâu nữa sẽ xuất hiện các trường hợp dùng kiểu này và một cuộc chạy đua vũ trang
      Một website tổ chức cuộc thi dựa một phần vào mã phía client đã bị làm rối, và những người tham gia khôn khéo giờ có thể đưa nó vào ChatGPT để tăng tỷ lệ thắng
      Đây mới chỉ là khởi đầu
  • Đây là một ví dụ về năng lực thực hiện trí tuệ vượt trội hơn con người
    Không thể phủ nhận. Nhiệm vụ này mang tính trí tuệ và không liên quan đến ghi nhớ đơn thuần. Trên web cũng không có vô số cặp code đã nén/code giải nén để LLM học
    LLM hiểu thứ mà nó đang giải nén, và riêng trong chủ đề cụ thể này thì nhìn chung vượt trội hơn con người

    • Đây chỉ là biến đổi văn bản thôi
      Có chắc là trên web không có nhiều cặp code nén và code chưa nén không? Có thể dễ dàng tạo chúng từ JS hiện có để dùng làm tập huấn luyện, và lượng JS chưa nén đã được dùng trong huấn luyện cũng cực kỳ lớn
    • Tôi lạc quan về AI, nhưng không chắc đây là ví dụ cho hiện tượng vừa nói
      Lý do con người khó hiểu code đã nén là vì tên biến mờ nghĩa, vòng lặp khó đọc, khoảng trắng bị tối giản, v.v. Máy tính không gặp khó với những thứ này, và đó cũng là lý do ban đầu chúng ta nén code. Kiến trúc attention nên làm tốt những việc như vậy
      Tôi cũng cho rằng có rất nhiều code nén/chưa nén. Mục đích của file source map chính là như vậy. Nhìn vào việc OpenAI đầu tư vào duyệt web và phát triển phần mềm, sẽ không ngạc nhiên nếu họ đã đưa dữ liệu nén/chưa nén vào huấn luyện
    • Tôi không có chuyên môn về AI hay hiểu biết về trí thông minh, nhưng chẳng phải cũng có thể nói rằng máy tính bỏ túi thật sự hiểu số học và có năng lực thực hiện trí tuệ vượt trội hơn con người sao?
    • Tại sao việc con người tạo ra công cụ hỗ trợ giải nén lại không được tính vào điểm số của con người?
      Việc máy tính nhân tức thì các số 1000 chữ số là ví dụ con người đã tạo ra công cụ đó trước và thành công trong phép nhân. Ngoài điều đó ra thì còn thành công trí tuệ nào khác? Máy tính cũng đâu tự tạo ra chính nó
      Nếu cắt ranh giới trí tuệ con người ở hộp sọ, và không tính các công cụ mà trí tuệ đó tạo ra và sử dụng như những bước trong quá trình giải quyết vấn đề, thì ta cũng phải chấp nhận rằng con người không đủ thông minh để bay vào vũ trụ, phẫu thuật hay nấu phần lớn bữa ăn
    • GPT-4 đã tiêu thụ nhiều code hơn tổng lượng code mà tất cả tổ tiên của bạn từng thấy trong đời, và hiểu các mẫu nội tại giữa code và phiên bản đã nén của nó
      Việc nhận ra hình dạng trừu tượng của code khi thiếu tên biến, rồi ánh xạ nó sang tên biến con người đọc được từ các mẫu tương tự đã thấy trên Internet khổng lồ, nghe không hề vô lý
  • Thú vị, và có vẻ đã cải thiện rất nhiều
    Hơn một năm trước một chút, tôi nhận được email nhờ giúp với một plugin tùy chỉnh trên một site WordPress bị hack, và đã thử dùng GPT để gỡ rối mã độc. Khi đó, tự mình lần theo code từng bước còn tiến triển tốt hơn nhiều
    Đọc bài này xong tôi đã thử lại [0]. Nó đưa ra một số nội dung có thể hiểu được, nhưng code bị làm rối đến mức về cơ bản là eval các chuỗi không đọc được thông qua đối tượng Window, nên chỉ như vậy là chưa đủ
    Một phần báo cáo tôi gửi khi đó có nội dung đại ý: tôi đã có thể đào sâu vào đoạn JavaScript bị làm rối nặng, phát hiện logic nhận biết việc tải trang, gọi một lớp vỏ tính toán về lý thuyết là hằng số, rồi chuyển hướng trang sang một site độc hại
    [0] https://chatgpt.com/share/f51fbd50-8df0-49e9-86ef-fc972bca6b...

  • Có ai đang làm về LLM decompiler không?
    Có vẻ như có thể biến mọi mã nguồn thành như mã nguồn mở
    Trong trường hợp này, dữ liệu huấn luyện dễ tạo. Chỉ cần build hàng loạt mã GitHub miễn phí bằng nhiều compiler khác nhau, rồi huấn luyện để đảo ngược quá trình biên dịch. Đây là trường hợp dữ liệu huấn luyện tổng hợp phù hợp và có thể tạo khá dễ dàng
    Decompiler chỉ cần được huấn luyện để đảo ngược việc biên dịch, còn các việc như thêm chú thích thì có thể dùng các code LLM lớn hơn hiện có

    • Tác động tiềm tàng là cực lớn
      Không chỉ mở nguồn, mà còn có thể tưởng tượng cảnh dễ dàng decompile và sửa các ứng dụng độc quyền để vá lỗi hoặc thêm tính năng. Đặc biệt với những chương trình đã bị bỏ rơi từ lâu, đây có thể là một vùng giải phóng lớn
      Vì lý do pháp lý, khả năng cao hành vi như vậy sẽ trở thành tính năng bị chặn trong các mô hình lớn
    • Có được mã nguồn chỉ là bước đầu; còn cần quyền đọc, sửa, chạy mã đó và phân phối lại bản sửa đổi
      Những quyền đó chỉ tác giả mã mới có thể cấp
    • Đồng ý. Tôi thấy chuyện “AI tạo/hiểu mã nguồn” là một mồi nhử đánh lạc hướng lớn
      Nếu AI thật sự hiểu mã tốt, nó sẽ cứ tạo hoặc sửa binary
      Tôi tin rằng khi lập trình viên AI thật sự xuất hiện, nó sẽ không bận tâm đến mã mà con người đọc được, mà sẽ code toàn bộ bằng mã máy. Nếu được giao nhiệm vụ bảo trì một hệ thống hiện có, nó sẽ hiểu trọn vẹn cả stack phần mềm lẫn phần cứng
      Tương tự như các mô hình khuếch tán tạo ảnh không bận tâm học các kỹ thuật vẽ thực tế
    • Có một LLM decompile từ x86 sang C: https://github.com/albertan017/LLM4Decompile
    • Giải nén/minify ngược không phải là decompile
      Nó gần với việc đổi tên biến, tên hàm và thêm xuống dòng hơn
  • Rất giống cách tôi thường dùng LLM [0]
    Khi buộc phải tự đọc mã, tôi dùng nó như bước đầu tiên để giải mã
    Việc có thể đưa vào một đống mã spaghetti rối tung rồi nhờ nó làm cho dễ đọc hơn để bắt đầu từ đó thật sự rất giải phóng
    Như tác giả cũng phát hiện, LLM đôi khi bỏ sót chi tiết, nhưng những phần đó thì tôi có thể tự bắt
    Một cách dùng khác là khi tôi hiểu mã làm gì nhưng không thể nắm được vì sao nó lại được viết theo cách đó. Đặc biệt là khi người viết đoạn mã đó không còn ở công ty nữa. Khi đó tôi đưa method vào chat LLM, giải thích nó làm gì, rồi hỏi vì sao một số thứ có thể đã được làm theo cách cụ thể đó
    Không phải lúc nào cũng hoàn hảo, nhưng khá thường xuyên nó đưa ra lời giải thích thật sự hợp lý, chịu được kiểm chứng và đem lại góc nhìn mới. Một hai lần nó còn giúp tôi tránh refactor theo cách mà về sau hẳn sẽ gây đau đầu
    [0] Tôi dùng chatGPT và gần đây là openwebUI làm frontend cho nhiều mô hình khác nhau, chủ yếu là các biến thể Claude, để xem khác biệt. Cũng có thể tạo cấu hình thú vị cho các mô hình khác nhau kiểm tra câu trả lời của nhau

  • Nếu mã được unminify không khớp với mã đã minify, như phần cuối nói “phản hồi của LLM dường như bỏ lỡ vài chi tiết triển khai”, thì tính hữu ích giảm đi rất nhiều
    Đặc biệt vì trong nhiều trường hợp ta không thể đơn giản chạy mã để kiểm tra khác biệt như trong bài viết
    Sửa: xem thread bên dưới thì có vẻ đó là vấn đề encoding. Dù vậy tôi vẫn hơi lo về độ chính xác

    • Việc đổi tên thật sự nên dùng công cụ khác như HumanifyJS: https://github.com/jehna/humanify
    • Mã được unminify trông khá gần với bản gốc
      Trong một số trường hợp, ngoài unminify, ChatGPT còn tự refactor
      Mã gốc không gọi handleResize ngay mà inline nội dung của nó. Có lẽ minifier đã thực sự inline. Khác biệt thực tế duy nhất ở đây là thiếu điều kiện if (typeof window < "u")
    • Phần đó có nghĩa là phiên bản do ChatGPT tạo bị thiếu một số ký tự dùng trong ví dụ gốc
      Cụ thể là các ký tự khối, nhưng có vẻ HN không cho dán ký tự Unicode. Chúng xuất hiện trong bản gốc nhưng không thấy trong phiên bản ChatGPT tạo
      Tuy nhiên cũng rất có thể là do tôi đã không đưa vào toàn bộ ngữ cảnh cần thiết
      Hạ thấp toàn bộ đầu ra chỉ vì thiếu vài ký tự thì gần như là soi quá kỹ. Ngoài điểm đó ra, theo tôi thấy thì đầu ra giống nhau
    • Hơn nữa còn yêu cầu nó triển khai lại từ JavaScript sang TypeScript
      Nếu chỉ bảo nó đổi tên biến và method thì có lẽ đã gần với bản gốc hơn