Viết mã RAG đơn giản từ đầu
Mô tả ngắn gọn
- 17 min read
Xây dựng Hệ thống RAG Đơn giản từ Đầu
Trong bài viết này, chúng ta sẽ tìm hiểu về Retrieval-Augmented Generation (RAG) và xây dựng một hệ thống RAG đơn giản từ đầu bằng Python và Ollama. Dự án này sẽ giúp bạn hiểu rõ các thành phần chính của hệ thống RAG và cách triển khai chúng bằng các khái niệm lập trình cơ bản.
![]()
RAG là gì?
Gần đây, Retrieval-Augmented Generation (RAG) đã nổi lên như một mô hình mạnh mẽ trong lĩnh vực AI và Mô hình Ngôn ngữ Lớn (LLM). RAG kết hợp khả năng truy xuất thông tin với khả năng tạo văn bản để nâng cao hiệu suất của các mô hình ngôn ngữ bằng cách tích hợp các nguồn kiến thức bên ngoài. Cách tiếp cận này đã cho thấy kết quả đầy hứa hẹn trong nhiều ứng dụng khác nhau, chẳng hạn như trả lời câu hỏi, hệ thống hội thoại và tạo nội dung.
Để bắt đầu, hãy xem xét một hệ thống chatbot đơn giản không có RAG:
Mặc dù chatbot có thể trả lời các câu hỏi thông thường dựa trên tập dữ liệu huấn luyện của nó, nhưng nó có thể thiếu kiến thức cập nhật nhất hoặc kiến thức chuyên ngành.
Một ví dụ thực tế là hỏi ChatGPT “Tên mẹ tôi là gì?”. ChatGPT không thể trả lời câu hỏi này vì nó không có quyền truy cập vào kiến thức bên ngoài, chẳng hạn như thông tin về các thành viên gia đình bạn.

Để giải quyết hạn chế này, chúng ta cần cung cấp kiến thức bên ngoài cho mô hình (trong ví dụ này, một danh sách tên thành viên gia đình):
Một hệ thống RAG bao gồm hai thành phần chính:
- Một mô hình truy xuất để lấy thông tin liên quan từ một nguồn kiến thức bên ngoài, có thể là cơ sở dữ liệu, công cụ tìm kiếm hoặc bất kỳ kho thông tin nào khác.
- Một mô hình ngôn ngữ để tạo phản hồi dựa trên kiến thức đã truy xuất.
Có nhiều cách để triển khai RAG, bao gồm Graph RAG, Hybrid RAG và Hierarchical RAG, mà chúng ta sẽ thảo luận vào cuối bài viết này.
RAG Đơn giản
Chúng ta hãy tạo một hệ thống RAG đơn giản để truy xuất thông tin từ một tập dữ liệu được định trước và tạo phản hồi dựa trên kiến thức đã truy xuất. Hệ thống sẽ bao gồm các thành phần sau:
- Mô hình nhúng: Một mô hình ngôn ngữ được huấn luyện trước để chuyển đổi văn bản đầu vào thành các vector nhúng - biểu diễn vector nắm bắt ý nghĩa ngữ nghĩa. Các vector này sẽ được sử dụng để tìm kiếm thông tin liên quan trong tập dữ liệu.
- Cơ sở dữ liệu vector: Một hệ thống lưu trữ kiến thức và các vector nhúng tương ứng. Mặc dù có nhiều công nghệ cơ sở dữ liệu vector như Qdrant, Pinecone và pgvector, chúng ta sẽ tự triển khai một cơ sở dữ liệu trong bộ nhớ đơn giản.
- Chatbot: Một mô hình ngôn ngữ tạo phản hồi dựa trên kiến thức đã truy xuất. Đây có thể là bất kỳ mô hình ngôn ngữ nào, chẳng hạn như Llama, Gemma hoặc GPT.
Giai đoạn lập chỉ mục
Giai đoạn lập chỉ mục là bước đầu tiên trong việc tạo hệ thống RAG. Nó bao gồm việc chia nhỏ tập dữ liệu (hoặc tài liệu) thành các chunk nhỏ và tính toán biểu diễn vector cho mỗi chunk có thể được tìm kiếm hiệu quả trong quá trình tạo.
Kích thước của mỗi chunk có thể thay đổi tùy thuộc vào tập dữ liệu và ứng dụng. Ví dụ, trong hệ thống truy xuất tài liệu, mỗi chunk có thể là một đoạn văn hoặc một câu. Trong hệ thống hội thoại, mỗi chunk có thể là một lượt hội thoại.
Sau giai đoạn lập chỉ mục, mỗi chunk cùng với vector nhúng tương ứng sẽ được lưu trữ trong cơ sở dữ liệu vector. Đây là ví dụ về cách cơ sở dữ liệu vector có thể trông như thế nào sau khi lập chỉ mục:
| Chunk | Vector Nhúng |
|---|---|
| Ý và Pháp sản xuất hơn 40% tổng sản lượng rượu vang trên thế giới. | [0.1, 0.04, -0.34, 0.21, …] |
| Taj Mahal ở Ấn Độ được làm hoàn toàn bằng đá cẩm thạch. | [-0.12, 0.03, 0.9, -0.1, …] |
| 90% lượng nước ngọt trên thế giới nằm ở Nam Cực. | [-0.02, 0.6, -0.54, 0.03, …] |
| … | … |
Các vector nhúng sau đó có thể được sử dụng để truy xuất thông tin liên quan dựa trên một truy vấn cho trước. Hãy coi nó như một mệnh đề WHERE của SQL, nhưng thay vì truy vấn dựa trên sự khớp văn bản chính xác, giờ đây chúng ta có thể truy vấn một tập hợp các chunk dựa trên biểu diễn vector của chúng.
Để so sánh sự tương đồng giữa hai vector, chúng ta có thể sử dụng độ tương tự cosine, khoảng cách Euclidean hoặc các thước đo khoảng cách khác. Trong ví dụ này, chúng ta sẽ sử dụng độ tương tự cosine. Đây là công thức tính độ tương tự cosine giữa hai vector A và B:

