OGIF Office Hours #19 - Golang weekly, Designing for forgiveness, File sharing system design, Dify AI demo
82 minutes
Topics & Highlights
00:00 - Introduction and Setup
Introduction to the session, checking attendance, and preparing for the recording.
00:06:00 - Go Programming and Common Pitfalls
Discussion on the release of Go 1.23, covering key updates and common mistakes in Go, such as pass by value issues and range loop behavior.
00:17:06 - User Interface (UI) Design and Error Management
Exploration of best practices in UI design, focusing on error management, user feedback, and improving user experience through clear communication and design choices.
00:39:05 - File Sharing System Design
Detailed presentation on the design and implementation of a file-sharing system, including database structure, permission handling, and file path logic.
00:47:44 - Query Optimization Strategies
Strategies for optimizing queries to achieve faster system performance.
01:00:17 - Dify AI Demo and Workflow Automation
Demonstration of Dify AI tools and workflow automation, showcasing how to integrate AI into system workflows and optimize processes using AI-powered agents.
01:12:16 - Summary and Concluding Thoughts
Wrapping up the session with key takeaways and a final summary.
Vietnamese transcript
00:00:00 - Anh Thành có ở đây không? Mọi người có nghe rõ không? Chắc là chưa, để kiểm tra lại xem. Alo, alo, mọi người nghe thấy chưa? OK rồi, restart lại phần này. Tuần này chúng ta sẽ tiếp tục chủ đề của tuần trước, có thể một số anh em chưa tham gia ngay được. Hiện tại thấy có hai team chuẩn bị vào, ai sẽ lên trước nhỉ? Sẵn sàng chưa?
00:06:00 - Mình đã record chưa nhỉ? Bật record lên nhé. OK rồi, mọi người cứ vào dần sau nhé. Để xem nào, mình có cần restart lại không? Không cần đâu, cứ tiếp tục thôi. Tuần trước có nói về Go 1.23 đã chính thức phát hành, mọi người nên xem qua các ghi chú của bản phát hành. Dù chúng ta đã giới thiệu về interactive note trước đó, nhưng xem qua bản đầy đủ cũng thấy có những chi tiết nhỏ đáng chú ý.
00:08:17 - Khi mọi người viết websocket trong Go, thường thì sẽ sử dụng Gorilla. Tuy nhiên, có một thư viện khác mà mình đã thử qua, nó có những tính năng đảm bảo tốt hơn, và không gặp nhiều vấn đề như Gorilla. Thư viện này có tuổi đời khá lâu, khoảng 5-6 năm rồi. Nhưng gần đây, do tác giả ban đầu không còn duy trì nó nữa, một nhóm mới đã tiếp nhận và tiếp tục phát triển. Nếu ai chưa thử thì có thể xem qua.
00:08:58 - Một số vấn đề thường gặp phải khi sử dụng Go, đặc biệt là với những người mới bắt đầu, có một số lỗi dễ mắc phải. Ví dụ, mình nhớ hồi mới làm intern, có một lần bị lỗi liên quan đến việc pass by value trong Go. Strings trong Go là pass by value, nên khi bạn thay đổi giá trị của một string trong một slice, nó sẽ không ảnh hưởng đến string ban đầu.
00:11:04 - Trong một vòng lặp range qua slice của các struct, nếu bạn muốn thay đổi giá trị của một field nào đó thì nó cũng sẽ không work như mong đợi, vì mỗi lần lặp qua range, nó tạo ra một bản copy của phần tử đó, nên khi bạn thay đổi giá trị thì chỉ thay đổi bản copy chứ không phải bản gốc.
00:11:43 - Để sửa lỗi này, bạn có thể sử dụng cách range qua index của array thay vì range qua từng phần tử. Điều này giúp bạn truy cập trực tiếp vào phần tử gốc của array và thay đổi giá trị một cách đúng đắn.
00:12:33 - Một ví dụ khác là khi bạn có một slice trong Go, và bạn muốn thao tác với một phần tử cụ thể. Ví dụ, bạn muốn thêm một giá trị vào một slice mới từ slice gốc, điều này có thể thay đổi cả slice gốc nếu không cẩn thận. Để tránh việc này, bạn có thể thêm một số vào cuối slice để chỉ định rằng slice mới này sẽ sử dụng một array khác làm backing store.
00:15:00 - Khi bạn sử dụng slice và muốn giữ nguyên giá trị của slice ban đầu, bạn cần cẩn thận với việc slice có thể chỉ đơn giản là tham chiếu đến cùng một array dưới lớp, dẫn đến việc thay đổi giá trị slice mới sẽ ảnh hưởng đến slice ban đầu.
00:17:06 - Việc quản lý lỗi trong thiết kế giao diện người dùng là rất quan trọng. Thay vì cố gắng làm cho người dùng không phạm lỗi, chúng ta nên tạo ra một giao diện thân thiện, cho phép họ hành động sai và sửa sai một cách dễ dàng. Việc thiết kế như vậy sẽ giúp cải thiện trải nghiệm người dùng.
00:18:07 - Một trong những cách để làm điều này là cung cấp cảnh báo và phản hồi ngay lập tức khi người dùng thực hiện hành động sai. Ví dụ, trong Shopify, họ đã thay đổi cách hiển thị trang chỉnh sửa sản phẩm để cảnh báo người dùng khi có thay đổi chưa lưu.
00:22:38 - Đôi khi việc cung cấp cho người dùng khả năng quay lại các bước trước đó có thể giúp họ cảm thấy thoải mái hơn khi sử dụng sản phẩm. Ví dụ, Google Drive cho phép bạn undo các hành động như xóa một file, giúp người dùng dễ dàng khôi phục lại những gì họ đã làm sai.
00:27:43 - Một ví dụ khác là trong các sản phẩm xây dựng, chúng ta cần đảm bảo rằng mỗi hành động của người dùng đều có cảnh báo hoặc thông báo nếu có vấn đề xảy ra, để họ có thể xử lý kịp thời.
00:30:39 - Trong các ứng dụng như Twitter hay Discord, việc giới hạn số ký tự và cung cấp phản hồi ngay lập tức khi người dùng vượt quá giới hạn giúp họ dễ dàng điều chỉnh và tránh lỗi.
00:31:23 - Việc cung cấp hướng dẫn và ngăn chặn lỗi ngay từ đầu là rất quan trọng. Các ứng dụng phức tạp thường sử dụng tooltips hoặc hướng dẫn on-boarding để giúp người dùng hiểu rõ cách sử dụng.
00:32:54 - Khi thiết kế sản phẩm, chúng ta cần phải quan tâm không chỉ đến các trường hợp sử dụng thông thường mà còn cả các trường hợp lỗi để đảm bảo rằng trải nghiệm người dùng luôn được cải thiện.
00:35:52 - Khi làm việc với UI, chúng ta cần phải xác định rõ các trường hợp lỗi và thiết kế sao cho chúng dễ dàng xử lý. Điều này không chỉ giúp cải thiện trải nghiệm người dùng mà còn giúp giảm thiểu lỗi trong quá trình sử dụng sản phẩm.
00:38:20 - Khi triển khai các tính năng mới, chúng ta cần đảm bảo rằng dữ liệu cần thiết luôn được lưu trữ và có thể truy xuất lại khi cần thiết. Điều này sẽ giúp đảm bảo rằng hệ thống luôn hoạt động một cách ổn định và tin cậy.
00:39:05 - Để hệ thống hoạt động một cách hiệu quả, chúng ta cần phải cẩn thận với việc quản lý quyền truy cập và chia sẻ dữ liệu. Các tính năng như phân quyền và thiết lập mật khẩu cần được triển khai cẩn thận để đảm bảo rằng chỉ những người được ủy quyền mới có thể truy cập vào dữ liệu quan trọng.
00:41:28 - Được rồi, giờ mọi người nhìn thấy màn hình của em chưa? OK, thấy rồi nhé, tiếp tục với agenda thôi anh em ơi.
00:42:43 - Bài hôm nay em sẽ nói về việc thiết kế một hệ thống chia sẻ tệp tin (sharing file system). Hệ thống này sẽ không có nhiều điều mới mẻ, nhưng em hy vọng nếu ai đó từng làm các project tương tự thì có thể tiết kiệm thời gian tìm hiểu. Bài thuyết trình của em sẽ gồm ba phần. Phần đầu tiên là overview của hệ thống.
00:43:27 - Đây là sơ đồ Use Case của hệ thống. Nó bao gồm các chức năng cơ bản như xem (view), chỉnh sửa (edit), và tải lên (upload). Những chức năng này khá là đơn giản, nhưng yêu cầu đặt ra là phải tối ưu hóa các câu truy vấn (query) để hệ thống hoạt động nhanh nhất có thể. Các chức năng khác liên quan đến việc quản lý quyền truy cập (permission) và chia sẻ tệp tin (sharing). Phần này em sẽ chủ yếu nói về business logic và cách em thiết kế hệ thống như thế nào. Anh Thành chuyển qua trang tiếp theo giúp em.
00:44:17 - Phần hệ thống tệp tin (file system) này hướng đến cấu trúc cơ bản giống như các thư mục (folders) và tệp tin (files) mà mọi người xử lý hàng ngày trên máy tính của mình. Vì vậy, em sẽ đi nhanh qua phần này. Tiếp theo là thiết kế cơ sở dữ liệu (database design) của hệ thống.
00:45:01 - Đây là sơ đồ Entity Relationship (ER diagram) của hệ thống. Hệ thống này có cấu trúc cao nhất là workspace, chứa các người dùng (users) trong một nhóm. Mỗi user khi được tạo ra sẽ có một workspace riêng, và người dùng đó có thể mời thêm các thành viên (members) khác vào cùng làm việc trong workspace này. Tiếp theo là phần asset, bao gồm các tệp tin (files) và thư mục (folders). Các thông tin cơ bản của tệp tin sẽ bao gồm tên (name), URL, kích thước (size), trạng thái (status), và trạng thái ghim (pin). Đặc biệt là trường ‘path’ (đường dẫn tệp tin) trong asset, giúp quá trình query trở nên nhanh hơn. Phần này em sẽ giải thích kỹ hơn ở phần sau. Hai bảng còn lại liên quan đến việc xử lý logic chia sẻ tệp tin (sharing) và quyền truy cập (permission).
00:45:38 - Đây là phần logic cho đường dẫn tệp tin (file path). Đường dẫn tệp tin (file path) trong hệ thống được thiết kế để mang lại hiệu quả cao cho việc query hoặc xử lý các tác vụ liên quan đến tệp tin trong cơ sở dữ liệu. Ví dụ, nếu bạn muốn lấy phản hồi (feedback) cho một tài liệu có ID là d123, nó sẽ được tổng hợp từ tất cả các thư mục cha (parent directories) của nó như f789 và f123, nằm trong workspace w123. Nhờ vậy, bạn có thể dễ dàng lấy được tất cả các ID của các thư mục cha.
00:46:16 - Khi bạn muốn liệt kê (listing) các thư mục con (subdirectories) của một thư mục cụ thể, chẳng hạn như f123, bạn chỉ cần query các trường chứa thông tin về đường dẫn của nó (file path) có chứa f123 và workspace w123. Điều này làm cho việc liệt kê các thư mục con trở nên rất đơn giản và hiệu quả.
00:46:59 - Một trường hợp khác là khi bạn muốn di chuyển (move) một tệp tin, việc đầu tiên cần làm là liệt kê các thư mục mà tệp tin có thể được di chuyển đến. Các thư mục này phải đảm bảo không phải là con của chính tệp tin đó. Việc này được thực hiện bằng cách loại bỏ tất cả các thư mục mà trong đường dẫn của nó có chứa ID của tệp tin cần di chuyển. Ví dụ, nếu bạn muốn di chuyển tệp F11 từ folder 2 sang folder 1 với ID là f123, bạn cần loại bỏ tất cả các thư mục mà trong đường dẫn của nó có chứa ký tự f111.
00:47:44 - Sau khi di chuyển tệp tin, bạn chỉ cần cập nhật đường dẫn của nó từ đường dẫn cũ sang đường dẫn mới, và tất cả các thư mục con của nó cũng sẽ được cập nhật trong cùng một câu lệnh query duy nhất. Như em đã trình bày từ đầu, tất cả các câu truy vấn trong hệ thống này đều có độ phức tạp là O(1) hoặc O(2), giúp hệ thống hoạt động rất nhanh và tránh phải dùng transaction, tránh ảnh hưởng đến tốc độ của các câu truy vấn khác.
00:48:34 - Phần tiếp theo là về quyền truy cập (permission) và chia sẻ tệp tin (sharing). Phần này có ba chức năng chính: thiết lập quyền truy cập (Set permission) cho các thành viên trong hệ thống, chia sẻ tệp tin công khai (sharing public files) cho người dùng bên ngoài hệ thống, và thiết lập mật khẩu (set password) cho các tệp tin. Để em chuyển sang phần tiếp theo.
00:49:17 - Đây là sơ đồ cơ bản thể hiện quá trình khi một người dùng truy cập vào một tệp tin, hệ thống sẽ kiểm tra quyền truy cập (permission) của người dùng như thế nào. Đầu tiên, hệ thống sẽ kiểm tra quyền truy cập trực tiếp (direct permission) và quyền truy cập qua dự án (project permission). Quyền truy cập trực tiếp là những quyền được gán trực tiếp cho email của người dùng, ví dụ như quyền chỉnh sửa (edit) nếu người sở hữu (owner) của tệp tin đó đã gán cho email của người dùng quyền này. Nếu cả hai quyền này không thỏa mãn, hệ thống sẽ kiểm tra quyền truy cập công khai (public access).
00:50:03 - Việc kiểm tra quyền truy cập công khai (public access) ở đây có nghĩa là quyền truy cập của những người dùng như guest, tức là người dùng nằm ngoài hệ thống hoặc không thuộc workspace. Nếu người dùng này đăng nhập vào hệ thống, họ sẽ có quyền tương ứng với quyền công khai (public access) đã được thiết lập. Còn nếu tệp tin đó là công khai nhưng người dùng chưa đăng nhập, họ chỉ có quyền đọc (Read Only) mà không có quyền chỉnh sửa.
00:50:46 - Về mô hình dữ liệu (data model), hệ thống sẽ có hai bảng chính như em đã giới thiệu ở phần đầu: bảng member_permission
, thể hiện quyền truy cập chung của tệp tin, và bảng sub_permission
, thể hiện quyền truy cập cụ thể của một người dùng với email cụ thể. Bảng member_permission
có mối quan hệ 1-1 với bảng asset
, nghĩa là một tệp tin chỉ có một bộ quyền truy cập chính (main permission). Nó bao gồm các trường như inherit
để cho biết quyền truy cập có được thừa hưởng từ thư mục cha hay không, asset_id
để liên kết với bảng asset, public_role
để chỉ định quyền công khai (public access), và has_password
để xác định xem tệp tin có yêu cầu mật khẩu hay không.
00:52:16 - Khi tạo một tệp tin, hệ thống sẽ tạo một bộ quyền truy cập với thuộc tính inherit
được thiết lập là true
, tức là tệp tin này sẽ thừa hưởng tất cả các quyền truy cập từ thư mục cha của nó. Nếu có bất kỳ sự thay đổi nào về quyền truy cập, hệ thống sẽ loại bỏ thuộc tính inherit
và tạo ra một bản ghi mới (record) trong bảng sub_permission
.
00:54:10 - Về cơ bản, hệ thống hoạt động giống như các dịch vụ lưu trữ đám mây khác như Google Drive hay Notion. Tuy nhiên, điểm khác biệt ở đây là cách hệ thống xử lý các quyền truy cập đa tầng (multi-level permissions) một cách hiệu quả, tránh việc phải lặp lại các kiểm tra không cần thiết.
00:56:41 - Nếu có 10 thư mục, và thư mục thứ 10 là con của thư mục thứ nhất, thư mục thứ nhất có mật khẩu, thì cách kiểm tra mật khẩu của thư mục thứ 10 sẽ như thế nào? Đầu tiên, hệ thống sẽ kiểm tra quyền truy cập của thư mục thứ 10. Nếu quyền này được thừa hưởng từ thư mục cha, hệ thống sẽ tiếp tục kiểm tra thư mục cha gần nhất mà không thừa hưởng quyền truy cập từ các thư mục trên nữa. Nếu thư mục thứ 8 không thừa hưởng, quyền của thư mục thứ 10 sẽ phụ thuộc vào thư mục thứ 8. Nếu chỉ có thư mục thứ nhất là không thừa hưởng quyền truy cập, hệ thống sẽ kiểm tra trực tiếp trên thư mục thứ nhất.
00:59:34 - Đó là toàn bộ phần trình bày của em. Không biết mọi người có câu hỏi nào không? Nếu không, em xin cảm ơn mọi người đã lắng nghe.
00:57:20 - Ví dụ nhé, nếu thư mục thứ tám không có thuộc tính inherit
, thì quyền của thư mục thứ mười sẽ phụ thuộc vào thư mục thứ tám. Nhưng nếu trường hợp của anh là chỉ có thư mục thứ nhất không có inherit
, thì mình sẽ kiểm tra trực tiếp từ thư mục thứ nhất luôn. Ok, không biết anh em có gì cần thảo luận thêm về vấn đề này không? Để em kiểm tra lại xem có gì cần bổ sung không. Đúng rồi, để thực hiện điều này, hệ thống sẽ phải truy vấn ngược lại, từ các thư mục con lên các thư mục cha.
00:58:18 - Anh Hiếu, chắc anh chưa xem kỹ đoạn này, vì mấy cái này phải hiểu kỹ mới xử lý đúng được. Em có câu hỏi nào khác không? Thực ra tính năng mà Đạt vừa chia sẻ về việc chia sẻ tệp (sharing feature) khá giống với Notion hoặc Google Drive đúng không? Ừ, đúng vậy, bên VOT trước đây cũng đã từng làm một tính năng tương tự, nhưng phải xử lý đến hai hoặc ba tầng permission đúng không? Ừ, đúng rồi, rất phức tạp.
00:59:34 - Ok, mình còn mấy phút nữa để hẹn Tom demo nhanh cái workflow bên phía đi. Trong hai tháng vừa qua, team đã xây dựng một server đơn giản để test các workflow. Mục đích là để hình dung các quy trình có thể tối ưu như thế nào từ phía khách hàng, không chỉ riêng phía khách hàng mà team mình cũng có vài nhu cầu sử dụng AI nhưng chưa rõ cách tối ưu nó sao cho nhanh và gọn. Mình có thể dùng các giải pháp như Flowwise hoặc Airflow, nhưng chúng hơi phức tạp. Vì vậy, mình chọn LLM (Large Language Model) cho đơn giản hơn, mình sẽ xây dựng một agent hoặc một tool để xử lý.
01:00:17 - Nếu chúng ta thành công trong việc này, khi xây dựng các ứng dụng khác cho khách hàng hoặc app của mình, việc triển khai sẽ dễ dàng hơn. Giờ mình sẽ giới thiệu mọi người xem cái Memo chatbot mà bên Memo đang dùng. Thực sự thì bên Memo cũng có một chatbot kết nối với Dify, do mình lười code nên kết nối với nó để xử lý nhanh chóng hơn. Để xem nào, mạng bên Vân Phẳng dạo này cũng bị lag.
01:00:59 - Ok, thử lệnh “get me the latest notes on Go”. Chatbot này sẽ theo một workflow giống như bên Eming xây dựng cho bên phía Tôn. Nó sẽ sử dụng mô hình AI để tạo câu lệnh SQL query, sau đó thực hiện truy vấn trên database. Mình có một file pre-setup để gọi query từ đấy và sau đó xuất kết quả ra dạng markdown. Mô hình (model) này không quá phức tạp, nhưng mình phải sử dụng một số kỹ thuật như prompt engineering, tức là kỹ thuật đưa đủ ngữ cảnh (context) và thông tin cho AI để nó trả lời đúng theo ý mình.
01:01:49 - Khi bạn thiết kế workflow như vậy, mục đích là làm sao cho nó chạy một cách trơn tru. Có hai cách: một là bạn phải code hành động (actions) đằng sau các function, hoặc là bạn phải làm prompt engineering để hướng dẫn AI tạo query SQL chính xác. Cái trick này hay dùng khi làm prototype hoặc làm nhanh cho khách hàng. Mình thiết lập server để mô hình AI tự suy luận cách query SQL, sau đó gửi kết quả trả về chatbot.
01:03:20 - Thực sự là phần “magic” của nó nằm ở chỗ mô tả (description) của các function call, nó chứa thông tin về các bảng (tables) trong Memo. Nếu không có phần này, khi mình chạy lệnh, AI không biết cần query bảng nào, dẫn đến lỗi. Đây là lý do tại sao prompt engineering rất quan trọng trong việc đảm bảo rằng AI nhận được đúng thông tin và tạo ra kết quả chính xác.
01:04:47 - Nếu mình thử chạy lại chatbot Memo mà không có description đúng, nó sẽ tạo ra một lỗi vì không biết bảng nào cần query. Trong trường hợp này, trick mà mình hay dùng cho prototype hoặc khi làm việc nhanh với khách hàng sẽ rất hữu ích. Những gì bạn có thể làm với AI gần như không giới hạn, miễn là bạn hiểu rõ cách làm prompt engineering.
01:05:31 - Ví dụ, bạn có thể thay đổi description để AI không query một cơ sở dữ liệu cụ thể nào đó, mà có thể là một bảng khác, hoặc một nguồn khác. Điều này thực sự là một lợi thế khi bạn cần tích hợp nhiều hệ thống hoặc xử lý nhiều loại dữ liệu khác nhau. Anh Hơn có nói rằng mình có nhiều trò hay ho, nhưng thực ra đó chỉ là các helper tools để hỗ trợ việc xây dựng ứng dụng AI. Trên server local, mình có thể chạy một số tool để dịch văn bản hoặc tạo các truy vấn SQL.
01:07:18 - Ví dụ, nếu mình cần một chatbot để trả lời các truy vấn SQL, miễn là mình có một database và một sơ đồ schema rõ ràng, mình có thể yêu cầu AI query các ghi chú mới nhất trong Memo. Một số truy vấn phức tạp có thể rất khó làm bằng tay, nhưng với việc sử dụng AI, mình có thể nhập đủ context và để AI xử lý phần còn lại.
01:09:55 - Những gì chúng ta làm với Dify chủ yếu là sắp xếp các assistant hoặc code các chức năng đơn giản như agent workflow. Đây chủ yếu là việc gọi function, nhưng khi làm việc phức tạp hơn, mô hình AI vẫn có thể xử lý tốt. Ví dụ, mình đã từng xây dựng một mô hình đơn giản để báo cáo, và Dify chỉ là một nền tảng để mình code những chức năng này.
01:11:27 - Nếu bạn có một server LLM hoặc một server API tương thích với OpenAI, bạn có thể tích hợp nó vào hệ thống của mình. Sau đó, bạn có thể thêm các module hoặc các chức năng mới vào nền tảng này. Điều này cho phép bạn tạo ra các ứng dụng AI phức tạp mà không cần phải xây dựng mọi thứ từ đầu.
01:12:16 - Dify có thể được so sánh với một công cụ code AI, nhưng mình không sử dụng code trực tiếp mà chủ yếu sử dụng các chức năng mà nền tảng cung cấp để tạo chatbot hoặc agent. Bạn có thể cấu hình các system prompt và tạo các agent để xử lý các tác vụ cụ thể. Dify giúp bạn tổ chức lại công việc này mà không cần phải rời khỏi nền tảng hoặc dùng các công cụ bên ngoài như OpenAI hoặc Anthropic.
01:13:13 - Nó cũng cho phép bạn thêm các tài liệu như PDF hoặc text để cung cấp kiến thức cho AI. Ví dụ, nếu bạn có kiến thức nội bộ trong công ty, bạn có thể upload và index nó để AI có thể hiểu và trả lời chính xác hơn. Một số chức năng như Google Search hoặc Doo có sẵn, bạn có thể dùng chúng để tìm kiếm trên internet và tương tác với AI.
01:13:13 - Nó cũng cho phép bạn thêm các tài liệu như PDF hoặc text để cung cấp kiến thức cho AI. Ví dụ, nếu bạn có kiến thức nội bộ trong công ty, bạn có thể upload và index nó để AI có thể hiểu và trả lời chính xác hơn. Một số chức năng như Google Search hoặc Doo có sẵn, bạn có thể dùng chúng để tìm kiếm trên internet và tương tác với AI.
01:14:49 - Một ví dụ khác là sử dụng công cụ Tally để tìm kiếm thông tin về Paris Olympics, nó sẽ thực hiện tìm kiếm trên internet và trả về kết quả qua API. Các workflow cũng được xây dựng như một sơ đồ, cho phép bạn hình dung cách thức hoạt động của toàn bộ hệ thống.
01:15:45 - Mình biết việc giới thiệu như vậy có hơi phức tạp, nhưng ý tưởng chính là Dify cho phép bạn tổ chức và sử dụng các công cụ AI một cách hiệu quả. Một số tool có sẵn trong Dify là hot code, không thể tùy chỉnh nhiều, nhưng bạn có thể tự tạo các custom tools theo nhu cầu của mình.
01:16:28 - Mình có tạo ra một vài tool riêng và cũng có các tool sẵn trên nền tảng, tất cả đều là workflow được chuyển đổi thành các yêu cầu API. Bạn có thể tạo các yêu cầu như vậy để tương tác với nền tảng Dify, ví dụ như tạo logo hoặc thực hiện các yêu cầu URL.
01:17:26 - Bạn có thể đặt API key của mình trong các yêu cầu này và sử dụng chúng để thực hiện các tác vụ như tìm kiếm, tạo các tác vụ mới hoặc tương tác với API bên ngoài. Điều này giúp bạn tạo ra các ứng dụng tùy chỉnh mà không cần phải viết code quá nhiều.
01:18:09 - Ví dụ, anh Quang đã triển khai việc tạo pull request (PR) tự động cho Playground dựa trên các agent mà chúng ta đã thiết lập. Chúng ta có thể tương tác với agent qua API để thực hiện các tác vụ phức tạp hơn, và việc này có thể giúp tối ưu hóa quy trình làm việc.
01:19:07 - Mọi người có câu hỏi gì không? Nếu không thì mình sẽ dừng lại ở đây. Mình nghĩ chắc buổi hôm nay vậy là đủ rồi, để dành phần tiếp theo cho các buổi sau.
English Transcript
00:00:00 - Is Anh Thành here? Can everyone hear clearly? Maybe not, let me check again. Hello, hello, can you hear me now? OK, let’s restart this section. This week, we’ll continue the topic from last week, as some people might not have joined right away. I see two teams getting ready to join, who will go first? Are you ready?
00:06:00 - Have we started recording? Please turn on the recording. OK, let’s continue, people can join as we go. Let me see, do we need to restart? No need, just keep going. Last week, we talked about Go 1.23, which has officially been released. Everyone should check out the release notes. Although we previously introduced the interactive notes, reviewing the full release notes reveals some small but noteworthy details.
00:08:17 - When writing WebSocket in Go, people usually use Gorilla. However, there’s another library I’ve tried, which offers better guarantees and avoids some of the issues encountered with Gorilla. This library has been around for quite some time, about 5-6 years. But recently, as the original author stopped maintaining it, a new team has taken over and continued its development. If you haven’t tried it, you might want to take a look.
00:08:58 - There are some common issues when using Go, especially for beginners, that can easily lead to mistakes. For example, I remember when I was an intern, I encountered an issue related to pass by value in Go. Strings in Go are passed by value, so when you modify the value of a string in a slice, it won’t affect the original string.
00:11:04 - In a loop that ranges over a slice of structs, if you want to modify a field value, it won’t work as expected because each iteration of the range creates a copy of the element. So, when you change the value, you’re only modifying the copy, not the original.
00:11:43 - To fix this, you can range over the index of the array instead of the elements. This allows you to access the original element of the array and modify its value correctly.
00:12:33 - Another example is when you have a slice in Go and want to manipulate a specific element. For instance, if you want to add a value to a new slice from the original slice, this could change the original slice if you’re not careful. To avoid this, you can append a value to the end of the new slice to specify that this new slice will use a different array as the backing store.
00:15:00 - When using slices and wanting to keep the original slice intact, you need to be careful because a slice can simply reference the same underlying array, leading to changes in the new slice affecting the original slice.
00:17:06 - Error management in user interface design is critical. Instead of trying to prevent users from making mistakes, we should create a user-friendly interface that allows them to make mistakes and easily correct them. Such a design approach will enhance the user experience.
00:18:07 - One way to achieve this is by providing immediate warnings and feedback when users make a mistake. For example, in Shopify, they changed the product edit page to warn users when there are unsaved changes.
00:22:38 - Sometimes, giving users the ability to revert to previous steps can make them feel more comfortable using the product. For instance, Google Drive allows you to undo actions like deleting a file, making it easy for users to recover what they’ve done wrong.
00:27:43 - Another example is in construction-related products; we need to ensure that each user action triggers a warning or notification if there’s an issue, allowing them to address it promptly.
00:30:39 - In applications like Twitter or Discord, limiting the character count and providing instant feedback when users exceed the limit helps them adjust and avoid mistakes.
00:31:23 - Providing guidance and preventing errors from the start is crucial. Complex applications often use tooltips or onboarding guides to help users understand how to use them correctly.
00:32:54 - When designing products, we need to consider not only the usual use cases but also error scenarios to ensure that the user experience is consistently improved.
00:35:52 - When working on UI, we need to clearly identify error cases and design them to be easily handled. This not only improves the user experience but also minimizes errors during product use.
00:38:20 - When deploying new features, we need to ensure that the necessary data is always stored and can be retrieved when needed. This will help ensure that the system operates reliably and stably.
00:39:05 - For the system to operate efficiently, we need to be careful with access management and data sharing. Features like permissions and password settings should be carefully implemented to ensure that only authorized users can access critical data.
00:41:28 - I think we should just reset everything. I’ve already sent that file to everyone; let me share my screen, Tom, could you check the screen for me? I’ve tried everything, but I can’t log in through the web anymore. If it still doesn’t work, I’ll completely log out of the web. OK, now can everyone see my screen? Great, let’s move on with the agenda.
00:42:43 - Today, I’ll talk about designing a sharing file system. This system might not have many new things, but I hope if anyone has worked on similar projects, they can save time in researching. My presentation will have three parts. The first part is the system overview.
00:43:27 - Here’s the Use Case diagram of the system. It includes basic functions like view, edit, and upload. These functions are quite simple, but the requirement is to optimize queries to make the system as fast as possible. Other functions related to permission and sharing will be covered in this part, focusing on the business logic and how I designed the system. Anh Thành, please move to the next slide.
00:44:17 - The file system part of this system aims for a basic structure similar to the folders and files that people handle daily on their computers. So, I will go through this part quickly. Next is the database design of the system.
00:45:01 - Here is the Entity Relationship (ER) diagram of the system. The highest structure in this system is the workspace, which contains users within a group. Each user, when created, will have their workspace, and that user can invite other members to work together in this workspace. Next are the assets, including files and folders. The basic information of a file will include its name, URL, size, status, and pin status. A special field is the ‘path’ in the asset, which helps make the query process faster. I will explain this further in the next part. The other two tables are related to handling the logic of sharing and permissions.
00:45:38 - This is the logic for file paths. The file path in the system is designed to be highly efficient for querying or handling tasks related to files in the database. For example, if you want to get feedback for a document with ID d123, it will be aggregated from all its parent directories like.
00:45:38 - This is the logic for file paths. The file path in the system is designed to be highly efficient for querying or handling tasks related to files in the database. For example, if you want to get feedback for a document with ID d123, it will be aggregated from all its parent directories like f789 and f123, located in workspace w123. This way, you can easily retrieve all the IDs of the parent directories.
00:46:16 - When you want to list the subdirectories of a specific directory, such as f123, you just need to query the fields containing its file path, including f123 and workspace w123. This makes listing subdirectories very simple and efficient.
00:46:59 - Another case is when you want to move a file. The first step is to list the directories where the file can be moved. These directories must ensure that they are not subdirectories of the file itself. This can be done by removing all directories whose path contains the ID of the file to be moved. For example, if you want to move file F11 from folder 2 to folder 1 with ID f123, you need to remove all directories whose path contains the character f111.
00:47:44 - After moving the file, you just need to update its path from the old path to the new path, and all its subdirectories will also be updated in a single query. As I’ve explained from the beginning, all queries in this system are of complexity O(1) or O(2), helping the system run very quickly and avoiding the need for transactions, which could affect the speed of other queries.
00:48:34 - The next part is about permissions and file sharing. This part has three main features: setting permissions for members within the system, sharing public files with users outside the system, and setting passwords for files. Let me move to the next section.
00:49:17 - Here is a basic diagram showing the process when a user accesses a file and how the system checks the user’s permissions. First, the system will check direct permissions and project permissions. Direct access refers to permissions directly assigned to the user’s email, such as edit rights if the file’s owner has granted that permission to the user. If neither of these permissions is satisfied, the system will check public access.
00:50:03 - Checking public access here means the access rights of users like guests, i.e., users outside the system or not part of the workspace. If these users log into the system, they will have the corresponding public access rights. However, if the file is public but the user has not logged in, they will only have read-only access, without the ability to edit.
00:50:46 - Regarding the data model, the system will have two main tables as I introduced earlier: the member_permission
table, which represents the general access rights of a file, and the sub_permission
table, which represents the specific access rights of a user with a specific email. The member_permission
table has a 1-to-1 relationship with the asset
table, meaning that a file only has one main permission set. It includes fields like inherit
to indicate whether the access rights are inherited from the parent directory, asset_id
to link with the asset table, public_role
to specify public access rights, and has_password
to determine whether the file requires a password.
00:52:16 - When creating a file, the system will generate an access set with the inherit
attribute set to true
, meaning this file will inherit all access rights from its parent directory. If there is any change in access rights, the system will remove the inherit
attribute and create a new record in the sub_permission
table.
00:54:10 - Essentially, the system operates similarly to other cloud storage services like Google Drive or Notion. However, the difference here is how the system handles multi-level permissions efficiently, avoiding unnecessary repeated checks.
00:56:41 - If there are 10 directories, and the 10th directory is a subdirectory of the first directory, and the first directory has a password, how would you check the password for the 10th directory? First, the system will check the access rights of the 10th directory. If these rights are inherited from the parent directory, the system will continue checking the nearest parent directory that does not inherit rights from further up. If the 8th directory does not inherit, the rights of the 10th directory will depend on the 8th directory. If only the first directory does not inherit rights, the system will check directly on the first directory.
00:57:20 - For example, if the 8th directory does not have the inherit
attribute, then the rights of the 10th directory will depend on the 8th directory. But if, in your case, only the first directory does not have inherit
, then we’ll check directly from the first directory. OK, not sure if anyone has anything else to discuss about this issue? Let me check again to see if anything needs to be added. Yes, to perform this, the system will have to query backward, from the subdirectories to the parent directories.
00:58:18 - Anh Hiếu, you probably haven’t looked at this section carefully because you need to understand it well to handle it correctly. Do you have any other questions? Actually, the sharing feature that Đạt just shared is quite similar to Notion or Google Drive, right? Yes, exactly, VOT also developed a similar feature before, but it had to handle two or three layers of permissions, right? Yes, correct, very complex.
00:59:34 - OK, we have a few minutes left to schedule a quick demo with Tom for the workflow on the other side. Over the past two months, the team has built a simple server to test workflows. The goal is to visualize how processes can be optimized from the customer’s side, not just from our side; our team also has some needs to use AI but isn’t sure how to optimize it for speed and efficiency. We could use solutions like Flowwise or Airflow, but they’re a bit complex. That’s why I chose LLM (Large Language Model) for simplicity; I’ll build an agent or a tool to handle it.
01:00:17 - If we succeed in this, when building other applications for customers or our app, implementation will be easier. Now I’ll introduce you to the Memo chatbot that Memo is using. Honestly, Memo also has a chatbot connected to Diffy; since I’m lazy to code, I connected it to handle things more quickly. Let’s see, the network has been lagging recently.
01:00:59 - OK, try the command “get me the latest notes on Go.” This chatbot will follow a workflow similar to what Eming built for Tôn. It will use an AI model to create SQL query commands, then execute the queries on the database. I have a pre-setup file to call the query from there and then output the results in markdown format. The model isn’t too complex, but I have to use some techniques like prompt engineering, which is the technique of providing enough context and information to the AI so that it responds exactly as I intend.
01:01:49 - When designing a workflow like this, the goal is to make it run smoothly. There are two ways: either you code the actions behind the functions, or you do prompt engineering to guide the AI in creating the correct SQL queries. This trick is often used when prototyping or quickly developing for customers. I set up the server for the AI model to infer how to query SQL, then return the results to the chatbot.
01:03:20 - The real “magic” of it lies in the description of the function calls, which contain information about the tables in Memo. Without this, when I run the command, the AI wouldn’t know which table to query, leading to errors. That’s why prompt engineering is so important in ensuring that the AI gets the correct information and produces the right results.
01:04:47 - If I try to rerun the Memo chatbot without the correct description, it will create an error because it doesn’t know which table to query. In this case, the trick I often use for prototyping or working quickly with customers becomes very useful. What you can do with AI is almost limitless as long as you understand how to do prompt engineering.
01:05:31 - For example, you can change the description so that the AI doesn’t query a specific database but another table or source. This is really advantageous when you need to integrate multiple systems or handle different types of data. Anh Hơn mentioned that we have many neat tricks, but in reality, these are just helper tools to support building AI applications. On a local server, I can run some tools to translate text or create SQL queries.
01:07:18 - For example, if I need a chatbot to respond to SQL queries, as long as I have a database and a clear schema, I can have the AI query the latest notes in Memo. Some complex queries can be very difficult to do manually, but with AI, I can input enough context and let the AI handle the rest.
01:09:55 - What we do with Dify is mainly to organize assistants or code simple functions like agent workflows. This mainly involves calling functions, but when doing more complex work, the AI model can still handle it well. For instance, I’ve built a simple reporting model before, and Dify is just a platform for me to code these functions.
01:11:27 - If you have an LLM server or an API server compatible with OpenAI, you can integrate it into your system. Then, you can add modules or new features to this platform. This allows you to create complex AI applications without having to build everything from scratch.
01:12:16 - Dify can be compared to an AI coding tool, but I don’t use direct code; instead, I mainly use the functions provided by the platform to create chatbots or agents. You can configure system prompts and create agents to handle specific tasks. Dify helps you organize this work without having to leave the platform or use external tools like OpenAI or Anthropic.
01:13:13 - It also allows you to add documents like PDFs or text to provide knowledge to the AI. For example, if you have internal knowledge within the company, you can upload and index it so that the AI can understand and respond more accurately. Some functions like Google Search or Doo are available, which you can use to search the internet and interact with the AI.
01:14:49 - Another example is using the Tally tool to search for information about the Paris Olympics; it will search the internet and return results via API. The workflows are also built like a diagram, allowing you to visualize how the entire system operates.
01:15:45 - I know this introduction might sound a bit complex, but the main idea is that Dify allows you to organize and use AI tools effectively. Some tools are pre-coded in Dify and can’t be customized much, but you can create custom tools according to your needs.
01:16:28 - I’ve created a few custom tools, and there are also tools available on the platform; all are workflows converted into API requests. You can create such requests to interact with the Dify platform, like generating a logo or making URL requests.
01:17:26 - You can place your API key in these requests and use them to perform tasks like searching, creating new tasks, or interacting with external APIs. This helps you create custom applications without having to write too much code.
01:18:09 - For example, Anh Quang has implemented automatic pull request (PR) creation for Playground based on the agents we’ve set up. We can interact with the agent via API to perform more complex tasks, and this can help optimize the workflow.
01:19:07 - Does anyone have any questions? If not, I’ll stop here. I think today’s session is enough, and we’ll save the rest for the next sessions.