Đừng lo lắng nếu bạn không quen thuộc với công thức trên, chúng ta sẽ triển khai nó trong phần tiếp theo.
Giai đoạn truy xuất
Trong sơ đồ dưới đây, chúng ta sẽ lấy một ví dụ về Truy vấn đầu vào từ Người dùng. Sau đó, chúng ta tính toán Vector Truy vấn để biểu diễn truy vấn và so sánh nó với các vector trong cơ sở dữ liệu để tìm các chunk liên quan nhất.
Kết quả trả về từ Cơ sở dữ liệu Vector sẽ chứa N chunk liên quan nhất đến truy vấn. Các chunk này sẽ được Chatbot sử dụng để tạo phản hồi.
Hãy mã hóa nó
Trong ví dụ này, chúng ta sẽ viết một triển khai Python đơn giản cho RAG.
Để chạy các mô hình, chúng ta sẽ sử dụng ollama, một công cụ dòng lệnh cho phép bạn chạy các mô hình từ Hugging Face. Với ollama, bạn không cần có quyền truy cập vào máy chủ hoặc dịch vụ đám mây để chạy các mô hình. Bạn có thể chạy các mô hình trực tiếp trên máy tính của mình.
Đối với các mô hình, chúng ta hãy sử dụng các mô hình sau:
- Mô hình nhúng: hf.co/CompendiumLabs/bge-base-en-v1.5-gguf
- Mô hình ngôn ngữ: hf.co/bartowski/Llama-3.2-1B-Instruct-GGUF
Và đối với tập dữ liệu, chúng ta sẽ sử dụng một danh sách đơn giản các sự thật về mèo. Mỗi sự thật sẽ được coi là một chunk trong giai đoạn lập chỉ mục.
Tải xuống ollama và các mô hình
Đầu tiên, hãy bắt đầu bằng việc cài đặt ollama từ trang web của dự án: ollama.com
Sau khi cài đặt, hãy mở một terminal và chạy lệnh sau để tải xuống các mô hình cần thiết:
bash ollama pull hf.co/CompendiumLabs/bge-base-en-v1.5-gguf ollama pull hf.co/bartowski/Llama-3.2-1B-Instruct-GGUF
Nếu bạn thấy kết quả đầu ra sau, điều đó có nghĩa là các mô hình đã được tải xuống thành công:
bash pulling manifest … verifying sha256 digest writing manifest success
Trước khi tiếp tục, để sử dụng ollama trong python, hãy cài đặt gói ollama:
bash pip install ollama
Tải tập dữ liệu
Tiếp theo, hãy tạo một script Python và tải tập dữ liệu vào bộ nhớ. Tập dữ liệu chứa một danh sách các sự thật về mèo sẽ được sử dụng làm chunk trong giai đoạn lập chỉ mục.
Bạn có thể tải tập dữ liệu ví dụ từ ở đây. Đây là ví dụ về mã để tải tập dữ liệu:
python dataset = [] with open(‘cat-facts.txt’, ‘r’) as file: dataset = file.readlines() print(f’Loaded {len(dataset)} entries’)
Triển khai cơ sở dữ liệu vector
Bây giờ, hãy triển khai cơ sở dữ liệu vector.
Chúng ta sẽ sử dụng mô hình nhúng từ ollama để chuyển đổi từng chunk thành vector nhúng, sau đó lưu trữ chunk và vector tương ứng vào một danh sách.
Đây là một ví dụ về hàm để tính toán vector nhúng cho một văn bản cho trước:
python import ollama
EMBEDDING_MODEL = ‘hf.co/CompendiumLabs/bge-base-en-v1.5-gguf’ LANGUAGE_MODEL = ‘hf.co/bartowski/Llama-3.2-1B-Instruct-GGUF’
Mỗi phần tử trong VECTOR_DB sẽ là một tuple (chunk, embedding)
Embedding là một danh sách các số float, ví dụ: [0.1, 0.04, -0.34, 0.21, …]
VECTOR_DB = []
def add_chunk_to_database(chunk): embedding = ollama.embed(model=EMBEDDING_MODEL, input=chunk)[’embeddings’][0] VECTOR_DB.append((chunk, embedding))
Trong ví dụ này, chúng ta sẽ coi mỗi dòng trong tập dữ liệu là một chunk để đơn giản hóa.
python for i, chunk in enumerate(dataset): add_chunk_to_database(chunk) print(f’Added chunk {i+1}/{len(dataset)} to the database’)
Triển khai hàm truy xuất
Tiếp theo, hãy triển khai hàm truy xuất lấy một truy vấn và trả về N chunk liên quan nhất dựa trên độ tương tự cosine. Chúng ta có thể tưởng tượng rằng độ tương tự cosine giữa hai vector càng cao, chúng càng “gần” nhau trong không gian vector. Điều này có nghĩa là chúng có ý nghĩa tương đồng hơn.
Đây là một ví dụ về hàm để tính toán độ tương tự cosine giữa hai vector:
python def cosine_similarity(a, b): dot_product = sum([x * y for x, y in zip(a, b)]) norm_a = sum([x ** 2 for x in a]) ** 0.5 norm_b = sum([x ** 2 for x in b]) ** 0.5 return dot_product / (norm_a * norm_b)
Bây giờ, hãy triển khai hàm truy xuất:
python def retrieve(query, top_n=3): query_embedding = ollama.embed(model=EMBEDDING_MODEL, input=query)[’embeddings’][0]
danh sách tạm thời để lưu trữ các cặp (chunk, similarity)
similarities = [] for chunk, embedding in VECTOR_DB: similarity = cosine_similarity(query_embedding, embedding) similarities.append((chunk, similarity))
sắp xếp theo độ tương tự giảm dần, vì độ tương tự cao hơn có nghĩa là các chunk liên quan hơn
similarities.sort(key=lambda x: x[1], reverse=True)
cuối cùng, trả về N chunk liên quan nhất
return similarities[:top_n]
Giai đoạn tạo
Trong giai đoạn này, chatbot sẽ tạo phản hồi dựa trên kiến thức đã truy xuất từ bước trên. Điều này được thực hiện bằng cách đơn giản thêm các chunk vào prompt sẽ được sử dụng làm đầu vào cho chatbot.
Ví dụ, một prompt có thể được xây dựng như sau:
python input_query = input(‘Ask me a question: ‘) retrieved_knowledge = retrieve(input_query)
print(‘Retrieved knowledge:’) for chunk, similarity in retrieved_knowledge: print(f’ - (similarity: {similarity:.2f}) {chunk}’)
instruction_prompt = f’‘‘You are a helpful chatbot. Use only the following pieces of context to answer the question. Don’t make up any new information: {’\n’.join([f’ - {chunk}’ for chunk, similarity in retrieved_knowledge])} ’’’
Sau đó, chúng ta sử dụng ollama để tạo phản hồi. Trong ví dụ này, chúng ta sẽ sử dụng instruction_prompt làm thông báo hệ thống:
python stream = ollama.chat( model=LANGUAGE_MODEL, messages=[ {‘role’: ‘system’, ‘content’: instruction_prompt}, {‘role’: ‘user’, ‘content’: input_query}, ], stream=True, )
in phản hồi từ chatbot theo thời gian thực
print(‘Chatbot response:’) for chunk in stream: print(chunk[‘message’][‘content’], end=’’, flush=True)
Tổng hợp lại
Bạn có thể tìm thấy mã cuối cùng trong tệp này. Để chạy mã, hãy lưu nó vào một tệp có tên demo.py và chạy lệnh sau:
bash python demo.py
Giờ đây, bạn có thể đặt câu hỏi cho chatbot và nó sẽ tạo phản hồi dựa trên kiến thức đã truy xuất từ tập dữ liệu.
Ask me a question: tell me about cat speed Retrieved chunks: … Chatbot response: According to the given context, cats can travel at approximately 31 mph (49 km) over a short distance. This is their top speed.
Các lĩnh vực cần cải thiện
Cho đến nay, chúng ta đã triển khai một hệ thống RAG đơn giản bằng cách sử dụng một tập dữ liệu nhỏ. Tuy nhiên, vẫn còn nhiều hạn chế:
- Nếu câu hỏi đề cập đến nhiều chủ đề cùng lúc, hệ thống có thể không cung cấp được câu trả lời tốt. Điều này là do hệ thống chỉ truy xuất các chunk dựa trên sự tương đồng của truy vấn với các chunk, mà không xem xét ngữ cảnh của truy vấn.
- Giải pháp có thể là yêu cầu chatbot tự viết truy vấn dựa trên đầu vào của người dùng, sau đó truy xuất kiến thức dựa trên truy vấn đã tạo. Chúng ta cũng có thể sử dụng nhiều truy vấn để truy xuất thêm thông tin liên quan.
- N top kết quả được trả về dựa trên độ tương tự cosine. Điều này có thể không phải lúc nào cũng cho kết quả tốt nhất, đặc biệt khi mỗi chunk chứa nhiều thông tin.
- Để giải quyết vấn đề này, chúng ta có thể sử dụng một mô hình xếp hạng lại để xếp hạng lại các chunk đã truy xuất dựa trên mức độ liên quan của chúng với truy vấn.
- Cơ sở dữ liệu được lưu trữ trong bộ nhớ, có thể không mở rộng được cho các tập dữ liệu lớn. Chúng ta có thể sử dụng cơ sở dữ liệu vector hiệu quả hơn như Qdrant, Pinecone, pgvector.
- Hiện tại chúng ta coi mỗi câu là một chunk. Đối với các tác vụ phức tạp hơn, chúng ta có thể cần sử dụng các kỹ thuật tinh vi hơn để chia nhỏ tập dữ liệu thành các chunk nhỏ hơn. Chúng ta cũng có thể tiền xử lý từng chunk trước khi thêm chúng vào cơ sở dữ liệu.
- Mô hình ngôn ngữ được sử dụng trong ví dụ này là một mô hình đơn giản chỉ có 1 tỷ tham số. Đối với các tác vụ phức tạp hơn, chúng ta có thể cần sử dụng mô hình ngôn ngữ lớn hơn.
Các loại RAG khác
Trong thực tế, có nhiều cách để triển khai các hệ thống RAG. Dưới đây là một số loại hệ thống RAG phổ biến:
- Graph RAG: Trong loại RAG này, nguồn kiến thức được biểu diễn dưới dạng một đồ thị, trong đó các nút là các thực thể và các cạnh là mối quan hệ giữa các thực thể. Mô hình ngôn ngữ có thể duyệt qua đồ thị để truy xuất thông tin liên quan. Có rất nhiều nghiên cứu tích cực về loại RAG này. Đây là tập hợp các bài báo về Graph RAG.
- Hybrid RAG: một loại RAG kết hợp Knowledge Graphs (KG) và kỹ thuật cơ sở dữ liệu vector để cải thiện hệ thống trả lời câu hỏi. Để biết thêm, bạn có thể đọc bài báo ở đây.
- Modular RAG: một loại RAG vượt ra ngoài quy trình “truy xuất rồi tạo” cơ bản, sử dụng các cơ chế định tuyến, lập lịch và hợp nhất để tạo ra một khuôn khổ linh hoạt và có thể cấu hình lại. Thiết kế mô-đun này cho phép các mẫu RAG khác nhau (tuyến tính, có điều kiện, phân nhánh và lặp lại), cho phép các ứng dụng chuyên sâu kiến thức phức tạp và thích ứng hơn. Để biết thêm, bạn có thể đọc bài báo ở đây.
Đối với các loại RAG khác, bạn có thể tham khảo bài đăng này của Rajeev Sharma.
Kết luận
RAG đại diện cho một bước tiến quan trọng trong việc làm cho các mô hình ngôn ngữ có kiến thức và chính xác hơn. Bằng cách triển khai một hệ thống RAG đơn giản từ đầu, chúng ta đã khám phá các nguyên tắc cơ bản của nhúng, truy xuất và tạo. Mặc dù việc triển khai của chúng ta rất cơ bản, nhưng nó minh họa các nguyên tắc cốt lõi thúc đẩy các hệ thống RAG phức tạp hơn được sử dụng trong môi trường sản xuất.
Tiềm năng mở rộng và cải thiện hệ thống RAG là rất lớn, từ việc triển khai các cơ sở dữ liệu vector hiệu quả hơn đến khám phá các kiến trúc nâng cao như Graph RAG và Hybrid RAG. Khi lĩnh vực này tiếp tục phát triển, RAG vẫn là một kỹ thuật quan trọng để nâng cao hệ thống AI bằng kiến thức bên ngoài trong khi vẫn duy trì khả năng tạo của chúng.
Tài liệu tham khảo
- https://arxiv.org/abs/2005.11401
- https://aws.amazon.com/what-is/retrieval-augmented-generation/
- https://github.com/varunvasudeva1/llm-server-docs
- https://github.com/ollama/ollama/blob/main/docs
- https://github.com/ollama/ollama-python
- https://www.pinecone.io/learn/series/rag/rerankers/
- https://arxiv.org/html/2407.21059v1
- https://newsletter.armand.so/p/comprehensive-guide-rag-implementations
Cộng đồng
Cảm ơn bạn đã làm bài viết này, nó thực sự dễ theo dõi và thân thiện với người mới bắt đầu.
Trong mã này, input= chunk nên là input= query:
python def retrieve(query, top_n=3): query_embedding = ollama.embed(model=EMBEDDING_MODEL, input=chunk)[’embeddings’][0]
Tuyệt vời & thân thiện với người mới bắt đầu, cảm ơn bạn!
RAG có tương lai tốt đẹp không, tôi nghĩ nó giống như một bong bóng – theo quan điểm lý thuyết
Khi độ dài ngữ cảnh của mô hình dài hơn, RAG sẽ ít quan trọng hơn nhưng hiện tại nó khá hữu ích, đặc biệt đối với các thiết bị không thể duy trì độ dài ngữ cảnh như vậy.
wow thật hữu ích
Tôi đang cố gắng thực hiện ví dụ này và tôi thấy
verifying sha256 digest
writing manifest
success
nhưng dưới .ollama\models\manifests\hf.co\bartowski\Llama-3.2-1B-Instruct-GGUF, chỉ có tệp mới nhất có kích thước 1k.
với ollama list, nó hiển thị
NAME ID SIZE MODIFIED
hf.co/bartowski/Llama-3.2-1B-Instruct-GGUF:latest 042cf58aa32f 807 MB 52 phút trước
hf.co/CompendiumLabs/bge-base-en-v1.5-gguf:latest 98c4eb4a3287 68 MB 58 phút trước
chúng ở đâu? nếu tôi không chỉ định đường dẫn đến mô hình và sử dụng ví dụ bên dưới, tôi gặp lỗi hết thời gian chờ 504 khi gọi “embedding = ollama.embed(model=EMBEDDING_MODEL, input=chunk)[’embeddings’][0]”
EMBEDDING_MODEL = ‘hf.co/CompendiumLabs/bge-base-en-v1.5-gguf’ LANGUAGE_MODEL = ‘hf.co/bartowski/Llama-3.2-1B-Instruct-GGUF’
cần chỉ định mã hóa như sau: with open('cat-facts.txt', 'r', encoding="utf8")
tôi đang gặp lỗi sau khi chạy mã trên. Có ý tưởng nào không? Traceback (most recent call last): File “c:\prakash\AI - CCA Practise\Agentic_Financial_Advisor\main.py”, line 8, in dataset = file.readlines() ^^^^^^^^^^^^^^^^ File “C:\Users\prakshar\AppData\Local\Programs\Python\Python312\Lib\encodings\cp1252.py”, line 23, in decode return codecs.charmap_decode(input,self.errors,decoding_table)[0] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UnicodeDecodeError: ‘charmap’ codec can’t decode byte 0x9d in position 405: character maps to
bài viết hay & thân thiện với người mới bắt đầu cảm ơn!
Tôi vừa tình cờ đọc bài viết hướng dẫn này sau khi tìm kiếm nhiều hướng dẫn để học RAG. Bài này có vẻ hứa hẹn. Tôi đang cố gắng bắt đầu nhỏ và phát triển từng chút một với mục tiêu xây dựng hệ thống RAG cấp doanh nghiệp cho các công ty lớn. Nếu bạn cùng hướng đi, vui lòng tham gia cùng tôi, chúng ta có thể học hỏi và xây dựng cùng nhau. #Team_work_dream_work.
bạn có nghĩ RAG có tương lai tốt đẹp không, tôi nghĩ nó giống như một bong bóng – theo quan điểm lý thuyết
Bạn có thể đăng ký ‘Cuộc gọi Khám phá’ bằng cổng Topmate của tôi để thảo luận về dự án của bạn và tìm kiếm hướng dẫn. https://topmate.io/sanjan_tp_gupta
bạn đang bỏ qua biến (rf). Mô hình không biết thông tin nào để sử dụng cho mục đích gì. Đây là nguồn gốc của nhiều vấn đề. Nếu điều này được giải quyết, sẽ có nhiều khả năng hơn. Và “nhiều hơn” có nghĩa là gì? Điều đó có nghĩa là cấp quyền cho mô hình thêm dữ liệu vào các nguồn RAG của riêng nó - bao gồm cả RF.
bài viết hay & thân thiện với người mới bắt đầu cảm ơn!
Chào Aboukary, bạn có thể đăng ký ‘Cuộc gọi Khám phá’ bằng cổng Topmate của tôi để thảo luận về dự án của bạn và tìm kiếm hướng dẫn. https://topmate.io/sanjan_tp_gupta
Tôi đang cố gắng thực hiện ví dụ này và tôi thấy
verifying sha256 digest
writing manifest
success
nhưng dưới .ollama\models\manifests\hf.co\bartowski\Llama-3.2-1B-Instruct-GGUF, chỉ có tệp mới nhất có kích thước 1k.
với ollama list, nó hiển thị
NAME ID SIZE MODIFIED
hf.co/bartowski/Llama-3.2-1B-Instruct-GGUF:latest 042cf58aa32f 807 MB 52 phút trước
hf.co/CompendiumLabs/bge-base-en-v1.5-gguf:latest 98c4eb4a3287 68 MB 58 phút trước
chúng ở đâu? nếu tôi không chỉ định đường dẫn đến mô hình và sử dụng ví dụ bên dưới, tôi gặp lỗi hết thời gian chờ 504 khi gọi “embedding = ollama.embed(model=EMBEDDING_MODEL, input=chunk)[’embeddings’][0]”
EMBEDDING_MODEL = ‘hf.co/CompendiumLabs/bge-base-en-v1.5-gguf’ LANGUAGE_MODEL = ‘hf.co/bartowski/Llama-3.2-1B-Instruct-GGUF’
cần chỉ định mã hóa như sau: with open('cat-facts.txt', 'r', encoding="utf8")
bài viết hay & thân thiện với người mới bắt đầu cảm ơn!
Hi, tôi nghĩ điều này nên được thêm/thay đổi trong bài viết, bài báo HybridRAG đã được xuất bản (https://dl.acm.org/doi/10.1145/3677052.3698671) @ngxson
Link bài viết gốc
- Tags:
- Ai
- Oct 29, 2024
- Huggingface.co